Compare commits

...

3 Commits

Author SHA1 Message Date
Siyuan Miao b707596f89 move event handling to native 2025-06-26 19:43:21 +00:00
Siyuan Miao b3243b2742 chore: upgrade deps 2025-06-25 19:42:54 +02:00
Siyuan Miao 3fa3af2d5b refactor functions 2025-06-24 07:10:54 +00:00
32 changed files with 58493 additions and 1415 deletions

View File

@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"os"
"strconv"
"sync"
"github.com/jetkvm/kvm/internal/logging"
@ -102,6 +103,25 @@ type Config struct {
DefaultLogLevel string `json:"default_log_level"`
}
func (c *Config) GetDisplayRotation() uint16 {
rotationInt, err := strconv.ParseUint(c.DisplayRotation, 10, 16)
if err != nil {
logger.Warn().Err(err).Msg("invalid display rotation, using default")
return 270
}
return uint16(rotationInt)
}
func (c *Config) SetDisplayRotation(rotation string) error {
_, err := strconv.ParseUint(rotation, 10, 16)
if err != nil {
logger.Warn().Err(err).Msg("invalid display rotation, using default")
return err
}
c.DisplayRotation = rotation
return nil
}
const configPath = "/userdata/kvm_config.json"
var defaultConfig = &Config{

View File

@ -35,24 +35,24 @@ func updateDisplay() {
nativeInstance.UpdateLabelIfChanged("home_info_ipv4_addr", networkState.IPv4String())
nativeInstance.UpdateLabelAndChangeVisibility("home_info_ipv6_addr", networkState.IPv6String())
nativeInstance.ObjHide("menu_btn_network")
nativeInstance.ObjHide("menu_btn_access")
nativeInstance.UIObjHide("menu_btn_network")
nativeInstance.UIObjHide("menu_btn_access")
nativeInstance.UpdateLabelIfChanged("home_info_mac_addr", networkState.MACString())
if usbState == "configured" {
nativeInstance.UpdateLabelIfChanged("usb_status_label", "Connected")
_, _ = nativeInstance.ObjSetState("usb_status", "LV_STATE_DEFAULT")
_, _ = nativeInstance.UIObjSetState("usb_status", "LV_STATE_DEFAULT")
} else {
nativeInstance.UpdateLabelIfChanged("usb_status_label", "Disconnected")
_, _ = nativeInstance.ObjSetState("usb_status", "LV_STATE_DISABLED")
_, _ = nativeInstance.UIObjSetState("usb_status", "LV_STATE_DISABLED")
}
if lastVideoState.Ready {
nativeInstance.UpdateLabelIfChanged("hdmi_status_label", "Connected")
_, _ = nativeInstance.ObjSetState("hdmi_status", "LV_STATE_DEFAULT")
_, _ = nativeInstance.UIObjSetState("hdmi_status", "LV_STATE_DEFAULT")
} else {
nativeInstance.UpdateLabelIfChanged("hdmi_status_label", "Disconnected")
_, _ = nativeInstance.ObjSetState("hdmi_status", "LV_STATE_DISABLED")
_, _ = nativeInstance.UIObjSetState("hdmi_status", "LV_STATE_DISABLED")
}
nativeInstance.UpdateLabelIfChanged("cloud_status_label", fmt.Sprintf("%d active", actionSessions))
@ -63,20 +63,20 @@ func updateDisplay() {
}
if cloudConnectionState == CloudConnectionStateNotConfigured {
_, _ = nativeInstance.ObjHide("cloud_status_icon")
_, _ = nativeInstance.UIObjHide("cloud_status_icon")
} else {
_, _ = nativeInstance.ObjShow("cloud_status_icon")
_, _ = nativeInstance.UIObjShow("cloud_status_icon")
}
switch cloudConnectionState {
case CloudConnectionStateDisconnected:
_, _ = nativeInstance.ImgSetSrc("cloud_status_icon", "cloud_disconnected")
_, _ = nativeInstance.UIObjSetImageSrc("cloud_status_icon", "cloud_disconnected")
stopCloudBlink()
case CloudConnectionStateConnecting:
_, _ = nativeInstance.ImgSetSrc("cloud_status_icon", "cloud")
_, _ = nativeInstance.UIObjSetImageSrc("cloud_status_icon", "cloud")
startCloudBlink()
case CloudConnectionStateConnected:
_, _ = nativeInstance.ImgSetSrc("cloud_status_icon", "cloud")
_, _ = nativeInstance.UIObjSetImageSrc("cloud_status_icon", "cloud")
stopCloudBlink()
}
}
@ -100,9 +100,9 @@ func startCloudBlink() {
if cloudConnectionState != CloudConnectionStateConnecting {
continue
}
_, _ = nativeInstance.ObjFadeOut("cloud_status_icon", 1000)
_, _ = nativeInstance.UIObjFadeOut("cloud_status_icon", 1000)
time.Sleep(1000 * time.Millisecond)
_, _ = nativeInstance.ObjFadeIn("cloud_status_icon", 1000)
_, _ = nativeInstance.UIObjFadeIn("cloud_status_icon", 1000)
time.Sleep(1000 * time.Millisecond)
}
}()
@ -153,13 +153,6 @@ func waitCtrlAndRequestDisplayUpdate(shouldWakeDisplay bool) {
func updateStaticContents() {
//contents that never change
nativeInstance.UpdateLabelIfChanged("home_info_mac_addr", networkState.MACString())
systemVersion, appVersion, err := GetLocalVersion()
if err == nil {
nativeInstance.UpdateLabelIfChanged("boot_screen_version", systemVersion.String())
nativeInstance.UpdateLabelIfChanged("boot_screen_app_version", appVersion.String())
nativeInstance.UpdateLabelAndChangeVisibility("system_version", systemVersion.String())
nativeInstance.UpdateLabelAndChangeVisibility("app_version", appVersion.String())
}
// get cpu info
cpuInfo, err := os.ReadFile("/proc/cpuinfo")
@ -266,34 +259,6 @@ func wakeDisplay(force bool) {
backlightState = 0
}
// watchTsEvents monitors the touchscreen for events and simply calls wakeDisplay() to ensure the
// touchscreen interface still works even with LCD dimming/off.
// TODO: This is quite a hack, really we should be getting an event from jetkvm_native, or the whole display backlight
// control should be hoisted up to jetkvm_native.
func watchTsEvents() {
ts, err := os.OpenFile(touchscreenDevice, os.O_RDONLY, 0666)
if err != nil {
displayLogger.Warn().Err(err).Msg("failed to open touchscreen device")
return
}
defer ts.Close()
// This buffer is set to 24 bytes as that's the normal size of events on /dev/input
// Reference: https://www.kernel.org/doc/Documentation/input/input.txt
// This could potentially be set higher, to require multiple events to wake the display.
buf := make([]byte, 24)
for {
_, err := ts.Read(buf)
if err != nil {
displayLogger.Warn().Err(err).Msg("failed to read from touchscreen device")
return
}
wakeDisplay(false)
}
}
// startBacklightTickers starts the two tickers for dimming and switching off the display
// if they're not already set. This is done separately to the init routine as the "never dim"
// option has the value set to zero, but time.NewTicker only accept positive values.
@ -347,7 +312,6 @@ func initDisplay() {
go func() {
displayLogger.Info().Msg("setting initial display contents")
time.Sleep(500 * time.Millisecond)
_, _ = nativeInstance.DispSetRotation(config.DisplayRotation)
updateStaticContents()
displayInited = true
displayLogger.Info().Msg("display inited")
@ -355,6 +319,4 @@ func initDisplay() {
wakeDisplay(true)
requestDisplayUpdate(true)
}()
go watchTsEvents()
}

54
go.mod
View File

@ -8,34 +8,34 @@ require (
github.com/Masterminds/semver/v3 v3.3.1
github.com/beevik/ntp v1.4.3
github.com/coder/websocket v1.8.13
github.com/coreos/go-oidc/v3 v3.11.0
github.com/creack/pty v1.1.23
github.com/coreos/go-oidc/v3 v3.14.1
github.com/creack/pty v1.1.24
github.com/fsnotify/fsnotify v1.9.0
github.com/gin-contrib/logger v1.2.5
github.com/gin-gonic/gin v1.10.0
github.com/gin-contrib/logger v1.2.6
github.com/gin-gonic/gin v1.10.1
github.com/google/gopacket v1.1.19
github.com/google/uuid v1.6.0
github.com/guregu/null/v6 v6.0.0
github.com/gwatts/rootcerts v0.0.0-20240401182218-3ab9db955caf
github.com/hanwen/go-fuse/v2 v2.5.1
github.com/gwatts/rootcerts v0.0.0-20250601184604-370a9a75f341
github.com/hanwen/go-fuse/v2 v2.8.0
github.com/hashicorp/go-getter/v2 v2.2.3
github.com/jellydator/ttlcache/v3 v3.3.0
github.com/pion/logging v0.2.3
github.com/pion/logging v0.2.4
github.com/pion/mdns/v2 v2.0.7
github.com/pion/webrtc/v4 v4.0.16
github.com/pion/webrtc/v4 v4.1.2
github.com/pojntfx/go-nbd v0.3.2
github.com/prometheus/client_golang v1.22.0
github.com/prometheus/common v0.62.0
github.com/prometheus/common v0.65.0
github.com/prometheus/procfs v0.16.1
github.com/psanford/httpreadat v0.1.0
github.com/rs/zerolog v1.34.0
github.com/sourcegraph/tf-dag v0.2.2-0.20250131204052-3e8ff1477b4f
github.com/stretchr/testify v1.10.0
github.com/vishvananda/netlink v1.3.0
go.bug.st/serial v1.6.2
golang.org/x/crypto v0.37.0
golang.org/x/net v0.39.0
golang.org/x/sys v0.32.0
github.com/vishvananda/netlink v1.3.1
go.bug.st/serial v1.6.4
golang.org/x/crypto v0.39.0
golang.org/x/net v0.41.0
golang.org/x/sys v0.33.0
)
replace github.com/pojntfx/go-nbd v0.3.2 => github.com/chemhack/go-nbd v0.0.0-20241006125820-59e45f5b1e7b
@ -43,11 +43,11 @@ replace github.com/pojntfx/go-nbd v0.3.2 => github.com/chemhack/go-nbd v0.0.0-20
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/bytedance/sonic v1.13.2 // indirect
github.com/bytedance/sonic v1.13.3 // indirect
github.com/bytedance/sonic/loader v0.2.4 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/creack/goselect v0.1.2 // indirect
github.com/creack/goselect v0.1.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
@ -63,7 +63,7 @@ require (
github.com/hashicorp/go-version v1.1.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/klauspost/cpuid/v2 v2.2.11 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
@ -80,25 +80,25 @@ require (
github.com/pion/interceptor v0.1.40 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/rtcp v1.2.15 // indirect
github.com/pion/rtp v1.8.18 // indirect
github.com/pion/rtp v1.8.19 // indirect
github.com/pion/sctp v1.8.39 // indirect
github.com/pion/sdp/v3 v3.0.13 // indirect
github.com/pion/srtp/v3 v3.0.5 // indirect
github.com/pion/sdp/v3 v3.0.14 // indirect
github.com/pion/srtp/v3 v3.0.6 // indirect
github.com/pion/stun/v3 v3.0.0 // indirect
github.com/pion/transport/v3 v3.0.7 // indirect
github.com/pion/turn/v4 v4.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/ugorji/go/codec v1.3.0 // indirect
github.com/ulikunitz/xz v0.5.8 // indirect
github.com/vishvananda/netns v0.0.4 // indirect
github.com/vishvananda/netns v0.0.5 // indirect
github.com/wlynxg/anet v0.0.5 // indirect
golang.org/x/arch v0.17.0 // indirect
golang.org/x/oauth2 v0.24.0 // indirect
golang.org/x/sync v0.13.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/arch v0.18.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sync v0.15.0 // indirect
golang.org/x/text v0.26.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

115
go.sum
View File

@ -6,8 +6,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0=
github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
@ -20,13 +20,13 @@ github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJ
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE=
github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI=
github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0=
github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk=
github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0=
github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/creack/goselect v0.1.3 h1:MaGNMclRo7P2Jl21hBpR1Cn33ITSbKP6E49RtfblLKc=
github.com/creack/goselect v0.1.3/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -34,12 +34,12 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
github.com/gin-contrib/logger v1.2.5 h1:qVQI4omayQecuN4zX9ZZnsOq7w9J/ZLds3J/FMn8ypM=
github.com/gin-contrib/logger v1.2.5/go.mod h1:/bj+vNMuA2xOEQ1aRHoJ1m9+uyaaXIAxQTvM2llsc6I=
github.com/gin-contrib/logger v1.2.6 h1:EPolruKUTzNXMVBD9LuAFQmRjTs7AH7yKGuXgYqrKWc=
github.com/gin-contrib/logger v1.2.6/go.mod h1:7niPrd7F0Nscw/zvgz8RiGJxSdbKM2yfQNy8xCHcm64=
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
@ -62,10 +62,10 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/guregu/null/v6 v6.0.0 h1:N14VRS+4di81i1PXRiprbQJ9EM9gqBa0+KVMeS/QSjQ=
github.com/guregu/null/v6 v6.0.0/go.mod h1:hrMIhIfrOZeLPZhROSn149tpw2gHkidAqxoXNyeX3iQ=
github.com/gwatts/rootcerts v0.0.0-20240401182218-3ab9db955caf h1:JO6ISZIvEUitto5zjQ3/VEnDM5rPbqIFuOhS0U0ByeA=
github.com/gwatts/rootcerts v0.0.0-20240401182218-3ab9db955caf/go.mod h1:5Kt9XkWvkGi2OHOq0QsGxebHmhCcqJ8KCbNg/a6+n+g=
github.com/hanwen/go-fuse/v2 v2.5.1 h1:OQBE8zVemSocRxA4OaFJbjJ5hlpCmIWbGr7r0M4uoQQ=
github.com/hanwen/go-fuse/v2 v2.5.1/go.mod h1:xKwi1cF7nXAOBCXujD5ie0ZKsxc8GGSA1rlMJc+8IJs=
github.com/gwatts/rootcerts v0.0.0-20250601184604-370a9a75f341 h1:zPrkLSKi7kKJoNJH4uUmsQ86+0/QqpwEns0NyNLwKv0=
github.com/gwatts/rootcerts v0.0.0-20250601184604-370a9a75f341/go.mod h1:5Kt9XkWvkGi2OHOq0QsGxebHmhCcqJ8KCbNg/a6+n+g=
github.com/hanwen/go-fuse/v2 v2.8.0 h1:wV8rG7rmCz8XHSOwBZhG5YcVqcYjkzivjmbaMafPlAs=
github.com/hanwen/go-fuse/v2 v2.8.0/go.mod h1:yE6D2PqWwm3CbYRxFXV9xUd8Md5d6NG0WBs5spCswmI=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
@ -85,8 +85,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU=
github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@ -95,7 +95,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
@ -111,8 +110,8 @@ github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnG
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -132,39 +131,39 @@ github.com/pion/ice/v4 v4.0.10 h1:P59w1iauC/wPk9PdY8Vjl4fOFL5B+USq1+xbDcN6gT4=
github.com/pion/ice/v4 v4.0.10/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw=
github.com/pion/interceptor v0.1.40 h1:e0BjnPcGpr2CFQgKhrQisBU7V3GXK6wrfYrGYaU6Jq4=
github.com/pion/interceptor v0.1.40/go.mod h1:Z6kqH7M/FYirg3frjGJ21VLSRJGBXB/KqaTIrdqnOic=
github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI=
github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90=
github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8=
github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so=
github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM=
github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA=
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo=
github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0=
github.com/pion/rtp v1.8.18 h1:yEAb4+4a8nkPCecWzQB6V/uEU18X1lQCGAQCjP+pyvU=
github.com/pion/rtp v1.8.18/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk=
github.com/pion/rtp v1.8.19 h1:jhdO/3XhL/aKm/wARFVmvTfq0lC/CvN1xwYKmduly3c=
github.com/pion/rtp v1.8.19/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk=
github.com/pion/sctp v1.8.39 h1:PJma40vRHa3UTO3C4MyeJDQ+KIobVYRZQZ0Nt7SjQnE=
github.com/pion/sctp v1.8.39/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE=
github.com/pion/sdp/v3 v3.0.13 h1:uN3SS2b+QDZnWXgdr69SM8KB4EbcnPnPf2Laxhty/l4=
github.com/pion/sdp/v3 v3.0.13/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E=
github.com/pion/srtp/v3 v3.0.5 h1:8XLB6Dt3QXkMkRFpoqC3314BemkpMQK2mZeJc4pUKqo=
github.com/pion/srtp/v3 v3.0.5/go.mod h1:r1G7y5r1scZRLe2QJI/is+/O83W2d+JoEsuIexpw+uM=
github.com/pion/sdp/v3 v3.0.14 h1:1h7gBr9FhOWH5GjWWY5lcw/U85MtdcibTyt/o6RxRUI=
github.com/pion/sdp/v3 v3.0.14/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E=
github.com/pion/srtp/v3 v3.0.6 h1:E2gyj1f5X10sB/qILUGIkL4C2CqK269Xq167PbGCc/4=
github.com/pion/srtp/v3 v3.0.6/go.mod h1:BxvziG3v/armJHAaJ87euvkhHqWe9I7iiOy50K2QkhY=
github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw=
github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU=
github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
github.com/pion/turn/v4 v4.0.2 h1:ZqgQ3+MjP32ug30xAbD6Mn+/K4Sxi3SdNOTFf+7mpps=
github.com/pion/turn/v4 v4.0.2/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7IToA1zs=
github.com/pion/webrtc/v4 v4.0.16 h1:5f8QMVIbNvJr2mPRGi2QamkPa/LVUB6NWolOCwphKHA=
github.com/pion/webrtc/v4 v4.0.16/go.mod h1:C3uTCPzVafUA0eUzru9f47OgNt3nEO7ZJ6zNY6VSJno=
github.com/pion/webrtc/v4 v4.1.2 h1:mpuUo/EJ1zMNKGE79fAdYNFZBX790KE7kQQpLMjjR54=
github.com/pion/webrtc/v4 v4.1.2/go.mod h1:xsCXiNAmMEjIdFxAYU0MbB3RwRieJsegSB2JZsGN+8U=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/psanford/httpreadat v0.1.0 h1:VleW1HS2zO7/4c7c7zNl33fO6oYACSagjJIyMIwZLUE=
@ -188,51 +187,49 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ=
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk=
github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs=
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0=
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
go.bug.st/serial v1.6.2 h1:kn9LRX3sdm+WxWKufMlIRndwGfPWsH1/9lCWXQCasq8=
go.bug.st/serial v1.6.2/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
go.bug.st/serial v1.6.4 h1:7FmqNPgVp3pu2Jz5PoPtbZ9jJO5gnEnZIvnI1lzve8A=
go.bug.st/serial v1.6.4/go.mod h1:nofMJxTeNVny/m6+KaafC6vJGj3miwQZ6vW4BZUGJPI=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU=
golang.org/x/arch v0.17.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc=
golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=

View File

@ -1,2 +1,3 @@
build
deps
ui_index.c

File diff suppressed because it is too large Load Diff

View File

@ -1,53 +1,43 @@
cmake_minimum_required(VERSION 3.14)
include(FetchContent)
include(ExternalProject)
project(jknative LANGUAGES C CXX)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Specify path to own LVGL config header
set(
LV_CONF_PATH
${CMAKE_CURRENT_SOURCE_DIR}/lv_conf.h
CACHE STRING "" FORCE
)
set(
LV_DRIVERS_PUBLIC_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/lv_drv_conf.h
CACHE STRING "" FORCE
)
# Rockchip SDK paths
set(RK_SDK_BASE "/opt/jetkvm-native-buildkit")
set(RK_MEDIA_OUTPUT "${RK_SDK_BASE}/media/out")
set(RK_MEDIA_INCLUDE_PATH "${RK_MEDIA_OUTPUT}/include")
set(RK_APP_MEDIA_LIBS_PATH "${RK_MEDIA_OUTPUT}/lib")
set(LV_BUILD_EXAMPLES 0 CACHE BOOL "" FORCE)
set(LV_USE_KCONFIG ON CACHE BOOL "" FORCE)
set(LV_BUILD_DEFCONFIG_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lvgl_defconfig CACHE PATH "" FORCE)
# # libgpiod
# ExternalProject_Add(libgpiod-project
# URL https://mirrors.edge.kernel.org/pub/software/libs/libgpiod/libgpiod-2.2.tar.gz
# URL_HASH SHA256=f89c2176250f1a9563265479eb8ad5f22a63f42db6a1f438effc570f0254d2f5
# SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/deps/libgpiod
# BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/deps/libgpiod
# CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env CPPFLAGS=-fPIC ${CMAKE_CURRENT_SOURCE_DIR}/deps/libgpiod/configure --enable-tools=no CC=${CMAKE_C_COMPILER} --host=${CMAKE_HOST_SYSTEM_PROCESSOR}
# BUILD_COMMAND make && make install
# BUILD_BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/deps/libgpiod/lib/libgpiod.a
# )
# Fetch LVGL from GitHub
FetchContent_Declare(
lvgl
GIT_REPOSITORY https://github.com/lvgl/lvgl.git
GIT_TAG v8.3.11
GIT_TAG v9.3.0
GIT_SHALLOW 1
UPDATE_DISCONNECTED 1
PATCH_COMMAND
git apply ${CMAKE_CURRENT_SOURCE_DIR}/lvgl-no-examples.patch
COMMAND rm -rf demos examples
)
)
FetchContent_MakeAvailable(lvgl)
# Fetch LVGL drivers from GitHub
FetchContent_Declare(
lv_drivers
GIT_REPOSITORY https://github.com/lvgl/lv_drivers.git
GIT_TAG v8.3.0
UPDATE_DISCONNECTED 1
)
FetchContent_MakeAvailable(lv_drivers)
# Get source files, excluding CMake generated files
file(GLOB_RECURSE sources CONFIGURE_DEPENDS "*.c" "ui/*.c")
list(FILTER sources EXCLUDE REGEX "CMakeFiles.*CompilerId.*\\.c$")
@ -60,18 +50,21 @@ target_include_directories(jknative PRIVATE
${RK_MEDIA_INCLUDE_PATH}/libdrm
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/ui
${CMAKE_CURRENT_BINARY_DIR}/deps/libgpiod/include
)
# Set library search path
target_link_directories(jknative PRIVATE ${RK_APP_MEDIA_LIBS_PATH})
# target_link_directories(jknative PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/deps/libgpiod/lib)
target_link_libraries(jknative PRIVATE
lvgl::lvgl
lvgl::drivers
pthread
rockit
rockchip_mpp
rga
m
)
# libgpiod
)
install(TARGETS jknative DESTINATION lib)

55746
internal/native/cgo/Makefile Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,53 @@
#!/bin/bash
set -e
C_RST="$(tput sgr0)"
C_ERR="$(tput setaf 1)"
C_OK="$(tput setaf 2)"
C_WARN="$(tput setaf 3)"
C_INFO="$(tput setaf 5)"
msg() { printf '%s%s%s\n' $2 "$1" $C_RST; }
msg_info() { msg "$1" $C_INFO; }
msg_ok() { msg "$1" $C_OK; }
msg_err() { msg "$1" $C_ERR; }
msg_warn() { msg "$1" $C_WARN; }
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
BUILD_DIR=${SCRIPT_DIR}/build
CMAKE_TOOLCHAIN_FILE=/opt/jetkvm-native-buildkit/rv1106-jetkvm-v2.cmake
CLEAN_ALL=${CLEAN_ALL:-0}
if [ "$CLEAN_ALL" -eq 1 ]; then
find . -name build -exec rm -r {} +
rm -rf "${BUILD_DIR}"
fi
set -x
VERBOSE=1 cmake -B build \
TMP_DIR=$(mktemp -d)
pushd "${SCRIPT_DIR}" > /dev/null
msg_info "▶ Generating UI index"
./ui_index.gen.sh
msg_info "▶ Building native library"
VERBOSE=1 cmake -B "${BUILD_DIR}" \
-DCMAKE_SYSTEM_PROCESSOR=armv7l \
-DCMAKE_SYSTEM_NAME=Linux \
-DCMAKE_CROSSCOMPILING=1 \
-DCMAKE_TOOLCHAIN_FILE=$CMAKE_TOOLCHAIN_FILE \
-DLV_BUILD_USE_KCONFIG=ON \
-DLV_BUILD_DEFCONFIG_PATH=${SCRIPT_DIR}/lvgl_defconfig \
-DCONFIG_LV_BUILD_EXAMPLES=OFF \
-DCONFIG_LV_BUILD_DEMOS=OFF \
-DSKIP_GLIBC_NAMES=ON \
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="${TMP_DIR}"
cmake --build build
msg_info "▶ Copying built library and header files"
cmake --build "${BUILD_DIR}" --target install
cp -r "${TMP_DIR}/include" ../
cp -r "${TMP_DIR}/lib" ../
rm -rf "${TMP_DIR}"
popd > /dev/null

View File

@ -12,7 +12,7 @@
#include "edid.h"
#include "ctrl.h"
#include <lvgl.h>
#include "ui/vars.h"
#include "ui_index.h"
#include "log.h"
#include "log_handler.h"
@ -30,6 +30,15 @@ void jetkvm_set_video_handler(jetkvm_video_handler_t *handler) {
video_handler = handler;
}
void jetkvm_set_indev_handler(jetkvm_indev_handler_t *handler) {
lvgl_set_indev_handler(handler);
}
const char *jetkvm_ui_event_code_to_name(int code) {
lv_event_code_t cCode = (lv_event_code_t)code;
return lv_event_code_get_name(code);
}
void video_report_format(bool ready, const char *error, u_int16_t width, u_int16_t height, double frame_per_second)
{
state.ready = ready;
@ -154,12 +163,28 @@ lv_obj_flag_t str_to_lv_obj_flag(const char *flag)
}
}
void jetkvm_set_app_version(const char *version) {
set_var_app_version(version);
void jetkvm_ui_set_var(const char *name, const char *value) {
for (int i = 0; i < ui_vars_size; i++) {
if (strcmp(ui_vars[i].name, name) == 0) {
ui_vars[i].setter(value);
return;
}
}
log_error("variable %s not found", name);
}
void jetkvm_ui_init() {
lvgl_init();
const char *jetkvm_ui_get_var(const char *name) {
for (int i = 0; i < ui_vars_size; i++) {
if (strcmp(ui_vars[i].name, name) == 0) {
return ui_vars[i].getter();
}
}
log_error("variable %s not found", name);
return NULL;
}
void jetkvm_ui_init(u_int16_t rotation) {
lvgl_init(rotation);
}
void jetkvm_ui_tick() {
@ -170,19 +195,9 @@ void jetkvm_set_video_state_handler(jetkvm_video_state_handler_t *handler) {
video_state_handler = handler;
}
void jetkvm_ui_set_rotation(u_int8_t rotation)
void jetkvm_ui_set_rotation(u_int16_t rotation)
{
printf("setting rotation to %d\n", rotation);
if (rotation == 90)
{
lv_disp_set_rotation(NULL, LV_DISP_ROT_90);
} else if (rotation == 180) {
lv_disp_set_rotation(NULL, LV_DISP_ROT_180);
} else if (rotation == 270) {
lv_disp_set_rotation(NULL, LV_DISP_ROT_270);
} else {
lv_disp_set_rotation(NULL, LV_DISP_ROT_NONE);
}
lvgl_set_rotation(NULL, rotation);
}
const char *jetkvm_ui_get_current_screen() {

View File

@ -16,20 +16,22 @@ typedef struct
typedef void (jetkvm_video_state_handler_t)(jetkvm_video_state_t *state);
typedef void (jetkvm_log_handler_t)(int level, const char *filename, const char *funcname, int line, const char *message);
typedef void (jetkvm_video_handler_t)(const uint8_t *frame, ssize_t len);
typedef void (jetkvm_indev_handler_t)(int code);
void jetkvm_set_log_handler(jetkvm_log_handler_t *handler);
void jetkvm_set_video_handler(jetkvm_video_handler_t *handler);
void jetkvm_set_app_version(const char *version);
void jetkvm_ui_init();
void jetkvm_ui_tick();
void jetkvm_set_indev_handler(jetkvm_indev_handler_t *handler);
void jetkvm_set_video_state_handler(jetkvm_video_state_handler_t *handler);
void jetkvm_ui_set_rotation(u_int8_t rotation);
void jetkvm_ui_set_var(const char *name, const char *value);
const char *jetkvm_ui_get_var(const char *name);
void jetkvm_ui_init(u_int16_t rotation);
void jetkvm_ui_tick();
void jetkvm_ui_set_rotation(u_int16_t rotation);
const char *jetkvm_ui_get_current_screen();
void jetkvm_ui_load_screen(const char *obj_name);
int jetkvm_ui_set_text(const char *obj_name, const char *text);
@ -41,6 +43,8 @@ void jetkvm_ui_set_opacity(const char *obj_name, u_int8_t opacity);
int jetkvm_ui_add_flag(const char *obj_name, const char *flag_name);
int jetkvm_ui_clear_flag(const char *obj_name, const char *flag_name);
const char *jetkvm_ui_event_code_to_name(int code);
int jetkvm_video_init();
void jetkvm_video_shutdown();
void jetkvm_video_start();
@ -54,4 +58,6 @@ jetkvm_video_state_t *jetkvm_video_get_status();
void video_report_format(bool ready, const char *error, u_int16_t width, u_int16_t height, double frame_per_second);
int video_send_frame(const uint8_t *frame, ssize_t len);
#endif //VIDEO_DAEMON_CTRL_H

View File

@ -1,608 +0,0 @@
/**
* @file lv_conf.h
* Configuration file for v8.1.0
*/
/*
* Copy this file as `lv_conf.h`
* 1. simply next to the `lvgl` folder
* 2. or any other places and
* - define `LV_CONF_INCLUDE_SIMPLE`
* - add the path as include path
*/
/* clang-format off */
#if 1 /*Set it to "1" to enable content*/
#ifndef LV_CONF_H
#define LV_CONF_H
#include <stdint.h>
/*====================
COLOR SETTINGS
*====================*/
/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/
#define LV_COLOR_DEPTH 16
/*Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface (e.g. SPI)*/
#define LV_COLOR_16_SWAP 0
/*Enable more complex drawing routines to manage screens transparency.
*Can be used if the UI is above another layer, e.g. an OSD menu or video player.
*Requires `LV_COLOR_DEPTH = 32` colors and the screen's `bg_opa` should be set to non LV_OPA_COVER value*/
#define LV_COLOR_SCREEN_TRANSP 0
/* Adjust color mix functions rounding. GPUs might calculate color mix (blending) differently.
* 0: round down, 64: round up from x.75, 128: round up from half, 192: round up from x.25, 254: round up */
#define LV_COLOR_MIX_ROUND_OFS (LV_COLOR_DEPTH == 32 ? 0: 128)
/*Images pixels with this color will not be drawn if they are chroma keyed)*/
#define LV_COLOR_CHROMA_KEY lv_color_hex(0x00ff00) /*pure green*/
/*=========================
MEMORY SETTINGS
*=========================*/
/*1: use custom malloc/free, 0: use the built-in `lv_mem_alloc()` and `lv_mem_free()`*/
#define LV_MEM_CUSTOM 1
#if LV_MEM_CUSTOM == 0
/*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/
# define LV_MEM_SIZE (32U * 1024U) /*[bytes]*/
/*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/
# define LV_MEM_ADR 0 /*0: unused*/
/*Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc*/
#if LV_MEM_ADR == 0
//#define LV_MEM_POOL_INCLUDE your_alloc_library /* Uncomment if using an external allocator*/
//#define LV_MEM_POOL_ALLOC your_alloc /* Uncomment if using an external allocator*/
#endif
#else /*LV_MEM_CUSTOM*/
# define LV_MEM_CUSTOM_INCLUDE <stdlib.h> /*Header for the dynamic memory function*/
# define LV_MEM_CUSTOM_ALLOC malloc
# define LV_MEM_CUSTOM_FREE free
# define LV_MEM_CUSTOM_REALLOC realloc
#endif /*LV_MEM_CUSTOM*/
/*Number of the intermediate memory buffer used during rendering and other internal processing mechanisms.
*You will see an error log message if there wasn't enough buffers. */
#define LV_MEM_BUF_MAX_NUM 16
/*Use the standard `memcpy` and `memset` instead of LVGL's own functions. (Might or might not be faster).*/
#define LV_MEMCPY_MEMSET_STD 0
/*====================
HAL SETTINGS
*====================*/
/*Default display refresh period. LVG will redraw changed areas with this period time*/
#define LV_DISP_DEF_REFR_PERIOD 10 /*[ms]*/
/*Input device read period in milliseconds*/
#define LV_INDEV_DEF_READ_PERIOD 10 /*[ms]*/
/*Use a custom tick source that tells the elapsed time in milliseconds.
*It removes the need to manually update the tick with `lv_tick_inc()`)*/
// #define LV_TICK_CUSTOM 0
// #if LV_TICK_CUSTOM
// #define LV_TICK_CUSTOM_INCLUDE "Arduino.h" /*Header for the system time function*/
// #define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current system time in ms*/
// #endif /*LV_TICK_CUSTOM*/
#define LV_TICK_CUSTOM 1
#if LV_TICK_CUSTOM
#define LV_TICK_CUSTOM_INCLUDE <stdint.h> /*Header for the system time function*/
#define LV_TICK_CUSTOM_SYS_TIME_EXPR (custom_tick_get()) /*Expression evaluating to current system time in ms*/
#endif /*LV_TICK_CUSTOM*/
/*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings.
*(Not so important, you can adjust it to modify default sizes and spaces)*/
#define LV_DPI_DEF 130 /*[px/inch]*/
/*=======================
* FEATURE CONFIGURATION
*=======================*/
/*-------------
* Drawing
*-----------*/
/*Enable complex draw engine.
*Required to draw shadow, gradient, rounded corners, circles, arc, skew lines, image transformations or any masks*/
#define LV_DRAW_COMPLEX 1
#if LV_DRAW_COMPLEX != 0
/*Allow buffering some shadow calculation.
*LV_SHADOW_CACHE_SIZE is the max. shadow size to buffer, where shadow size is `shadow_width + radius`
*Caching has LV_SHADOW_CACHE_SIZE^2 RAM cost*/
#define LV_SHADOW_CACHE_SIZE 0
/* Set number of maximally cached circle data.
* The circumference of 1/4 circle are saved for anti-aliasing
* radius * 4 bytes are used per circle (the most often used radiuses are saved)
* 0: to disable caching */
#define LV_CIRCLE_CACHE_SIZE 4
#endif /*LV_DRAW_COMPLEX*/
/*Default image cache size. Image caching keeps the images opened.
*If only the built-in image formats are used there is no real advantage of caching. (I.e. if no new image decoder is added)
*With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images.
*However the opened images might consume additional RAM.
*0: to disable caching*/
#define LV_IMG_CACHE_DEF_SIZE 0
/*Maximum buffer size to allocate for rotation. Only used if software rotation is enabled in the display driver.*/
#define LV_DISP_ROT_MAX_BUF (10*1024)
/*-------------
* GPU
*-----------*/
/*Use STM32's DMA2D (aka Chrom Art) GPU*/
#define LV_USE_GPU_STM32_DMA2D 0
#if LV_USE_GPU_STM32_DMA2D
/*Must be defined to include path of CMSIS header of target processor
e.g. "stm32f769xx.h" or "stm32f429xx.h"*/
#define LV_GPU_DMA2D_CMSIS_INCLUDE
#endif
/*Use NXP's PXP GPU iMX RTxxx platforms*/
#define LV_USE_GPU_NXP_PXP 0
#if LV_USE_GPU_NXP_PXP
/*1: Add default bare metal and FreeRTOS interrupt handling routines for PXP (lv_gpu_nxp_pxp_osa.c)
* and call lv_gpu_nxp_pxp_init() automatically during lv_init(). Note that symbol SDK_OS_FREE_RTOS
* has to be defined in order to use FreeRTOS OSA, otherwise bare-metal implementation is selected.
*0: lv_gpu_nxp_pxp_init() has to be called manually before lv_init()
*/
#define LV_USE_GPU_NXP_PXP_AUTO_INIT 0
#endif
/*Use NXP's VG-Lite GPU iMX RTxxx platforms*/
#define LV_USE_GPU_NXP_VG_LITE 0
/*Use exnternal renderer*/
#define LV_USE_EXTERNAL_RENDERER 0
/*Use SDL renderer API. Requires LV_USE_EXTERNAL_RENDERER*/
#define LV_USE_GPU_SDL 0
#if LV_USE_GPU_SDL
# define LV_GPU_SDL_INCLUDE_PATH <SDL2/SDL.h>
#endif
/*-------------
* Logging
*-----------*/
/*Enable the log module*/
#define LV_USE_LOG 0
#if LV_USE_LOG
/*How important log should be added:
*LV_LOG_LEVEL_TRACE A lot of logs to give detailed information
*LV_LOG_LEVEL_INFO Log important events
*LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem
*LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail
*LV_LOG_LEVEL_USER Only logs added by the user
*LV_LOG_LEVEL_NONE Do not log anything*/
# define LV_LOG_LEVEL LV_LOG_LEVEL_WARN
/*1: Print the log with 'printf';
*0: User need to register a callback with `lv_log_register_print_cb()`*/
# define LV_LOG_PRINTF 0
/*Enable/disable LV_LOG_TRACE in modules that produces a huge number of logs*/
# define LV_LOG_TRACE_MEM 1
# define LV_LOG_TRACE_TIMER 1
# define LV_LOG_TRACE_INDEV 1
# define LV_LOG_TRACE_DISP_REFR 1
# define LV_LOG_TRACE_EVENT 1
# define LV_LOG_TRACE_OBJ_CREATE 1
# define LV_LOG_TRACE_LAYOUT 1
# define LV_LOG_TRACE_ANIM 1
#endif /*LV_USE_LOG*/
/*-------------
* Asserts
*-----------*/
/*Enable asserts if an operation is failed or an invalid data is found.
*If LV_USE_LOG is enabled an error message will be printed on failure*/
#define LV_USE_ASSERT_NULL 1 /*Check if the parameter is NULL. (Very fast, recommended)*/
#define LV_USE_ASSERT_MALLOC 1 /*Checks is the memory is successfully allocated or no. (Very fast, recommended)*/
#define LV_USE_ASSERT_STYLE 0 /*Check if the styles are properly initialized. (Very fast, recommended)*/
#define LV_USE_ASSERT_MEM_INTEGRITY 0 /*Check the integrity of `lv_mem` after critical operations. (Slow)*/
#define LV_USE_ASSERT_OBJ 0 /*Check the object's type and existence (e.g. not deleted). (Slow)*/
/*Add a custom handler when assert happens e.g. to restart the MCU*/
#define LV_ASSERT_HANDLER_INCLUDE <stdint.h>
#define LV_ASSERT_HANDLER while(1); /*Halt by default*/
/*-------------
* Others
*-----------*/
/*1: Show CPU usage and FPS count in the right bottom corner*/
#define LV_USE_PERF_MONITOR 0
#if LV_USE_PERF_MONITOR
#define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT
#endif
/*1: Show the used memory and the memory fragmentation in the left bottom corner
* Requires LV_MEM_CUSTOM = 0*/
#define LV_USE_MEM_MONITOR 0
#if LV_USE_PERF_MONITOR
#define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT
#endif
/*1: Draw random colored rectangles over the redrawn areas*/
#define LV_USE_REFR_DEBUG 0
/*Change the built in (v)snprintf functions*/
#define LV_SPRINTF_CUSTOM 0
#if LV_SPRINTF_CUSTOM
# define LV_SPRINTF_INCLUDE <stdio.h>
# define lv_snprintf snprintf
# define lv_vsnprintf vsnprintf
#else /*LV_SPRINTF_CUSTOM*/
# define LV_SPRINTF_USE_FLOAT 0
#endif /*LV_SPRINTF_CUSTOM*/
#define LV_USE_USER_DATA 1
/*Garbage Collector settings
*Used if lvgl is bound to higher level language and the memory is managed by that language*/
#define LV_ENABLE_GC 0
#if LV_ENABLE_GC != 0
# define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/
#endif /*LV_ENABLE_GC*/
/*=====================
* COMPILER SETTINGS
*====================*/
/*For big endian systems set to 1*/
#define LV_BIG_ENDIAN_SYSTEM 0
/*Define a custom attribute to `lv_tick_inc` function*/
#define LV_ATTRIBUTE_TICK_INC
/*Define a custom attribute to `lv_timer_handler` function*/
#define LV_ATTRIBUTE_TIMER_HANDLER
/*Define a custom attribute to `lv_disp_flush_ready` function*/
#define LV_ATTRIBUTE_FLUSH_READY
/*Required alignment size for buffers*/
#define LV_ATTRIBUTE_MEM_ALIGN_SIZE 1
/*Will be added where memories needs to be aligned (with -Os data might not be aligned to boundary by default).
* E.g. __attribute__((aligned(4)))*/
#define LV_ATTRIBUTE_MEM_ALIGN
/*Attribute to mark large constant arrays for example font's bitmaps*/
#define LV_ATTRIBUTE_LARGE_CONST
/*Complier prefix for a big array declaration in RAM*/
#define LV_ATTRIBUTE_LARGE_RAM_ARRAY
/*Place performance critical functions into a faster memory (e.g RAM)*/
#define LV_ATTRIBUTE_FAST_MEM
/*Prefix variables that are used in GPU accelerated operations, often these need to be placed in RAM sections that are DMA accessible*/
#define LV_ATTRIBUTE_DMA
/*Export integer constant to binding. This macro is used with constants in the form of LV_<CONST> that
*should also appear on LVGL binding API such as Micropython.*/
#define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning /*The default value just prevents GCC warning*/
/*Extend the default -32k..32k coordinate range to -4M..4M by using int32_t for coordinates instead of int16_t*/
#define LV_USE_LARGE_COORD 0
/*==================
* FONT USAGE
*===================*/
/*Montserrat fonts with ASCII range and some symbols using bpp = 4
*https://fonts.google.com/specimen/Montserrat*/
#define LV_FONT_MONTSERRAT_8 0
#define LV_FONT_MONTSERRAT_10 0
#define LV_FONT_MONTSERRAT_12 0
#define LV_FONT_MONTSERRAT_14 1
#define LV_FONT_MONTSERRAT_16 0
#define LV_FONT_MONTSERRAT_18 0
#define LV_FONT_MONTSERRAT_20 0
#define LV_FONT_MONTSERRAT_22 0
#define LV_FONT_MONTSERRAT_24 0
#define LV_FONT_MONTSERRAT_26 0
#define LV_FONT_MONTSERRAT_28 0
#define LV_FONT_MONTSERRAT_30 0
#define LV_FONT_MONTSERRAT_32 0
#define LV_FONT_MONTSERRAT_34 0
#define LV_FONT_MONTSERRAT_36 0
#define LV_FONT_MONTSERRAT_38 0
#define LV_FONT_MONTSERRAT_40 0
#define LV_FONT_MONTSERRAT_42 0
#define LV_FONT_MONTSERRAT_44 0
#define LV_FONT_MONTSERRAT_46 0
#define LV_FONT_MONTSERRAT_48 0
/*Demonstrate special features*/
#define LV_FONT_MONTSERRAT_12_SUBPX 0
#define LV_FONT_MONTSERRAT_28_COMPRESSED 0 /*bpp = 3*/
#define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 0 /*Hebrew, Arabic, Perisan letters and all their forms*/
#define LV_FONT_SIMSUN_16_CJK 0 /*1000 most common CJK radicals*/
/*Pixel perfect monospace fonts*/
#define LV_FONT_UNSCII_8 0
#define LV_FONT_UNSCII_16 0
/*Optionally declare custom fonts here.
*You can use these fonts as default font too and they will be available globally.
*E.g. #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) LV_FONT_DECLARE(my_font_2)*/
#define LV_FONT_CUSTOM_DECLARE
/*Always set a default font*/
#define LV_FONT_DEFAULT &lv_font_montserrat_14
/*Enable handling large font and/or fonts with a lot of characters.
*The limit depends on the font size, font face and bpp.
*Compiler error will be triggered if a font needs it.*/
#define LV_FONT_FMT_TXT_LARGE 0
/*Enables/disables support for compressed fonts.*/
#define LV_USE_FONT_COMPRESSED 0
/*Enable subpixel rendering*/
#define LV_USE_FONT_SUBPX 0
#if LV_USE_FONT_SUBPX
/*Set the pixel order of the display. Physical order of RGB channels. Doesn't matter with "normal" fonts.*/
#define LV_FONT_SUBPX_BGR 0 /*0: RGB; 1:BGR order*/
#endif
/*=================
* TEXT SETTINGS
*=================*/
/**
* Select a character encoding for strings.
* Your IDE or editor should have the same character encoding
* - LV_TXT_ENC_UTF8
* - LV_TXT_ENC_ASCII
*/
#define LV_TXT_ENC LV_TXT_ENC_UTF8
/*Can break (wrap) texts on these chars*/
#define LV_TXT_BREAK_CHARS " ,.;:-_"
/*If a word is at least this long, will break wherever "prettiest"
*To disable, set to a value <= 0*/
#define LV_TXT_LINE_BREAK_LONG_LEN 0
/*Minimum number of characters in a long word to put on a line before a break.
*Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/
#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3
/*Minimum number of characters in a long word to put on a line after a break.
*Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/
#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3
/*The control character to use for signalling text recoloring.*/
#define LV_TXT_COLOR_CMD "#"
/*Support bidirectional texts. Allows mixing Left-to-Right and Right-to-Left texts.
*The direction will be processed according to the Unicode Bidirectional Algorithm:
*https://www.w3.org/International/articles/inline-bidi-markup/uba-basics*/
#define LV_USE_BIDI 0
#if LV_USE_BIDI
/*Set the default direction. Supported values:
*`LV_BASE_DIR_LTR` Left-to-Right
*`LV_BASE_DIR_RTL` Right-to-Left
*`LV_BASE_DIR_AUTO` detect texts base direction*/
#define LV_BIDI_BASE_DIR_DEF LV_BASE_DIR_AUTO
#endif
/*Enable Arabic/Persian processing
*In these languages characters should be replaced with an other form based on their position in the text*/
#define LV_USE_ARABIC_PERSIAN_CHARS 0
/*==================
* WIDGET USAGE
*================*/
/*Documentation of the widgets: https://docs.lvgl.io/latest/en/html/widgets/index.html*/
#define LV_USE_ARC 1
#define LV_USE_ANIMIMG 1
#define LV_USE_BAR 1
#define LV_USE_BTN 1
#define LV_USE_BTNMATRIX 1
#define LV_USE_CANVAS 1
#define LV_USE_CHECKBOX 1
#define LV_USE_DROPDOWN 0 /*Requires: lv_label*/
#define LV_USE_IMG 1 /*Requires: lv_label*/
#define LV_USE_LABEL 1
#if LV_USE_LABEL
# define LV_LABEL_TEXT_SELECTION 1 /*Enable selecting text of the label*/
# define LV_LABEL_LONG_TXT_HINT 1 /*Store some extra info in labels to speed up drawing of very long texts*/
#endif
#define LV_USE_LINE 1
#define LV_USE_ROLLER 0 /*Requires: lv_label*/
#if LV_USE_ROLLER
# define LV_ROLLER_INF_PAGES 7 /*Number of extra "pages" when the roller is infinite*/
#endif
#define LV_USE_SLIDER 1 /*Requires: lv_bar*/
#define LV_USE_SWITCH 1
#define LV_USE_TEXTAREA 1 /*Requires: lv_label*/
#if LV_USE_TEXTAREA != 0
# define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /*ms*/
#endif
#define LV_USE_TABLE 0
/*==================
* EXTRA COMPONENTS
*==================*/
/*-----------
* Widgets
*----------*/
#define LV_USE_CALENDAR 0
#if LV_USE_CALENDAR
# define LV_CALENDAR_WEEK_STARTS_MONDAY 0
# if LV_CALENDAR_WEEK_STARTS_MONDAY
# define LV_CALENDAR_DEFAULT_DAY_NAMES {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"}
# else
# define LV_CALENDAR_DEFAULT_DAY_NAMES {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"}
# endif
# define LV_CALENDAR_DEFAULT_MONTH_NAMES {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}
# define LV_USE_CALENDAR_HEADER_ARROW 1
# define LV_USE_CALENDAR_HEADER_DROPDOWN 1
#endif /*LV_USE_CALENDAR*/
#define LV_USE_CHART 0
#define LV_USE_COLORWHEEL 0
#define LV_USE_IMGBTN 1
#define LV_USE_KEYBOARD 1
#define LV_USE_LED 0
#define LV_USE_LIST 1
#define LV_USE_METER 0
#define LV_USE_MSGBOX 0
#define LV_USE_SPINBOX 0
#define LV_USE_SPINNER 0
#define LV_USE_TABVIEW 0
#define LV_USE_TILEVIEW 0
#define LV_USE_WIN 0
#define LV_USE_SPAN 1
#if LV_USE_SPAN
/*A line text can contain maximum num of span descriptor */
# define LV_SPAN_SNIPPET_STACK_SIZE 64
#endif
/*-----------
* Themes
*----------*/
/*A simple, impressive and very complete theme*/
#define LV_USE_THEME_DEFAULT 1
#if LV_USE_THEME_DEFAULT
/*0: Light mode; 1: Dark mode*/
# define LV_THEME_DEFAULT_DARK 0
/*1: Enable grow on press*/
# define LV_THEME_DEFAULT_GROW 1
/*Default transition time in [ms]*/
# define LV_THEME_DEFAULT_TRANSITION_TIME 80
#endif /*LV_USE_THEME_DEFAULT*/
/*A very simple theme that is a good starting point for a custom theme*/
#define LV_USE_THEME_BASIC 1
/*A theme designed for monochrome displays*/
#define LV_USE_THEME_MONO 0
/*-----------
* Layouts
*----------*/
/*A layout similar to Flexbox in CSS.*/
#define LV_USE_FLEX 1
/*A layout similar to Grid in CSS.*/
#define LV_USE_GRID 1
/*---------------------
* 3rd party libraries
*--------------------*/
/*File system interfaces for common APIs
*To enable set a driver letter for that API*/
#define LV_USE_FS_STDIO '\0' /*Uses fopen, fread, etc*/
//#define LV_FS_STDIO_PATH "/home/john/" /*Set the working directory. If commented it will be "./" */
#define LV_USE_FS_POSIX '\0' /*Uses open, read, etc*/
//#define LV_FS_POSIX_PATH "/home/john/" /*Set the working directory. If commented it will be "./" */
#define LV_USE_FS_WIN32 '\0' /*Uses CreateFile, ReadFile, etc*/
//#define LV_FS_WIN32_PATH "C:\\Users\\john\\" /*Set the working directory. If commented it will be ".\\" */
#define LV_USE_FS_FATFS '\0' /*Uses f_open, f_read, etc*/
/*PNG decoder library*/
#define LV_USE_PNG 0
/*BMP decoder library*/
#define LV_USE_BMP 0
/* JPG + split JPG decoder library.
* Split JPG is a custom format optimized for embedded systems. */
#define LV_USE_SJPG 0
/*GIF decoder library*/
#define LV_USE_GIF 0
/*QR code library*/
#define LV_USE_QRCODE 0
/*FreeType library*/
#define LV_USE_FREETYPE 0
#if LV_USE_FREETYPE
/*Memory used by FreeType to cache characters [bytes] (-1: no caching)*/
# define LV_FREETYPE_CACHE_SIZE (16 * 1024)
#endif
/*Rlottie library*/
#define LV_USE_RLOTTIE 0
/*-----------
* Others
*----------*/
/*1: Enable API to take snapshot for object*/
#define LV_USE_SNAPSHOT 1
/*==================
* EXAMPLES
*==================*/
/*Enable the examples to be built with the library*/
#define LV_BUILD_EXAMPLES 0
/*--END OF LV_CONF_H--*/
#endif /*LV_CONF_H*/
#endif /*End of "Content enable"*/

View File

@ -0,0 +1,23 @@
CONFIG_LV_OS_PTHREAD=y
CONFIG_LV_USE_OBJ_ID=y
CONFIG_LV_USE_OBJ_NAME=y
CONFIG_LV_USE_OBJ_ID_BUILTIN=y
CONFIG_LV_USE_OBJ_PROPERTY=y
CONFIG_LV_USE_OBJ_PROPERTY_NAME=y
CONFIG_LV_USE_PRIVATE_API=y
# CONFIG_LV_USE_CALENDAR is not set
# CONFIG_LV_USE_CHART is not set
# CONFIG_LV_USE_CHECKBOX is not set
# CONFIG_LV_USE_MSGBOX is not set
# CONFIG_LV_USE_ROLLER is not set
# CONFIG_LV_USE_SCALE is not set
# CONFIG_LV_USE_SLIDER is not set
# CONFIG_LV_USE_TABLE is not set
# CONFIG_LV_USE_TABVIEW is not set
# CONFIG_LV_USE_TILEVIEW is not set
CONFIG_LV_USE_QRCODE=y
CONFIG_LV_USE_LINUX_FBDEV=y
CONFIG_LV_USE_EVDEV=y
CONFIG_LV_USE_ST7789=y
CONFIG_LV_BUILD_EXAMPLES=n
CONFIG_LV_BUILD_DEMOS=n

View File

@ -6,45 +6,74 @@
#include "log.h"
#include "screen.h"
#include <lvgl.h>
#include <display/fbdev.h>
#include <indev/evdev.h>
// #include "st7789/lcd.h"
#include "ui/ui.h"
#include "ui_index.h"
#define DISP_BUF_SIZE (300 * 240 * 2)
static lv_color_t buf[DISP_BUF_SIZE];
static lv_disp_draw_buf_t disp_buf;
static lv_disp_drv_t disp_drv;
static lv_indev_drv_t indev_drv;
void lvgl_init(void) {
indev_handler_t *indev_handler = NULL;
void lvgl_set_indev_handler(indev_handler_t *handler) {
indev_handler = handler;
}
void handle_indev_event(lv_event_t *e) {
if (indev_handler == NULL) {
return;
}
indev_handler(lv_event_get_code(e));
}
void lvgl_init(u_int16_t rotation) {
log_trace("initalizing lvgl");
/*LittlevGL init*/
lv_init();
log_trace("initalizing fbdev");
fbdev_init();
lv_disp_draw_buf_init(&disp_buf, buf, NULL, DISP_BUF_SIZE);
lv_disp_drv_init(&disp_drv);
disp_drv.draw_buf = &disp_buf;
disp_drv.flush_cb = fbdev_flush;
disp_drv.hor_res = 240;
disp_drv.ver_res = 300;
disp_drv.rotated = LV_DISP_ROT_270;
disp_drv.sw_rotate = true;
// disp_drv.full_refresh = true;
/*Linux frame buffer device init*/
lv_disp_drv_register(&disp_drv);
/*Linux frame buffer device init*/
lv_display_t *disp = lv_linux_fbdev_create();
// lv_display_set_physical_resolution(disp, 240, 300);
lv_display_set_resolution(disp, 240, 300);
lv_linux_fbdev_set_file(disp, "/dev/fb0");
log_trace("initalizing evdev");
evdev_init();
evdev_set_file("/dev/input/event1");
lvgl_set_rotation(disp, rotation);
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = evdev_read;
lv_indev_drv_register(&indev_drv);
// lv_display_t *disp = lv_st7789_create(LCD_H_RES, LCD_V_RES, LV_LCD_FLAG_NONE, lcd_send_cmd, lcd_send_color);
// lv_display_set_resolution(disp, 240, 300);
// lv_display_set_rotation(disp, LV_DISP_ROTATION_270);
// lv_color_t * buf1 = NULL;
// lv_color_t * buf2 = NULL;
// uint32_t buf_size = LCD_H_RES * LCD_V_RES / 10 * lv_color_format_get_size(lv_display_get_color_format(disp));
// buf1 = lv_malloc(buf_size);
// if(buf1 == NULL) {
// log_error("display draw buffer malloc failed");
// return;
// }
// buf2 = lv_malloc(buf_size);
// if(buf2 == NULL) {
// log_error("display buffer malloc failed");
// lv_free(buf1);
// return;
// }
// lv_display_set_buffers(disp, buf1, buf2, buf_size, LV_DISPLAY_RENDER_MODE_PARTIAL);
/* Linux input device init */
lv_indev_t *mouse = lv_evdev_create(LV_INDEV_TYPE_POINTER, "/dev/input/event1");
lv_indev_set_group(mouse, lv_group_get_default());
lv_indev_set_display(mouse, disp);
lv_indev_add_event_cb(mouse, handle_indev_event, LV_EVENT_ALL, NULL);
log_trace("initalizing ui");
ui_init();
log_info("ui initalized");
@ -58,6 +87,45 @@ void lvgl_tick(void) {
ui_tick();
}
void lvgl_set_rotation(lv_display_t *disp, u_int16_t rotation) {
log_info("setting rotation to %d", rotation);
if (rotation == 0) {
lv_display_set_rotation(disp, LV_DISP_ROTATION_0);
} else if (rotation == 90) {
lv_display_set_rotation(disp, LV_DISP_ROTATION_90);
} else if (rotation == 180) {
lv_display_set_rotation(disp, LV_DISP_ROTATION_180);
} else if (rotation == 270) {
lv_display_set_rotation(disp, LV_DISP_ROTATION_270);
} else {
log_error("invalid rotation %d", rotation);
}
lv_style_t *flex_screen_style = ui_get_style("flex_screen");
if (flex_screen_style == NULL) {
log_error("flex_screen style not found");
return;
}
lv_style_t *flex_screen_menu_style = ui_get_style("flex_screen_menu");
if (flex_screen_menu_style == NULL) {
log_error("flex_screen_menu style not found");
return;
}
if (rotation == 90) {
lv_style_set_pad_left(flex_screen_style, 24);
lv_style_set_pad_right(flex_screen_style, 44);
} else if (rotation == 270) {
lv_style_set_pad_left(flex_screen_style, 44);
lv_style_set_pad_right(flex_screen_style, 24);
}
log_info("refreshing objects");
lv_obj_report_style_change(&flex_screen_style);
lv_obj_report_style_change(&flex_screen_menu_style);
}
uint32_t custom_tick_get(void)
{
static uint64_t start_ms = 0;
@ -85,6 +153,16 @@ lv_obj_t *ui_get_obj(const char *name) {
return NULL;
}
lv_style_t *ui_get_style(const char *name) {
for (size_t i = 0; i < ui_styles_size; i++) {
if (strcmp(ui_styles[i].name, name) == 0) {
return ui_styles[i].getter();
}
}
return NULL;
}
const char *ui_get_current_screen() {
lv_obj_t *scr = lv_scr_act();
if (scr == NULL) {

View File

@ -3,11 +3,19 @@
#include <lvgl.h>
void lvgl_init(void);
typedef void (indev_handler_t)(lv_event_code_t code);
void lvgl_set_indev_handler(indev_handler_t *handler);
void lvgl_init(u_int16_t rotation);
void lvgl_tick(void);
void lvgl_set_rotation(lv_display_t *disp, u_int16_t rotation);
void ui_set_text(const char *name, const char *text);
lv_obj_t *ui_get_obj(const char *name);
lv_style_t *ui_get_style(const char *name);
lv_img_dsc_t *ui_get_image(const char *name);
#endif // SCREEN_H

View File

@ -12,6 +12,14 @@ done)
const int ui_objects_size = sizeof(ui_objects) / sizeof(ui_objects[0]);
ui_style_map ui_styles[] = {
$(grep 'lv_style_t \*get_style_' ui/styles.h | sed 's/lv_style_t \*get_style_//g' | sed 's/_MAIN_DEFAULT();//g' | sed 's/\r//' | while read -r line; do
echo " {\"$line\", &get_style_${line}_MAIN_DEFAULT},"
done)
};
const int ui_styles_size = sizeof(ui_styles) / sizeof(ui_styles[0]);
ui_img_map ui_images[] = {
$(grep "extern const lv_img_dsc_t " ui/images.h | sed 's/extern const lv_img_dsc_t //g' | sed 's/;//g' | while read -r line; do
echo " {\"$line\", &$line},"
@ -19,6 +27,14 @@ done)
};
const int ui_images_size = sizeof(ui_images) / sizeof(ui_images[0]);
ui_var_map ui_vars[] = {
$(grep 'extern const char \*get_var_' ui/vars.h | sed 's/extern const char \*get_var_//g' | sed 's/();//g' | sed 's/\r//' | while read -r line; do
echo " {\"$line\", &get_var_$line, &set_var_$line},"
done)
};
const int ui_vars_size = sizeof(ui_vars) / sizeof(ui_vars[0]);
EOF
echo "ui_index.c has been generated successfully."

View File

@ -3,7 +3,9 @@
#include "ui/ui.h"
#include "ui/screens.h"
#include "ui/styles.h"
#include "ui/images.h"
#include "ui/vars.h"
typedef struct {
const char *name;
@ -13,6 +15,14 @@ typedef struct {
extern ui_obj_map ui_objects[];
extern const int ui_objects_size;
typedef struct {
const char *name;
lv_style_t *(*getter)();
} ui_style_map;
extern ui_style_map ui_styles[];
extern const int ui_styles_size;
typedef struct {
const char *name;
const lv_img_dsc_t *img; // Pointer to the image descriptor const
@ -21,5 +31,13 @@ typedef struct {
extern ui_img_map ui_images[];
extern const int ui_images_size;
typedef struct {
const char *name;
const char *(*getter)();
void (*setter)(const char *value);
} ui_var_map;
extern ui_var_map ui_vars[];
extern const int ui_vars_size;
#endif // UI_INDEX_H

View File

@ -0,0 +1,276 @@
//go:build linux
package native
import (
"fmt"
"unsafe"
"github.com/rs/zerolog"
)
/*
#cgo LDFLAGS: -Llib -ljknative -llvgl
#cgo CFLAGS: -Iinclude
#include "ctrl.h"
#include <stdlib.h>
typedef const char cchar_t;
typedef const uint8_t cuint8_t;
extern void jetkvm_go_log_handler(int level, cchar_t *filename, cchar_t *funcname, int line, cchar_t *message);
static inline void jetkvm_cgo_setup_log_handler() {
jetkvm_set_log_handler(&jetkvm_go_log_handler);
}
extern void jetkvm_go_video_state_handler(jetkvm_video_state_t *state);
static inline void jetkvm_cgo_setup_video_state_handler() {
jetkvm_set_video_state_handler(&jetkvm_go_video_state_handler);
}
extern void jetkvm_go_video_handler(cuint8_t *frame, ssize_t len);
static inline void jetkvm_cgo_setup_video_handler() {
jetkvm_set_video_handler(&jetkvm_go_video_handler);
}
extern void jetkvm_go_indev_handler(int code);
static inline void jetkvm_cgo_setup_indev_handler() {
jetkvm_set_indev_handler(&jetkvm_go_indev_handler);
}
*/
import "C"
//export jetkvm_go_video_state_handler
func jetkvm_go_video_state_handler(state *C.jetkvm_video_state_t) {
videoState := VideoState{
Ready: bool(state.ready),
Error: C.GoString(state.error),
Width: int(state.width),
Height: int(state.height),
FramePerSecond: float64(state.frame_per_second),
}
videoStateChan <- videoState
}
//export jetkvm_go_log_handler
func jetkvm_go_log_handler(level C.int, filename *C.cchar_t, funcname *C.cchar_t, line C.int, message *C.cchar_t) {
logMessage := nativeLogMessage{
Level: zerolog.Level(level),
Message: C.GoString(message),
File: C.GoString(filename),
FuncName: C.GoString(funcname),
Line: int(line),
}
logChan <- logMessage
}
//export jetkvm_go_video_handler
func jetkvm_go_video_handler(frame *C.cuint8_t, len C.ssize_t) {
videoFrameChan <- C.GoBytes(unsafe.Pointer(frame), C.int(len))
}
//export jetkvm_go_indev_handler
func jetkvm_go_indev_handler(code C.int) {
indevEventChan <- int(code)
}
var eventCodeToNameMap = map[int]string{}
func uiEventCodeToName(code int) string {
name, ok := eventCodeToNameMap[code]
if !ok {
cCode := C.int(code)
cName := C.jetkvm_ui_event_code_to_name(cCode)
name = C.GoString(cName)
eventCodeToNameMap[code] = name
}
return name
}
func setUpNativeHandlers() {
C.jetkvm_cgo_setup_log_handler()
C.jetkvm_cgo_setup_video_state_handler()
C.jetkvm_cgo_setup_video_handler()
C.jetkvm_cgo_setup_indev_handler()
}
func uiInit(rotation uint16) {
cRotation := C.u_int16_t(rotation)
defer C.free(unsafe.Pointer(&cRotation))
C.jetkvm_ui_init(cRotation)
}
func uiTick() {
C.jetkvm_ui_tick()
}
func videoInit() error {
ret := C.jetkvm_video_init()
if ret != 0 {
return fmt.Errorf("failed to initialize video: %d", ret)
}
return nil
}
func videoShutdown() {
C.jetkvm_video_shutdown()
}
func videoStart() {
C.jetkvm_video_start()
}
func videoStop() {
C.jetkvm_video_stop()
}
func uiSetVar(name string, value string) {
nameCStr := C.CString(name)
defer C.free(unsafe.Pointer(nameCStr))
valueCStr := C.CString(value)
defer C.free(unsafe.Pointer(valueCStr))
C.jetkvm_ui_set_var(nameCStr, valueCStr)
}
func uiGetVar(name string) string {
nameCStr := C.CString(name)
defer C.free(unsafe.Pointer(nameCStr))
return C.GoString(C.jetkvm_ui_get_var(nameCStr))
}
func uiSwitchToScreen(screen string) {
screenCStr := C.CString(screen)
defer C.free(unsafe.Pointer(screenCStr))
C.jetkvm_ui_load_screen(screenCStr)
}
func uiGetCurrentScreen() string {
screenCStr := C.jetkvm_ui_get_current_screen()
return C.GoString(screenCStr)
}
func uiObjSetState(objName string, state string) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
stateCStr := C.CString(state)
defer C.free(unsafe.Pointer(stateCStr))
C.jetkvm_ui_set_state(objNameCStr, stateCStr)
return true, nil
}
// TODO: use Enum instead of string but it's not a hot path and performance is not a concern now
func uiObjAddFlag(objName string, flag string) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
flagCStr := C.CString(flag)
defer C.free(unsafe.Pointer(flagCStr))
C.jetkvm_ui_add_flag(objNameCStr, flagCStr)
return true, nil
}
func uiObjClearFlag(objName string, flag string) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
flagCStr := C.CString(flag)
defer C.free(unsafe.Pointer(flagCStr))
C.jetkvm_ui_clear_flag(objNameCStr, flagCStr)
return true, nil
}
func uiObjHide(objName string) (bool, error) {
return uiObjAddFlag(objName, "LV_OBJ_FLAG_HIDDEN")
}
func uiObjShow(objName string) (bool, error) {
return uiObjClearFlag(objName, "LV_OBJ_FLAG_HIDDEN")
}
func uiObjSetOpacity(objName string, opacity int) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
C.jetkvm_ui_set_opacity(objNameCStr, C.u_int8_t(opacity))
return true, nil
}
func uiObjFadeIn(objName string, duration uint32) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
C.jetkvm_ui_fade_in(objNameCStr, C.u_int32_t(duration))
return true, nil
}
func uiObjFadeOut(objName string, duration uint32) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
C.jetkvm_ui_fade_out(objNameCStr, C.u_int32_t(duration))
return true, nil
}
func uiLabelSetText(objName string, text string) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
textCStr := C.CString(text)
defer C.free(unsafe.Pointer(textCStr))
ret := C.jetkvm_ui_set_text(objNameCStr, textCStr)
if ret < 0 {
return false, fmt.Errorf("failed to set text: %d", ret)
}
return ret == 0, nil
}
func uiImgSetSrc(objName string, src string) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
srcCStr := C.CString(src)
defer C.free(unsafe.Pointer(srcCStr))
C.jetkvm_ui_set_image(objNameCStr, srcCStr)
return true, nil
}
func uiDispSetRotation(rotation uint16) (bool, error) {
nativeLogger.Info().Uint16("rotation", rotation).Msg("setting rotation")
cRotation := C.u_int16_t(rotation)
defer C.free(unsafe.Pointer(&cRotation))
C.jetkvm_ui_set_rotation(cRotation)
return true, nil
}
func videoGetStreamQualityFactor() (float64, error) {
factor := C.jetkvm_video_get_quality_factor()
return float64(factor), nil
}
func videoSetStreamQualityFactor(factor float64) error {
C.jetkvm_video_set_quality_factor(C.float(factor))
return nil
}
func videoGetEDID() (string, error) {
edidCStr := C.jetkvm_video_get_edid_hex()
return C.GoString(edidCStr), nil
}
func videoSetEDID(edid string) error {
edidCStr := C.CString(edid)
defer C.free(unsafe.Pointer(edidCStr))
C.jetkvm_video_set_edid(edidCStr)
return nil
}

View File

@ -0,0 +1,109 @@
//go:build !linux
package native
func panicPlatformNotSupported() {
panic("platform not supported")
}
func setUpNativeHandlers() {
panicPlatformNotSupported()
}
func uiSetVar(name string, value string) {
panicPlatformNotSupported()
}
func uiGetVar(name string) string {
panicPlatformNotSupported()
return ""
}
func uiSwitchToScreen(screen string) {
panicPlatformNotSupported()
}
func uiGetCurrentScreen() string {
panicPlatformNotSupported()
return ""
}
func uiObjSetState(objName string, state string) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiObjAddFlag(objName string, flag string) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiObjClearFlag(objName string, flag string) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiObjHide(objName string) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiObjShow(objName string) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiObjSetOpacity(objName string, opacity int) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiObjFadeIn(objName string, duration uint32) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiObjFadeOut(objName string, duration uint32) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiLabelSetText(objName string, text string) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiImgSetSrc(objName string, src string) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiDispSetRotation(rotation uint16) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiEventCodeToName(code int) string {
panicPlatformNotSupported()
return ""
}
func videoGetStreamQualityFactor() (float64, error) {
panicPlatformNotSupported()
return 0, nil
}
func videoSetStreamQualityFactor(factor float64) error {
panicPlatformNotSupported()
return nil
}
func videoGetEDID() (string, error) {
panicPlatformNotSupported()
return "", nil
}
func videoSetEDID(edid string) error {
panicPlatformNotSupported()
return nil
}

72
internal/native/chan.go Normal file
View File

@ -0,0 +1,72 @@
package native
import (
"time"
"github.com/rs/zerolog"
)
var (
videoFrameChan chan []byte = make(chan []byte)
videoStateChan chan VideoState = make(chan VideoState)
logChan chan nativeLogMessage = make(chan nativeLogMessage)
indevEventChan chan int = make(chan int)
)
func (n *Native) handleVideoFrameChan() {
lastFrame := time.Now()
for {
frame := <-videoFrameChan
now := time.Now()
sinceLastFrame := now.Sub(lastFrame)
lastFrame = now
n.onVideoFrameReceived(frame, sinceLastFrame)
}
}
func (n *Native) handleVideoStateChan() {
for {
state := <-videoStateChan
n.onVideoStateChange(state)
}
}
func (n *Native) handleLogChan() {
for {
entry := <-logChan
l := n.l.With().
Str("file", entry.File).
Str("func", entry.FuncName).
Int("line", entry.Line).
Logger()
switch entry.Level {
case zerolog.DebugLevel:
l.Debug().Msg(entry.Message)
case zerolog.InfoLevel:
l.Info().Msg(entry.Message)
case zerolog.WarnLevel:
l.Warn().Msg(entry.Message)
case zerolog.ErrorLevel:
l.Error().Msg(entry.Message)
case zerolog.PanicLevel:
l.Panic().Msg(entry.Message)
case zerolog.FatalLevel:
l.Fatal().Msg(entry.Message)
case zerolog.TraceLevel:
l.Trace().Msg(entry.Message)
case zerolog.NoLevel:
l.Info().Msg(entry.Message)
default:
l.Info().Msg(entry.Message)
}
}
}
func (n *Native) handleIndevEventChan() {
for {
event := <-indevEventChan
name := uiEventCodeToName(event)
n.onIndevEvent(name)
}
}

View File

@ -1,282 +0,0 @@
//go:build linux
package native
import (
"fmt"
"strconv"
"sync"
"time"
"unsafe"
"github.com/rs/zerolog"
)
// #cgo LDFLAGS: -Lcgo/build -Lcgo/build/lib -ljknative -llvgl
// #cgo CFLAGS: -Icgo -Icgo/ui -Icgo/build/_deps/lvgl-src
// #include "ctrl.h"
// #include <stdlib.h>
// typedef const char cchar_t;
// typedef const uint8_t cuint8_t;
// extern void jetkvm_video_state_handler(jetkvm_video_state_t *state);
// static inline void jetkvm_setup_video_state_handler() {
// jetkvm_set_video_state_handler(&jetkvm_video_state_handler);
// }
// extern void jetkvm_go_log_handler(int level, cchar_t *filename, cchar_t *funcname, int line, cchar_t *message);
// static inline void jetkvm_setup_log_handler() {
// jetkvm_set_log_handler(&jetkvm_go_log_handler);
// }
// extern void jetkvm_video_handler(cuint8_t *frame, ssize_t len);
// static inline void jetkvm_setup_video_handler() {
// jetkvm_set_video_handler(&jetkvm_video_handler);
// }
import "C"
var (
jkInstance *Native
jkInstanceLock sync.RWMutex
jkVideoChan chan []byte = make(chan []byte)
)
func setUpJkInstance(instance *Native) {
jkInstanceLock.Lock()
defer jkInstanceLock.Unlock()
if jkInstance == nil {
jkInstance = instance
}
if jkInstance != instance {
panic("jkInstance is already set")
}
}
//export jetkvm_video_state_handler
func jetkvm_video_state_handler(state *C.jetkvm_video_state_t) {
jkInstanceLock.RLock()
defer jkInstanceLock.RUnlock()
if jkInstance != nil {
// convert state to VideoState
videoState := VideoState{
Ready: bool(state.ready),
Error: C.GoString(state.error),
Width: int(state.width),
Height: int(state.height),
FramePerSecond: float64(state.frame_per_second),
}
jkInstance.handleVideoStateMessage(videoState)
}
}
//export jetkvm_go_log_handler
func jetkvm_go_log_handler(level C.int, filename *C.cchar_t, funcname *C.cchar_t, line C.int, message *C.cchar_t) {
l := nativeLogger.With().
Str("file", C.GoString(filename)).
Str("function", C.GoString(funcname)).
Int("line", int(line)).
Logger()
gLevel := zerolog.Level(level)
switch gLevel {
case zerolog.DebugLevel:
l.Debug().Msg(C.GoString(message))
case zerolog.InfoLevel:
l.Info().Msg(C.GoString(message))
case zerolog.WarnLevel:
l.Warn().Msg(C.GoString(message))
case zerolog.ErrorLevel:
l.Error().Msg(C.GoString(message))
case zerolog.PanicLevel:
l.Panic().Msg(C.GoString(message))
case zerolog.FatalLevel:
l.Fatal().Msg(C.GoString(message))
case zerolog.TraceLevel:
l.Trace().Msg(C.GoString(message))
case zerolog.NoLevel:
l.Info().Msg(C.GoString(message))
default:
l.Info().Msg(C.GoString(message))
}
}
//export jetkvm_video_handler
func jetkvm_video_handler(frame *C.cuint8_t, len C.ssize_t) {
jkVideoChan <- C.GoBytes(unsafe.Pointer(frame), C.int(len))
}
func setVideoStateHandler() {
C.jetkvm_setup_video_state_handler()
}
func setLogHandler() {
C.jetkvm_setup_log_handler()
}
func setVideoHandler() {
C.jetkvm_setup_video_handler()
}
func (n *Native) StartNativeVideo() {
setUpJkInstance(n)
setVideoStateHandler()
setLogHandler()
setVideoHandler()
C.jetkvm_set_app_version(C.CString(n.appVersion.String()))
C.jetkvm_ui_init()
n.UpdateLabelIfChanged("boot_screen_version", n.appVersion.String())
go func() {
for {
C.jetkvm_ui_tick()
time.Sleep(5 * time.Millisecond)
}
}()
if C.jetkvm_video_init() != 0 {
nativeLogger.Error().Msg("failed to initialize video")
return
}
C.jetkvm_video_start()
close(n.ready)
}
func (n *Native) StopNativeVideo() {
C.jetkvm_video_stop()
}
func (n *Native) SwitchToScreen(screen string) {
screenCStr := C.CString(screen)
defer C.free(unsafe.Pointer(screenCStr))
C.jetkvm_ui_load_screen(screenCStr)
}
func (n *Native) GetCurrentScreen() string {
screenCStr := C.jetkvm_ui_get_current_screen()
return C.GoString(screenCStr)
}
func (n *Native) ObjSetState(objName string, state string) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
stateCStr := C.CString(state)
defer C.free(unsafe.Pointer(stateCStr))
C.jetkvm_ui_set_state(objNameCStr, stateCStr)
return true, nil
}
func (n *Native) ObjAddFlag(objName string, flag string) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
flagCStr := C.CString(flag)
defer C.free(unsafe.Pointer(flagCStr))
C.jetkvm_ui_add_flag(objNameCStr, flagCStr)
return true, nil
}
func (n *Native) ObjClearFlag(objName string, flag string) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
flagCStr := C.CString(flag)
defer C.free(unsafe.Pointer(flagCStr))
C.jetkvm_ui_clear_flag(objNameCStr, flagCStr)
return true, nil
}
func (n *Native) ObjHide(objName string) (bool, error) {
return n.ObjAddFlag(objName, "LV_OBJ_FLAG_HIDDEN")
}
func (n *Native) ObjShow(objName string) (bool, error) {
return n.ObjClearFlag(objName, "LV_OBJ_FLAG_HIDDEN")
}
func (n *Native) ObjSetOpacity(objName string, opacity int) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
C.jetkvm_ui_set_opacity(objNameCStr, C.u_int8_t(opacity))
return true, nil
}
func (n *Native) ObjFadeIn(objName string, duration uint32) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
C.jetkvm_ui_fade_in(objNameCStr, C.u_int32_t(duration))
return true, nil
}
func (n *Native) ObjFadeOut(objName string, duration uint32) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
C.jetkvm_ui_fade_out(objNameCStr, C.u_int32_t(duration))
return true, nil
}
func (n *Native) LabelSetText(objName string, text string) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
textCStr := C.CString(text)
defer C.free(unsafe.Pointer(textCStr))
ret := C.jetkvm_ui_set_text(objNameCStr, textCStr)
if ret < 0 {
return false, fmt.Errorf("failed to set text: %d", ret)
}
return ret == 0, nil
}
func (n *Native) ImgSetSrc(objName string, src string) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
srcCStr := C.CString(src)
defer C.free(unsafe.Pointer(srcCStr))
C.jetkvm_ui_set_image(objNameCStr, srcCStr)
return true, nil
}
func (n *Native) DispSetRotation(rotation string) (bool, error) {
rotationInt, err := strconv.Atoi(rotation)
if err != nil {
return false, err
}
nativeLogger.Info().Int("rotation", rotationInt).Msg("setting rotation")
// C.jetkvm_ui_set_rotation(C.u_int8_t(rotationInt))
return true, nil
}
func (n *Native) GetStreamQualityFactor() (float64, error) {
factor := C.jetkvm_video_get_quality_factor()
return float64(factor), nil
}
func (n *Native) SetStreamQualityFactor(factor float64) error {
C.jetkvm_video_set_quality_factor(C.float(factor))
return nil
}
func (n *Native) GetEDID() (string, error) {
edidCStr := C.jetkvm_video_get_edid_hex()
return C.GoString(edidCStr), nil
}
func (n *Native) SetEDID(edid string) error {
edidCStr := C.CString(edid)
defer C.free(unsafe.Pointer(edidCStr))
C.jetkvm_video_set_edid(edidCStr)
return nil
}

View File

@ -1,88 +0,0 @@
//go:build !linux
package native
func panicNotImplemented() {
panic("not supported")
}
func (n *Native) StartNativeVideo() {
panicNotImplemented()
}
func (n *Native) StopNativeVideo() {
panicNotImplemented()
}
func (n *Native) SwitchToScreen(screen string) {
panicNotImplemented()
}
func (n *Native) GetCurrentScreen() string {
panicNotImplemented()
}
func (n *Native) ObjSetState(objName string, state string) (bool, error) {
panicNotImplemented()
return false, nil
}
func (n *Native) ObjAddFlag(objName string, flag string) (bool, error) {
panicNotImplemented()
return false, nil
}
func (n *Native) ObjClearFlag(objName string, flag string) (bool, error) {
panicNotImplemented()
return false, nil
}
func (n *Native) ObjHide(objName string) (bool, error) {
panicNotImplemented()
return false, nil
}
func (n *Native) ObjShow(objName string) (bool, error) {
panicNotImplemented()
return false, nil
}
func (n *Native) ObjSetOpacity(objName string, opacity int) (bool, error) { // nolint:unused
panicNotImplemented()
return false, nil
}
func (n *Native) ObjFadeIn(objName string, duration uint32) (bool, error) {
panicNotImplemented()
return false, nil
}
func (n *Native) ObjFadeOut(objName string, duration uint32) (bool, error) {
panicNotImplemented()
return false, nil
}
func (n *Native) LabelSetText(objName string, text string) (bool, error) {
panicNotImplemented()
return false, nil
}
func (n *Native) ImgSetSrc(objName string, src string) (bool, error) {
panicNotImplemented()
return false, nil
}
func (n *Native) DispSetRotation(rotation string) (bool, error) {
panicNotImplemented()
return false, nil
}
func (n *Native) GetStreamQualityFactor() (float64, error) {
panicNotImplemented()
return 0, nil
}
func (n *Native) SetStreamQualityFactor(factor float64) error {
panicNotImplemented()
return nil
}

View File

@ -1,11 +1,75 @@
package native
import "slices"
import (
"slices"
"time"
)
func (n *Native) setUIVars() {
uiSetVar("app_version", n.appVersion.String())
uiSetVar("system_version", n.systemVersion.String())
}
func (n *Native) initUI() {
uiInit(n.displayRotation)
n.setUIVars()
}
func (n *Native) tickUI() {
for {
uiTick()
time.Sleep(5 * time.Millisecond)
}
}
func (n *Native) UIObjHide(objName string) (bool, error) {
return uiObjHide(objName)
}
func (n *Native) UIObjShow(objName string) (bool, error) {
return uiObjShow(objName)
}
func (n *Native) UIObjSetState(objName string, state string) (bool, error) {
return uiObjSetState(objName, state)
}
func (n *Native) UIObjAddFlag(objName string, flag string) (bool, error) {
return uiObjAddFlag(objName, flag)
}
func (n *Native) UIObjClearFlag(objName string, flag string) (bool, error) {
return uiObjClearFlag(objName, flag)
}
func (n *Native) UIObjSetOpacity(objName string, opacity int) (bool, error) {
return uiObjSetOpacity(objName, opacity)
}
func (n *Native) UIObjFadeIn(objName string, duration uint32) (bool, error) {
return uiObjFadeIn(objName, duration)
}
func (n *Native) UIObjFadeOut(objName string, duration uint32) (bool, error) {
return uiObjFadeOut(objName, duration)
}
func (n *Native) UIObjSetLabelText(objName string, text string) (bool, error) {
return uiLabelSetText(objName, text)
}
func (n *Native) UIObjSetImageSrc(objName string, image string) (bool, error) {
return uiImgSetSrc(objName, image)
}
func (n *Native) DisplaySetRotation(rotation uint16) (bool, error) {
return uiDispSetRotation(rotation)
}
func (n *Native) UpdateLabelIfChanged(objName string, newText string) {
l := n.lD.Trace().Str("obj", objName).Str("text", newText)
changed, err := n.LabelSetText(objName, newText)
changed, err := n.UIObjSetLabelText(objName, newText)
if err != nil {
n.lD.Warn().Str("obj", objName).Str("text", newText).Err(err).Msg("failed to update label")
return
@ -21,27 +85,27 @@ func (n *Native) UpdateLabelIfChanged(objName string, newText string) {
func (n *Native) UpdateLabelAndChangeVisibility(objName string, newText string) {
containerName := objName + "_container"
if newText == "" {
_, _ = n.ObjHide(objName)
_, _ = n.ObjHide(containerName)
_, _ = n.UIObjHide(objName)
_, _ = n.UIObjHide(containerName)
} else {
_, _ = n.ObjShow(objName)
_, _ = n.ObjShow(containerName)
_, _ = n.UIObjShow(objName)
_, _ = n.UIObjShow(containerName)
}
n.UpdateLabelIfChanged(objName, newText)
}
func (n *Native) SwitchToScreenIf(screenName string, shouldSwitch []string) {
currentScreen := n.GetCurrentScreen()
currentScreen := uiGetCurrentScreen()
if currentScreen == screenName {
return
}
if !slices.Contains(shouldSwitch, currentScreen) {
displayLogger.Trace().Str("from", currentScreen).Str("to", screenName).Msg("skipping screen switch")
n.lD.Trace().Str("from", currentScreen).Str("to", screenName).Msg("skipping screen switch")
return
}
displayLogger.Info().Str("from", currentScreen).Str("to", screenName).Msg("switching screen")
n.SwitchToScreen(screenName)
n.lD.Info().Str("from", currentScreen).Str("to", screenName).Msg("switching screen")
uiSwitchToScreen(screenName)
}
func (n *Native) SwitchToScreenIfDifferent(screenName string) {

File diff suppressed because one or more lines are too long

View File

@ -1,12 +1,12 @@
{
"navigation": {
"selectedUserPageObject": "[jetkvm.eez-project]:/userPages/0",
"selectedUserWidgetObject": "[jetkvm.eez-project]:/userWidgets/-1",
"selectedActionObject": "[jetkvm.eez-project]:/actions/7",
"selectedGlobalVariableObject": "[jetkvm.eez-project]:/variables/globalVariables/0",
"selectedUserPageObject": "[jetkvm.eez-project]:/userPages/5",
"selectedActionObject": "[jetkvm.eez-project]:/actions/0",
"selectedGlobalVariableObject": "[jetkvm.eez-project]:/variables/globalVariables/1",
"selectedStyleObject": "[jetkvm.eez-project]:/lvglStyles/styles/0",
"selectedThemeObject": "[jetkvm.eez-project]:/themes/0",
"selectedFontObject": "[jetkvm.eez-project]:/fonts/1",
"selectedFontObject": "[jetkvm.eez-project]:/fonts/4",
"selectedBitmapObject": "[jetkvm.eez-project]:/bitmaps/11",
"subnavigationSelectedItems": {
"variables-tab/sub-navigation/selected-item": "Global"
}
@ -29,7 +29,7 @@
},
{
"type": "border",
"selected": 4,
"selected": 2,
"size": 113.5,
"location": "right",
"children": [
@ -77,7 +77,7 @@
},
{
"type": "border",
"selected": 0,
"selected": 1,
"location": "bottom",
"children": [
{
@ -130,7 +130,6 @@
"type": "tabset",
"id": "#2e3d9a08-c69c-42b5-b434-525109f2a5a7",
"weight": 1,
"selected": 1,
"enableClose": false,
"children": [
{
@ -189,7 +188,8 @@
"enableClose": false,
"icon": "svg:variable"
}
]
],
"active": true
}
]
},
@ -197,46 +197,46 @@
"type": "tabset",
"id": "EDITORS",
"weight": 49.31058517127421,
"selected": 1,
"selected": 4,
"enableDeleteWhenEmpty": false,
"enableClose": false,
"children": [
{
"type": "tab",
"id": "#467ae6a9-0f08-4392-a580-25f8916524e4",
"name": "BootScreen",
"id": "#aec56ae8-5b75-4a2f-ad81-f0d7d683c77a",
"name": "FontBook24",
"component": "editor",
"config": {
"objectPath": "[jetkvm.eez-project]:/userPages/0",
"objectPath": "[jetkvm.eez-project]:/fonts/4",
"permanent": false
},
"icon": "material:font_download"
},
{
"type": "tab",
"id": "#c8dece00-e490-46b8-8a14-5dcfa8bbce36",
"name": "AboutScreen",
"component": "editor",
"config": {
"objectPath": "[jetkvm.eez-project]:/userPages/5",
"permanent": false
},
"icon": "svg:page"
},
{
"type": "tab",
"id": "#d1704ae1-993d-4a7d-bade-0903bb61e9c6",
"name": "AboutScreen",
"id": "#e9afee88-d3be-4069-aa77-22930d1efcf3",
"name": "HomeScreen",
"component": "editor",
"config": {
"objectPath": "[jetkvm.eez-project]:/userPages/5",
"objectPath": "[jetkvm.eez-project]:/userPages/1",
"permanent": true
},
"icon": "svg:page"
},
{
"type": "tab",
"id": "#b444e818-4663-4332-ab40-a34acb670c8c",
"name": "MenuAdvancedScreen",
"component": "editor",
"config": {
"objectPath": "[jetkvm.eez-project]:/userPages/4",
"permanent": true
},
"icon": "svg:page"
},
{
"type": "tab",
"id": "#f8258cf3-e12a-4be9-8021-33199674dbe6",
"id": "#f5a057a5-977c-46be-8702-5447d603a34f",
"name": "MenuScreen",
"component": "editor",
"config": {
@ -247,7 +247,7 @@
},
{
"type": "tab",
"id": "#5dcfc8a4-36a7-4dea-a0cc-8dbeb4aab914",
"id": "#8af97439-79ee-4705-be34-53ca8814f3a0",
"name": "Settings",
"component": "editor",
"config": {
@ -257,8 +257,7 @@
},
"icon": "material:settings"
}
],
"active": true
]
},
{
"type": "row",
@ -1066,7 +1065,8 @@
"0": {
"0": {
"0": {
"1": {}
"1": {},
"$selected": true
}
}
}
@ -1090,6 +1090,7 @@
"0": {
"0": {
"0": {
"0": {},
"1": {
"0": {}
}
@ -1101,7 +1102,9 @@
"3": {
"0": {
"0": {},
"1": {}
"1": {
"$selected": true
}
},
"1": {
"0": {},
@ -1154,8 +1157,8 @@
},
"transform": {
"translate": {
"x": -194,
"y": -454
"x": -230,
"y": -94
},
"scale": 1
},
@ -1171,9 +1174,8 @@
"0": {
"0": {
"0": {
"0": {
"$selected": true
}
"0": {},
"$selected": true
},
"1": {
"0": {
@ -1201,11 +1203,28 @@
}
},
"[jetkvm.eez-project]:/userPages/4[flow-state]": {
"selection": {},
"selection": {
"0": {
"0": {
"0": {
"0": {
"$selected": true
}
},
"1": {
"0": {
"0": {},
"1": {},
"2": {}
}
}
}
}
},
"transform": {
"translate": {
"x": -150,
"y": -120
"x": -176,
"y": -127
},
"scale": 1
},
@ -1226,12 +1245,19 @@
"1": {
"0": {
"0": {},
"1": {},
"1": {
"1": {}
},
"2": {},
"3": {},
"4": {},
"5": {},
"6": {}
"5": {
"1": {
"$selected": true
}
},
"6": {},
"7": {}
}
}
}
@ -1266,6 +1292,38 @@
"secondToPx": 200,
"scrollLeft": 0
}
},
"[jetkvm.eez-project]:/userPages/6[flow-state]": {
"selection": {
"0": {
"0": {
"0": {},
"1": {
"0": {
"0": {},
"2": {
"1": {
"$selected": true
}
}
}
}
}
}
},
"transform": {
"translate": {
"x": -138,
"y": -122
},
"scale": 1
},
"timeline": {
"isEditorActive": false,
"position": 0,
"secondToPx": 200,
"scrollLeft": 0
}
}
},
"activeOutputSection": 0,
@ -1281,13 +1339,10 @@
"logsPanelFilter": "all",
"selectedStylePropertyName": "",
"lvglPart": "MAIN",
"lvglState": "DEFAULT",
"lvglState": "DISABLED",
"lvglExpandedPropertiesGroup": [
"POSITION AND SIZE",
"PADDING",
"MARGIN",
"TEXT",
"BACKGROUND"
"TEXT"
],
"showInactiveFlowsInDebugger": true,
"globalFlowZoom": true,

View File

@ -1,6 +1,17 @@
package native
import "github.com/jetkvm/kvm/internal/logging"
import (
"github.com/jetkvm/kvm/internal/logging"
"github.com/rs/zerolog"
)
var nativeLogger = logging.GetSubsystemLogger("native")
var displayLogger = logging.GetSubsystemLogger("display")
type nativeLogMessage struct {
Level zerolog.Level
Message string
File string
FuncName string
Line int
}

View File

@ -13,15 +13,19 @@ type Native struct {
lD *zerolog.Logger
systemVersion *semver.Version
appVersion *semver.Version
displayRotation uint16
onVideoStateChange func(state VideoState)
onVideoFrameReceived func(frame []byte, duration time.Duration)
onIndevEvent func(event string)
}
type NativeOptions struct {
SystemVersion *semver.Version
AppVersion *semver.Version
DisplayRotation uint16
OnVideoStateChange func(state VideoState)
OnVideoFrameReceived func(frame []byte, duration time.Duration)
OnIndevEvent func(event string)
}
func NewNative(opts NativeOptions) *Native {
@ -39,123 +43,39 @@ func NewNative(opts NativeOptions) *Native {
}
}
onIndevEvent := opts.OnIndevEvent
if onIndevEvent == nil {
onIndevEvent = func(event string) {
nativeLogger.Info().Str("event", event).Msg("indev event")
}
}
return &Native{
ready: make(chan struct{}),
l: nativeLogger,
lD: displayLogger,
systemVersion: opts.SystemVersion,
appVersion: opts.AppVersion,
displayRotation: opts.DisplayRotation,
onVideoStateChange: opts.OnVideoStateChange,
onVideoFrameReceived: opts.OnVideoFrameReceived,
onIndevEvent: opts.OnIndevEvent,
}
}
func (n *Native) Start() {
go n.StartNativeVideo()
go n.HandleVideoChan()
// set up singleton
setInstance(n)
setUpNativeHandlers()
// start the native video
go n.handleLogChan()
go n.handleVideoStateChan()
go n.handleVideoFrameChan()
go n.handleIndevEventChan()
n.initUI()
go n.tickUI()
close(n.ready)
}
func (n *Native) HandleVideoChan() {
lastFrame := time.Now()
for {
frame := <-jkVideoChan
now := time.Now()
sinceLastFrame := now.Sub(lastFrame)
lastFrame = now
n.onVideoFrameReceived(frame, sinceLastFrame)
}
}
// func handleCtrlClient(conn net.Conn) {
// defer conn.Close()
// scopedLogger := nativeLogger.With().
// Str("addr", conn.RemoteAddr().String()).
// Str("type", "ctrl").
// Logger()
// scopedLogger.Info().Msg("native ctrl socket client connected")
// if ctrlSocketConn != nil {
// scopedLogger.Debug().Msg("closing existing native socket connection")
// ctrlSocketConn.Close()
// }
// ctrlSocketConn = conn
// // Restore HDMI EDID if applicable
// go restoreHdmiEdid()
// readBuf := make([]byte, 4096)
// for {
// n, err := conn.Read(readBuf)
// if err != nil {
// scopedLogger.Warn().Err(err).Msg("error reading from ctrl sock")
// break
// }
// readMsg := string(readBuf[:n])
// ctrlResp := CtrlResponse{}
// err = json.Unmarshal([]byte(readMsg), &ctrlResp)
// if err != nil {
// scopedLogger.Warn().Err(err).Str("data", readMsg).Msg("error parsing ctrl sock msg")
// continue
// }
// scopedLogger.Trace().Interface("data", ctrlResp).Msg("ctrl sock msg")
// if ctrlResp.Seq != 0 {
// responseChan, ok := ongoingRequests[ctrlResp.Seq]
// if ok {
// responseChan <- &ctrlResp
// }
// }
// switch ctrlResp.Event {
// case "video_input_state":
// HandleVideoStateMessage(ctrlResp)
// }
// }
// scopedLogger.Debug().Msg("ctrl sock disconnected")
// }
// func handleVideoClient(conn net.Conn) {
// defer conn.Close()
// scopedLogger := nativeLogger.With().
// Str("addr", conn.RemoteAddr().String()).
// Str("type", "video").
// Logger()
// scopedLogger.Info().Msg("native video socket client connected")
// inboundPacket := make([]byte, maxFrameSize)
// lastFrame := time.Now()
// for {
// n, err := conn.Read(inboundPacket)
// if err != nil {
// scopedLogger.Warn().Err(err).Msg("error during read")
// return
// }
// now := time.Now()
// sinceLastFrame := now.Sub(lastFrame)
// lastFrame = now
// if currentSession != nil {
// err := currentSession.VideoTrack.WriteSample(media.Sample{Data: inboundPacket[:n], Duration: sinceLastFrame})
// if err != nil {
// scopedLogger.Warn().Err(err).Msg("error writing sample")
// }
// }
// }
// }
// // Restore the HDMI EDID value from the config.
// // Called after successful connection to jetkvm_native.
// func restoreHdmiEdid() {
// if config.EdidString != "" {
// nativeLogger.Info().Str("edid", config.EdidString).Msg("Restoring HDMI EDID")
// _, err := CallCtrlAction("set_edid", map[string]interface{}{"edid": config.EdidString})
// if err != nil {
// nativeLogger.Warn().Err(err).Msg("Failed to restore HDMI EDID")
// }
// }
// }

21
internal/native/single.go Normal file
View File

@ -0,0 +1,21 @@
package native
import "sync"
var (
instance *Native
instanceLock sync.RWMutex
)
func setInstance(n *Native) {
instanceLock.Lock()
defer instanceLock.Unlock()
if instance == nil {
instance = n
}
if instance != n {
panic("instance is already set")
}
}

View File

@ -1,7 +1,5 @@
package native
import "fmt"
type VideoState struct {
Ready bool `json:"ready"`
Error string `json:"error,omitempty"` //no_signal, no_lock, out_of_range
@ -10,7 +8,18 @@ type VideoState struct {
FramePerSecond float64 `json:"fps"`
}
func (n *Native) handleVideoStateMessage(state VideoState) {
nativeLogger.Info().Msg("video state handler")
nativeLogger.Info().Msg(fmt.Sprintf("state: %+v", state))
func (n *Native) VideoSetQualityFactor(factor float64) error {
return videoSetStreamQualityFactor(factor)
}
func (n *Native) VideoGetQualityFactor() (float64, error) {
return videoGetStreamQualityFactor()
}
func (n *Native) VideoSetEDID(edid string) error {
return videoSetEDID(edid)
}
func (n *Native) VideoGetEDID() (string, error) {
return videoGetEDID()
}

View File

@ -200,7 +200,7 @@ func rpcGetStreamQualityFactor() (float64, error) {
func rpcSetStreamQualityFactor(factor float64) error {
logger.Info().Float64("factor", factor).Msg("Setting stream quality factor")
err := nativeInstance.SetStreamQualityFactor(factor)
err := nativeInstance.VideoSetQualityFactor(factor)
if err != nil {
return err
}
@ -222,7 +222,7 @@ func rpcSetAutoUpdateState(enabled bool) (bool, error) {
}
func rpcGetEDID() (string, error) {
resp, err := nativeInstance.GetEDID()
resp, err := nativeInstance.VideoGetEDID()
if err != nil {
return "", err
}
@ -236,7 +236,7 @@ func rpcSetEDID(edid string) error {
} else {
logger.Info().Str("edid", edid).Msg("Setting EDID")
}
err := nativeInstance.SetEDID(edid)
err := nativeInstance.VideoSetEDID(edid)
if err != nil {
return err
}
@ -286,14 +286,25 @@ func rpcTryUpdate() error {
}
func rpcSetDisplayRotation(params DisplayRotationSettings) error {
var err error
_, err = nativeInstance.DispSetRotation(params.Rotation)
if err == nil {
config.DisplayRotation = params.Rotation
if err := SaveConfig(); err != nil {
return fmt.Errorf("failed to save config: %w", err)
}
currentRotation := config.DisplayRotation
if currentRotation == params.Rotation {
return nil
}
err := config.SetDisplayRotation(params.Rotation)
if err != nil {
return err
}
_, err = nativeInstance.DisplaySetRotation(config.GetDisplayRotation())
if err != nil {
return err
}
if err := SaveConfig(); err != nil {
return fmt.Errorf("failed to save config: %w", err)
}
return err
}

View File

@ -11,14 +11,20 @@ import (
var nativeInstance *native.Native
func initNative(systemVersion *semver.Version, appVersion *semver.Version) {
nativeInstance = native.NewNative(native.NativeOptions{
SystemVersion: systemVersion,
AppVersion: appVersion,
SystemVersion: systemVersion,
AppVersion: appVersion,
DisplayRotation: config.GetDisplayRotation(),
OnVideoStateChange: func(state native.VideoState) {
lastVideoState = state
triggerVideoStateUpdate()
requestDisplayUpdate(true)
},
OnIndevEvent: func(event string) {
nativeLogger.Trace().Str("event", event).Msg("indev event received")
wakeDisplay(false)
},
OnVideoFrameReceived: func(frame []byte, duration time.Duration) {
if currentSession != nil {
err := currentSession.VideoTrack.WriteSample(media.Sample{Data: frame, Duration: duration})