diff --git a/.devcontainer/install-deps.sh b/.devcontainer/install-deps.sh index e2ff43e6f..043c5c44a 100755 --- a/.devcontainer/install-deps.sh +++ b/.devcontainer/install-deps.sh @@ -18,6 +18,7 @@ sudo apt-get install -y --no-install-recommends \ build-essential \ device-tree-compiler \ gperf g++-multilib gcc-multilib \ + gdb-multiarch \ libnl-3-dev libdbus-1-dev libelf-dev libmpc-dev dwarves \ bc openssl flex bison libssl-dev python3 python-is-python3 texinfo kmod cmake \ wget zstd \ diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 000000000..91241db04 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,17 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "compilerPath": "/opt/jetkvm-native-buildkit/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc", + "cStandard": "c17", + "cppStandard": "gnu++17", + "intelliSenseMode": "linux-gcc-arm", + "configurationProvider": "ms-vscode.cmake-tools" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..710996ccb --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "GDB Debug - Native (binary)", + "type": "cppdbg", + "request": "launch", + "program": "internal/native/cgo/build/jknative-bin", + "args": [], + "stopAtEntry": true, + "cwd": "${workspaceFolder}", + "environment": [], + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb-multiarch", + "miDebuggerServerAddress": "${config:TARGET_IP}:${config:DEBUG_PORT}", + "targetArchitecture": "arm", + "preLaunchTask": "deploy", + "setupCommands": [ + { + "description": "Pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "externalConsole": true + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 5aeb206a9..a610159c9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,8 +10,13 @@ ] }, "git.ignoreLimitWarning": true, - "cmake.sourceDirectory": "/workspaces/kvm-static-ip/internal/native/cgo", + "cmake.sourceDirectory": "${workspaceFolder}/internal/native/cgo", "cmake.ignoreCMakeListsMissing": true, + "C_Cpp.inlayHints.autoDeclarationTypes.enabled": true, + "C_Cpp.inlayHints.parameterNames.enabled": true, + "C_Cpp.inlayHints.referenceOperator.enabled": true, + "TARGET_IP": "192.168.0.199", + "DEBUG_PORT": "2345", "json.schemas": [ { "fileMatch": [ diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..e1f9accca --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,30 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "deploy", + "isBackground": true, + "type": "shell", + "command": "bash", + "args": [ + "dev_deploy.sh", + "-r", + "${config:TARGET_IP}", + "--gdb-port", + "${config:DEBUG_PORT}", + "--native-binary", + "--disable-docker" + ], + "problemMatcher": { + "base": "$gcc", + "background": { + "activeOnStart": true, + "beginsPattern": "${config:BINARY}", + "endsPattern": "Listening on port [0-9]{4}" + } + } + }, + ] +} diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 0ab65c4f2..9d3a4b624 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -208,6 +208,12 @@ rm /userdata/kvm_config.json systemctl restart jetkvm ``` +### Debug native code with gdbserver + +Change the `TARGET_IP` in `.vscode/settings.json` to your JetKVM device IP, then set breakpoints in your native code and start the `Debug Native` configuration in VSCode. + +The code and GDB server will be deployed automatically. + --- ## Testing Your Changes diff --git a/Makefile b/Makefile index e519a75a2..525114e38 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,8 @@ SKIP_NATIVE_IF_EXISTS ?= 0 SKIP_UI_BUILD ?= 0 ENABLE_SYNC_TRACE ?= 0 +CMAKE_BUILD_TYPE ?= Release + GO_BUILD_ARGS := -tags netgo,timetzdata,nomsgpack ifeq ($(ENABLE_SYNC_TRACE), 1) GO_BUILD_ARGS := $(GO_BUILD_ARGS),synctrace @@ -52,6 +54,7 @@ build_native: echo "Building native..."; \ CC="$(BUILDKIT_PATH)/bin/$(BUILDKIT_FLAVOR)-gcc" \ LD="$(BUILDKIT_PATH)/bin/$(BUILDKIT_FLAVOR)-ld" \ + CMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE) \ ./scripts/build_cgo.sh; \ fi diff --git a/cmd/main.go b/cmd/main.go index 9a1e18997..fcf2cdfee 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -13,23 +13,39 @@ import ( "github.com/erikdubbelboer/gspt" "github.com/jetkvm/kvm" + "github.com/jetkvm/kvm/internal/native" + "github.com/jetkvm/kvm/internal/supervisor" ) -const ( - envChildID = "JETKVM_CHILD_ID" - errorDumpDir = "/userdata/jetkvm/crashdump" - errorDumpLastFile = "last-crash.log" - errorDumpTemplate = "jetkvm-%s.log" +var ( + subcomponent string ) func program() { - gspt.SetProcTitle(os.Args[0] + " [app]") - kvm.Main() + subcomponentOverride := os.Getenv(supervisor.EnvSubcomponent) + if subcomponentOverride != "" { + subcomponent = subcomponentOverride + } + switch subcomponent { + case "native": + native.RunNativeProcess(os.Args[0]) + default: + kvm.Main() + } +} + +func setProcTitle(status string) { + if status != "" { + status = " " + status + } + title := fmt.Sprintf("jetkvm: [supervisor]%s", status) + gspt.SetProcTitle(title) } func main() { versionPtr := flag.Bool("version", false, "print version and exit") versionJSONPtr := flag.Bool("version-json", false, "print version as json and exit") + flag.StringVar(&subcomponent, "subcomponent", "", "subcomponent to run") flag.Parse() if *versionPtr || *versionJSONPtr { @@ -42,7 +58,7 @@ func main() { return } - childID := os.Getenv(envChildID) + childID := os.Getenv(supervisor.EnvChildID) switch childID { case "": doSupervise() @@ -55,6 +71,8 @@ func main() { } func supervise() error { + setProcTitle("") + // check binary path binPath, err := os.Executable() if err != nil { @@ -74,11 +92,11 @@ func supervise() error { // run the child binary cmd := exec.Command(binPath) - lastFilePath := filepath.Join(errorDumpDir, errorDumpLastFile) + lastFilePath := filepath.Join(supervisor.ErrorDumpDir, supervisor.ErrorDumpLastFile) cmd.Env = append(os.Environ(), []string{ - fmt.Sprintf("%s=%s", envChildID, kvm.GetBuiltAppVersion()), - fmt.Sprintf("JETKVM_LAST_ERROR_PATH=%s", lastFilePath), + fmt.Sprintf("%s=%s", supervisor.EnvChildID, kvm.GetBuiltAppVersion()), + fmt.Sprintf("%s=%s", supervisor.ErrorDumpLastFile, lastFilePath), }...) cmd.Args = os.Args @@ -99,6 +117,8 @@ func supervise() error { return fmt.Errorf("failed to start command: %w", startErr) } + setProcTitle(fmt.Sprintf("started (pid=%d)", cmd.Process.Pid)) + go func() { sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGTERM) @@ -107,8 +127,6 @@ func supervise() error { _ = cmd.Process.Signal(sig) }() - gspt.SetProcTitle(os.Args[0] + " [sup]") - cmdErr := cmd.Wait() if cmdErr == nil { return nil @@ -186,11 +204,11 @@ func renameFile(f *os.File, newName string) error { func ensureErrorDumpDir() error { // TODO: check if the directory is writable - f, err := os.Stat(errorDumpDir) + f, err := os.Stat(supervisor.ErrorDumpDir) if err == nil && f.IsDir() { return nil } - if err := os.MkdirAll(errorDumpDir, 0755); err != nil { + if err := os.MkdirAll(supervisor.ErrorDumpDir, 0755); err != nil { return fmt.Errorf("failed to create error dump directory: %w", err) } return nil @@ -200,7 +218,7 @@ func createErrorDump(logFile *os.File) { fmt.Println() fileName := fmt.Sprintf( - errorDumpTemplate, + supervisor.ErrorDumpTemplate, time.Now().Format("20060102-150405"), ) @@ -210,7 +228,7 @@ func createErrorDump(logFile *os.File) { return } - filePath := filepath.Join(errorDumpDir, fileName) + filePath := filepath.Join(supervisor.ErrorDumpDir, fileName) if err := renameFile(logFile, filePath); err != nil { fmt.Printf("failed to rename file: %v\n", err) return @@ -218,7 +236,7 @@ func createErrorDump(logFile *os.File) { fmt.Printf("error dump copied: %s\n", filePath) - lastFilePath := filepath.Join(errorDumpDir, errorDumpLastFile) + lastFilePath := filepath.Join(supervisor.ErrorDumpDir, supervisor.ErrorDumpLastFile) if err := ensureSymlink(filePath, lastFilePath); err != nil { fmt.Printf("failed to create symlink: %v\n", err) diff --git a/config.go b/config.go index 471c7665e..a06febd56 100644 --- a/config.go +++ b/config.go @@ -113,6 +113,7 @@ type Config struct { DefaultLogLevel string `json:"default_log_level"` VideoSleepAfterSec int `json:"video_sleep_after_sec"` VideoQualityFactor float64 `json:"video_quality_factor"` + NativeMaxRestart uint `json:"native_max_restart_attempts"` } // GetUpdateAPIURL returns the update API URL diff --git a/display.go b/display.go index 68723b598..bf802e05d 100644 --- a/display.go +++ b/display.go @@ -232,6 +232,14 @@ func updateStaticContents() { // nativeInstance.UpdateLabelAndChangeVisibility("boot_screen_device_id", GetDeviceID()) } +// configureDisplayOnNativeRestart is called when the native process restarts +// it ensures the display is configured correctly after the restart +func configureDisplayOnNativeRestart() { + displayLogger.Info().Msg("native restarted, configuring display") + updateStaticContents() + requestDisplayUpdate(true, "native_restart") +} + // setDisplayBrightness sets /sys/class/backlight/backlight/brightness to alter // the backlight brightness of the JetKVM hardware's display. func setDisplayBrightness(brightness int, reason string) error { diff --git a/failsafe.go b/failsafe.go index 3c6b3d3aa..14e7bbd5b 100644 --- a/failsafe.go +++ b/failsafe.go @@ -77,10 +77,12 @@ func checkFailsafeReason() { _ = os.Remove(lastCrashPath) // TODO: read the goroutine stack trace and check which goroutine is panicking + failsafeModeActive = true if strings.Contains(failsafeCrashLog, "runtime.cgocall") { - failsafeModeActive = true failsafeModeReason = "video" return + } else { + failsafeModeReason = "unknown" } }) } diff --git a/go.mod b/go.mod index 404215b0f..aac587993 100644 --- a/go.mod +++ b/go.mod @@ -44,6 +44,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bytedance/sonic v1.14.0 // indirect github.com/bytedance/sonic/loader v0.3.0 // indirect + github.com/caarlos0/env/v11 v11.3.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect github.com/creack/goselect v0.1.2 // indirect @@ -87,6 +88,7 @@ require ( github.com/prometheus/client_model v0.6.2 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect github.com/ugorji/go/codec v1.3.0 // indirect @@ -97,6 +99,9 @@ require ( golang.org/x/oauth2 v0.32.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/text v0.30.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b // indirect + google.golang.org/grpc v1.76.0 // indirect + google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 // indirect google.golang.org/protobuf v1.36.10 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 1cb90138c..8d33b159a 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQ github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA= +github.com/caarlos0/env/v11 v11.3.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chemhack/go-nbd v0.0.0-20241006125820-59e45f5b1e7b h1:dSbDgy72Y1sjLPWLv7vs0fMFuhMBMViiT9PJZiZWZNs= @@ -173,6 +175,8 @@ github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= github.com/sourcegraph/tf-dag v0.2.2-0.20250131204052-3e8ff1477b4f h1:VgoRCP1efSCEZIcF2THLQ46+pIBzzgNiaUBe9wEDwYU= github.com/sourcegraph/tf-dag v0.2.2-0.20250131204052-3e8ff1477b4f/go.mod h1:pzro7BGorij2WgrjEammtrkbo3+xldxo+KaGLGUiD+Q= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -226,6 +230,12 @@ golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b h1:zPKJod4w6F1+nRGDI9ubnXYhU9NSWoFAijkHkUXeTK8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= +google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA= google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/native/README.md b/internal/native/README.md new file mode 100644 index 000000000..97a2cf79a --- /dev/null +++ b/internal/native/README.md @@ -0,0 +1,20 @@ +# jetkvm-native + +This component (`internal/native/`) acts as a bridge between Golang and native (C/C++) code. +It manages spawning and communicating with a native process via sockets (gRPC and Unix stream). + +For performance-critical operations such as video frame, **a dedicated Unix socket should be used** to avoid the overhead of gRPC and ensure low-latency communication. + +## Debugging + +To enable debug mode, create a file called `.native-debug-mode` in the `/userdata/jetkvm` directory. + +```bash +touch /userdata/jetkvm/.native-debug-mode +``` + +This will cause the native process to listen for SIGHUP signal and crash the process. + +```bash +pgrep native | xargs kill -SIGHUP +``` \ No newline at end of file diff --git a/internal/native/cgo/CMakeLists.txt b/internal/native/cgo/CMakeLists.txt index 739dabe52..c4c01dedf 100644 --- a/internal/native/cgo/CMakeLists.txt +++ b/internal/native/cgo/CMakeLists.txt @@ -42,6 +42,8 @@ FetchContent_MakeAvailable(lvgl) # Get source files, excluding CMake generated files file(GLOB_RECURSE sources CONFIGURE_DEPENDS "*.c" "ui/*.c") list(FILTER sources EXCLUDE REGEX "CMakeFiles.*CompilerId.*\\.c$") +# Exclude main.c from library sources (it's used for the binary target) +list(FILTER sources EXCLUDE REGEX "main\\.c$") add_library(jknative STATIC ${sources} ${CMAKE_CURRENT_SOURCE_DIR}/ctrl.h) @@ -68,4 +70,14 @@ target_link_libraries(jknative PRIVATE # libgpiod ) -install(TARGETS jknative DESTINATION lib) \ No newline at end of file +# Binary target using main.c as entry point +add_executable(jknative-bin ${CMAKE_CURRENT_SOURCE_DIR}/main.c) + +# Link the binary to the library (if needed in the future) +target_link_libraries(jknative-bin PRIVATE + jknative + pthread +) + +install(TARGETS jknative DESTINATION lib) +install(TARGETS jknative-bin DESTINATION bin) \ No newline at end of file diff --git a/internal/native/cgo/main.c b/internal/native/cgo/main.c new file mode 100644 index 000000000..a3e0e22fd --- /dev/null +++ b/internal/native/cgo/main.c @@ -0,0 +1,227 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ctrl.h" +#include "main.h" + +#define SOCKET_PATH "/tmp/video.sock" +#define BUFFER_SIZE 4096 + +// Global state +static int client_fd = -1; +static pthread_mutex_t client_fd_mutex = PTHREAD_MUTEX_INITIALIZER; + +void jetkvm_c_log_handler(int level, const char *filename, const char *funcname, int line, const char *message) { + // printf("[%s] %s:%d %s: %s\n", filename ? filename : "unknown", funcname ? funcname : "unknown", line, message ? message : ""); + fprintf(stderr, "[%s] %s:%d %s: %s\n", filename ? filename : "unknown", funcname ? funcname : "unknown", line, message ? message : ""); +} + +// Video handler that pipes frames to the Unix socket +// This will be called by the video subsystem via video_send_frame -> jetkvm_set_video_handler's handler +void jetkvm_video_handler(const uint8_t *frame, ssize_t len) { + // pthread_mutex_lock(&client_fd_mutex); + // if (client_fd >= 0 && frame != NULL && len > 0) { + // ssize_t bytes_written = 0; + // while (bytes_written < len) { + // ssize_t n = write(client_fd, frame + bytes_written, len - bytes_written); + // if (n < 0) { + // if (errno == EPIPE || errno == ECONNRESET) { + // // Client disconnected + // close(client_fd); + // client_fd = -1; + // break; + // } + // perror("write"); + // break; + // } + // bytes_written += n; + // } + // } + // pthread_mutex_unlock(&client_fd_mutex); +} + +void jetkvm_video_state_handler(jetkvm_video_state_t *state) { + fprintf(stderr, "Video state: {\n" + "\"ready\": %d,\n" + "\"error\": \"%s\",\n" + "\"width\": %d,\n" + "\"height\": %d,\n" + "\"frame_per_second\": %f\n" + "}\n", state->ready, state->error, state->width, state->height, state->frame_per_second); +} + +void jetkvm_indev_handler(int code) { + fprintf(stderr, "Video indev: %d\n", code); +} + +void jetkvm_rpc_handler(const char *method, const char *params) { + fprintf(stderr, "Video rpc: %s %s\n", method, params); +} + +// Note: jetkvm_set_video_handler, jetkvm_set_indev_handler, jetkvm_set_rpc_handler, +// jetkvm_call_rpc_handler, and jetkvm_set_video_state_handler are implemented in +// the library (ctrl.c) and will be used from there when linking. + +int main(int argc, char *argv[]) { + const char *socket_path = SOCKET_PATH; + + // Allow custom socket path via command line argument + if (argc > 1) { + socket_path = argv[1]; + } + + // Remove existing socket file if it exists + unlink(socket_path); + + // Set handlers + jetkvm_set_log_handler(&jetkvm_c_log_handler); + jetkvm_set_video_handler(&jetkvm_video_handler); + jetkvm_set_video_state_handler(&jetkvm_video_state_handler); + jetkvm_set_indev_handler(&jetkvm_indev_handler); + jetkvm_set_rpc_handler(&jetkvm_rpc_handler); + + // Initialize video first (before accepting connections) + fprintf(stderr, "Initializing video...\n"); + if (jetkvm_video_init(1.0) != 0) { + fprintf(stderr, "Failed to initialize video\n"); + return 1; + } + + // Start video streaming - frames will be sent via video_send_frame + // which calls the video handler we set up + jetkvm_video_start(); + fprintf(stderr, "Video streaming started.\n"); + + // Create Unix domain socket + int server_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (server_fd < 0) { + perror("socket"); + jetkvm_video_stop(); + jetkvm_video_shutdown(); + return 1; + } + + // Make socket non-blocking + int flags = fcntl(server_fd, F_GETFL, 0); + if (flags < 0 || fcntl(server_fd, F_SETFL, flags | O_NONBLOCK) < 0) { + perror("fcntl"); + close(server_fd); + jetkvm_video_stop(); + jetkvm_video_shutdown(); + return 1; + } + + // Bind socket to path + struct sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); + + if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("bind"); + close(server_fd); + jetkvm_video_stop(); + jetkvm_video_shutdown(); + return 1; + } + + // Listen for connections + if (listen(server_fd, 1) < 0) { + perror("listen"); + close(server_fd); + jetkvm_video_stop(); + jetkvm_video_shutdown(); + return 1; + } + + fprintf(stderr, "Listening on Unix socket: %s (non-blocking)\n", socket_path); + fprintf(stderr, "Video frames will be sent to connected clients...\n"); + + // Main loop: check for new connections and handle client disconnections + fd_set read_fds; + struct timeval timeout; + + while (1) { + FD_ZERO(&read_fds); + FD_SET(server_fd, &read_fds); + + pthread_mutex_lock(&client_fd_mutex); + int current_client_fd = client_fd; + if (current_client_fd >= 0) { + FD_SET(current_client_fd, &read_fds); + } + int max_fd = (current_client_fd > server_fd) ? current_client_fd : server_fd; + pthread_mutex_unlock(&client_fd_mutex); + + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + int result = select(max_fd + 1, &read_fds, NULL, NULL, &timeout); + if (result < 0) { + if (errno == EINTR) { + continue; + } + perror("select"); + break; + } + + // Check for new connection + if (FD_ISSET(server_fd, &read_fds)) { + int accepted_fd = accept(server_fd, NULL, NULL); + if (accepted_fd >= 0) { + fprintf(stderr, "Client connected\n"); + pthread_mutex_lock(&client_fd_mutex); + if (client_fd >= 0) { + // Close previous client if any + close(client_fd); + } + client_fd = accepted_fd; + pthread_mutex_unlock(&client_fd_mutex); + } else if (errno != EAGAIN && errno != EWOULDBLOCK) { + perror("accept"); + } + } + + // Check if client disconnected + pthread_mutex_lock(&client_fd_mutex); + current_client_fd = client_fd; + pthread_mutex_unlock(&client_fd_mutex); + + if (current_client_fd >= 0 && FD_ISSET(current_client_fd, &read_fds)) { + // Client sent data or closed connection + char buffer[1]; + if (read(current_client_fd, buffer, 1) <= 0) { + fprintf(stderr, "Client disconnected\n"); + pthread_mutex_lock(&client_fd_mutex); + close(client_fd); + client_fd = -1; + pthread_mutex_unlock(&client_fd_mutex); + } + } + } + + // Stop video streaming + jetkvm_video_stop(); + jetkvm_video_shutdown(); + + // Cleanup + pthread_mutex_lock(&client_fd_mutex); + if (client_fd >= 0) { + close(client_fd); + client_fd = -1; + } + pthread_mutex_unlock(&client_fd_mutex); + + close(server_fd); + unlink(socket_path); + + return 0; +} + diff --git a/internal/native/cgo/main.h b/internal/native/cgo/main.h new file mode 100644 index 000000000..d9cc6c498 --- /dev/null +++ b/internal/native/cgo/main.h @@ -0,0 +1,26 @@ +#ifndef JETKVM_NATIVE_MAIN_H +#define JETKVM_NATIVE_MAIN_H + +#include +#include +#include +#include +#include +#include +#include +#include "ctrl.h" + +void jetkvm_c_log_handler(int level, const char *filename, const char *funcname, int line, const char *message); +void jetkvm_video_handler(const uint8_t *frame, ssize_t len); +void jetkvm_video_state_handler(jetkvm_video_state_t *state); +void jetkvm_indev_handler(int code); +void jetkvm_rpc_handler(const char *method, const char *params); + + +// 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_rpc_handler_t)(const char *method, const char *params); +// typedef void (jetkvm_video_handler_t)(const uint8_t *frame, ssize_t len); +// typedef void (jetkvm_indev_handler_t)(int code); + +#endif \ No newline at end of file diff --git a/internal/native/cgo/test.patch b/internal/native/cgo/test.patch new file mode 100644 index 000000000..5f32a357b --- /dev/null +++ b/internal/native/cgo/test.patch @@ -0,0 +1,210 @@ +diff --git a/internal/native/cgo/video.c b/internal/native/cgo/video.c +index 2a4a034..760621a 100644 +--- a/internal/native/cgo/video.c ++++ b/internal/native/cgo/video.c +@@ -354,6 +354,10 @@ bool detected_signal = false, streaming_flag = false, streaming_stopped = true; + pthread_t *streaming_thread = NULL; + pthread_mutex_t streaming_mutex = PTHREAD_MUTEX_INITIALIZER; + ++// Diagnostic tracking for validation ++static uint64_t last_close_time = 0; ++static int consecutive_failures = 0; ++ + bool get_streaming_flag() + { + log_info("getting streaming flag"); +@@ -395,6 +399,12 @@ void *run_video_stream(void *arg) + continue; + } + ++ // Log attempt to open with timing info ++ RK_U64 time_since_close = last_close_time > 0 ? (get_us() - last_close_time) : 0; ++ log_info("[DIAG] Attempting to open %s (time_since_last_close=%llu us)", ++ VIDEO_DEV, time_since_close); ++ ++ RK_U64 open_start_time = get_us(); + int video_dev_fd = open(VIDEO_DEV, O_RDWR); + if (video_dev_fd < 0) + { +@@ -402,7 +412,9 @@ void *run_video_stream(void *arg) + usleep(1000000); + continue; + } +- log_info("opened video capture device %s", VIDEO_DEV); ++ RK_U64 open_end_time = get_us(); ++ log_info("[DIAG] opened video capture device %s in %llu us", ++ VIDEO_DEV, open_end_time - open_start_time); + + uint32_t width = detected_width; + uint32_t height = detected_height; +@@ -414,14 +426,45 @@ void *run_video_stream(void *arg) + fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUYV; + fmt.fmt.pix_mp.field = V4L2_FIELD_ANY; + ++ // Probe device state before attempting format set ++ struct v4l2_format query_fmt; ++ memset(&query_fmt, 0, sizeof(query_fmt)); ++ query_fmt.type = type; ++ int query_ret = ioctl(video_dev_fd, VIDIOC_G_FMT, &query_fmt); ++ log_info("[DIAG] VIDIOC_G_FMT probe: ret=%d, errno=%d (%s)", ++ query_ret, query_ret < 0 ? errno : 0, ++ query_ret < 0 ? strerror(errno) : "OK"); ++ ++ RK_U64 set_fmt_start_time = get_us(); ++ log_info("[DIAG] Attempting VIDIOC_S_FMT: %ux%u, time_since_open=%llu us", ++ width, height, set_fmt_start_time - open_end_time); ++ + if (ioctl(video_dev_fd, VIDIOC_S_FMT, &fmt) < 0) + { +- log_error("Set format fail: %s", strerror(errno)); ++ RK_U64 failure_time = get_us(); ++ int saved_errno = errno; ++ consecutive_failures++; ++ ++ log_error("[DIAG] Set format fail: errno=%d (%s)", saved_errno, strerror(saved_errno)); ++ log_error("[DIAG] Failure context: consecutive_failures=%d, time_since_open=%llu us, " ++ "time_since_last_close=%llu us, resolution=%ux%u, streaming_flag=%d", ++ consecutive_failures, ++ failure_time - open_end_time, ++ last_close_time > 0 ? (open_start_time - last_close_time) : 0, ++ width, height, ++ streaming_flag); ++ + usleep(100000); // Sleep for 100 milliseconds + close(video_dev_fd); ++ last_close_time = get_us(); ++ log_info("[DIAG] Closed device after format failure at %llu us", last_close_time); + continue; + } + ++ // Success - reset failure counter ++ log_info("[DIAG] VIDIOC_S_FMT succeeded (previous consecutive failures: %d)", consecutive_failures); ++ consecutive_failures = 0; ++ + struct v4l2_buffer buf; + + struct v4l2_requestbuffers req; +@@ -601,9 +644,46 @@ void *run_video_stream(void *arg) + } + cleanup: + log_info("cleaning up video capture device %s", VIDEO_DEV); +- if (ioctl(video_dev_fd, VIDIOC_STREAMOFF, &type) < 0) ++ ++ RK_U64 streamoff_start = get_us(); ++ log_info("[DIAG] Attempting VIDIOC_STREAMOFF"); ++ ++ int streamoff_ret = ioctl(video_dev_fd, VIDIOC_STREAMOFF, &type); ++ RK_U64 streamoff_end = get_us(); ++ ++ if (streamoff_ret < 0) ++ { ++ log_error("[DIAG] VIDIOC_STREAMOFF failed: errno=%d (%s), duration=%llu us", ++ errno, strerror(errno), streamoff_end - streamoff_start); ++ } ++ else ++ { ++ log_info("[DIAG] VIDIOC_STREAMOFF succeeded in %llu us", ++ streamoff_end - streamoff_start); ++ } ++ ++ // VALIDATION TEST: Explicitly free V4L2 buffer queue ++ struct v4l2_requestbuffers req_free; ++ memset(&req_free, 0, sizeof(req_free)); ++ req_free.count = 0; // Tell driver to free all buffers ++ req_free.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; ++ req_free.memory = V4L2_MEMORY_DMABUF; ++ ++ RK_U64 reqbufs_start = get_us(); ++ log_info("[DIAG] VALIDATION: Calling VIDIOC_REQBUFS(count=0) to free buffer queue"); ++ ++ int reqbufs_ret = ioctl(video_dev_fd, VIDIOC_REQBUFS, &req_free); ++ RK_U64 reqbufs_end = get_us(); ++ ++ if (reqbufs_ret < 0) ++ { ++ log_error("[DIAG] VALIDATION: REQBUFS(0) FAILED - errno=%d (%s), duration=%llu us", ++ errno, strerror(errno), reqbufs_end - reqbufs_start); ++ } ++ else + { +- log_error("VIDIOC_STREAMOFF failed: %s", strerror(errno)); ++ log_info("[DIAG] VALIDATION: REQBUFS(0) SUCCEEDED - freed buffers in %llu us", ++ reqbufs_end - reqbufs_start); + } + + venc_stop(); +@@ -617,9 +697,13 @@ void *run_video_stream(void *arg) + } + + log_info("closing video capture device %s", VIDEO_DEV); ++ RK_U64 close_start = get_us(); + close(video_dev_fd); ++ last_close_time = get_us(); ++ log_info("[DIAG] Device closed, took %llu us, timestamp=%llu", ++ last_close_time - close_start, last_close_time); + } +- ++ + log_info("video stream thread exiting"); + + streaming_stopped = true; +@@ -648,7 +732,7 @@ void video_shutdown() + RK_MPI_MB_DestroyPool(memPool); + } + log_info("Destroyed memory pool"); +- ++ + pthread_mutex_destroy(&streaming_mutex); + log_info("Destroyed streaming mutex"); + } +@@ -665,14 +749,14 @@ void video_start_streaming() + log_warn("video streaming already started"); + return; + } +- ++ + pthread_t *new_thread = malloc(sizeof(pthread_t)); + if (new_thread == NULL) + { + log_error("Failed to allocate memory for streaming thread"); + return; + } +- ++ + set_streaming_flag(true); + int result = pthread_create(new_thread, NULL, run_video_stream, NULL); + if (result != 0) +@@ -682,7 +766,7 @@ void video_start_streaming() + free(new_thread); + return; + } +- ++ + // Only set streaming_thread after successful creation + streaming_thread = new_thread; + } +@@ -693,7 +777,7 @@ void video_stop_streaming() + log_info("video streaming already stopped"); + return; + } +- ++ + log_info("stopping video streaming"); + set_streaming_flag(false); + +@@ -711,7 +795,7 @@ void video_stop_streaming() + free(streaming_thread); + streaming_thread = NULL; + +- log_info("video streaming stopped"); ++ log_info("video streaming stopped"); + } + + void video_restart_streaming() +@@ -818,4 +902,4 @@ void video_set_quality_factor(float factor) + + float video_get_quality_factor() { + return quality_factor; +-} +\ No newline at end of file ++} diff --git a/internal/native/cgo_linux.go b/internal/native/cgo_linux.go index be1a5a361..b33eb5347 100644 --- a/internal/native/cgo_linux.go +++ b/internal/native/cgo_linux.go @@ -50,17 +50,9 @@ static inline void jetkvm_cgo_setup_rpc_handler() { import "C" var ( - cgoLock sync.Mutex - cgoDisabled bool + cgoLock sync.Mutex ) -func setCgoDisabled(disabled bool) { - cgoLock.Lock() - defer cgoLock.Unlock() - - cgoDisabled = disabled -} - //export jetkvm_go_video_state_handler func jetkvm_go_video_state_handler(state *C.jetkvm_video_state_t) { videoState := VideoState{ @@ -104,10 +96,6 @@ func jetkvm_go_rpc_handler(method *C.cchar_t, params *C.cchar_t) { var eventCodeToNameMap = map[int]string{} func uiEventCodeToName(code int) string { - if cgoDisabled { - return "" - } - name, ok := eventCodeToNameMap[code] if !ok { cCode := C.int(code) @@ -120,10 +108,6 @@ func uiEventCodeToName(code int) string { } func setUpNativeHandlers() { - if cgoDisabled { - return - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -135,10 +119,6 @@ func setUpNativeHandlers() { } func uiInit(rotation uint16) { - if cgoDisabled { - return - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -148,10 +128,6 @@ func uiInit(rotation uint16) { } func uiTick() { - if cgoDisabled { - return - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -159,10 +135,6 @@ func uiTick() { } func videoInit(factor float64) error { - if cgoDisabled { - return nil - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -176,10 +148,6 @@ func videoInit(factor float64) error { } func videoShutdown() { - if cgoDisabled { - return - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -187,10 +155,6 @@ func videoShutdown() { } func videoStart() { - if cgoDisabled { - return - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -198,10 +162,6 @@ func videoStart() { } func videoStop() { - if cgoDisabled { - return - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -209,10 +169,6 @@ func videoStop() { } func videoLogStatus() string { - if cgoDisabled { - return "" - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -223,10 +179,6 @@ func videoLogStatus() string { } func uiSetVar(name string, value string) { - if cgoDisabled { - return - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -240,10 +192,6 @@ func uiSetVar(name string, value string) { } func uiGetVar(name string) string { - if cgoDisabled { - return "" - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -254,10 +202,6 @@ func uiGetVar(name string) string { } func uiSwitchToScreen(screen string) { - if cgoDisabled { - return - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -267,10 +211,6 @@ func uiSwitchToScreen(screen string) { } func uiGetCurrentScreen() string { - if cgoDisabled { - return "" - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -279,10 +219,6 @@ func uiGetCurrentScreen() string { } func uiObjAddState(objName string, state string) (bool, error) { - if cgoDisabled { - return false, nil - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -295,10 +231,6 @@ func uiObjAddState(objName string, state string) (bool, error) { } func uiObjClearState(objName string, state string) (bool, error) { - if cgoDisabled { - return false, nil - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -311,10 +243,6 @@ func uiObjClearState(objName string, state string) (bool, error) { } func uiGetLVGLVersion() string { - if cgoDisabled { - return "" - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -323,10 +251,6 @@ func uiGetLVGLVersion() string { // 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) { - if cgoDisabled { - return false, nil - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -339,10 +263,6 @@ func uiObjAddFlag(objName string, flag string) (bool, error) { } func uiObjClearFlag(objName string, flag string) (bool, error) { - if cgoDisabled { - return false, nil - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -363,10 +283,6 @@ func uiObjShow(objName string) (bool, error) { } func uiObjSetOpacity(objName string, opacity int) (bool, error) { - if cgoDisabled { - return false, nil - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -378,10 +294,6 @@ func uiObjSetOpacity(objName string, opacity int) (bool, error) { } func uiObjFadeIn(objName string, duration uint32) (bool, error) { - if cgoDisabled { - return false, nil - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -394,10 +306,6 @@ func uiObjFadeIn(objName string, duration uint32) (bool, error) { } func uiObjFadeOut(objName string, duration uint32) (bool, error) { - if cgoDisabled { - return false, nil - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -410,10 +318,6 @@ func uiObjFadeOut(objName string, duration uint32) (bool, error) { } func uiLabelSetText(objName string, text string) (bool, error) { - if cgoDisabled { - return false, nil - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -431,10 +335,6 @@ func uiLabelSetText(objName string, text string) (bool, error) { } func uiImgSetSrc(objName string, src string) (bool, error) { - if cgoDisabled { - return false, nil - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -450,10 +350,6 @@ func uiImgSetSrc(objName string, src string) (bool, error) { } func uiDispSetRotation(rotation uint16) (bool, error) { - if cgoDisabled { - return false, nil - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -466,10 +362,6 @@ func uiDispSetRotation(rotation uint16) (bool, error) { } func videoGetStreamQualityFactor() (float64, error) { - if cgoDisabled { - return 0, nil - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -478,10 +370,6 @@ func videoGetStreamQualityFactor() (float64, error) { } func videoSetStreamQualityFactor(factor float64) error { - if cgoDisabled { - return nil - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -490,10 +378,6 @@ func videoSetStreamQualityFactor(factor float64) error { } func videoGetEDID() (string, error) { - if cgoDisabled { - return "", nil - } - cgoLock.Lock() defer cgoLock.Unlock() @@ -502,10 +386,6 @@ func videoGetEDID() (string, error) { } func videoSetEDID(edid string) error { - if cgoDisabled { - return nil - } - cgoLock.Lock() defer cgoLock.Unlock() diff --git a/internal/native/empty.go b/internal/native/empty.go new file mode 100644 index 000000000..846cf9aed --- /dev/null +++ b/internal/native/empty.go @@ -0,0 +1,111 @@ +package native + +type EmptyNativeInterface struct { +} + +func (e *EmptyNativeInterface) Start() error { return nil } + +func (e *EmptyNativeInterface) VideoSetSleepMode(enabled bool) error { return nil } + +func (e *EmptyNativeInterface) VideoGetSleepMode() (bool, error) { return false, nil } + +func (e *EmptyNativeInterface) VideoSleepModeSupported() bool { + return false +} + +func (e *EmptyNativeInterface) VideoSetQualityFactor(factor float64) error { + return nil +} + +func (e *EmptyNativeInterface) VideoGetQualityFactor() (float64, error) { + return 0, nil +} + +func (e *EmptyNativeInterface) VideoSetEDID(edid string) error { + return nil +} + +func (e *EmptyNativeInterface) VideoGetEDID() (string, error) { + return "", nil +} + +func (e *EmptyNativeInterface) VideoLogStatus() (string, error) { + return "", nil +} + +func (e *EmptyNativeInterface) VideoStop() error { + return nil +} + +func (e *EmptyNativeInterface) VideoStart() error { + return nil +} + +func (e *EmptyNativeInterface) GetLVGLVersion() (string, error) { + return "", nil +} + +func (e *EmptyNativeInterface) UIObjHide(objName string) (bool, error) { + return false, nil +} + +func (e *EmptyNativeInterface) UIObjShow(objName string) (bool, error) { + return false, nil +} + +func (e *EmptyNativeInterface) UISetVar(name string, value string) { +} + +func (e *EmptyNativeInterface) UIGetVar(name string) string { + return "" +} + +func (e *EmptyNativeInterface) UIObjAddState(objName string, state string) (bool, error) { + return false, nil +} + +func (e *EmptyNativeInterface) UIObjClearState(objName string, state string) (bool, error) { + return false, nil +} + +func (e *EmptyNativeInterface) UIObjAddFlag(objName string, flag string) (bool, error) { + return false, nil +} + +func (e *EmptyNativeInterface) UIObjClearFlag(objName string, flag string) (bool, error) { + return false, nil +} + +func (e *EmptyNativeInterface) UIObjSetOpacity(objName string, opacity int) (bool, error) { + return false, nil +} + +func (e *EmptyNativeInterface) UIObjFadeIn(objName string, duration uint32) (bool, error) { + return false, nil +} + +func (e *EmptyNativeInterface) UIObjFadeOut(objName string, duration uint32) (bool, error) { + return false, nil +} + +func (e *EmptyNativeInterface) UIObjSetLabelText(objName string, text string) (bool, error) { + return false, nil +} + +func (e *EmptyNativeInterface) UIObjSetImageSrc(objName string, image string) (bool, error) { + return false, nil +} + +func (e *EmptyNativeInterface) DisplaySetRotation(rotation uint16) (bool, error) { + return false, nil +} + +func (e *EmptyNativeInterface) UpdateLabelIfChanged(objName string, newText string) {} + +func (e *EmptyNativeInterface) UpdateLabelAndChangeVisibility(objName string, newText string) {} + +func (e *EmptyNativeInterface) SwitchToScreenIf(screenName string, shouldSwitch []string) {} + +func (e *EmptyNativeInterface) SwitchToScreenIfDifferent(screenName string) {} + +func (e *EmptyNativeInterface) DoNotUseThisIsForCrashTestingOnly() {} diff --git a/internal/native/grpc_client.go b/internal/native/grpc_client.go new file mode 100644 index 000000000..300a22848 --- /dev/null +++ b/internal/native/grpc_client.go @@ -0,0 +1,274 @@ +package native + +import ( + "context" + "errors" + "fmt" + "io" + "sync" + "time" + + "github.com/rs/zerolog" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/status" + + pb "github.com/jetkvm/kvm/internal/native/proto" +) + +// GRPCClient wraps the gRPC client for the native service +type GRPCClient struct { + ctx context.Context + cancel context.CancelFunc + + conn *grpc.ClientConn + client pb.NativeServiceClient + logger *zerolog.Logger + + eventStream pb.NativeService_StreamEventsClient + eventM sync.RWMutex + eventCh chan *pb.Event + eventDone chan struct{} + + onVideoStateChange func(state VideoState) + onIndevEvent func(event string) + onRpcEvent func(event string) + + closed bool + closeM sync.Mutex +} + +type grpcClientOptions struct { + SocketPath string + Logger *zerolog.Logger + OnVideoStateChange func(state VideoState) + OnIndevEvent func(event string) + OnRpcEvent func(event string) +} + +// NewGRPCClient creates a new gRPC client connected to the native service +func NewGRPCClient(opts grpcClientOptions) (*GRPCClient, error) { + // Connect to the Unix domain socket + conn, err := grpc.NewClient( + fmt.Sprintf("unix-abstract:%v", opts.SocketPath), + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + return nil, fmt.Errorf("failed to connect to gRPC server: %w", err) + } + + client := pb.NewNativeServiceClient(conn) + + ctx, cancel := context.WithCancel(context.Background()) + + grpcClient := &GRPCClient{ + ctx: ctx, + cancel: cancel, + conn: conn, + client: client, + logger: opts.Logger, + eventCh: make(chan *pb.Event, 100), + eventDone: make(chan struct{}), + onVideoStateChange: opts.OnVideoStateChange, + onIndevEvent: opts.OnIndevEvent, + onRpcEvent: opts.OnRpcEvent, + } + + // Start event stream + go grpcClient.startEventStream() + + return grpcClient, nil +} + +func (c *GRPCClient) handleEventStream(stream pb.NativeService_StreamEventsClient) { + c.eventM.Lock() + c.eventStream = stream + defer func() { + c.eventStream = nil + c.eventM.Unlock() + }() + + for { + logger := c.logger.With().Interface("stream", stream).Logger() + if stream == nil { + logger.Error().Msg("event stream is nil") + break + } + + event, err := stream.Recv() + + if err != nil { + if errors.Is(err, io.EOF) { + logger.Debug().Msg("event stream closed") + } else { + logger.Warn().Err(err).Msg("event stream error") + } + break + } + + // enrich the logger with the event type and data, if debug mode is enabled + if c.logger.GetLevel() <= zerolog.DebugLevel { + logger = logger.With(). + Str("type", event.Type). + Interface("data", event.Data). + Logger() + } + logger.Trace().Msg("received event") + + select { + case c.eventCh <- event: + default: + logger.Warn().Msg("event channel full, dropping event") + } + } +} + +func (c *GRPCClient) startEventStream() { + for { + // check if the client is closed + c.closeM.Lock() + if c.closed { + c.closeM.Unlock() + return + } + c.closeM.Unlock() + + // check if the context is done + select { + case <-c.ctx.Done(): + c.logger.Info().Msg("event stream context done, closing") + return + default: + } + + stream, err := c.client.StreamEvents(c.ctx, &pb.Empty{}) + if err != nil { + c.logger.Warn().Err(err).Msg("failed to start event stream, retrying ...") + time.Sleep(5 * time.Second) + continue + } + + c.handleEventStream(stream) + + // Wait before retrying + time.Sleep(1 * time.Second) + } +} + +func (c *GRPCClient) checkIsReady(ctx context.Context) error { + c.logger.Trace().Msg("connection is idle, connecting ...") + + resp, err := c.client.IsReady(ctx, &pb.IsReadyRequest{}) + if err != nil { + if errors.Is(err, status.Error(codes.Unavailable, "")) { + return fmt.Errorf("timeout waiting for ready: %w", err) + } + return fmt.Errorf("failed to check if ready: %w", err) + } + if resp.Ready { + return nil + } + return nil +} + +// WaitReady waits for the gRPC connection to be ready +func (c *GRPCClient) WaitReady() error { + ctx, cancel := context.WithTimeout(c.ctx, 60*time.Second) + defer cancel() + + prevState := connectivity.Idle + for { + state := c.conn.GetState() + c.logger. + With(). + Str("state", state.String()). + Int("prev_state", int(prevState)). + Logger() + + prevState = state + if state == connectivity.Idle || state == connectivity.Ready { + if err := c.checkIsReady(ctx); err != nil { + time.Sleep(1 * time.Second) + continue + } + } + + c.logger.Info().Msg("waiting for connection to be ready") + + if state == connectivity.Ready { + return nil + } + if state == connectivity.Shutdown { + return fmt.Errorf("connection failed: %v", state) + } + + if !c.conn.WaitForStateChange(ctx, state) { + return ctx.Err() + } + } +} + +func (c *GRPCClient) handleEvent(event *pb.Event) { + switch event.Type { + case "video_state_change": + state := event.GetVideoState() + if state == nil { + c.logger.Warn().Msg("video state event is nil") + return + } + c.onVideoStateChange(VideoState{ + Ready: state.Ready, + Error: state.Error, + Width: int(state.Width), + Height: int(state.Height), + FramePerSecond: state.FramePerSecond, + }) + case "indev_event": + c.onIndevEvent(event.GetIndevEvent()) + case "rpc_event": + c.onRpcEvent(event.GetRpcEvent()) + default: + c.logger.Warn().Str("type", event.Type).Msg("unknown event type") + } +} + +// OnEvent registers an event handler +func (c *GRPCClient) OnEvent(eventType string, handler func(data interface{})) { + go func() { + for { + select { + case event := <-c.eventCh: + c.handleEvent(event) + case <-c.eventDone: + return + } + } + }() +} + +// Close closes the gRPC client +func (c *GRPCClient) Close() error { + c.closeM.Lock() + defer c.closeM.Unlock() + if c.closed { + return nil + } + c.closed = true + + // cancel all ongoing operations + c.cancel() + + close(c.eventDone) + + c.eventM.Lock() + if c.eventStream != nil { + if err := c.eventStream.CloseSend(); err != nil { + c.logger.Warn().Err(err).Msg("failed to close event stream") + } + } + c.eventM.Unlock() + + return c.conn.Close() +} diff --git a/internal/native/grpc_clientmethods.go b/internal/native/grpc_clientmethods.go new file mode 100644 index 000000000..c9a83adce --- /dev/null +++ b/internal/native/grpc_clientmethods.go @@ -0,0 +1,212 @@ +package native + +import ( + "context" + + pb "github.com/jetkvm/kvm/internal/native/proto" +) + +// Below are generated methods, do not edit manually + +// Video methods +func (c *GRPCClient) VideoSetSleepMode(enabled bool) error { + _, err := c.client.VideoSetSleepMode(context.Background(), &pb.VideoSetSleepModeRequest{Enabled: enabled}) + return err +} + +func (c *GRPCClient) VideoGetSleepMode() (bool, error) { + resp, err := c.client.VideoGetSleepMode(context.Background(), &pb.Empty{}) + if err != nil { + return false, err + } + return resp.Enabled, nil +} + +func (c *GRPCClient) VideoSleepModeSupported() bool { + resp, err := c.client.VideoSleepModeSupported(context.Background(), &pb.Empty{}) + if err != nil { + return false + } + return resp.Supported +} + +func (c *GRPCClient) VideoSetQualityFactor(factor float64) error { + _, err := c.client.VideoSetQualityFactor(context.Background(), &pb.VideoSetQualityFactorRequest{Factor: factor}) + return err +} + +func (c *GRPCClient) VideoGetQualityFactor() (float64, error) { + resp, err := c.client.VideoGetQualityFactor(context.Background(), &pb.Empty{}) + if err != nil { + return 0, err + } + return resp.Factor, nil +} + +func (c *GRPCClient) VideoSetEDID(edid string) error { + _, err := c.client.VideoSetEDID(context.Background(), &pb.VideoSetEDIDRequest{Edid: edid}) + return err +} + +func (c *GRPCClient) VideoGetEDID() (string, error) { + resp, err := c.client.VideoGetEDID(context.Background(), &pb.Empty{}) + if err != nil { + return "", err + } + return resp.Edid, nil +} + +func (c *GRPCClient) VideoLogStatus() (string, error) { + resp, err := c.client.VideoLogStatus(context.Background(), &pb.Empty{}) + if err != nil { + return "", err + } + return resp.Status, nil +} + +func (c *GRPCClient) VideoStop() error { + _, err := c.client.VideoStop(context.Background(), &pb.Empty{}) + return err +} + +func (c *GRPCClient) VideoStart() error { + _, err := c.client.VideoStart(context.Background(), &pb.Empty{}) + return err +} + +// UI methods +func (c *GRPCClient) GetLVGLVersion() (string, error) { + resp, err := c.client.GetLVGLVersion(context.Background(), &pb.Empty{}) + if err != nil { + return "", err + } + return resp.Version, nil +} + +func (c *GRPCClient) UIObjHide(objName string) (bool, error) { + resp, err := c.client.UIObjHide(context.Background(), &pb.UIObjHideRequest{ObjName: objName}) + if err != nil { + return false, err + } + return resp.Success, nil +} + +func (c *GRPCClient) UIObjShow(objName string) (bool, error) { + resp, err := c.client.UIObjShow(context.Background(), &pb.UIObjShowRequest{ObjName: objName}) + if err != nil { + return false, err + } + return resp.Success, nil +} + +func (c *GRPCClient) UISetVar(name string, value string) { + _, _ = c.client.UISetVar(context.Background(), &pb.UISetVarRequest{Name: name, Value: value}) +} + +func (c *GRPCClient) UIGetVar(name string) string { + resp, err := c.client.UIGetVar(context.Background(), &pb.UIGetVarRequest{Name: name}) + if err != nil { + return "" + } + return resp.Value +} + +func (c *GRPCClient) UIObjAddState(objName string, state string) (bool, error) { + resp, err := c.client.UIObjAddState(context.Background(), &pb.UIObjAddStateRequest{ObjName: objName, State: state}) + if err != nil { + return false, err + } + return resp.Success, nil +} + +func (c *GRPCClient) UIObjClearState(objName string, state string) (bool, error) { + resp, err := c.client.UIObjClearState(context.Background(), &pb.UIObjClearStateRequest{ObjName: objName, State: state}) + if err != nil { + return false, err + } + return resp.Success, nil +} + +func (c *GRPCClient) UIObjAddFlag(objName string, flag string) (bool, error) { + resp, err := c.client.UIObjAddFlag(context.Background(), &pb.UIObjAddFlagRequest{ObjName: objName, Flag: flag}) + if err != nil { + return false, err + } + return resp.Success, nil +} + +func (c *GRPCClient) UIObjClearFlag(objName string, flag string) (bool, error) { + resp, err := c.client.UIObjClearFlag(context.Background(), &pb.UIObjClearFlagRequest{ObjName: objName, Flag: flag}) + if err != nil { + return false, err + } + return resp.Success, nil +} + +func (c *GRPCClient) UIObjSetOpacity(objName string, opacity int) (bool, error) { + resp, err := c.client.UIObjSetOpacity(context.Background(), &pb.UIObjSetOpacityRequest{ObjName: objName, Opacity: int32(opacity)}) + if err != nil { + return false, err + } + return resp.Success, nil +} + +func (c *GRPCClient) UIObjFadeIn(objName string, duration uint32) (bool, error) { + resp, err := c.client.UIObjFadeIn(context.Background(), &pb.UIObjFadeInRequest{ObjName: objName, Duration: duration}) + if err != nil { + return false, err + } + return resp.Success, nil +} + +func (c *GRPCClient) UIObjFadeOut(objName string, duration uint32) (bool, error) { + resp, err := c.client.UIObjFadeOut(context.Background(), &pb.UIObjFadeOutRequest{ObjName: objName, Duration: duration}) + if err != nil { + return false, err + } + return resp.Success, nil +} + +func (c *GRPCClient) UIObjSetLabelText(objName string, text string) (bool, error) { + resp, err := c.client.UIObjSetLabelText(context.Background(), &pb.UIObjSetLabelTextRequest{ObjName: objName, Text: text}) + if err != nil { + return false, err + } + return resp.Success, nil +} + +func (c *GRPCClient) UIObjSetImageSrc(objName string, image string) (bool, error) { + resp, err := c.client.UIObjSetImageSrc(context.Background(), &pb.UIObjSetImageSrcRequest{ObjName: objName, Image: image}) + if err != nil { + return false, err + } + return resp.Success, nil +} + +func (c *GRPCClient) DisplaySetRotation(rotation uint16) (bool, error) { + resp, err := c.client.DisplaySetRotation(context.Background(), &pb.DisplaySetRotationRequest{Rotation: uint32(rotation)}) + if err != nil { + return false, err + } + return resp.Success, nil +} + +func (c *GRPCClient) UpdateLabelIfChanged(objName string, newText string) { + _, _ = c.client.UpdateLabelIfChanged(context.Background(), &pb.UpdateLabelIfChangedRequest{ObjName: objName, NewText: newText}) +} + +func (c *GRPCClient) UpdateLabelAndChangeVisibility(objName string, newText string) { + _, _ = c.client.UpdateLabelAndChangeVisibility(context.Background(), &pb.UpdateLabelAndChangeVisibilityRequest{ObjName: objName, NewText: newText}) +} + +func (c *GRPCClient) SwitchToScreenIf(screenName string, shouldSwitch []string) { + _, _ = c.client.SwitchToScreenIf(context.Background(), &pb.SwitchToScreenIfRequest{ScreenName: screenName, ShouldSwitch: shouldSwitch}) +} + +func (c *GRPCClient) SwitchToScreenIfDifferent(screenName string) { + _, _ = c.client.SwitchToScreenIfDifferent(context.Background(), &pb.SwitchToScreenIfDifferentRequest{ScreenName: screenName}) +} + +func (c *GRPCClient) DoNotUseThisIsForCrashTestingOnly() { + _, _ = c.client.DoNotUseThisIsForCrashTestingOnly(context.Background(), &pb.Empty{}) +} diff --git a/internal/native/grpc_server.go b/internal/native/grpc_server.go new file mode 100644 index 000000000..304203ced --- /dev/null +++ b/internal/native/grpc_server.go @@ -0,0 +1,157 @@ +package native + +import ( + "context" + "fmt" + "net" + "sync" + + "github.com/rs/zerolog" + "google.golang.org/grpc" + + pb "github.com/jetkvm/kvm/internal/native/proto" +) + +// grpcServer wraps the Native instance and implements the gRPC service +type grpcServer struct { + pb.UnimplementedNativeServiceServer + native *Native + logger *zerolog.Logger + eventChs []chan *pb.Event + eventM sync.Mutex +} + +// NewGRPCServer creates a new gRPC server for the native service +func NewGRPCServer(n *Native, logger *zerolog.Logger) *grpcServer { + s := &grpcServer{ + native: n, + logger: logger, + eventChs: make([]chan *pb.Event, 0), + } + + // Store original callbacks and wrap them to also broadcast events + originalVideoStateChange := n.onVideoStateChange + originalIndevEvent := n.onIndevEvent + originalRpcEvent := n.onRpcEvent + + // Wrap callbacks to both call original and broadcast events + n.onVideoStateChange = func(state VideoState) { + if originalVideoStateChange != nil { + originalVideoStateChange(state) + } + event := &pb.Event{ + Type: "video_state_change", + Data: &pb.Event_VideoState{ + VideoState: &pb.VideoState{ + Ready: state.Ready, + Error: state.Error, + Width: int32(state.Width), + Height: int32(state.Height), + FramePerSecond: state.FramePerSecond, + }, + }, + } + s.broadcastEvent(event) + } + + n.onIndevEvent = func(event string) { + if originalIndevEvent != nil { + originalIndevEvent(event) + } + s.broadcastEvent(&pb.Event{ + Type: "indev_event", + Data: &pb.Event_IndevEvent{ + IndevEvent: event, + }, + }) + } + + n.onRpcEvent = func(event string) { + if originalRpcEvent != nil { + originalRpcEvent(event) + } + s.broadcastEvent(&pb.Event{ + Type: "rpc_event", + Data: &pb.Event_RpcEvent{ + RpcEvent: event, + }, + }) + } + + return s +} + +func (s *grpcServer) broadcastEvent(event *pb.Event) { + s.eventM.Lock() + defer s.eventM.Unlock() + + for _, ch := range s.eventChs { + select { + case ch <- event: + default: + // Channel full, skip + } + } +} + +func (s *grpcServer) IsReady(ctx context.Context, req *pb.IsReadyRequest) (*pb.IsReadyResponse, error) { + return &pb.IsReadyResponse{Ready: true, VideoReady: true}, nil +} + +// StreamEvents streams events from the native process +func (s *grpcServer) StreamEvents(req *pb.Empty, stream pb.NativeService_StreamEventsServer) error { + setProcTitle("connected") + defer setProcTitle("waiting") + + eventCh := make(chan *pb.Event, 100) + + // Register this channel for events + s.eventM.Lock() + s.eventChs = append(s.eventChs, eventCh) + s.eventM.Unlock() + + // Unregister on exit + defer func() { + s.eventM.Lock() + defer s.eventM.Unlock() + for i, ch := range s.eventChs { + if ch == eventCh { + s.eventChs = append(s.eventChs[:i], s.eventChs[i+1:]...) + break + } + } + close(eventCh) + }() + + // Stream events + for { + select { + case event := <-eventCh: + if err := stream.Send(event); err != nil { + return err + } + case <-stream.Context().Done(): + return stream.Context().Err() + } + } +} + +// StartGRPCServer starts the gRPC server on a Unix domain socket +func StartGRPCServer(server *grpcServer, socketPath string, logger *zerolog.Logger) (*grpc.Server, net.Listener, error) { + lis, err := net.Listen("unix", socketPath) + if err != nil { + return nil, nil, fmt.Errorf("failed to listen on socket: %w", err) + } + + s := grpc.NewServer() + pb.RegisterNativeServiceServer(s, server) + + go func() { + if err := s.Serve(lis); err != nil { + logger.Error().Err(err).Msg("gRPC server error") + } + }() + + logger.Info().Str("socket", socketPath).Msg("gRPC server started") + return s, lis, nil +} diff --git a/internal/native/grpc_servermethods.go b/internal/native/grpc_servermethods.go new file mode 100644 index 000000000..cc16dfd10 --- /dev/null +++ b/internal/native/grpc_servermethods.go @@ -0,0 +1,230 @@ +package native + +import ( + "context" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + pb "github.com/jetkvm/kvm/internal/native/proto" +) + +// Below are generated methods, do not edit manually + +// Video methods +func (s *grpcServer) VideoSetSleepMode(ctx context.Context, req *pb.VideoSetSleepModeRequest) (*pb.Empty, error) { + if err := s.native.VideoSetSleepMode(req.Enabled); err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.Empty{}, nil +} + +func (s *grpcServer) VideoGetSleepMode(ctx context.Context, req *pb.Empty) (*pb.VideoGetSleepModeResponse, error) { + enabled, err := s.native.VideoGetSleepMode() + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.VideoGetSleepModeResponse{Enabled: enabled}, nil +} + +func (s *grpcServer) VideoSleepModeSupported(ctx context.Context, req *pb.Empty) (*pb.VideoSleepModeSupportedResponse, error) { + return &pb.VideoSleepModeSupportedResponse{Supported: s.native.VideoSleepModeSupported()}, nil +} + +func (s *grpcServer) VideoSetQualityFactor(ctx context.Context, req *pb.VideoSetQualityFactorRequest) (*pb.Empty, error) { + if err := s.native.VideoSetQualityFactor(req.Factor); err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.Empty{}, nil +} + +func (s *grpcServer) VideoGetQualityFactor(ctx context.Context, req *pb.Empty) (*pb.VideoGetQualityFactorResponse, error) { + factor, err := s.native.VideoGetQualityFactor() + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.VideoGetQualityFactorResponse{Factor: factor}, nil +} + +func (s *grpcServer) VideoSetEDID(ctx context.Context, req *pb.VideoSetEDIDRequest) (*pb.Empty, error) { + if err := s.native.VideoSetEDID(req.Edid); err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.Empty{}, nil +} + +func (s *grpcServer) VideoGetEDID(ctx context.Context, req *pb.Empty) (*pb.VideoGetEDIDResponse, error) { + edid, err := s.native.VideoGetEDID() + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.VideoGetEDIDResponse{Edid: edid}, nil +} + +func (s *grpcServer) VideoLogStatus(ctx context.Context, req *pb.Empty) (*pb.VideoLogStatusResponse, error) { + logStatus, err := s.native.VideoLogStatus() + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.VideoLogStatusResponse{Status: logStatus}, nil +} + +func (s *grpcServer) VideoStop(ctx context.Context, req *pb.Empty) (*pb.Empty, error) { + procPrefix = "jetkvm: [native]" + setProcTitle(lastProcTitle) + + if err := s.native.VideoStop(); err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.Empty{}, nil +} + +func (s *grpcServer) VideoStart(ctx context.Context, req *pb.Empty) (*pb.Empty, error) { + procPrefix = "jetkvm: [native+video]" + setProcTitle(lastProcTitle) + + if err := s.native.VideoStart(); err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.Empty{}, nil +} + +// UI methods +func (s *grpcServer) GetLVGLVersion(ctx context.Context, req *pb.Empty) (*pb.GetLVGLVersionResponse, error) { + version, err := s.native.GetLVGLVersion() + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.GetLVGLVersionResponse{Version: version}, nil +} + +func (s *grpcServer) UIObjHide(ctx context.Context, req *pb.UIObjHideRequest) (*pb.UIObjHideResponse, error) { + success, err := s.native.UIObjHide(req.ObjName) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.UIObjHideResponse{Success: success}, nil +} + +func (s *grpcServer) UIObjShow(ctx context.Context, req *pb.UIObjShowRequest) (*pb.UIObjShowResponse, error) { + success, err := s.native.UIObjShow(req.ObjName) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.UIObjShowResponse{Success: success}, nil +} + +func (s *grpcServer) UISetVar(ctx context.Context, req *pb.UISetVarRequest) (*pb.Empty, error) { + s.native.UISetVar(req.Name, req.Value) + return &pb.Empty{}, nil +} + +func (s *grpcServer) UIGetVar(ctx context.Context, req *pb.UIGetVarRequest) (*pb.UIGetVarResponse, error) { + value := s.native.UIGetVar(req.Name) + return &pb.UIGetVarResponse{Value: value}, nil +} + +func (s *grpcServer) UIObjAddState(ctx context.Context, req *pb.UIObjAddStateRequest) (*pb.UIObjAddStateResponse, error) { + success, err := s.native.UIObjAddState(req.ObjName, req.State) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.UIObjAddStateResponse{Success: success}, nil +} + +func (s *grpcServer) UIObjClearState(ctx context.Context, req *pb.UIObjClearStateRequest) (*pb.UIObjClearStateResponse, error) { + success, err := s.native.UIObjClearState(req.ObjName, req.State) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.UIObjClearStateResponse{Success: success}, nil +} + +func (s *grpcServer) UIObjAddFlag(ctx context.Context, req *pb.UIObjAddFlagRequest) (*pb.UIObjAddFlagResponse, error) { + success, err := s.native.UIObjAddFlag(req.ObjName, req.Flag) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.UIObjAddFlagResponse{Success: success}, nil +} + +func (s *grpcServer) UIObjClearFlag(ctx context.Context, req *pb.UIObjClearFlagRequest) (*pb.UIObjClearFlagResponse, error) { + success, err := s.native.UIObjClearFlag(req.ObjName, req.Flag) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.UIObjClearFlagResponse{Success: success}, nil +} + +func (s *grpcServer) UIObjSetOpacity(ctx context.Context, req *pb.UIObjSetOpacityRequest) (*pb.UIObjSetOpacityResponse, error) { + success, err := s.native.UIObjSetOpacity(req.ObjName, int(req.Opacity)) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.UIObjSetOpacityResponse{Success: success}, nil +} + +func (s *grpcServer) UIObjFadeIn(ctx context.Context, req *pb.UIObjFadeInRequest) (*pb.UIObjFadeInResponse, error) { + success, err := s.native.UIObjFadeIn(req.ObjName, req.Duration) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.UIObjFadeInResponse{Success: success}, nil +} + +func (s *grpcServer) UIObjFadeOut(ctx context.Context, req *pb.UIObjFadeOutRequest) (*pb.UIObjFadeOutResponse, error) { + success, err := s.native.UIObjFadeOut(req.ObjName, req.Duration) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.UIObjFadeOutResponse{Success: success}, nil +} + +func (s *grpcServer) UIObjSetLabelText(ctx context.Context, req *pb.UIObjSetLabelTextRequest) (*pb.UIObjSetLabelTextResponse, error) { + success, err := s.native.UIObjSetLabelText(req.ObjName, req.Text) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.UIObjSetLabelTextResponse{Success: success}, nil +} + +func (s *grpcServer) UIObjSetImageSrc(ctx context.Context, req *pb.UIObjSetImageSrcRequest) (*pb.UIObjSetImageSrcResponse, error) { + success, err := s.native.UIObjSetImageSrc(req.ObjName, req.Image) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.UIObjSetImageSrcResponse{Success: success}, nil +} + +func (s *grpcServer) DisplaySetRotation(ctx context.Context, req *pb.DisplaySetRotationRequest) (*pb.DisplaySetRotationResponse, error) { + success, err := s.native.DisplaySetRotation(uint16(req.Rotation)) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &pb.DisplaySetRotationResponse{Success: success}, nil +} + +func (s *grpcServer) UpdateLabelIfChanged(ctx context.Context, req *pb.UpdateLabelIfChangedRequest) (*pb.Empty, error) { + s.native.UpdateLabelIfChanged(req.ObjName, req.NewText) + return &pb.Empty{}, nil +} + +func (s *grpcServer) UpdateLabelAndChangeVisibility(ctx context.Context, req *pb.UpdateLabelAndChangeVisibilityRequest) (*pb.Empty, error) { + s.native.UpdateLabelAndChangeVisibility(req.ObjName, req.NewText) + return &pb.Empty{}, nil +} + +func (s *grpcServer) SwitchToScreenIf(ctx context.Context, req *pb.SwitchToScreenIfRequest) (*pb.Empty, error) { + s.native.SwitchToScreenIf(req.ScreenName, req.ShouldSwitch) + return &pb.Empty{}, nil +} + +func (s *grpcServer) SwitchToScreenIfDifferent(ctx context.Context, req *pb.SwitchToScreenIfDifferentRequest) (*pb.Empty, error) { + s.native.SwitchToScreenIfDifferent(req.ScreenName) + return &pb.Empty{}, nil +} + +func (s *grpcServer) DoNotUseThisIsForCrashTestingOnly(ctx context.Context, req *pb.Empty) (*pb.Empty, error) { + s.native.DoNotUseThisIsForCrashTestingOnly() + return &pb.Empty{}, nil +} diff --git a/internal/native/interface.go b/internal/native/interface.go new file mode 100644 index 000000000..9974399df --- /dev/null +++ b/internal/native/interface.go @@ -0,0 +1,36 @@ +package native + +// NativeInterface defines the interface that both Native and NativeProxy implement +type NativeInterface interface { + Start() error + VideoSetSleepMode(enabled bool) error + VideoGetSleepMode() (bool, error) + VideoSleepModeSupported() bool + VideoSetQualityFactor(factor float64) error + VideoGetQualityFactor() (float64, error) + VideoSetEDID(edid string) error + VideoGetEDID() (string, error) + VideoLogStatus() (string, error) + VideoStop() error + VideoStart() error + GetLVGLVersion() (string, error) + UIObjHide(objName string) (bool, error) + UIObjShow(objName string) (bool, error) + UISetVar(name string, value string) + UIGetVar(name string) string + UIObjAddState(objName string, state string) (bool, error) + UIObjClearState(objName string, state string) (bool, error) + UIObjAddFlag(objName string, flag string) (bool, error) + UIObjClearFlag(objName string, flag string) (bool, error) + UIObjSetOpacity(objName string, opacity int) (bool, error) + UIObjFadeIn(objName string, duration uint32) (bool, error) + UIObjFadeOut(objName string, duration uint32) (bool, error) + UIObjSetLabelText(objName string, text string) (bool, error) + UIObjSetImageSrc(objName string, image string) (bool, error) + DisplaySetRotation(rotation uint16) (bool, error) + UpdateLabelIfChanged(objName string, newText string) + UpdateLabelAndChangeVisibility(objName string, newText string) + SwitchToScreenIf(screenName string, shouldSwitch []string) + SwitchToScreenIfDifferent(screenName string) + DoNotUseThisIsForCrashTestingOnly() +} diff --git a/internal/native/native.go b/internal/native/native.go index cb8761cf1..61c4b0ac7 100644 --- a/internal/native/native.go +++ b/internal/native/native.go @@ -1,6 +1,7 @@ package native import ( + "os" "sync" "time" @@ -9,7 +10,6 @@ import ( ) type Native struct { - disable bool ready chan struct{} l *zerolog.Logger lD *zerolog.Logger @@ -28,18 +28,23 @@ type Native struct { } type NativeOptions struct { - Disable bool SystemVersion *semver.Version AppVersion *semver.Version DisplayRotation uint16 DefaultQualityFactor float64 + MaxRestartAttempts uint OnVideoStateChange func(state VideoState) OnVideoFrameReceived func(frame []byte, duration time.Duration) OnIndevEvent func(event string) OnRpcEvent func(event string) + OnNativeRestart func() } func NewNative(opts NativeOptions) *Native { + pid := os.Getpid() + nativeSubLogger := nativeLogger.With().Int("pid", pid).Str("scope", "native").Logger() + displaySubLogger := displayLogger.With().Int("pid", pid).Str("scope", "native").Logger() + onVideoStateChange := opts.OnVideoStateChange if onVideoStateChange == nil { onVideoStateChange = func(state VideoState) { @@ -50,7 +55,7 @@ func NewNative(opts NativeOptions) *Native { onVideoFrameReceived := opts.OnVideoFrameReceived if onVideoFrameReceived == nil { onVideoFrameReceived = func(frame []byte, duration time.Duration) { - nativeLogger.Info().Interface("frame", frame).Dur("duration", duration).Msg("video frame received") + nativeLogger.Trace().Interface("frame", frame).Dur("duration", duration).Msg("video frame received") } } @@ -76,10 +81,9 @@ func NewNative(opts NativeOptions) *Native { } return &Native{ - disable: opts.Disable, ready: make(chan struct{}), - l: nativeLogger, - lD: displayLogger, + l: &nativeSubLogger, + lD: &displaySubLogger, systemVersion: opts.SystemVersion, appVersion: opts.AppVersion, displayRotation: opts.DisplayRotation, @@ -94,13 +98,7 @@ func NewNative(opts NativeOptions) *Native { } } -func (n *Native) Start() { - if n.disable { - nativeLogger.Warn().Msg("native is disabled, skipping initialization") - setCgoDisabled(true) - return - } - +func (n *Native) Start() error { // set up singleton setInstance(n) setUpNativeHandlers() @@ -117,9 +115,11 @@ func (n *Native) Start() { if err := videoInit(n.defaultQualityFactor); err != nil { n.l.Error().Err(err).Msg("failed to initialize video") + return err } close(n.ready) + return nil } // DoNotUseThisIsForCrashTestingOnly diff --git a/internal/native/proto/README.md b/internal/native/proto/README.md new file mode 100644 index 000000000..aa619f6d9 --- /dev/null +++ b/internal/native/proto/README.md @@ -0,0 +1,33 @@ +# Proto Files + +This directory contains the Protocol Buffer definitions for the native service. + +## Generating Code + +To generate the Go code from the proto files, run: + +```bash +./scripts/generate_proto.sh +``` + +Or manually: + +```bash +protoc \ + --go_out=. \ + --go_opt=paths=source_relative \ + --go-grpc_out=. \ + --go-grpc_opt=paths=source_relative \ + internal/native/proto/native.proto +``` + +## Prerequisites + +- `protoc` - Protocol Buffer compiler +- `protoc-gen-go` - Go plugin for protoc (install with: `go install google.golang.org/protobuf/cmd/protoc-gen-go@latest`) +- `protoc-gen-go-grpc` - gRPC Go plugin for protoc (install with: `go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest`) + +## Note + +The current `native.pb.go` and `native_grpc.pb.go` files are placeholder/stub files. They should be regenerated from `native.proto` using the commands above. + diff --git a/internal/native/proto/native.pb.go b/internal/native/proto/native.pb.go new file mode 100644 index 000000000..7eb5fdcc8 --- /dev/null +++ b/internal/native/proto/native.pb.go @@ -0,0 +1,2775 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.5 +// protoc v3.21.12 +// source: internal/native/proto/native.proto + +package proto + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Messages +type Empty struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Empty) Reset() { + *x = Empty{} + mi := &file_internal_native_proto_native_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Empty) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Empty) ProtoMessage() {} + +func (x *Empty) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Empty.ProtoReflect.Descriptor instead. +func (*Empty) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{0} +} + +type IsReadyRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *IsReadyRequest) Reset() { + *x = IsReadyRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *IsReadyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IsReadyRequest) ProtoMessage() {} + +func (x *IsReadyRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IsReadyRequest.ProtoReflect.Descriptor instead. +func (*IsReadyRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{1} +} + +type IsReadyResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Ready bool `protobuf:"varint,1,opt,name=ready,proto3" json:"ready,omitempty"` + Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` + VideoReady bool `protobuf:"varint,3,opt,name=video_ready,json=videoReady,proto3" json:"video_ready,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *IsReadyResponse) Reset() { + *x = IsReadyResponse{} + mi := &file_internal_native_proto_native_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *IsReadyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IsReadyResponse) ProtoMessage() {} + +func (x *IsReadyResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IsReadyResponse.ProtoReflect.Descriptor instead. +func (*IsReadyResponse) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{2} +} + +func (x *IsReadyResponse) GetReady() bool { + if x != nil { + return x.Ready + } + return false +} + +func (x *IsReadyResponse) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +func (x *IsReadyResponse) GetVideoReady() bool { + if x != nil { + return x.VideoReady + } + return false +} + +type VideoState struct { + state protoimpl.MessageState `protogen:"open.v1"` + Ready bool `protobuf:"varint,1,opt,name=ready,proto3" json:"ready,omitempty"` + Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` + Width int32 `protobuf:"varint,3,opt,name=width,proto3" json:"width,omitempty"` + Height int32 `protobuf:"varint,4,opt,name=height,proto3" json:"height,omitempty"` + FramePerSecond float64 `protobuf:"fixed64,5,opt,name=frame_per_second,json=framePerSecond,proto3" json:"frame_per_second,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VideoState) Reset() { + *x = VideoState{} + mi := &file_internal_native_proto_native_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VideoState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VideoState) ProtoMessage() {} + +func (x *VideoState) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VideoState.ProtoReflect.Descriptor instead. +func (*VideoState) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{3} +} + +func (x *VideoState) GetReady() bool { + if x != nil { + return x.Ready + } + return false +} + +func (x *VideoState) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +func (x *VideoState) GetWidth() int32 { + if x != nil { + return x.Width + } + return 0 +} + +func (x *VideoState) GetHeight() int32 { + if x != nil { + return x.Height + } + return 0 +} + +func (x *VideoState) GetFramePerSecond() float64 { + if x != nil { + return x.FramePerSecond + } + return 0 +} + +type VideoSetSleepModeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VideoSetSleepModeRequest) Reset() { + *x = VideoSetSleepModeRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VideoSetSleepModeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VideoSetSleepModeRequest) ProtoMessage() {} + +func (x *VideoSetSleepModeRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VideoSetSleepModeRequest.ProtoReflect.Descriptor instead. +func (*VideoSetSleepModeRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{4} +} + +func (x *VideoSetSleepModeRequest) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +type VideoGetSleepModeResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VideoGetSleepModeResponse) Reset() { + *x = VideoGetSleepModeResponse{} + mi := &file_internal_native_proto_native_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VideoGetSleepModeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VideoGetSleepModeResponse) ProtoMessage() {} + +func (x *VideoGetSleepModeResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VideoGetSleepModeResponse.ProtoReflect.Descriptor instead. +func (*VideoGetSleepModeResponse) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{5} +} + +func (x *VideoGetSleepModeResponse) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +type VideoSleepModeSupportedResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Supported bool `protobuf:"varint,1,opt,name=supported,proto3" json:"supported,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VideoSleepModeSupportedResponse) Reset() { + *x = VideoSleepModeSupportedResponse{} + mi := &file_internal_native_proto_native_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VideoSleepModeSupportedResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VideoSleepModeSupportedResponse) ProtoMessage() {} + +func (x *VideoSleepModeSupportedResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VideoSleepModeSupportedResponse.ProtoReflect.Descriptor instead. +func (*VideoSleepModeSupportedResponse) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{6} +} + +func (x *VideoSleepModeSupportedResponse) GetSupported() bool { + if x != nil { + return x.Supported + } + return false +} + +type VideoSetQualityFactorRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Factor float64 `protobuf:"fixed64,1,opt,name=factor,proto3" json:"factor,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VideoSetQualityFactorRequest) Reset() { + *x = VideoSetQualityFactorRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VideoSetQualityFactorRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VideoSetQualityFactorRequest) ProtoMessage() {} + +func (x *VideoSetQualityFactorRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VideoSetQualityFactorRequest.ProtoReflect.Descriptor instead. +func (*VideoSetQualityFactorRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{7} +} + +func (x *VideoSetQualityFactorRequest) GetFactor() float64 { + if x != nil { + return x.Factor + } + return 0 +} + +type VideoGetQualityFactorResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Factor float64 `protobuf:"fixed64,1,opt,name=factor,proto3" json:"factor,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VideoGetQualityFactorResponse) Reset() { + *x = VideoGetQualityFactorResponse{} + mi := &file_internal_native_proto_native_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VideoGetQualityFactorResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VideoGetQualityFactorResponse) ProtoMessage() {} + +func (x *VideoGetQualityFactorResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VideoGetQualityFactorResponse.ProtoReflect.Descriptor instead. +func (*VideoGetQualityFactorResponse) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{8} +} + +func (x *VideoGetQualityFactorResponse) GetFactor() float64 { + if x != nil { + return x.Factor + } + return 0 +} + +type VideoSetEDIDRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Edid string `protobuf:"bytes,1,opt,name=edid,proto3" json:"edid,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VideoSetEDIDRequest) Reset() { + *x = VideoSetEDIDRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VideoSetEDIDRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VideoSetEDIDRequest) ProtoMessage() {} + +func (x *VideoSetEDIDRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VideoSetEDIDRequest.ProtoReflect.Descriptor instead. +func (*VideoSetEDIDRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{9} +} + +func (x *VideoSetEDIDRequest) GetEdid() string { + if x != nil { + return x.Edid + } + return "" +} + +type VideoGetEDIDResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Edid string `protobuf:"bytes,1,opt,name=edid,proto3" json:"edid,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VideoGetEDIDResponse) Reset() { + *x = VideoGetEDIDResponse{} + mi := &file_internal_native_proto_native_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VideoGetEDIDResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VideoGetEDIDResponse) ProtoMessage() {} + +func (x *VideoGetEDIDResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VideoGetEDIDResponse.ProtoReflect.Descriptor instead. +func (*VideoGetEDIDResponse) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{10} +} + +func (x *VideoGetEDIDResponse) GetEdid() string { + if x != nil { + return x.Edid + } + return "" +} + +type VideoLogStatusResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VideoLogStatusResponse) Reset() { + *x = VideoLogStatusResponse{} + mi := &file_internal_native_proto_native_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VideoLogStatusResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VideoLogStatusResponse) ProtoMessage() {} + +func (x *VideoLogStatusResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VideoLogStatusResponse.ProtoReflect.Descriptor instead. +func (*VideoLogStatusResponse) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{11} +} + +func (x *VideoLogStatusResponse) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +type GetLVGLVersionResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetLVGLVersionResponse) Reset() { + *x = GetLVGLVersionResponse{} + mi := &file_internal_native_proto_native_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetLVGLVersionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetLVGLVersionResponse) ProtoMessage() {} + +func (x *GetLVGLVersionResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetLVGLVersionResponse.ProtoReflect.Descriptor instead. +func (*GetLVGLVersionResponse) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{12} +} + +func (x *GetLVGLVersionResponse) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +type UIObjHideRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ObjName string `protobuf:"bytes,1,opt,name=obj_name,json=objName,proto3" json:"obj_name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjHideRequest) Reset() { + *x = UIObjHideRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjHideRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjHideRequest) ProtoMessage() {} + +func (x *UIObjHideRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjHideRequest.ProtoReflect.Descriptor instead. +func (*UIObjHideRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{13} +} + +func (x *UIObjHideRequest) GetObjName() string { + if x != nil { + return x.ObjName + } + return "" +} + +type UIObjHideResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjHideResponse) Reset() { + *x = UIObjHideResponse{} + mi := &file_internal_native_proto_native_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjHideResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjHideResponse) ProtoMessage() {} + +func (x *UIObjHideResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjHideResponse.ProtoReflect.Descriptor instead. +func (*UIObjHideResponse) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{14} +} + +func (x *UIObjHideResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +type UIObjShowRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ObjName string `protobuf:"bytes,1,opt,name=obj_name,json=objName,proto3" json:"obj_name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjShowRequest) Reset() { + *x = UIObjShowRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjShowRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjShowRequest) ProtoMessage() {} + +func (x *UIObjShowRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[15] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjShowRequest.ProtoReflect.Descriptor instead. +func (*UIObjShowRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{15} +} + +func (x *UIObjShowRequest) GetObjName() string { + if x != nil { + return x.ObjName + } + return "" +} + +type UIObjShowResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjShowResponse) Reset() { + *x = UIObjShowResponse{} + mi := &file_internal_native_proto_native_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjShowResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjShowResponse) ProtoMessage() {} + +func (x *UIObjShowResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[16] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjShowResponse.ProtoReflect.Descriptor instead. +func (*UIObjShowResponse) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{16} +} + +func (x *UIObjShowResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +type UISetVarRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UISetVarRequest) Reset() { + *x = UISetVarRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UISetVarRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UISetVarRequest) ProtoMessage() {} + +func (x *UISetVarRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[17] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UISetVarRequest.ProtoReflect.Descriptor instead. +func (*UISetVarRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{17} +} + +func (x *UISetVarRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *UISetVarRequest) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type UIGetVarRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIGetVarRequest) Reset() { + *x = UIGetVarRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIGetVarRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIGetVarRequest) ProtoMessage() {} + +func (x *UIGetVarRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[18] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIGetVarRequest.ProtoReflect.Descriptor instead. +func (*UIGetVarRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{18} +} + +func (x *UIGetVarRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type UIGetVarResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIGetVarResponse) Reset() { + *x = UIGetVarResponse{} + mi := &file_internal_native_proto_native_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIGetVarResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIGetVarResponse) ProtoMessage() {} + +func (x *UIGetVarResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[19] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIGetVarResponse.ProtoReflect.Descriptor instead. +func (*UIGetVarResponse) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{19} +} + +func (x *UIGetVarResponse) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type UIObjAddStateRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ObjName string `protobuf:"bytes,1,opt,name=obj_name,json=objName,proto3" json:"obj_name,omitempty"` + State string `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjAddStateRequest) Reset() { + *x = UIObjAddStateRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjAddStateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjAddStateRequest) ProtoMessage() {} + +func (x *UIObjAddStateRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[20] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjAddStateRequest.ProtoReflect.Descriptor instead. +func (*UIObjAddStateRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{20} +} + +func (x *UIObjAddStateRequest) GetObjName() string { + if x != nil { + return x.ObjName + } + return "" +} + +func (x *UIObjAddStateRequest) GetState() string { + if x != nil { + return x.State + } + return "" +} + +type UIObjAddStateResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjAddStateResponse) Reset() { + *x = UIObjAddStateResponse{} + mi := &file_internal_native_proto_native_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjAddStateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjAddStateResponse) ProtoMessage() {} + +func (x *UIObjAddStateResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[21] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjAddStateResponse.ProtoReflect.Descriptor instead. +func (*UIObjAddStateResponse) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{21} +} + +func (x *UIObjAddStateResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +type UIObjClearStateRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ObjName string `protobuf:"bytes,1,opt,name=obj_name,json=objName,proto3" json:"obj_name,omitempty"` + State string `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjClearStateRequest) Reset() { + *x = UIObjClearStateRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjClearStateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjClearStateRequest) ProtoMessage() {} + +func (x *UIObjClearStateRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[22] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjClearStateRequest.ProtoReflect.Descriptor instead. +func (*UIObjClearStateRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{22} +} + +func (x *UIObjClearStateRequest) GetObjName() string { + if x != nil { + return x.ObjName + } + return "" +} + +func (x *UIObjClearStateRequest) GetState() string { + if x != nil { + return x.State + } + return "" +} + +type UIObjClearStateResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjClearStateResponse) Reset() { + *x = UIObjClearStateResponse{} + mi := &file_internal_native_proto_native_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjClearStateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjClearStateResponse) ProtoMessage() {} + +func (x *UIObjClearStateResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[23] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjClearStateResponse.ProtoReflect.Descriptor instead. +func (*UIObjClearStateResponse) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{23} +} + +func (x *UIObjClearStateResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +type UIObjAddFlagRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ObjName string `protobuf:"bytes,1,opt,name=obj_name,json=objName,proto3" json:"obj_name,omitempty"` + Flag string `protobuf:"bytes,2,opt,name=flag,proto3" json:"flag,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjAddFlagRequest) Reset() { + *x = UIObjAddFlagRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjAddFlagRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjAddFlagRequest) ProtoMessage() {} + +func (x *UIObjAddFlagRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[24] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjAddFlagRequest.ProtoReflect.Descriptor instead. +func (*UIObjAddFlagRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{24} +} + +func (x *UIObjAddFlagRequest) GetObjName() string { + if x != nil { + return x.ObjName + } + return "" +} + +func (x *UIObjAddFlagRequest) GetFlag() string { + if x != nil { + return x.Flag + } + return "" +} + +type UIObjAddFlagResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjAddFlagResponse) Reset() { + *x = UIObjAddFlagResponse{} + mi := &file_internal_native_proto_native_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjAddFlagResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjAddFlagResponse) ProtoMessage() {} + +func (x *UIObjAddFlagResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[25] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjAddFlagResponse.ProtoReflect.Descriptor instead. +func (*UIObjAddFlagResponse) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{25} +} + +func (x *UIObjAddFlagResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +type UIObjClearFlagRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ObjName string `protobuf:"bytes,1,opt,name=obj_name,json=objName,proto3" json:"obj_name,omitempty"` + Flag string `protobuf:"bytes,2,opt,name=flag,proto3" json:"flag,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjClearFlagRequest) Reset() { + *x = UIObjClearFlagRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjClearFlagRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjClearFlagRequest) ProtoMessage() {} + +func (x *UIObjClearFlagRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[26] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjClearFlagRequest.ProtoReflect.Descriptor instead. +func (*UIObjClearFlagRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{26} +} + +func (x *UIObjClearFlagRequest) GetObjName() string { + if x != nil { + return x.ObjName + } + return "" +} + +func (x *UIObjClearFlagRequest) GetFlag() string { + if x != nil { + return x.Flag + } + return "" +} + +type UIObjClearFlagResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjClearFlagResponse) Reset() { + *x = UIObjClearFlagResponse{} + mi := &file_internal_native_proto_native_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjClearFlagResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjClearFlagResponse) ProtoMessage() {} + +func (x *UIObjClearFlagResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[27] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjClearFlagResponse.ProtoReflect.Descriptor instead. +func (*UIObjClearFlagResponse) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{27} +} + +func (x *UIObjClearFlagResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +type UIObjSetOpacityRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ObjName string `protobuf:"bytes,1,opt,name=obj_name,json=objName,proto3" json:"obj_name,omitempty"` + Opacity int32 `protobuf:"varint,2,opt,name=opacity,proto3" json:"opacity,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjSetOpacityRequest) Reset() { + *x = UIObjSetOpacityRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjSetOpacityRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjSetOpacityRequest) ProtoMessage() {} + +func (x *UIObjSetOpacityRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[28] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjSetOpacityRequest.ProtoReflect.Descriptor instead. +func (*UIObjSetOpacityRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{28} +} + +func (x *UIObjSetOpacityRequest) GetObjName() string { + if x != nil { + return x.ObjName + } + return "" +} + +func (x *UIObjSetOpacityRequest) GetOpacity() int32 { + if x != nil { + return x.Opacity + } + return 0 +} + +type UIObjSetOpacityResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjSetOpacityResponse) Reset() { + *x = UIObjSetOpacityResponse{} + mi := &file_internal_native_proto_native_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjSetOpacityResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjSetOpacityResponse) ProtoMessage() {} + +func (x *UIObjSetOpacityResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[29] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjSetOpacityResponse.ProtoReflect.Descriptor instead. +func (*UIObjSetOpacityResponse) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{29} +} + +func (x *UIObjSetOpacityResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +type UIObjFadeInRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ObjName string `protobuf:"bytes,1,opt,name=obj_name,json=objName,proto3" json:"obj_name,omitempty"` + Duration uint32 `protobuf:"varint,2,opt,name=duration,proto3" json:"duration,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjFadeInRequest) Reset() { + *x = UIObjFadeInRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjFadeInRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjFadeInRequest) ProtoMessage() {} + +func (x *UIObjFadeInRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[30] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjFadeInRequest.ProtoReflect.Descriptor instead. +func (*UIObjFadeInRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{30} +} + +func (x *UIObjFadeInRequest) GetObjName() string { + if x != nil { + return x.ObjName + } + return "" +} + +func (x *UIObjFadeInRequest) GetDuration() uint32 { + if x != nil { + return x.Duration + } + return 0 +} + +type UIObjFadeInResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjFadeInResponse) Reset() { + *x = UIObjFadeInResponse{} + mi := &file_internal_native_proto_native_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjFadeInResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjFadeInResponse) ProtoMessage() {} + +func (x *UIObjFadeInResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[31] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjFadeInResponse.ProtoReflect.Descriptor instead. +func (*UIObjFadeInResponse) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{31} +} + +func (x *UIObjFadeInResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +type UIObjFadeOutRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ObjName string `protobuf:"bytes,1,opt,name=obj_name,json=objName,proto3" json:"obj_name,omitempty"` + Duration uint32 `protobuf:"varint,2,opt,name=duration,proto3" json:"duration,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjFadeOutRequest) Reset() { + *x = UIObjFadeOutRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjFadeOutRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjFadeOutRequest) ProtoMessage() {} + +func (x *UIObjFadeOutRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[32] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjFadeOutRequest.ProtoReflect.Descriptor instead. +func (*UIObjFadeOutRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{32} +} + +func (x *UIObjFadeOutRequest) GetObjName() string { + if x != nil { + return x.ObjName + } + return "" +} + +func (x *UIObjFadeOutRequest) GetDuration() uint32 { + if x != nil { + return x.Duration + } + return 0 +} + +type UIObjFadeOutResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjFadeOutResponse) Reset() { + *x = UIObjFadeOutResponse{} + mi := &file_internal_native_proto_native_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjFadeOutResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjFadeOutResponse) ProtoMessage() {} + +func (x *UIObjFadeOutResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[33] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjFadeOutResponse.ProtoReflect.Descriptor instead. +func (*UIObjFadeOutResponse) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{33} +} + +func (x *UIObjFadeOutResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +type UIObjSetLabelTextRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ObjName string `protobuf:"bytes,1,opt,name=obj_name,json=objName,proto3" json:"obj_name,omitempty"` + Text string `protobuf:"bytes,2,opt,name=text,proto3" json:"text,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjSetLabelTextRequest) Reset() { + *x = UIObjSetLabelTextRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjSetLabelTextRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjSetLabelTextRequest) ProtoMessage() {} + +func (x *UIObjSetLabelTextRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[34] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjSetLabelTextRequest.ProtoReflect.Descriptor instead. +func (*UIObjSetLabelTextRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{34} +} + +func (x *UIObjSetLabelTextRequest) GetObjName() string { + if x != nil { + return x.ObjName + } + return "" +} + +func (x *UIObjSetLabelTextRequest) GetText() string { + if x != nil { + return x.Text + } + return "" +} + +type UIObjSetLabelTextResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjSetLabelTextResponse) Reset() { + *x = UIObjSetLabelTextResponse{} + mi := &file_internal_native_proto_native_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjSetLabelTextResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjSetLabelTextResponse) ProtoMessage() {} + +func (x *UIObjSetLabelTextResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[35] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjSetLabelTextResponse.ProtoReflect.Descriptor instead. +func (*UIObjSetLabelTextResponse) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{35} +} + +func (x *UIObjSetLabelTextResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +type UIObjSetImageSrcRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ObjName string `protobuf:"bytes,1,opt,name=obj_name,json=objName,proto3" json:"obj_name,omitempty"` + Image string `protobuf:"bytes,2,opt,name=image,proto3" json:"image,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjSetImageSrcRequest) Reset() { + *x = UIObjSetImageSrcRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjSetImageSrcRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjSetImageSrcRequest) ProtoMessage() {} + +func (x *UIObjSetImageSrcRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[36] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjSetImageSrcRequest.ProtoReflect.Descriptor instead. +func (*UIObjSetImageSrcRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{36} +} + +func (x *UIObjSetImageSrcRequest) GetObjName() string { + if x != nil { + return x.ObjName + } + return "" +} + +func (x *UIObjSetImageSrcRequest) GetImage() string { + if x != nil { + return x.Image + } + return "" +} + +type UIObjSetImageSrcResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UIObjSetImageSrcResponse) Reset() { + *x = UIObjSetImageSrcResponse{} + mi := &file_internal_native_proto_native_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UIObjSetImageSrcResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UIObjSetImageSrcResponse) ProtoMessage() {} + +func (x *UIObjSetImageSrcResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[37] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UIObjSetImageSrcResponse.ProtoReflect.Descriptor instead. +func (*UIObjSetImageSrcResponse) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{37} +} + +func (x *UIObjSetImageSrcResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +type DisplaySetRotationRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Rotation uint32 `protobuf:"varint,1,opt,name=rotation,proto3" json:"rotation,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DisplaySetRotationRequest) Reset() { + *x = DisplaySetRotationRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[38] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DisplaySetRotationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DisplaySetRotationRequest) ProtoMessage() {} + +func (x *DisplaySetRotationRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[38] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DisplaySetRotationRequest.ProtoReflect.Descriptor instead. +func (*DisplaySetRotationRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{38} +} + +func (x *DisplaySetRotationRequest) GetRotation() uint32 { + if x != nil { + return x.Rotation + } + return 0 +} + +type DisplaySetRotationResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DisplaySetRotationResponse) Reset() { + *x = DisplaySetRotationResponse{} + mi := &file_internal_native_proto_native_proto_msgTypes[39] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DisplaySetRotationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DisplaySetRotationResponse) ProtoMessage() {} + +func (x *DisplaySetRotationResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[39] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DisplaySetRotationResponse.ProtoReflect.Descriptor instead. +func (*DisplaySetRotationResponse) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{39} +} + +func (x *DisplaySetRotationResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +type UpdateLabelIfChangedRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ObjName string `protobuf:"bytes,1,opt,name=obj_name,json=objName,proto3" json:"obj_name,omitempty"` + NewText string `protobuf:"bytes,2,opt,name=new_text,json=newText,proto3" json:"new_text,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateLabelIfChangedRequest) Reset() { + *x = UpdateLabelIfChangedRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[40] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateLabelIfChangedRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateLabelIfChangedRequest) ProtoMessage() {} + +func (x *UpdateLabelIfChangedRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[40] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateLabelIfChangedRequest.ProtoReflect.Descriptor instead. +func (*UpdateLabelIfChangedRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{40} +} + +func (x *UpdateLabelIfChangedRequest) GetObjName() string { + if x != nil { + return x.ObjName + } + return "" +} + +func (x *UpdateLabelIfChangedRequest) GetNewText() string { + if x != nil { + return x.NewText + } + return "" +} + +type UpdateLabelAndChangeVisibilityRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ObjName string `protobuf:"bytes,1,opt,name=obj_name,json=objName,proto3" json:"obj_name,omitempty"` + NewText string `protobuf:"bytes,2,opt,name=new_text,json=newText,proto3" json:"new_text,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateLabelAndChangeVisibilityRequest) Reset() { + *x = UpdateLabelAndChangeVisibilityRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateLabelAndChangeVisibilityRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateLabelAndChangeVisibilityRequest) ProtoMessage() {} + +func (x *UpdateLabelAndChangeVisibilityRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[41] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateLabelAndChangeVisibilityRequest.ProtoReflect.Descriptor instead. +func (*UpdateLabelAndChangeVisibilityRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{41} +} + +func (x *UpdateLabelAndChangeVisibilityRequest) GetObjName() string { + if x != nil { + return x.ObjName + } + return "" +} + +func (x *UpdateLabelAndChangeVisibilityRequest) GetNewText() string { + if x != nil { + return x.NewText + } + return "" +} + +type SwitchToScreenIfRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ScreenName string `protobuf:"bytes,1,opt,name=screen_name,json=screenName,proto3" json:"screen_name,omitempty"` + ShouldSwitch []string `protobuf:"bytes,2,rep,name=should_switch,json=shouldSwitch,proto3" json:"should_switch,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SwitchToScreenIfRequest) Reset() { + *x = SwitchToScreenIfRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SwitchToScreenIfRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SwitchToScreenIfRequest) ProtoMessage() {} + +func (x *SwitchToScreenIfRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[42] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SwitchToScreenIfRequest.ProtoReflect.Descriptor instead. +func (*SwitchToScreenIfRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{42} +} + +func (x *SwitchToScreenIfRequest) GetScreenName() string { + if x != nil { + return x.ScreenName + } + return "" +} + +func (x *SwitchToScreenIfRequest) GetShouldSwitch() []string { + if x != nil { + return x.ShouldSwitch + } + return nil +} + +type SwitchToScreenIfDifferentRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ScreenName string `protobuf:"bytes,1,opt,name=screen_name,json=screenName,proto3" json:"screen_name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SwitchToScreenIfDifferentRequest) Reset() { + *x = SwitchToScreenIfDifferentRequest{} + mi := &file_internal_native_proto_native_proto_msgTypes[43] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SwitchToScreenIfDifferentRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SwitchToScreenIfDifferentRequest) ProtoMessage() {} + +func (x *SwitchToScreenIfDifferentRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[43] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SwitchToScreenIfDifferentRequest.ProtoReflect.Descriptor instead. +func (*SwitchToScreenIfDifferentRequest) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{43} +} + +func (x *SwitchToScreenIfDifferentRequest) GetScreenName() string { + if x != nil { + return x.ScreenName + } + return "" +} + +type Event struct { + state protoimpl.MessageState `protogen:"open.v1"` + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + // Types that are valid to be assigned to Data: + // + // *Event_VideoState + // *Event_IndevEvent + // *Event_RpcEvent + // *Event_VideoFrame + Data isEvent_Data `protobuf_oneof:"data"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Event) Reset() { + *x = Event{} + mi := &file_internal_native_proto_native_proto_msgTypes[44] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Event) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Event) ProtoMessage() {} + +func (x *Event) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[44] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Event.ProtoReflect.Descriptor instead. +func (*Event) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{44} +} + +func (x *Event) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *Event) GetData() isEvent_Data { + if x != nil { + return x.Data + } + return nil +} + +func (x *Event) GetVideoState() *VideoState { + if x != nil { + if x, ok := x.Data.(*Event_VideoState); ok { + return x.VideoState + } + } + return nil +} + +func (x *Event) GetIndevEvent() string { + if x != nil { + if x, ok := x.Data.(*Event_IndevEvent); ok { + return x.IndevEvent + } + } + return "" +} + +func (x *Event) GetRpcEvent() string { + if x != nil { + if x, ok := x.Data.(*Event_RpcEvent); ok { + return x.RpcEvent + } + } + return "" +} + +func (x *Event) GetVideoFrame() *VideoFrame { + if x != nil { + if x, ok := x.Data.(*Event_VideoFrame); ok { + return x.VideoFrame + } + } + return nil +} + +type isEvent_Data interface { + isEvent_Data() +} + +type Event_VideoState struct { + VideoState *VideoState `protobuf:"bytes,2,opt,name=video_state,json=videoState,proto3,oneof"` +} + +type Event_IndevEvent struct { + IndevEvent string `protobuf:"bytes,3,opt,name=indev_event,json=indevEvent,proto3,oneof"` +} + +type Event_RpcEvent struct { + RpcEvent string `protobuf:"bytes,4,opt,name=rpc_event,json=rpcEvent,proto3,oneof"` +} + +type Event_VideoFrame struct { + VideoFrame *VideoFrame `protobuf:"bytes,5,opt,name=video_frame,json=videoFrame,proto3,oneof"` +} + +func (*Event_VideoState) isEvent_Data() {} + +func (*Event_IndevEvent) isEvent_Data() {} + +func (*Event_RpcEvent) isEvent_Data() {} + +func (*Event_VideoFrame) isEvent_Data() {} + +type VideoFrame struct { + state protoimpl.MessageState `protogen:"open.v1"` + Frame []byte `protobuf:"bytes,1,opt,name=frame,proto3" json:"frame,omitempty"` + DurationNs int64 `protobuf:"varint,2,opt,name=duration_ns,json=durationNs,proto3" json:"duration_ns,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VideoFrame) Reset() { + *x = VideoFrame{} + mi := &file_internal_native_proto_native_proto_msgTypes[45] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VideoFrame) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VideoFrame) ProtoMessage() {} + +func (x *VideoFrame) ProtoReflect() protoreflect.Message { + mi := &file_internal_native_proto_native_proto_msgTypes[45] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VideoFrame.ProtoReflect.Descriptor instead. +func (*VideoFrame) Descriptor() ([]byte, []int) { + return file_internal_native_proto_native_proto_rawDescGZIP(), []int{45} +} + +func (x *VideoFrame) GetFrame() []byte { + if x != nil { + return x.Frame + } + return nil +} + +func (x *VideoFrame) GetDurationNs() int64 { + if x != nil { + return x.DurationNs + } + return 0 +} + +var File_internal_native_proto_native_proto protoreflect.FileDescriptor + +var file_internal_native_proto_native_proto_rawDesc = string([]byte{ + 0x0a, 0x22, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6e, 0x61, 0x74, 0x69, 0x76, + 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x22, 0x07, 0x0a, 0x05, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x10, 0x0a, 0x0e, 0x49, 0x73, 0x52, 0x65, 0x61, 0x64, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x5e, 0x0a, 0x0f, 0x49, 0x73, 0x52, 0x65, 0x61, + 0x64, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, + 0x61, 0x64, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x5f, + 0x72, 0x65, 0x61, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x76, 0x69, 0x64, + 0x65, 0x6f, 0x52, 0x65, 0x61, 0x64, 0x79, 0x22, 0x90, 0x01, 0x0a, 0x0a, 0x56, 0x69, 0x64, 0x65, + 0x6f, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, 0x12, 0x14, 0x0a, 0x05, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x12, 0x28, 0x0a, 0x10, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x73, 0x65, + 0x63, 0x6f, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0e, 0x66, 0x72, 0x61, 0x6d, + 0x65, 0x50, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x22, 0x34, 0x0a, 0x18, 0x56, 0x69, + 0x64, 0x65, 0x6f, 0x53, 0x65, 0x74, 0x53, 0x6c, 0x65, 0x65, 0x70, 0x4d, 0x6f, 0x64, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x22, 0x35, 0x0a, 0x19, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x47, 0x65, 0x74, 0x53, 0x6c, 0x65, 0x65, + 0x70, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x3f, 0x0a, 0x1f, 0x56, 0x69, 0x64, 0x65, 0x6f, + 0x53, 0x6c, 0x65, 0x65, 0x70, 0x4d, 0x6f, 0x64, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, + 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, + 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x22, 0x36, 0x0a, 0x1c, 0x56, 0x69, 0x64, 0x65, + 0x6f, 0x53, 0x65, 0x74, 0x51, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x46, 0x61, 0x63, 0x74, 0x6f, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x61, 0x63, 0x74, + 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, + 0x22, 0x37, 0x0a, 0x1d, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x47, 0x65, 0x74, 0x51, 0x75, 0x61, 0x6c, + 0x69, 0x74, 0x79, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x06, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x29, 0x0a, 0x13, 0x56, 0x69, 0x64, + 0x65, 0x6f, 0x53, 0x65, 0x74, 0x45, 0x44, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x65, 0x64, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x65, 0x64, 0x69, 0x64, 0x22, 0x2a, 0x0a, 0x14, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x47, 0x65, 0x74, + 0x45, 0x44, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x65, 0x64, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x65, 0x64, 0x69, 0x64, + 0x22, 0x30, 0x0a, 0x16, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x4c, 0x6f, 0x67, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x22, 0x32, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x4c, 0x56, 0x47, 0x4c, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x2d, 0x0a, 0x10, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x48, + 0x69, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x62, + 0x6a, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x62, + 0x6a, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x2d, 0x0a, 0x11, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x48, 0x69, + 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x22, 0x2d, 0x0a, 0x10, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x53, 0x68, 0x6f, + 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x62, 0x6a, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x62, 0x6a, 0x4e, + 0x61, 0x6d, 0x65, 0x22, 0x2d, 0x0a, 0x11, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x53, 0x68, 0x6f, 0x77, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x22, 0x3b, 0x0a, 0x0f, 0x55, 0x49, 0x53, 0x65, 0x74, 0x56, 0x61, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, + 0x25, 0x0a, 0x0f, 0x55, 0x49, 0x47, 0x65, 0x74, 0x56, 0x61, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x28, 0x0a, 0x10, 0x55, 0x49, 0x47, 0x65, 0x74, 0x56, + 0x61, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x22, 0x47, 0x0a, 0x14, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x41, 0x64, 0x64, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x62, 0x6a, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x62, 0x6a, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x31, 0x0a, 0x15, 0x55, 0x49, 0x4f, + 0x62, 0x6a, 0x41, 0x64, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x49, 0x0a, 0x16, + 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x62, 0x6a, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x62, 0x6a, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x33, 0x0a, 0x17, 0x55, 0x49, 0x4f, 0x62, 0x6a, + 0x43, 0x6c, 0x65, 0x61, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x44, 0x0a, 0x13, + 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x41, 0x64, 0x64, 0x46, 0x6c, 0x61, 0x67, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x62, 0x6a, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x62, 0x6a, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x66, 0x6c, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x6c, + 0x61, 0x67, 0x22, 0x30, 0x0a, 0x14, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x41, 0x64, 0x64, 0x46, 0x6c, + 0x61, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x22, 0x46, 0x0a, 0x15, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x43, 0x6c, 0x65, + 0x61, 0x72, 0x46, 0x6c, 0x61, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, + 0x08, 0x6f, 0x62, 0x6a, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x6f, 0x62, 0x6a, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6c, 0x61, 0x67, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x6c, 0x61, 0x67, 0x22, 0x32, 0x0a, 0x16, + 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x46, 0x6c, 0x61, 0x67, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x22, 0x4d, 0x0a, 0x16, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x61, 0x63, + 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x62, + 0x6a, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x62, + 0x6a, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x6f, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x22, + 0x33, 0x0a, 0x17, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x61, 0x63, 0x69, + 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x22, 0x4b, 0x0a, 0x12, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x46, 0x61, 0x64, + 0x65, 0x49, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x62, + 0x6a, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x62, + 0x6a, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x22, 0x2f, 0x0a, 0x13, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x46, 0x61, 0x64, 0x65, 0x49, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x22, 0x4c, 0x0a, 0x13, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x46, 0x61, 0x64, 0x65, 0x4f, + 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x62, 0x6a, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x62, 0x6a, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x22, 0x30, 0x0a, 0x14, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x46, 0x61, 0x64, 0x65, 0x4f, 0x75, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x22, 0x49, 0x0a, 0x18, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x53, 0x65, 0x74, 0x4c, 0x61, + 0x62, 0x65, 0x6c, 0x54, 0x65, 0x78, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, + 0x0a, 0x08, 0x6f, 0x62, 0x6a, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x6f, 0x62, 0x6a, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x22, 0x35, 0x0a, + 0x19, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x53, 0x65, 0x74, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x54, 0x65, + 0x78, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x22, 0x4a, 0x0a, 0x17, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x53, 0x65, 0x74, + 0x49, 0x6d, 0x61, 0x67, 0x65, 0x53, 0x72, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x19, 0x0a, 0x08, 0x6f, 0x62, 0x6a, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x6f, 0x62, 0x6a, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, + 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, + 0x22, 0x34, 0x0a, 0x18, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x53, 0x65, 0x74, 0x49, 0x6d, 0x61, 0x67, + 0x65, 0x53, 0x72, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, + 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x37, 0x0a, 0x19, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, + 0x79, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, + 0x36, 0x0a, 0x1a, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x53, 0x0a, 0x1b, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x49, 0x66, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x62, 0x6a, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x62, 0x6a, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x54, 0x65, 0x78, 0x74, 0x22, 0x5d, 0x0a, 0x25, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x41, 0x6e, 0x64, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x56, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x62, 0x6a, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x62, 0x6a, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x54, 0x65, 0x78, 0x74, 0x22, 0x5f, 0x0a, 0x17, 0x53, + 0x77, 0x69, 0x74, 0x63, 0x68, 0x54, 0x6f, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x49, 0x66, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x63, 0x72, + 0x65, 0x65, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x68, 0x6f, 0x75, 0x6c, + 0x64, 0x5f, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, + 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x22, 0x43, 0x0a, 0x20, + 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x54, 0x6f, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x49, 0x66, + 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x4e, 0x61, 0x6d, + 0x65, 0x22, 0xd3, 0x01, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x35, 0x0a, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x56, 0x69, + 0x64, 0x65, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x76, 0x69, 0x64, 0x65, + 0x6f, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0b, 0x69, 0x6e, 0x64, 0x65, 0x76, 0x5f, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0a, 0x69, + 0x6e, 0x64, 0x65, 0x76, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x09, 0x72, 0x70, 0x63, + 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, + 0x72, 0x70, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x35, 0x0a, 0x0b, 0x76, 0x69, 0x64, 0x65, + 0x6f, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, + 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x46, 0x72, 0x61, 0x6d, + 0x65, 0x48, 0x00, 0x52, 0x0a, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x42, + 0x06, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x43, 0x0a, 0x0a, 0x56, 0x69, 0x64, 0x65, 0x6f, + 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0a, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x73, 0x32, 0xfd, 0x11, 0x0a, + 0x0d, 0x4e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3a, + 0x0a, 0x07, 0x49, 0x73, 0x52, 0x65, 0x61, 0x64, 0x79, 0x12, 0x16, 0x2e, 0x6e, 0x61, 0x74, 0x69, + 0x76, 0x65, 0x2e, 0x49, 0x73, 0x52, 0x65, 0x61, 0x64, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x49, 0x73, 0x52, 0x65, 0x61, + 0x64, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x11, 0x56, 0x69, + 0x64, 0x65, 0x6f, 0x53, 0x65, 0x74, 0x53, 0x6c, 0x65, 0x65, 0x70, 0x4d, 0x6f, 0x64, 0x65, 0x12, + 0x20, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x53, 0x65, + 0x74, 0x53, 0x6c, 0x65, 0x65, 0x70, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x0d, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x12, 0x45, 0x0a, 0x11, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x47, 0x65, 0x74, 0x53, 0x6c, 0x65, 0x65, + 0x70, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0d, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x21, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x56, 0x69, + 0x64, 0x65, 0x6f, 0x47, 0x65, 0x74, 0x53, 0x6c, 0x65, 0x65, 0x70, 0x4d, 0x6f, 0x64, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x17, 0x56, 0x69, 0x64, 0x65, 0x6f, + 0x53, 0x6c, 0x65, 0x65, 0x70, 0x4d, 0x6f, 0x64, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x12, 0x0d, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x1a, 0x27, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, + 0x53, 0x6c, 0x65, 0x65, 0x70, 0x4d, 0x6f, 0x64, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x15, 0x56, 0x69, + 0x64, 0x65, 0x6f, 0x53, 0x65, 0x74, 0x51, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x46, 0x61, 0x63, + 0x74, 0x6f, 0x72, 0x12, 0x24, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x56, 0x69, 0x64, + 0x65, 0x6f, 0x53, 0x65, 0x74, 0x51, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x46, 0x61, 0x63, 0x74, + 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x6e, 0x61, 0x74, 0x69, + 0x76, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4d, 0x0a, 0x15, 0x56, 0x69, 0x64, 0x65, + 0x6f, 0x47, 0x65, 0x74, 0x51, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x46, 0x61, 0x63, 0x74, 0x6f, + 0x72, 0x12, 0x0d, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x1a, 0x25, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x47, + 0x65, 0x74, 0x51, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0c, 0x56, 0x69, 0x64, 0x65, 0x6f, + 0x53, 0x65, 0x74, 0x45, 0x44, 0x49, 0x44, 0x12, 0x1b, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x53, 0x65, 0x74, 0x45, 0x44, 0x49, 0x44, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x12, 0x3b, 0x0a, 0x0c, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x47, 0x65, 0x74, 0x45, + 0x44, 0x49, 0x44, 0x12, 0x0d, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x56, 0x69, 0x64, 0x65, + 0x6f, 0x47, 0x65, 0x74, 0x45, 0x44, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x3f, 0x0a, 0x0e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x4c, 0x6f, 0x67, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x0d, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x1a, 0x1e, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, + 0x4c, 0x6f, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x29, 0x0a, 0x09, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x0d, + 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0d, 0x2e, + 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x2a, 0x0a, 0x0a, + 0x56, 0x69, 0x64, 0x65, 0x6f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x0d, 0x2e, 0x6e, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0d, 0x2e, 0x6e, 0x61, 0x74, 0x69, + 0x76, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, + 0x56, 0x47, 0x4c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0d, 0x2e, 0x6e, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1e, 0x2e, 0x6e, 0x61, 0x74, 0x69, + 0x76, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x56, 0x47, 0x4c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x09, 0x55, 0x49, 0x4f, + 0x62, 0x6a, 0x48, 0x69, 0x64, 0x65, 0x12, 0x18, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, + 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x48, 0x69, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x19, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x48, + 0x69, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x09, 0x55, + 0x49, 0x4f, 0x62, 0x6a, 0x53, 0x68, 0x6f, 0x77, 0x12, 0x18, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, + 0x65, 0x2e, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x53, 0x68, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x55, 0x49, 0x4f, 0x62, + 0x6a, 0x53, 0x68, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, + 0x08, 0x55, 0x49, 0x53, 0x65, 0x74, 0x56, 0x61, 0x72, 0x12, 0x17, 0x2e, 0x6e, 0x61, 0x74, 0x69, + 0x76, 0x65, 0x2e, 0x55, 0x49, 0x53, 0x65, 0x74, 0x56, 0x61, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x12, 0x3d, 0x0a, 0x08, 0x55, 0x49, 0x47, 0x65, 0x74, 0x56, 0x61, 0x72, 0x12, 0x17, 0x2e, + 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x55, 0x49, 0x47, 0x65, 0x74, 0x56, 0x61, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, + 0x55, 0x49, 0x47, 0x65, 0x74, 0x56, 0x61, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x4c, 0x0a, 0x0d, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x41, 0x64, 0x64, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x1c, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x55, 0x49, 0x4f, 0x62, 0x6a, + 0x41, 0x64, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1d, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x41, 0x64, + 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, + 0x0a, 0x0f, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x1e, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x55, 0x49, 0x4f, 0x62, 0x6a, + 0x43, 0x6c, 0x65, 0x61, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1f, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x55, 0x49, 0x4f, 0x62, 0x6a, + 0x43, 0x6c, 0x65, 0x61, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0c, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x41, 0x64, 0x64, 0x46, 0x6c, + 0x61, 0x67, 0x12, 0x1b, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x55, 0x49, 0x4f, 0x62, + 0x6a, 0x41, 0x64, 0x64, 0x46, 0x6c, 0x61, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1c, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x41, 0x64, + 0x64, 0x46, 0x6c, 0x61, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, + 0x0e, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x46, 0x6c, 0x61, 0x67, 0x12, + 0x1d, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x43, 0x6c, + 0x65, 0x61, 0x72, 0x46, 0x6c, 0x61, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, + 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x43, 0x6c, 0x65, + 0x61, 0x72, 0x46, 0x6c, 0x61, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, + 0x0a, 0x0f, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x61, 0x63, 0x69, 0x74, + 0x79, 0x12, 0x1e, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x55, 0x49, 0x4f, 0x62, 0x6a, + 0x53, 0x65, 0x74, 0x4f, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1f, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x55, 0x49, 0x4f, 0x62, 0x6a, + 0x53, 0x65, 0x74, 0x4f, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x46, 0x61, 0x64, 0x65, 0x49, + 0x6e, 0x12, 0x1a, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x55, 0x49, 0x4f, 0x62, 0x6a, + 0x46, 0x61, 0x64, 0x65, 0x49, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, + 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x46, 0x61, 0x64, 0x65, + 0x49, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0c, 0x55, 0x49, + 0x4f, 0x62, 0x6a, 0x46, 0x61, 0x64, 0x65, 0x4f, 0x75, 0x74, 0x12, 0x1b, 0x2e, 0x6e, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x2e, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x46, 0x61, 0x64, 0x65, 0x4f, 0x75, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x2e, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x46, 0x61, 0x64, 0x65, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x53, 0x65, + 0x74, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x54, 0x65, 0x78, 0x74, 0x12, 0x20, 0x2e, 0x6e, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x2e, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x53, 0x65, 0x74, 0x4c, 0x61, 0x62, 0x65, + 0x6c, 0x54, 0x65, 0x78, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6e, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x53, 0x65, 0x74, 0x4c, 0x61, + 0x62, 0x65, 0x6c, 0x54, 0x65, 0x78, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x55, 0x0a, 0x10, 0x55, 0x49, 0x4f, 0x62, 0x6a, 0x53, 0x65, 0x74, 0x49, 0x6d, 0x61, 0x67, 0x65, + 0x53, 0x72, 0x63, 0x12, 0x1f, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x55, 0x49, 0x4f, + 0x62, 0x6a, 0x53, 0x65, 0x74, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x53, 0x72, 0x63, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x55, 0x49, + 0x4f, 0x62, 0x6a, 0x53, 0x65, 0x74, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x53, 0x72, 0x63, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x12, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, + 0x79, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x2e, 0x6e, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x53, 0x65, 0x74, + 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x22, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, + 0x53, 0x65, 0x74, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x62, + 0x65, 0x6c, 0x49, 0x66, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x12, 0x23, 0x2e, 0x6e, 0x61, + 0x74, 0x69, 0x76, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x62, 0x65, 0x6c, + 0x49, 0x66, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x0d, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, + 0x5e, 0x0a, 0x1e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x41, 0x6e, + 0x64, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x56, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, + 0x79, 0x12, 0x2d, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x41, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x56, + 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x0d, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, + 0x42, 0x0a, 0x10, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x54, 0x6f, 0x53, 0x63, 0x72, 0x65, 0x65, + 0x6e, 0x49, 0x66, 0x12, 0x1f, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x53, 0x77, 0x69, + 0x74, 0x63, 0x68, 0x54, 0x6f, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x49, 0x66, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x12, 0x54, 0x0a, 0x19, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x54, 0x6f, 0x53, + 0x63, 0x72, 0x65, 0x65, 0x6e, 0x49, 0x66, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, + 0x12, 0x28, 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, + 0x54, 0x6f, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x49, 0x66, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, + 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x6e, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x41, 0x0a, 0x21, 0x44, 0x6f, 0x4e, + 0x6f, 0x74, 0x55, 0x73, 0x65, 0x54, 0x68, 0x69, 0x73, 0x49, 0x73, 0x46, 0x6f, 0x72, 0x43, 0x72, + 0x61, 0x73, 0x68, 0x54, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x0d, + 0x2e, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0d, 0x2e, + 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x2e, 0x0a, 0x0c, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x0d, 0x2e, 0x6e, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0d, 0x2e, 0x6e, 0x61, + 0x74, 0x69, 0x76, 0x65, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x42, 0x2d, 0x5a, 0x2b, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x65, 0x74, 0x6b, 0x76, + 0x6d, 0x2f, 0x6b, 0x76, 0x6d, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6e, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +}) + +var ( + file_internal_native_proto_native_proto_rawDescOnce sync.Once + file_internal_native_proto_native_proto_rawDescData []byte +) + +func file_internal_native_proto_native_proto_rawDescGZIP() []byte { + file_internal_native_proto_native_proto_rawDescOnce.Do(func() { + file_internal_native_proto_native_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_internal_native_proto_native_proto_rawDesc), len(file_internal_native_proto_native_proto_rawDesc))) + }) + return file_internal_native_proto_native_proto_rawDescData +} + +var file_internal_native_proto_native_proto_msgTypes = make([]protoimpl.MessageInfo, 46) +var file_internal_native_proto_native_proto_goTypes = []any{ + (*Empty)(nil), // 0: native.Empty + (*IsReadyRequest)(nil), // 1: native.IsReadyRequest + (*IsReadyResponse)(nil), // 2: native.IsReadyResponse + (*VideoState)(nil), // 3: native.VideoState + (*VideoSetSleepModeRequest)(nil), // 4: native.VideoSetSleepModeRequest + (*VideoGetSleepModeResponse)(nil), // 5: native.VideoGetSleepModeResponse + (*VideoSleepModeSupportedResponse)(nil), // 6: native.VideoSleepModeSupportedResponse + (*VideoSetQualityFactorRequest)(nil), // 7: native.VideoSetQualityFactorRequest + (*VideoGetQualityFactorResponse)(nil), // 8: native.VideoGetQualityFactorResponse + (*VideoSetEDIDRequest)(nil), // 9: native.VideoSetEDIDRequest + (*VideoGetEDIDResponse)(nil), // 10: native.VideoGetEDIDResponse + (*VideoLogStatusResponse)(nil), // 11: native.VideoLogStatusResponse + (*GetLVGLVersionResponse)(nil), // 12: native.GetLVGLVersionResponse + (*UIObjHideRequest)(nil), // 13: native.UIObjHideRequest + (*UIObjHideResponse)(nil), // 14: native.UIObjHideResponse + (*UIObjShowRequest)(nil), // 15: native.UIObjShowRequest + (*UIObjShowResponse)(nil), // 16: native.UIObjShowResponse + (*UISetVarRequest)(nil), // 17: native.UISetVarRequest + (*UIGetVarRequest)(nil), // 18: native.UIGetVarRequest + (*UIGetVarResponse)(nil), // 19: native.UIGetVarResponse + (*UIObjAddStateRequest)(nil), // 20: native.UIObjAddStateRequest + (*UIObjAddStateResponse)(nil), // 21: native.UIObjAddStateResponse + (*UIObjClearStateRequest)(nil), // 22: native.UIObjClearStateRequest + (*UIObjClearStateResponse)(nil), // 23: native.UIObjClearStateResponse + (*UIObjAddFlagRequest)(nil), // 24: native.UIObjAddFlagRequest + (*UIObjAddFlagResponse)(nil), // 25: native.UIObjAddFlagResponse + (*UIObjClearFlagRequest)(nil), // 26: native.UIObjClearFlagRequest + (*UIObjClearFlagResponse)(nil), // 27: native.UIObjClearFlagResponse + (*UIObjSetOpacityRequest)(nil), // 28: native.UIObjSetOpacityRequest + (*UIObjSetOpacityResponse)(nil), // 29: native.UIObjSetOpacityResponse + (*UIObjFadeInRequest)(nil), // 30: native.UIObjFadeInRequest + (*UIObjFadeInResponse)(nil), // 31: native.UIObjFadeInResponse + (*UIObjFadeOutRequest)(nil), // 32: native.UIObjFadeOutRequest + (*UIObjFadeOutResponse)(nil), // 33: native.UIObjFadeOutResponse + (*UIObjSetLabelTextRequest)(nil), // 34: native.UIObjSetLabelTextRequest + (*UIObjSetLabelTextResponse)(nil), // 35: native.UIObjSetLabelTextResponse + (*UIObjSetImageSrcRequest)(nil), // 36: native.UIObjSetImageSrcRequest + (*UIObjSetImageSrcResponse)(nil), // 37: native.UIObjSetImageSrcResponse + (*DisplaySetRotationRequest)(nil), // 38: native.DisplaySetRotationRequest + (*DisplaySetRotationResponse)(nil), // 39: native.DisplaySetRotationResponse + (*UpdateLabelIfChangedRequest)(nil), // 40: native.UpdateLabelIfChangedRequest + (*UpdateLabelAndChangeVisibilityRequest)(nil), // 41: native.UpdateLabelAndChangeVisibilityRequest + (*SwitchToScreenIfRequest)(nil), // 42: native.SwitchToScreenIfRequest + (*SwitchToScreenIfDifferentRequest)(nil), // 43: native.SwitchToScreenIfDifferentRequest + (*Event)(nil), // 44: native.Event + (*VideoFrame)(nil), // 45: native.VideoFrame +} +var file_internal_native_proto_native_proto_depIdxs = []int32{ + 3, // 0: native.Event.video_state:type_name -> native.VideoState + 45, // 1: native.Event.video_frame:type_name -> native.VideoFrame + 1, // 2: native.NativeService.IsReady:input_type -> native.IsReadyRequest + 4, // 3: native.NativeService.VideoSetSleepMode:input_type -> native.VideoSetSleepModeRequest + 0, // 4: native.NativeService.VideoGetSleepMode:input_type -> native.Empty + 0, // 5: native.NativeService.VideoSleepModeSupported:input_type -> native.Empty + 7, // 6: native.NativeService.VideoSetQualityFactor:input_type -> native.VideoSetQualityFactorRequest + 0, // 7: native.NativeService.VideoGetQualityFactor:input_type -> native.Empty + 9, // 8: native.NativeService.VideoSetEDID:input_type -> native.VideoSetEDIDRequest + 0, // 9: native.NativeService.VideoGetEDID:input_type -> native.Empty + 0, // 10: native.NativeService.VideoLogStatus:input_type -> native.Empty + 0, // 11: native.NativeService.VideoStop:input_type -> native.Empty + 0, // 12: native.NativeService.VideoStart:input_type -> native.Empty + 0, // 13: native.NativeService.GetLVGLVersion:input_type -> native.Empty + 13, // 14: native.NativeService.UIObjHide:input_type -> native.UIObjHideRequest + 15, // 15: native.NativeService.UIObjShow:input_type -> native.UIObjShowRequest + 17, // 16: native.NativeService.UISetVar:input_type -> native.UISetVarRequest + 18, // 17: native.NativeService.UIGetVar:input_type -> native.UIGetVarRequest + 20, // 18: native.NativeService.UIObjAddState:input_type -> native.UIObjAddStateRequest + 22, // 19: native.NativeService.UIObjClearState:input_type -> native.UIObjClearStateRequest + 24, // 20: native.NativeService.UIObjAddFlag:input_type -> native.UIObjAddFlagRequest + 26, // 21: native.NativeService.UIObjClearFlag:input_type -> native.UIObjClearFlagRequest + 28, // 22: native.NativeService.UIObjSetOpacity:input_type -> native.UIObjSetOpacityRequest + 30, // 23: native.NativeService.UIObjFadeIn:input_type -> native.UIObjFadeInRequest + 32, // 24: native.NativeService.UIObjFadeOut:input_type -> native.UIObjFadeOutRequest + 34, // 25: native.NativeService.UIObjSetLabelText:input_type -> native.UIObjSetLabelTextRequest + 36, // 26: native.NativeService.UIObjSetImageSrc:input_type -> native.UIObjSetImageSrcRequest + 38, // 27: native.NativeService.DisplaySetRotation:input_type -> native.DisplaySetRotationRequest + 40, // 28: native.NativeService.UpdateLabelIfChanged:input_type -> native.UpdateLabelIfChangedRequest + 41, // 29: native.NativeService.UpdateLabelAndChangeVisibility:input_type -> native.UpdateLabelAndChangeVisibilityRequest + 42, // 30: native.NativeService.SwitchToScreenIf:input_type -> native.SwitchToScreenIfRequest + 43, // 31: native.NativeService.SwitchToScreenIfDifferent:input_type -> native.SwitchToScreenIfDifferentRequest + 0, // 32: native.NativeService.DoNotUseThisIsForCrashTestingOnly:input_type -> native.Empty + 0, // 33: native.NativeService.StreamEvents:input_type -> native.Empty + 2, // 34: native.NativeService.IsReady:output_type -> native.IsReadyResponse + 0, // 35: native.NativeService.VideoSetSleepMode:output_type -> native.Empty + 5, // 36: native.NativeService.VideoGetSleepMode:output_type -> native.VideoGetSleepModeResponse + 6, // 37: native.NativeService.VideoSleepModeSupported:output_type -> native.VideoSleepModeSupportedResponse + 0, // 38: native.NativeService.VideoSetQualityFactor:output_type -> native.Empty + 8, // 39: native.NativeService.VideoGetQualityFactor:output_type -> native.VideoGetQualityFactorResponse + 0, // 40: native.NativeService.VideoSetEDID:output_type -> native.Empty + 10, // 41: native.NativeService.VideoGetEDID:output_type -> native.VideoGetEDIDResponse + 11, // 42: native.NativeService.VideoLogStatus:output_type -> native.VideoLogStatusResponse + 0, // 43: native.NativeService.VideoStop:output_type -> native.Empty + 0, // 44: native.NativeService.VideoStart:output_type -> native.Empty + 12, // 45: native.NativeService.GetLVGLVersion:output_type -> native.GetLVGLVersionResponse + 14, // 46: native.NativeService.UIObjHide:output_type -> native.UIObjHideResponse + 16, // 47: native.NativeService.UIObjShow:output_type -> native.UIObjShowResponse + 0, // 48: native.NativeService.UISetVar:output_type -> native.Empty + 19, // 49: native.NativeService.UIGetVar:output_type -> native.UIGetVarResponse + 21, // 50: native.NativeService.UIObjAddState:output_type -> native.UIObjAddStateResponse + 23, // 51: native.NativeService.UIObjClearState:output_type -> native.UIObjClearStateResponse + 25, // 52: native.NativeService.UIObjAddFlag:output_type -> native.UIObjAddFlagResponse + 27, // 53: native.NativeService.UIObjClearFlag:output_type -> native.UIObjClearFlagResponse + 29, // 54: native.NativeService.UIObjSetOpacity:output_type -> native.UIObjSetOpacityResponse + 31, // 55: native.NativeService.UIObjFadeIn:output_type -> native.UIObjFadeInResponse + 33, // 56: native.NativeService.UIObjFadeOut:output_type -> native.UIObjFadeOutResponse + 35, // 57: native.NativeService.UIObjSetLabelText:output_type -> native.UIObjSetLabelTextResponse + 37, // 58: native.NativeService.UIObjSetImageSrc:output_type -> native.UIObjSetImageSrcResponse + 39, // 59: native.NativeService.DisplaySetRotation:output_type -> native.DisplaySetRotationResponse + 0, // 60: native.NativeService.UpdateLabelIfChanged:output_type -> native.Empty + 0, // 61: native.NativeService.UpdateLabelAndChangeVisibility:output_type -> native.Empty + 0, // 62: native.NativeService.SwitchToScreenIf:output_type -> native.Empty + 0, // 63: native.NativeService.SwitchToScreenIfDifferent:output_type -> native.Empty + 0, // 64: native.NativeService.DoNotUseThisIsForCrashTestingOnly:output_type -> native.Empty + 44, // 65: native.NativeService.StreamEvents:output_type -> native.Event + 34, // [34:66] is the sub-list for method output_type + 2, // [2:34] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_internal_native_proto_native_proto_init() } +func file_internal_native_proto_native_proto_init() { + if File_internal_native_proto_native_proto != nil { + return + } + file_internal_native_proto_native_proto_msgTypes[44].OneofWrappers = []any{ + (*Event_VideoState)(nil), + (*Event_IndevEvent)(nil), + (*Event_RpcEvent)(nil), + (*Event_VideoFrame)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_internal_native_proto_native_proto_rawDesc), len(file_internal_native_proto_native_proto_rawDesc)), + NumEnums: 0, + NumMessages: 46, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_internal_native_proto_native_proto_goTypes, + DependencyIndexes: file_internal_native_proto_native_proto_depIdxs, + MessageInfos: file_internal_native_proto_native_proto_msgTypes, + }.Build() + File_internal_native_proto_native_proto = out.File + file_internal_native_proto_native_proto_goTypes = nil + file_internal_native_proto_native_proto_depIdxs = nil +} diff --git a/internal/native/proto/native.proto b/internal/native/proto/native.proto new file mode 100644 index 000000000..538d33033 --- /dev/null +++ b/internal/native/proto/native.proto @@ -0,0 +1,258 @@ +syntax = "proto3"; + +package native; + +option go_package = "github.com/jetkvm/kvm/internal/native/proto"; + +// NativeService provides methods to interact with the native layer +service NativeService { + // Ready check + rpc IsReady(IsReadyRequest) returns (IsReadyResponse); + + // Video methods + rpc VideoSetSleepMode(VideoSetSleepModeRequest) returns (Empty); + rpc VideoGetSleepMode(Empty) returns (VideoGetSleepModeResponse); + rpc VideoSleepModeSupported(Empty) returns (VideoSleepModeSupportedResponse); + rpc VideoSetQualityFactor(VideoSetQualityFactorRequest) returns (Empty); + rpc VideoGetQualityFactor(Empty) returns (VideoGetQualityFactorResponse); + rpc VideoSetEDID(VideoSetEDIDRequest) returns (Empty); + rpc VideoGetEDID(Empty) returns (VideoGetEDIDResponse); + rpc VideoLogStatus(Empty) returns (VideoLogStatusResponse); + rpc VideoStop(Empty) returns (Empty); + rpc VideoStart(Empty) returns (Empty); + + // UI methods + rpc GetLVGLVersion(Empty) returns (GetLVGLVersionResponse); + rpc UIObjHide(UIObjHideRequest) returns (UIObjHideResponse); + rpc UIObjShow(UIObjShowRequest) returns (UIObjShowResponse); + rpc UISetVar(UISetVarRequest) returns (Empty); + rpc UIGetVar(UIGetVarRequest) returns (UIGetVarResponse); + rpc UIObjAddState(UIObjAddStateRequest) returns (UIObjAddStateResponse); + rpc UIObjClearState(UIObjClearStateRequest) returns (UIObjClearStateResponse); + rpc UIObjAddFlag(UIObjAddFlagRequest) returns (UIObjAddFlagResponse); + rpc UIObjClearFlag(UIObjClearFlagRequest) returns (UIObjClearFlagResponse); + rpc UIObjSetOpacity(UIObjSetOpacityRequest) returns (UIObjSetOpacityResponse); + rpc UIObjFadeIn(UIObjFadeInRequest) returns (UIObjFadeInResponse); + rpc UIObjFadeOut(UIObjFadeOutRequest) returns (UIObjFadeOutResponse); + rpc UIObjSetLabelText(UIObjSetLabelTextRequest) returns (UIObjSetLabelTextResponse); + rpc UIObjSetImageSrc(UIObjSetImageSrcRequest) returns (UIObjSetImageSrcResponse); + rpc DisplaySetRotation(DisplaySetRotationRequest) returns (DisplaySetRotationResponse); + rpc UpdateLabelIfChanged(UpdateLabelIfChangedRequest) returns (Empty); + rpc UpdateLabelAndChangeVisibility(UpdateLabelAndChangeVisibilityRequest) returns (Empty); + rpc SwitchToScreenIf(SwitchToScreenIfRequest) returns (Empty); + rpc SwitchToScreenIfDifferent(SwitchToScreenIfDifferentRequest) returns (Empty); + + // Testing + rpc DoNotUseThisIsForCrashTestingOnly(Empty) returns (Empty); + + // Events stream + rpc StreamEvents(Empty) returns (stream Event); +} + +// Messages +message Empty {} + +message IsReadyRequest {} + +message IsReadyResponse { + bool ready = 1; + string error = 2; + bool video_ready = 3; +} + +message VideoState { + bool ready = 1; + string error = 2; + int32 width = 3; + int32 height = 4; + double frame_per_second = 5; +} + +message VideoSetSleepModeRequest { + bool enabled = 1; +} + +message VideoGetSleepModeResponse { + bool enabled = 1; +} + +message VideoSleepModeSupportedResponse { + bool supported = 1; +} + +message VideoSetQualityFactorRequest { + double factor = 1; +} + +message VideoGetQualityFactorResponse { + double factor = 1; +} + +message VideoSetEDIDRequest { + string edid = 1; +} + +message VideoGetEDIDResponse { + string edid = 1; +} + +message VideoLogStatusResponse { + string status = 1; +} + +message GetLVGLVersionResponse { + string version = 1; +} + +message UIObjHideRequest { + string obj_name = 1; +} + +message UIObjHideResponse { + bool success = 1; +} + +message UIObjShowRequest { + string obj_name = 1; +} + +message UIObjShowResponse { + bool success = 1; +} + +message UISetVarRequest { + string name = 1; + string value = 2; +} + +message UIGetVarRequest { + string name = 1; +} + +message UIGetVarResponse { + string value = 1; +} + +message UIObjAddStateRequest { + string obj_name = 1; + string state = 2; +} + +message UIObjAddStateResponse { + bool success = 1; +} + +message UIObjClearStateRequest { + string obj_name = 1; + string state = 2; +} + +message UIObjClearStateResponse { + bool success = 1; +} + +message UIObjAddFlagRequest { + string obj_name = 1; + string flag = 2; +} + +message UIObjAddFlagResponse { + bool success = 1; +} + +message UIObjClearFlagRequest { + string obj_name = 1; + string flag = 2; +} + +message UIObjClearFlagResponse { + bool success = 1; +} + +message UIObjSetOpacityRequest { + string obj_name = 1; + int32 opacity = 2; +} + +message UIObjSetOpacityResponse { + bool success = 1; +} + +message UIObjFadeInRequest { + string obj_name = 1; + uint32 duration = 2; +} + +message UIObjFadeInResponse { + bool success = 1; +} + +message UIObjFadeOutRequest { + string obj_name = 1; + uint32 duration = 2; +} + +message UIObjFadeOutResponse { + bool success = 1; +} + +message UIObjSetLabelTextRequest { + string obj_name = 1; + string text = 2; +} + +message UIObjSetLabelTextResponse { + bool success = 1; +} + +message UIObjSetImageSrcRequest { + string obj_name = 1; + string image = 2; +} + +message UIObjSetImageSrcResponse { + bool success = 1; +} + +message DisplaySetRotationRequest { + uint32 rotation = 1; +} + +message DisplaySetRotationResponse { + bool success = 1; +} + +message UpdateLabelIfChangedRequest { + string obj_name = 1; + string new_text = 2; +} + +message UpdateLabelAndChangeVisibilityRequest { + string obj_name = 1; + string new_text = 2; +} + +message SwitchToScreenIfRequest { + string screen_name = 1; + repeated string should_switch = 2; +} + +message SwitchToScreenIfDifferentRequest { + string screen_name = 1; +} + +message Event { + string type = 1; + oneof data { + VideoState video_state = 2; + string indev_event = 3; + string rpc_event = 4; + VideoFrame video_frame = 5; + } +} + +message VideoFrame { + bytes frame = 1; + int64 duration_ns = 2; +} + diff --git a/internal/native/proto/native_grpc.pb.go b/internal/native/proto/native_grpc.pb.go new file mode 100644 index 000000000..abdd38bb0 --- /dev/null +++ b/internal/native/proto/native_grpc.pb.go @@ -0,0 +1,1326 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v3.21.12 +// source: internal/native/proto/native.proto + +package proto + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 + +const ( + NativeService_IsReady_FullMethodName = "/native.NativeService/IsReady" + NativeService_VideoSetSleepMode_FullMethodName = "/native.NativeService/VideoSetSleepMode" + NativeService_VideoGetSleepMode_FullMethodName = "/native.NativeService/VideoGetSleepMode" + NativeService_VideoSleepModeSupported_FullMethodName = "/native.NativeService/VideoSleepModeSupported" + NativeService_VideoSetQualityFactor_FullMethodName = "/native.NativeService/VideoSetQualityFactor" + NativeService_VideoGetQualityFactor_FullMethodName = "/native.NativeService/VideoGetQualityFactor" + NativeService_VideoSetEDID_FullMethodName = "/native.NativeService/VideoSetEDID" + NativeService_VideoGetEDID_FullMethodName = "/native.NativeService/VideoGetEDID" + NativeService_VideoLogStatus_FullMethodName = "/native.NativeService/VideoLogStatus" + NativeService_VideoStop_FullMethodName = "/native.NativeService/VideoStop" + NativeService_VideoStart_FullMethodName = "/native.NativeService/VideoStart" + NativeService_GetLVGLVersion_FullMethodName = "/native.NativeService/GetLVGLVersion" + NativeService_UIObjHide_FullMethodName = "/native.NativeService/UIObjHide" + NativeService_UIObjShow_FullMethodName = "/native.NativeService/UIObjShow" + NativeService_UISetVar_FullMethodName = "/native.NativeService/UISetVar" + NativeService_UIGetVar_FullMethodName = "/native.NativeService/UIGetVar" + NativeService_UIObjAddState_FullMethodName = "/native.NativeService/UIObjAddState" + NativeService_UIObjClearState_FullMethodName = "/native.NativeService/UIObjClearState" + NativeService_UIObjAddFlag_FullMethodName = "/native.NativeService/UIObjAddFlag" + NativeService_UIObjClearFlag_FullMethodName = "/native.NativeService/UIObjClearFlag" + NativeService_UIObjSetOpacity_FullMethodName = "/native.NativeService/UIObjSetOpacity" + NativeService_UIObjFadeIn_FullMethodName = "/native.NativeService/UIObjFadeIn" + NativeService_UIObjFadeOut_FullMethodName = "/native.NativeService/UIObjFadeOut" + NativeService_UIObjSetLabelText_FullMethodName = "/native.NativeService/UIObjSetLabelText" + NativeService_UIObjSetImageSrc_FullMethodName = "/native.NativeService/UIObjSetImageSrc" + NativeService_DisplaySetRotation_FullMethodName = "/native.NativeService/DisplaySetRotation" + NativeService_UpdateLabelIfChanged_FullMethodName = "/native.NativeService/UpdateLabelIfChanged" + NativeService_UpdateLabelAndChangeVisibility_FullMethodName = "/native.NativeService/UpdateLabelAndChangeVisibility" + NativeService_SwitchToScreenIf_FullMethodName = "/native.NativeService/SwitchToScreenIf" + NativeService_SwitchToScreenIfDifferent_FullMethodName = "/native.NativeService/SwitchToScreenIfDifferent" + NativeService_DoNotUseThisIsForCrashTestingOnly_FullMethodName = "/native.NativeService/DoNotUseThisIsForCrashTestingOnly" + NativeService_StreamEvents_FullMethodName = "/native.NativeService/StreamEvents" +) + +// NativeServiceClient is the client API for NativeService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type NativeServiceClient interface { + // Ready check + IsReady(ctx context.Context, in *IsReadyRequest, opts ...grpc.CallOption) (*IsReadyResponse, error) + // Video methods + VideoSetSleepMode(ctx context.Context, in *VideoSetSleepModeRequest, opts ...grpc.CallOption) (*Empty, error) + VideoGetSleepMode(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*VideoGetSleepModeResponse, error) + VideoSleepModeSupported(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*VideoSleepModeSupportedResponse, error) + VideoSetQualityFactor(ctx context.Context, in *VideoSetQualityFactorRequest, opts ...grpc.CallOption) (*Empty, error) + VideoGetQualityFactor(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*VideoGetQualityFactorResponse, error) + VideoSetEDID(ctx context.Context, in *VideoSetEDIDRequest, opts ...grpc.CallOption) (*Empty, error) + VideoGetEDID(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*VideoGetEDIDResponse, error) + VideoLogStatus(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*VideoLogStatusResponse, error) + VideoStop(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) + VideoStart(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) + // UI methods + GetLVGLVersion(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*GetLVGLVersionResponse, error) + UIObjHide(ctx context.Context, in *UIObjHideRequest, opts ...grpc.CallOption) (*UIObjHideResponse, error) + UIObjShow(ctx context.Context, in *UIObjShowRequest, opts ...grpc.CallOption) (*UIObjShowResponse, error) + UISetVar(ctx context.Context, in *UISetVarRequest, opts ...grpc.CallOption) (*Empty, error) + UIGetVar(ctx context.Context, in *UIGetVarRequest, opts ...grpc.CallOption) (*UIGetVarResponse, error) + UIObjAddState(ctx context.Context, in *UIObjAddStateRequest, opts ...grpc.CallOption) (*UIObjAddStateResponse, error) + UIObjClearState(ctx context.Context, in *UIObjClearStateRequest, opts ...grpc.CallOption) (*UIObjClearStateResponse, error) + UIObjAddFlag(ctx context.Context, in *UIObjAddFlagRequest, opts ...grpc.CallOption) (*UIObjAddFlagResponse, error) + UIObjClearFlag(ctx context.Context, in *UIObjClearFlagRequest, opts ...grpc.CallOption) (*UIObjClearFlagResponse, error) + UIObjSetOpacity(ctx context.Context, in *UIObjSetOpacityRequest, opts ...grpc.CallOption) (*UIObjSetOpacityResponse, error) + UIObjFadeIn(ctx context.Context, in *UIObjFadeInRequest, opts ...grpc.CallOption) (*UIObjFadeInResponse, error) + UIObjFadeOut(ctx context.Context, in *UIObjFadeOutRequest, opts ...grpc.CallOption) (*UIObjFadeOutResponse, error) + UIObjSetLabelText(ctx context.Context, in *UIObjSetLabelTextRequest, opts ...grpc.CallOption) (*UIObjSetLabelTextResponse, error) + UIObjSetImageSrc(ctx context.Context, in *UIObjSetImageSrcRequest, opts ...grpc.CallOption) (*UIObjSetImageSrcResponse, error) + DisplaySetRotation(ctx context.Context, in *DisplaySetRotationRequest, opts ...grpc.CallOption) (*DisplaySetRotationResponse, error) + UpdateLabelIfChanged(ctx context.Context, in *UpdateLabelIfChangedRequest, opts ...grpc.CallOption) (*Empty, error) + UpdateLabelAndChangeVisibility(ctx context.Context, in *UpdateLabelAndChangeVisibilityRequest, opts ...grpc.CallOption) (*Empty, error) + SwitchToScreenIf(ctx context.Context, in *SwitchToScreenIfRequest, opts ...grpc.CallOption) (*Empty, error) + SwitchToScreenIfDifferent(ctx context.Context, in *SwitchToScreenIfDifferentRequest, opts ...grpc.CallOption) (*Empty, error) + // Testing + DoNotUseThisIsForCrashTestingOnly(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) + // Events stream + StreamEvents(ctx context.Context, in *Empty, opts ...grpc.CallOption) (NativeService_StreamEventsClient, error) +} + +type nativeServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewNativeServiceClient(cc grpc.ClientConnInterface) NativeServiceClient { + return &nativeServiceClient{cc} +} + +func (c *nativeServiceClient) IsReady(ctx context.Context, in *IsReadyRequest, opts ...grpc.CallOption) (*IsReadyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(IsReadyResponse) + err := c.cc.Invoke(ctx, NativeService_IsReady_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) VideoSetSleepMode(ctx context.Context, in *VideoSetSleepModeRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, NativeService_VideoSetSleepMode_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) VideoGetSleepMode(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*VideoGetSleepModeResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(VideoGetSleepModeResponse) + err := c.cc.Invoke(ctx, NativeService_VideoGetSleepMode_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) VideoSleepModeSupported(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*VideoSleepModeSupportedResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(VideoSleepModeSupportedResponse) + err := c.cc.Invoke(ctx, NativeService_VideoSleepModeSupported_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) VideoSetQualityFactor(ctx context.Context, in *VideoSetQualityFactorRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, NativeService_VideoSetQualityFactor_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) VideoGetQualityFactor(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*VideoGetQualityFactorResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(VideoGetQualityFactorResponse) + err := c.cc.Invoke(ctx, NativeService_VideoGetQualityFactor_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) VideoSetEDID(ctx context.Context, in *VideoSetEDIDRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, NativeService_VideoSetEDID_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) VideoGetEDID(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*VideoGetEDIDResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(VideoGetEDIDResponse) + err := c.cc.Invoke(ctx, NativeService_VideoGetEDID_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) VideoLogStatus(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*VideoLogStatusResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(VideoLogStatusResponse) + err := c.cc.Invoke(ctx, NativeService_VideoLogStatus_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) VideoStop(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, NativeService_VideoStop_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) VideoStart(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, NativeService_VideoStart_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) GetLVGLVersion(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*GetLVGLVersionResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetLVGLVersionResponse) + err := c.cc.Invoke(ctx, NativeService_GetLVGLVersion_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) UIObjHide(ctx context.Context, in *UIObjHideRequest, opts ...grpc.CallOption) (*UIObjHideResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UIObjHideResponse) + err := c.cc.Invoke(ctx, NativeService_UIObjHide_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) UIObjShow(ctx context.Context, in *UIObjShowRequest, opts ...grpc.CallOption) (*UIObjShowResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UIObjShowResponse) + err := c.cc.Invoke(ctx, NativeService_UIObjShow_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) UISetVar(ctx context.Context, in *UISetVarRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, NativeService_UISetVar_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) UIGetVar(ctx context.Context, in *UIGetVarRequest, opts ...grpc.CallOption) (*UIGetVarResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UIGetVarResponse) + err := c.cc.Invoke(ctx, NativeService_UIGetVar_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) UIObjAddState(ctx context.Context, in *UIObjAddStateRequest, opts ...grpc.CallOption) (*UIObjAddStateResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UIObjAddStateResponse) + err := c.cc.Invoke(ctx, NativeService_UIObjAddState_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) UIObjClearState(ctx context.Context, in *UIObjClearStateRequest, opts ...grpc.CallOption) (*UIObjClearStateResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UIObjClearStateResponse) + err := c.cc.Invoke(ctx, NativeService_UIObjClearState_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) UIObjAddFlag(ctx context.Context, in *UIObjAddFlagRequest, opts ...grpc.CallOption) (*UIObjAddFlagResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UIObjAddFlagResponse) + err := c.cc.Invoke(ctx, NativeService_UIObjAddFlag_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) UIObjClearFlag(ctx context.Context, in *UIObjClearFlagRequest, opts ...grpc.CallOption) (*UIObjClearFlagResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UIObjClearFlagResponse) + err := c.cc.Invoke(ctx, NativeService_UIObjClearFlag_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) UIObjSetOpacity(ctx context.Context, in *UIObjSetOpacityRequest, opts ...grpc.CallOption) (*UIObjSetOpacityResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UIObjSetOpacityResponse) + err := c.cc.Invoke(ctx, NativeService_UIObjSetOpacity_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) UIObjFadeIn(ctx context.Context, in *UIObjFadeInRequest, opts ...grpc.CallOption) (*UIObjFadeInResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UIObjFadeInResponse) + err := c.cc.Invoke(ctx, NativeService_UIObjFadeIn_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) UIObjFadeOut(ctx context.Context, in *UIObjFadeOutRequest, opts ...grpc.CallOption) (*UIObjFadeOutResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UIObjFadeOutResponse) + err := c.cc.Invoke(ctx, NativeService_UIObjFadeOut_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) UIObjSetLabelText(ctx context.Context, in *UIObjSetLabelTextRequest, opts ...grpc.CallOption) (*UIObjSetLabelTextResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UIObjSetLabelTextResponse) + err := c.cc.Invoke(ctx, NativeService_UIObjSetLabelText_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) UIObjSetImageSrc(ctx context.Context, in *UIObjSetImageSrcRequest, opts ...grpc.CallOption) (*UIObjSetImageSrcResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UIObjSetImageSrcResponse) + err := c.cc.Invoke(ctx, NativeService_UIObjSetImageSrc_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) DisplaySetRotation(ctx context.Context, in *DisplaySetRotationRequest, opts ...grpc.CallOption) (*DisplaySetRotationResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(DisplaySetRotationResponse) + err := c.cc.Invoke(ctx, NativeService_DisplaySetRotation_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) UpdateLabelIfChanged(ctx context.Context, in *UpdateLabelIfChangedRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, NativeService_UpdateLabelIfChanged_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) UpdateLabelAndChangeVisibility(ctx context.Context, in *UpdateLabelAndChangeVisibilityRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, NativeService_UpdateLabelAndChangeVisibility_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) SwitchToScreenIf(ctx context.Context, in *SwitchToScreenIfRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, NativeService_SwitchToScreenIf_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) SwitchToScreenIfDifferent(ctx context.Context, in *SwitchToScreenIfDifferentRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, NativeService_SwitchToScreenIfDifferent_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) DoNotUseThisIsForCrashTestingOnly(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, NativeService_DoNotUseThisIsForCrashTestingOnly_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nativeServiceClient) StreamEvents(ctx context.Context, in *Empty, opts ...grpc.CallOption) (NativeService_StreamEventsClient, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &NativeService_ServiceDesc.Streams[0], NativeService_StreamEvents_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &nativeServiceStreamEventsClient{ClientStream: stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type NativeService_StreamEventsClient interface { + Recv() (*Event, error) + grpc.ClientStream +} + +type nativeServiceStreamEventsClient struct { + grpc.ClientStream +} + +func (x *nativeServiceStreamEventsClient) Recv() (*Event, error) { + m := new(Event) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// NativeServiceServer is the server API for NativeService service. +// All implementations must embed UnimplementedNativeServiceServer +// for forward compatibility +type NativeServiceServer interface { + // Ready check + IsReady(context.Context, *IsReadyRequest) (*IsReadyResponse, error) + // Video methods + VideoSetSleepMode(context.Context, *VideoSetSleepModeRequest) (*Empty, error) + VideoGetSleepMode(context.Context, *Empty) (*VideoGetSleepModeResponse, error) + VideoSleepModeSupported(context.Context, *Empty) (*VideoSleepModeSupportedResponse, error) + VideoSetQualityFactor(context.Context, *VideoSetQualityFactorRequest) (*Empty, error) + VideoGetQualityFactor(context.Context, *Empty) (*VideoGetQualityFactorResponse, error) + VideoSetEDID(context.Context, *VideoSetEDIDRequest) (*Empty, error) + VideoGetEDID(context.Context, *Empty) (*VideoGetEDIDResponse, error) + VideoLogStatus(context.Context, *Empty) (*VideoLogStatusResponse, error) + VideoStop(context.Context, *Empty) (*Empty, error) + VideoStart(context.Context, *Empty) (*Empty, error) + // UI methods + GetLVGLVersion(context.Context, *Empty) (*GetLVGLVersionResponse, error) + UIObjHide(context.Context, *UIObjHideRequest) (*UIObjHideResponse, error) + UIObjShow(context.Context, *UIObjShowRequest) (*UIObjShowResponse, error) + UISetVar(context.Context, *UISetVarRequest) (*Empty, error) + UIGetVar(context.Context, *UIGetVarRequest) (*UIGetVarResponse, error) + UIObjAddState(context.Context, *UIObjAddStateRequest) (*UIObjAddStateResponse, error) + UIObjClearState(context.Context, *UIObjClearStateRequest) (*UIObjClearStateResponse, error) + UIObjAddFlag(context.Context, *UIObjAddFlagRequest) (*UIObjAddFlagResponse, error) + UIObjClearFlag(context.Context, *UIObjClearFlagRequest) (*UIObjClearFlagResponse, error) + UIObjSetOpacity(context.Context, *UIObjSetOpacityRequest) (*UIObjSetOpacityResponse, error) + UIObjFadeIn(context.Context, *UIObjFadeInRequest) (*UIObjFadeInResponse, error) + UIObjFadeOut(context.Context, *UIObjFadeOutRequest) (*UIObjFadeOutResponse, error) + UIObjSetLabelText(context.Context, *UIObjSetLabelTextRequest) (*UIObjSetLabelTextResponse, error) + UIObjSetImageSrc(context.Context, *UIObjSetImageSrcRequest) (*UIObjSetImageSrcResponse, error) + DisplaySetRotation(context.Context, *DisplaySetRotationRequest) (*DisplaySetRotationResponse, error) + UpdateLabelIfChanged(context.Context, *UpdateLabelIfChangedRequest) (*Empty, error) + UpdateLabelAndChangeVisibility(context.Context, *UpdateLabelAndChangeVisibilityRequest) (*Empty, error) + SwitchToScreenIf(context.Context, *SwitchToScreenIfRequest) (*Empty, error) + SwitchToScreenIfDifferent(context.Context, *SwitchToScreenIfDifferentRequest) (*Empty, error) + // Testing + DoNotUseThisIsForCrashTestingOnly(context.Context, *Empty) (*Empty, error) + // Events stream + StreamEvents(*Empty, NativeService_StreamEventsServer) error + mustEmbedUnimplementedNativeServiceServer() +} + +// UnimplementedNativeServiceServer must be embedded to have forward compatible implementations. +type UnimplementedNativeServiceServer struct { +} + +func (UnimplementedNativeServiceServer) IsReady(context.Context, *IsReadyRequest) (*IsReadyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method IsReady not implemented") +} +func (UnimplementedNativeServiceServer) VideoSetSleepMode(context.Context, *VideoSetSleepModeRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method VideoSetSleepMode not implemented") +} +func (UnimplementedNativeServiceServer) VideoGetSleepMode(context.Context, *Empty) (*VideoGetSleepModeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method VideoGetSleepMode not implemented") +} +func (UnimplementedNativeServiceServer) VideoSleepModeSupported(context.Context, *Empty) (*VideoSleepModeSupportedResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method VideoSleepModeSupported not implemented") +} +func (UnimplementedNativeServiceServer) VideoSetQualityFactor(context.Context, *VideoSetQualityFactorRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method VideoSetQualityFactor not implemented") +} +func (UnimplementedNativeServiceServer) VideoGetQualityFactor(context.Context, *Empty) (*VideoGetQualityFactorResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method VideoGetQualityFactor not implemented") +} +func (UnimplementedNativeServiceServer) VideoSetEDID(context.Context, *VideoSetEDIDRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method VideoSetEDID not implemented") +} +func (UnimplementedNativeServiceServer) VideoGetEDID(context.Context, *Empty) (*VideoGetEDIDResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method VideoGetEDID not implemented") +} +func (UnimplementedNativeServiceServer) VideoLogStatus(context.Context, *Empty) (*VideoLogStatusResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method VideoLogStatus not implemented") +} +func (UnimplementedNativeServiceServer) VideoStop(context.Context, *Empty) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method VideoStop not implemented") +} +func (UnimplementedNativeServiceServer) VideoStart(context.Context, *Empty) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method VideoStart not implemented") +} +func (UnimplementedNativeServiceServer) GetLVGLVersion(context.Context, *Empty) (*GetLVGLVersionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetLVGLVersion not implemented") +} +func (UnimplementedNativeServiceServer) UIObjHide(context.Context, *UIObjHideRequest) (*UIObjHideResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UIObjHide not implemented") +} +func (UnimplementedNativeServiceServer) UIObjShow(context.Context, *UIObjShowRequest) (*UIObjShowResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UIObjShow not implemented") +} +func (UnimplementedNativeServiceServer) UISetVar(context.Context, *UISetVarRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method UISetVar not implemented") +} +func (UnimplementedNativeServiceServer) UIGetVar(context.Context, *UIGetVarRequest) (*UIGetVarResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UIGetVar not implemented") +} +func (UnimplementedNativeServiceServer) UIObjAddState(context.Context, *UIObjAddStateRequest) (*UIObjAddStateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UIObjAddState not implemented") +} +func (UnimplementedNativeServiceServer) UIObjClearState(context.Context, *UIObjClearStateRequest) (*UIObjClearStateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UIObjClearState not implemented") +} +func (UnimplementedNativeServiceServer) UIObjAddFlag(context.Context, *UIObjAddFlagRequest) (*UIObjAddFlagResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UIObjAddFlag not implemented") +} +func (UnimplementedNativeServiceServer) UIObjClearFlag(context.Context, *UIObjClearFlagRequest) (*UIObjClearFlagResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UIObjClearFlag not implemented") +} +func (UnimplementedNativeServiceServer) UIObjSetOpacity(context.Context, *UIObjSetOpacityRequest) (*UIObjSetOpacityResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UIObjSetOpacity not implemented") +} +func (UnimplementedNativeServiceServer) UIObjFadeIn(context.Context, *UIObjFadeInRequest) (*UIObjFadeInResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UIObjFadeIn not implemented") +} +func (UnimplementedNativeServiceServer) UIObjFadeOut(context.Context, *UIObjFadeOutRequest) (*UIObjFadeOutResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UIObjFadeOut not implemented") +} +func (UnimplementedNativeServiceServer) UIObjSetLabelText(context.Context, *UIObjSetLabelTextRequest) (*UIObjSetLabelTextResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UIObjSetLabelText not implemented") +} +func (UnimplementedNativeServiceServer) UIObjSetImageSrc(context.Context, *UIObjSetImageSrcRequest) (*UIObjSetImageSrcResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UIObjSetImageSrc not implemented") +} +func (UnimplementedNativeServiceServer) DisplaySetRotation(context.Context, *DisplaySetRotationRequest) (*DisplaySetRotationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DisplaySetRotation not implemented") +} +func (UnimplementedNativeServiceServer) UpdateLabelIfChanged(context.Context, *UpdateLabelIfChangedRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateLabelIfChanged not implemented") +} +func (UnimplementedNativeServiceServer) UpdateLabelAndChangeVisibility(context.Context, *UpdateLabelAndChangeVisibilityRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateLabelAndChangeVisibility not implemented") +} +func (UnimplementedNativeServiceServer) SwitchToScreenIf(context.Context, *SwitchToScreenIfRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method SwitchToScreenIf not implemented") +} +func (UnimplementedNativeServiceServer) SwitchToScreenIfDifferent(context.Context, *SwitchToScreenIfDifferentRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method SwitchToScreenIfDifferent not implemented") +} +func (UnimplementedNativeServiceServer) DoNotUseThisIsForCrashTestingOnly(context.Context, *Empty) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method DoNotUseThisIsForCrashTestingOnly not implemented") +} +func (UnimplementedNativeServiceServer) StreamEvents(*Empty, NativeService_StreamEventsServer) error { + return status.Errorf(codes.Unimplemented, "method StreamEvents not implemented") +} +func (UnimplementedNativeServiceServer) mustEmbedUnimplementedNativeServiceServer() {} + +// UnsafeNativeServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to NativeServiceServer will +// result in compilation errors. +type UnsafeNativeServiceServer interface { + mustEmbedUnimplementedNativeServiceServer() +} + +func RegisterNativeServiceServer(s grpc.ServiceRegistrar, srv NativeServiceServer) { + s.RegisterService(&NativeService_ServiceDesc, srv) +} + +func _NativeService_IsReady_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(IsReadyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).IsReady(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_IsReady_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).IsReady(ctx, req.(*IsReadyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_VideoSetSleepMode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(VideoSetSleepModeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).VideoSetSleepMode(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_VideoSetSleepMode_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).VideoSetSleepMode(ctx, req.(*VideoSetSleepModeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_VideoGetSleepMode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).VideoGetSleepMode(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_VideoGetSleepMode_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).VideoGetSleepMode(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_VideoSleepModeSupported_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).VideoSleepModeSupported(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_VideoSleepModeSupported_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).VideoSleepModeSupported(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_VideoSetQualityFactor_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(VideoSetQualityFactorRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).VideoSetQualityFactor(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_VideoSetQualityFactor_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).VideoSetQualityFactor(ctx, req.(*VideoSetQualityFactorRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_VideoGetQualityFactor_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).VideoGetQualityFactor(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_VideoGetQualityFactor_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).VideoGetQualityFactor(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_VideoSetEDID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(VideoSetEDIDRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).VideoSetEDID(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_VideoSetEDID_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).VideoSetEDID(ctx, req.(*VideoSetEDIDRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_VideoGetEDID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).VideoGetEDID(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_VideoGetEDID_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).VideoGetEDID(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_VideoLogStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).VideoLogStatus(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_VideoLogStatus_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).VideoLogStatus(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_VideoStop_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).VideoStop(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_VideoStop_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).VideoStop(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_VideoStart_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).VideoStart(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_VideoStart_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).VideoStart(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_GetLVGLVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).GetLVGLVersion(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_GetLVGLVersion_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).GetLVGLVersion(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_UIObjHide_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UIObjHideRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).UIObjHide(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_UIObjHide_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).UIObjHide(ctx, req.(*UIObjHideRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_UIObjShow_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UIObjShowRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).UIObjShow(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_UIObjShow_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).UIObjShow(ctx, req.(*UIObjShowRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_UISetVar_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UISetVarRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).UISetVar(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_UISetVar_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).UISetVar(ctx, req.(*UISetVarRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_UIGetVar_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UIGetVarRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).UIGetVar(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_UIGetVar_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).UIGetVar(ctx, req.(*UIGetVarRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_UIObjAddState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UIObjAddStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).UIObjAddState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_UIObjAddState_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).UIObjAddState(ctx, req.(*UIObjAddStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_UIObjClearState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UIObjClearStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).UIObjClearState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_UIObjClearState_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).UIObjClearState(ctx, req.(*UIObjClearStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_UIObjAddFlag_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UIObjAddFlagRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).UIObjAddFlag(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_UIObjAddFlag_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).UIObjAddFlag(ctx, req.(*UIObjAddFlagRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_UIObjClearFlag_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UIObjClearFlagRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).UIObjClearFlag(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_UIObjClearFlag_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).UIObjClearFlag(ctx, req.(*UIObjClearFlagRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_UIObjSetOpacity_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UIObjSetOpacityRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).UIObjSetOpacity(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_UIObjSetOpacity_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).UIObjSetOpacity(ctx, req.(*UIObjSetOpacityRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_UIObjFadeIn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UIObjFadeInRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).UIObjFadeIn(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_UIObjFadeIn_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).UIObjFadeIn(ctx, req.(*UIObjFadeInRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_UIObjFadeOut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UIObjFadeOutRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).UIObjFadeOut(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_UIObjFadeOut_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).UIObjFadeOut(ctx, req.(*UIObjFadeOutRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_UIObjSetLabelText_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UIObjSetLabelTextRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).UIObjSetLabelText(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_UIObjSetLabelText_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).UIObjSetLabelText(ctx, req.(*UIObjSetLabelTextRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_UIObjSetImageSrc_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UIObjSetImageSrcRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).UIObjSetImageSrc(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_UIObjSetImageSrc_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).UIObjSetImageSrc(ctx, req.(*UIObjSetImageSrcRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_DisplaySetRotation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DisplaySetRotationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).DisplaySetRotation(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_DisplaySetRotation_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).DisplaySetRotation(ctx, req.(*DisplaySetRotationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_UpdateLabelIfChanged_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateLabelIfChangedRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).UpdateLabelIfChanged(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_UpdateLabelIfChanged_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).UpdateLabelIfChanged(ctx, req.(*UpdateLabelIfChangedRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_UpdateLabelAndChangeVisibility_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateLabelAndChangeVisibilityRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).UpdateLabelAndChangeVisibility(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_UpdateLabelAndChangeVisibility_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).UpdateLabelAndChangeVisibility(ctx, req.(*UpdateLabelAndChangeVisibilityRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_SwitchToScreenIf_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SwitchToScreenIfRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).SwitchToScreenIf(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_SwitchToScreenIf_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).SwitchToScreenIf(ctx, req.(*SwitchToScreenIfRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_SwitchToScreenIfDifferent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SwitchToScreenIfDifferentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).SwitchToScreenIfDifferent(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_SwitchToScreenIfDifferent_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).SwitchToScreenIfDifferent(ctx, req.(*SwitchToScreenIfDifferentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_DoNotUseThisIsForCrashTestingOnly_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NativeServiceServer).DoNotUseThisIsForCrashTestingOnly(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NativeService_DoNotUseThisIsForCrashTestingOnly_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NativeServiceServer).DoNotUseThisIsForCrashTestingOnly(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _NativeService_StreamEvents_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(Empty) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(NativeServiceServer).StreamEvents(m, &nativeServiceStreamEventsServer{ServerStream: stream}) +} + +type NativeService_StreamEventsServer interface { + Send(*Event) error + grpc.ServerStream +} + +type nativeServiceStreamEventsServer struct { + grpc.ServerStream +} + +func (x *nativeServiceStreamEventsServer) Send(m *Event) error { + return x.ServerStream.SendMsg(m) +} + +// NativeService_ServiceDesc is the grpc.ServiceDesc for NativeService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var NativeService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "native.NativeService", + HandlerType: (*NativeServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "IsReady", + Handler: _NativeService_IsReady_Handler, + }, + { + MethodName: "VideoSetSleepMode", + Handler: _NativeService_VideoSetSleepMode_Handler, + }, + { + MethodName: "VideoGetSleepMode", + Handler: _NativeService_VideoGetSleepMode_Handler, + }, + { + MethodName: "VideoSleepModeSupported", + Handler: _NativeService_VideoSleepModeSupported_Handler, + }, + { + MethodName: "VideoSetQualityFactor", + Handler: _NativeService_VideoSetQualityFactor_Handler, + }, + { + MethodName: "VideoGetQualityFactor", + Handler: _NativeService_VideoGetQualityFactor_Handler, + }, + { + MethodName: "VideoSetEDID", + Handler: _NativeService_VideoSetEDID_Handler, + }, + { + MethodName: "VideoGetEDID", + Handler: _NativeService_VideoGetEDID_Handler, + }, + { + MethodName: "VideoLogStatus", + Handler: _NativeService_VideoLogStatus_Handler, + }, + { + MethodName: "VideoStop", + Handler: _NativeService_VideoStop_Handler, + }, + { + MethodName: "VideoStart", + Handler: _NativeService_VideoStart_Handler, + }, + { + MethodName: "GetLVGLVersion", + Handler: _NativeService_GetLVGLVersion_Handler, + }, + { + MethodName: "UIObjHide", + Handler: _NativeService_UIObjHide_Handler, + }, + { + MethodName: "UIObjShow", + Handler: _NativeService_UIObjShow_Handler, + }, + { + MethodName: "UISetVar", + Handler: _NativeService_UISetVar_Handler, + }, + { + MethodName: "UIGetVar", + Handler: _NativeService_UIGetVar_Handler, + }, + { + MethodName: "UIObjAddState", + Handler: _NativeService_UIObjAddState_Handler, + }, + { + MethodName: "UIObjClearState", + Handler: _NativeService_UIObjClearState_Handler, + }, + { + MethodName: "UIObjAddFlag", + Handler: _NativeService_UIObjAddFlag_Handler, + }, + { + MethodName: "UIObjClearFlag", + Handler: _NativeService_UIObjClearFlag_Handler, + }, + { + MethodName: "UIObjSetOpacity", + Handler: _NativeService_UIObjSetOpacity_Handler, + }, + { + MethodName: "UIObjFadeIn", + Handler: _NativeService_UIObjFadeIn_Handler, + }, + { + MethodName: "UIObjFadeOut", + Handler: _NativeService_UIObjFadeOut_Handler, + }, + { + MethodName: "UIObjSetLabelText", + Handler: _NativeService_UIObjSetLabelText_Handler, + }, + { + MethodName: "UIObjSetImageSrc", + Handler: _NativeService_UIObjSetImageSrc_Handler, + }, + { + MethodName: "DisplaySetRotation", + Handler: _NativeService_DisplaySetRotation_Handler, + }, + { + MethodName: "UpdateLabelIfChanged", + Handler: _NativeService_UpdateLabelIfChanged_Handler, + }, + { + MethodName: "UpdateLabelAndChangeVisibility", + Handler: _NativeService_UpdateLabelAndChangeVisibility_Handler, + }, + { + MethodName: "SwitchToScreenIf", + Handler: _NativeService_SwitchToScreenIf_Handler, + }, + { + MethodName: "SwitchToScreenIfDifferent", + Handler: _NativeService_SwitchToScreenIfDifferent_Handler, + }, + { + MethodName: "DoNotUseThisIsForCrashTestingOnly", + Handler: _NativeService_DoNotUseThisIsForCrashTestingOnly_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "StreamEvents", + Handler: _NativeService_StreamEvents_Handler, + ServerStreams: true, + }, + }, + Metadata: "internal/native/proto/native.proto", +} diff --git a/internal/native/proxy.go b/internal/native/proxy.go new file mode 100644 index 000000000..dda6a86e6 --- /dev/null +++ b/internal/native/proxy.go @@ -0,0 +1,687 @@ +package native + +import ( + "context" + "crypto/rand" + "encoding/hex" + "fmt" + "net" + "os" + "os/exec" + "runtime" + "strings" + "sync" + "syscall" + "time" + + "github.com/Masterminds/semver/v3" + "github.com/jetkvm/kvm/internal/utils" + "github.com/rs/zerolog" +) + +const ( + maxFrameSize = 1920 * 1080 / 2 + defaultMaxRestartAttempts uint = 5 +) + +type nativeProxyOptions struct { + Disable bool `env:"JETKVM_NATIVE_DISABLE"` + SystemVersion *semver.Version `env:"JETKVM_NATIVE_SYSTEM_VERSION"` + AppVersion *semver.Version `env:"JETKVM_NATIVE_APP_VERSION"` + DisplayRotation uint16 `env:"JETKVM_NATIVE_DISPLAY_ROTATION"` + DefaultQualityFactor float64 `env:"JETKVM_NATIVE_DEFAULT_QUALITY_FACTOR"` + CtrlUnixSocket string `env:"JETKVM_NATIVE_CTRL_UNIX_SOCKET"` + VideoStreamUnixSocket string `env:"JETKVM_NATIVE_VIDEO_STREAM_UNIX_SOCKET"` + BinaryPath string `env:"JETKVM_NATIVE_BINARY_PATH"` + LoggerLevel zerolog.Level `env:"JETKVM_NATIVE_LOGGER_LEVEL"` + HandshakeMessage string `env:"JETKVM_NATIVE_HANDSHAKE_MESSAGE"` + MaxRestartAttempts uint + + OnVideoFrameReceived func(frame []byte, duration time.Duration) + OnIndevEvent func(event string) + OnRpcEvent func(event string) + OnVideoStateChange func(state VideoState) + OnNativeRestart func() +} + +func randomId(binaryLength int) string { + s := make([]byte, binaryLength) + _, err := rand.Read(s) + if err != nil { + nativeLogger.Error().Err(err).Msg("failed to generate random ID") + return strings.Repeat("0", binaryLength*2) // return all zeros if error + } + return hex.EncodeToString(s) +} + +func (n *NativeOptions) toProxyOptions() *nativeProxyOptions { + // random 16 bytes hex string + handshakeMessage := randomId(16) + maxRestartAttempts := defaultMaxRestartAttempts + if n.MaxRestartAttempts > 0 { + maxRestartAttempts = n.MaxRestartAttempts + } + return &nativeProxyOptions{ + SystemVersion: n.SystemVersion, + AppVersion: n.AppVersion, + DisplayRotation: n.DisplayRotation, + DefaultQualityFactor: n.DefaultQualityFactor, + OnVideoFrameReceived: n.OnVideoFrameReceived, + OnIndevEvent: n.OnIndevEvent, + OnRpcEvent: n.OnRpcEvent, + OnVideoStateChange: n.OnVideoStateChange, + OnNativeRestart: n.OnNativeRestart, + HandshakeMessage: handshakeMessage, + MaxRestartAttempts: maxRestartAttempts, + } +} + +func (p *nativeProxyOptions) toNativeOptions() *NativeOptions { + return &NativeOptions{ + SystemVersion: p.SystemVersion, + AppVersion: p.AppVersion, + DisplayRotation: p.DisplayRotation, + DefaultQualityFactor: p.DefaultQualityFactor, + } +} + +// cmdWrapper wraps exec.Cmd to implement processCmd interface +type cmdWrapper struct { + *exec.Cmd + stdoutHandler *nativeProxyStdoutHandler +} + +func (c *cmdWrapper) GetProcess() interface { + Kill() error + Signal(sig interface{}) error +} { + return &processWrapper{Process: c.Process} +} + +type processWrapper struct { + *os.Process +} + +func (p *processWrapper) Signal(sig interface{}) error { + if sig == nil { + // Check if process is alive by sending signal 0 + return p.Process.Signal(os.Signal(syscall.Signal(0))) + } + if s, ok := sig.(os.Signal); ok { + return p.Process.Signal(s) + } + return fmt.Errorf("invalid signal type") +} + +// NativeProxy is a proxy that communicates with a separate native process +type NativeProxy struct { + nativeUnixSocket string + videoStreamUnixSocket string + videoStreamListener net.Listener + binaryPath string + + startMu sync.Mutex // mutex for the start process (context and isStopped) + ctx context.Context + cancel context.CancelFunc + + client *GRPCClient + clientMu sync.RWMutex // mutex for the client + + cmd *cmdWrapper + cmdMu sync.Mutex // mutex for the cmd + + logger *zerolog.Logger + options *nativeProxyOptions + restarts uint + stopped bool +} + +// NewNativeProxy creates a new NativeProxy that spawns a separate process +func NewNativeProxy(opts NativeOptions) (*NativeProxy, error) { + proxyOptions := opts.toProxyOptions() + proxyOptions.VideoStreamUnixSocket = fmt.Sprintf("@jetkvm/native/video-stream/%s", randomId(4)) + + // Get the current executable path to spawn itself + exePath, err := os.Executable() + if err != nil { + return nil, fmt.Errorf("failed to get executable path: %w", err) + } + + proxy := &NativeProxy{ + nativeUnixSocket: proxyOptions.CtrlUnixSocket, + videoStreamUnixSocket: proxyOptions.VideoStreamUnixSocket, + binaryPath: exePath, + logger: nativeLogger, + options: proxyOptions, + restarts: 0, + } + + return proxy, nil +} + +func (p *NativeProxy) startVideoStreamListener() error { + if p.videoStreamListener != nil { + return nil + } + + logger := p.logger.With().Str("socketPath", p.videoStreamUnixSocket).Logger() + listener, err := net.Listen("unixpacket", p.videoStreamUnixSocket) + if err != nil { + logger.Warn().Err(err).Msg("failed to start video stream listener") + return fmt.Errorf("failed to start video stream listener: %w", err) + } + logger.Info().Msg("video stream listener started") + p.videoStreamListener = listener + + go func() { + for { + conn, err := listener.Accept() + if err != nil { + logger.Warn().Err(err).Msg("failed to accept socket") + continue + } + + logger.Info().Msg("video stream socket accepted") + go p.handleVideoFrame(conn) + } + }() + + return nil +} + +type nativeProxyStdoutHandler struct { + mu *sync.Mutex + handshakeCh chan bool + handshakeMessage string + handshakeDone bool +} + +func (w *nativeProxyStdoutHandler) Write(p []byte) (n int, err error) { + w.mu.Lock() + defer w.mu.Unlock() + + if !w.handshakeDone && strings.Contains(string(p), w.handshakeMessage) { + w.handshakeDone = true + w.handshakeCh <- true + return len(p), nil + } + + os.Stdout.Write(p) + + return len(p), nil +} + +func (p *NativeProxy) toProcessCommand() (*cmdWrapper, error) { + // generate a new random ID for the gRPC socket on each restart + // sometimes the socket is not closed properly when the process exits + // this is a workaround to avoid the issue + p.nativeUnixSocket = fmt.Sprintf("jetkvm/native/grpc/%s", randomId(4)) + p.options.CtrlUnixSocket = p.nativeUnixSocket + + envArgs, err := utils.MarshalEnv(p.options) + if err != nil { + return nil, fmt.Errorf("failed to marshal environment variables: %w", err) + } + + cmd := &cmdWrapper{ + Cmd: exec.Command( + p.binaryPath, + "-subcomponent=native", + ), + stdoutHandler: &nativeProxyStdoutHandler{ + mu: &sync.Mutex{}, + handshakeCh: make(chan bool), + handshakeMessage: p.options.HandshakeMessage, + }, + } + cmd.Stdout = cmd.stdoutHandler + cmd.Stderr = os.Stderr + cmd.SysProcAttr = &syscall.SysProcAttr{ + Setpgid: true, + Pdeathsig: syscall.SIGTERM, + } + // Set environment variable to indicate native process mode + cmd.Env = append( + os.Environ(), + envArgs..., + ) + + return cmd, nil +} + +func (p *NativeProxy) handleVideoFrame(conn net.Conn) { + defer conn.Close() + + inboundPacket := make([]byte, maxFrameSize) + lastFrame := time.Now() + + for { + n, err := conn.Read(inboundPacket) + if err != nil { + p.logger.Warn().Err(err).Msg("failed to read video frame from socket") + break + } + now := time.Now() + sinceLastFrame := now.Sub(lastFrame) + lastFrame = now + p.options.OnVideoFrameReceived(inboundPacket[:n], sinceLastFrame) + } +} + +// it should be only called by start() method, as it isn't thread-safe +func (p *NativeProxy) setUpGRPCClient() error { + // wait until handshake completed + select { + case <-p.cmd.stdoutHandler.handshakeCh: + p.logger.Info().Msg("handshake completed") + case <-time.After(10 * time.Second): + return fmt.Errorf("handshake not completed within 10 seconds") + } + + logger := p.logger.With().Str("socketPath", "@"+p.nativeUnixSocket).Logger() + client, err := NewGRPCClient(grpcClientOptions{ + SocketPath: p.nativeUnixSocket, + Logger: &logger, + OnIndevEvent: p.options.OnIndevEvent, + OnRpcEvent: p.options.OnRpcEvent, + OnVideoStateChange: p.options.OnVideoStateChange, + }) + + logger.Info().Msg("created gRPC client") + if err != nil { + return fmt.Errorf("failed to create gRPC client: %w", err) + } + p.client = client + + // Wait for ready signal from the native process + if err := p.client.WaitReady(); err != nil { + // Clean up if ready failed + if p.cmd.Process != nil { + _ = p.cmd.Process.Kill() + _ = p.cmd.Wait() + } + return fmt.Errorf("failed to wait for ready: %w", err) + } + + // Call on native restart callback if it exists and restarts are greater than 0 + if p.options.OnNativeRestart != nil && p.restarts > 0 { + go p.options.OnNativeRestart() + } + + return nil +} + +func (p *NativeProxy) doStart() error { + p.cmdMu.Lock() + defer p.cmdMu.Unlock() + + // lock OS thread to prevent the process from being moved to a different thread + // see also https://go.dev/issue/27505 + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + cmd, err := p.toProcessCommand() + if err != nil { + return fmt.Errorf("failed to create process: %w", err) + } + + p.cmd = cmd + + if err := p.cmd.Start(); err != nil { + return fmt.Errorf("failed to start native process: %w", err) + } + + // here we'll replace the logger with a new one that includes the process ID + // there's no need to lock the mutex here as the side effect is acceptable + newLogger := p.logger.With().Int("pid", p.cmd.Process.Pid).Logger() + p.logger = &newLogger + + p.logger.Info().Msg("native process started") + + if err := p.setUpGRPCClient(); err != nil { + return fmt.Errorf("failed to set up gRPC client: %w", err) + } + + return nil +} + +// Start starts the native process +func (p *NativeProxy) Start() error { + p.startMu.Lock() + defer p.startMu.Unlock() + + p.ctx, p.cancel = context.WithCancel(context.Background()) + + if p.stopped { + return fmt.Errorf("proxy is stopped") + } + + if err := p.startVideoStreamListener(); err != nil { + return fmt.Errorf("failed to start video stream listener: %w", err) + } + + if err := p.doStart(); err != nil { + return fmt.Errorf("failed to start native process: %w", err) + } + + go p.monitorProcess() + + return nil +} + +// monitorProcess monitors the native process and restarts it if it crashes +func (p *NativeProxy) monitorProcess() { + for { + if p.stopped { + return + } + + select { + case <-p.ctx.Done(): + p.logger.Trace().Msg("context done, stopping monitor process [before wait]") + return + default: + } + + p.cmdMu.Lock() + err := fmt.Errorf("native process not started") + if p.cmd != nil { + err = p.cmd.Wait() + } + p.cmdMu.Unlock() + + if p.stopped { + return + } + + select { + case <-p.ctx.Done(): + p.logger.Trace().Msg("context done, stopping monitor process [after wait]") + return + default: + } + + p.logger.Warn().Err(err).Msg("native process exited, restarting ...") + + // Wait a bit before restarting + time.Sleep(1 * time.Second) + + // Restart the process + if err := p.restartProcess(); err != nil { + p.logger.Error().Err(err).Msg("failed to restart native process") + // Wait longer before retrying + time.Sleep(5 * time.Second) + continue + } + } +} + +// restartProcess restarts the native process +func (p *NativeProxy) restartProcess() error { + p.restarts++ + logger := p.logger.With().Uint("attempt", p.restarts).Uint("maxAttempts", p.options.MaxRestartAttempts).Logger() + + if p.restarts >= p.options.MaxRestartAttempts { + logger.Fatal().Msg("max restart attempts reached, exiting") + return fmt.Errorf("max restart attempts reached") + } + + if p.stopped { + return fmt.Errorf("proxy is stopped") + } + + // Close old client + p.clientMu.Lock() + if p.client != nil { + if err := p.client.Close(); err != nil { + logger.Warn().Err(err).Msg("failed to close gRPC client") + } + p.client = nil // set to nil to avoid closing it again + } + p.clientMu.Unlock() + logger.Info().Msg("gRPC client closed") + + logger.Info().Msg("attempting to restart native process") + if err := p.doStart(); err != nil { + logger.Error().Err(err).Msg("failed to start native process") + return fmt.Errorf("failed to start native process: %w", err) + } + + logger.Info().Msg("native process restarted successfully") + return nil +} + +// Stop stops the native process +func (p *NativeProxy) Stop() error { + p.startMu.Lock() + defer p.startMu.Unlock() + + p.stopped = true + + if p.cmd.Process != nil { + if err := p.cmd.Process.Kill(); err != nil { + return fmt.Errorf("failed to kill native process: %w", err) + } + _ = p.cmd.Wait() + } + + return nil +} + +func zeroValue[V string | bool | float64]() V { + var v V + return v +} + +func nativeProxyClientExec[K comparable, V string | bool | float64](p *NativeProxy, fn func(*GRPCClient) (V, error)) (V, error) { + p.clientMu.RLock() + defer p.clientMu.RUnlock() + + if p.client == nil { + return zeroValue[V](), fmt.Errorf("gRPC client not initialized") + } + + return fn(p.client) +} + +func nativeProxyClientExecWithoutArgument(p *NativeProxy, fn func(*GRPCClient) error) error { + p.clientMu.RLock() + defer p.clientMu.RUnlock() + + if p.client == nil { + return fmt.Errorf("gRPC client not initialized") + } + + return fn(p.client) +} + +// Implement all Native methods by forwarding to gRPC client +func (p *NativeProxy) VideoSetSleepMode(enabled bool) error { + return nativeProxyClientExecWithoutArgument(p, func(client *GRPCClient) error { + return client.VideoSetSleepMode(enabled) + }) +} + +func (p *NativeProxy) VideoGetSleepMode() (bool, error) { + return nativeProxyClientExec[bool](p, func(client *GRPCClient) (bool, error) { + return client.VideoGetSleepMode() + }) +} + +func (p *NativeProxy) VideoSleepModeSupported() bool { + result, _ := nativeProxyClientExec[bool](p, func(client *GRPCClient) (bool, error) { + return client.VideoSleepModeSupported(), nil + }) + return result +} + +func (p *NativeProxy) VideoSetQualityFactor(factor float64) error { + return nativeProxyClientExecWithoutArgument(p, func(client *GRPCClient) error { + return client.VideoSetQualityFactor(factor) + }) +} + +func (p *NativeProxy) VideoGetQualityFactor() (float64, error) { + return nativeProxyClientExec[float64](p, func(client *GRPCClient) (float64, error) { + return client.VideoGetQualityFactor() + }) +} + +func (p *NativeProxy) VideoSetEDID(edid string) error { + return nativeProxyClientExecWithoutArgument(p, func(client *GRPCClient) error { + return client.VideoSetEDID(edid) + }) +} + +func (p *NativeProxy) VideoGetEDID() (string, error) { + return nativeProxyClientExec[string](p, func(client *GRPCClient) (string, error) { + return client.VideoGetEDID() + }) +} + +func (p *NativeProxy) VideoLogStatus() (string, error) { + return nativeProxyClientExec[string](p, func(client *GRPCClient) (string, error) { + return client.VideoLogStatus() + }) +} + +func (p *NativeProxy) VideoStop() error { + return nativeProxyClientExecWithoutArgument(p, func(client *GRPCClient) error { + return client.VideoStop() + }) +} + +func (p *NativeProxy) VideoStart() error { + return nativeProxyClientExecWithoutArgument(p, func(client *GRPCClient) error { + return client.VideoStart() + }) +} + +func (p *NativeProxy) GetLVGLVersion() (string, error) { + return nativeProxyClientExec[string](p, func(client *GRPCClient) (string, error) { + return client.GetLVGLVersion() + }) +} + +func (p *NativeProxy) UIObjHide(objName string) (bool, error) { + result, err := nativeProxyClientExec[bool](p, func(client *GRPCClient) (bool, error) { + return client.UIObjHide(objName) + }) + return result, err +} + +func (p *NativeProxy) UIObjShow(objName string) (bool, error) { + result, err := nativeProxyClientExec[bool](p, func(client *GRPCClient) (bool, error) { + return client.UIObjShow(objName) + }) + return result, err +} + +func (p *NativeProxy) UISetVar(name string, value string) { + _ = nativeProxyClientExecWithoutArgument(p, func(client *GRPCClient) error { + client.UISetVar(name, value) + return nil + }) +} + +func (p *NativeProxy) UIGetVar(name string) string { + result, _ := nativeProxyClientExec[string](p, func(client *GRPCClient) (string, error) { + return client.UIGetVar(name), nil + }) + return result +} + +func (p *NativeProxy) UIObjAddState(objName string, state string) (bool, error) { + return nativeProxyClientExec[bool](p, func(client *GRPCClient) (bool, error) { + return client.UIObjAddState(objName, state) + }) +} + +func (p *NativeProxy) UIObjClearState(objName string, state string) (bool, error) { + return nativeProxyClientExec[bool](p, func(client *GRPCClient) (bool, error) { + return client.UIObjClearState(objName, state) + }) +} + +func (p *NativeProxy) UIObjAddFlag(objName string, flag string) (bool, error) { + return nativeProxyClientExec[bool](p, func(client *GRPCClient) (bool, error) { + return client.UIObjAddFlag(objName, flag) + }) +} + +func (p *NativeProxy) UIObjClearFlag(objName string, flag string) (bool, error) { + return nativeProxyClientExec[bool](p, func(client *GRPCClient) (bool, error) { + return client.UIObjClearFlag(objName, flag) + }) +} + +func (p *NativeProxy) UIObjFadeIn(objName string, duration uint32) (bool, error) { + return nativeProxyClientExec[bool](p, func(client *GRPCClient) (bool, error) { + return client.UIObjFadeIn(objName, duration) + }) +} + +func (p *NativeProxy) UIObjFadeOut(objName string, duration uint32) (bool, error) { + return nativeProxyClientExec[bool](p, func(client *GRPCClient) (bool, error) { + return client.UIObjFadeOut(objName, duration) + }) +} + +func (p *NativeProxy) UIObjSetLabelText(objName string, text string) (bool, error) { + return nativeProxyClientExec[bool](p, func(client *GRPCClient) (bool, error) { + return client.UIObjSetLabelText(objName, text) + }) +} + +func (p *NativeProxy) UIObjSetImageSrc(objName string, image string) (bool, error) { + return nativeProxyClientExec[bool](p, func(client *GRPCClient) (bool, error) { + return client.UIObjSetImageSrc(objName, image) + }) +} + +func (p *NativeProxy) UIObjSetOpacity(objName string, opacity int) (bool, error) { + return nativeProxyClientExec[bool](p, func(client *GRPCClient) (bool, error) { + return client.UIObjSetOpacity(objName, opacity) + }) +} + +func (p *NativeProxy) DisplaySetRotation(rotation uint16) (bool, error) { + return nativeProxyClientExec[bool](p, func(client *GRPCClient) (bool, error) { + return client.DisplaySetRotation(rotation) + }) +} + +func (p *NativeProxy) UpdateLabelIfChanged(objName string, newText string) { + _ = nativeProxyClientExecWithoutArgument(p, func(client *GRPCClient) error { + client.UpdateLabelIfChanged(objName, newText) + return nil + }) +} + +func (p *NativeProxy) UpdateLabelAndChangeVisibility(objName string, newText string) { + _ = nativeProxyClientExecWithoutArgument(p, func(client *GRPCClient) error { + client.UpdateLabelAndChangeVisibility(objName, newText) + return nil + }) +} + +func (p *NativeProxy) SwitchToScreenIf(screenName string, shouldSwitch []string) { + _ = nativeProxyClientExecWithoutArgument(p, func(client *GRPCClient) error { + client.SwitchToScreenIf(screenName, shouldSwitch) + return nil + }) +} + +func (p *NativeProxy) SwitchToScreenIfDifferent(screenName string) { + _ = nativeProxyClientExecWithoutArgument(p, func(client *GRPCClient) error { + client.SwitchToScreenIfDifferent(screenName) + return nil + }) +} + +func (p *NativeProxy) DoNotUseThisIsForCrashTestingOnly() { + _ = nativeProxyClientExecWithoutArgument(p, func(client *GRPCClient) error { + client.DoNotUseThisIsForCrashTestingOnly() + return nil + }) +} diff --git a/internal/native/server.go b/internal/native/server.go new file mode 100644 index 000000000..ae983159d --- /dev/null +++ b/internal/native/server.go @@ -0,0 +1,137 @@ +package native + +import ( + "context" + "fmt" + "net" + "os" + "os/signal" + "syscall" + "time" + + "github.com/caarlos0/env/v11" + "github.com/erikdubbelboer/gspt" + "github.com/rs/zerolog" +) + +// Native Process +// stdout - exchange messages with the parent process +// stderr - logging and error messages + +var ( + procPrefix string = "jetkvm: [native]" + lastProcTitle string +) + +const ( + DebugModeFile = "/userdata/jetkvm/.native-debug-mode" +) + +func setProcTitle(status string) { + lastProcTitle = status + if status != "" { + status = " " + status + } + title := fmt.Sprintf("%s%s", procPrefix, status) + gspt.SetProcTitle(title) +} + +func monitorCrashSignal(ctx context.Context, logger *zerolog.Logger, nativeInstance NativeInterface) { + logger.Info().Msg("DEBUG mode: will crash the process on SIGHUP signal") + + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, syscall.SIGHUP) + + for { + select { + case sig := <-sigChan: + logger.Info().Str("signal", sig.String()).Msg("received termination signal") + nativeInstance.DoNotUseThisIsForCrashTestingOnly() + case <-ctx.Done(): + logger.Info().Msg("context done, stopping monitor process") + return + } + } +} + +// RunNativeProcess runs the native process mode +func RunNativeProcess(binaryName string) { + appCtx, appCtxCancel := context.WithCancel(context.Background()) + defer appCtxCancel() + + logger := nativeLogger.With().Int("pid", os.Getpid()).Logger() + setProcTitle("starting") + + // Parse native options + var proxyOptions nativeProxyOptions + if err := env.Parse(&proxyOptions); err != nil { + logger.Fatal().Err(err).Msg("failed to parse native proxy options") + } + + // Connect to video stream socket + conn, err := net.Dial("unixpacket", proxyOptions.VideoStreamUnixSocket) + if err != nil { + logger.Fatal().Err(err).Msg("failed to connect to video stream socket") + } + logger.Info().Str("videoStreamSocketPath", proxyOptions.VideoStreamUnixSocket).Msg("connected to video stream socket") + + nativeOptions := proxyOptions.toNativeOptions() + nativeOptions.OnVideoFrameReceived = func(frame []byte, duration time.Duration) { + _, err := conn.Write(frame) + if err != nil { + logger.Fatal().Err(err).Msg("failed to write frame to video stream socket") + } + } + + // Create native instance + nativeInstance := NewNative(*nativeOptions) + gspt.SetProcTitle("jetkvm: [native] initializing") + + // Start native instance + if err := nativeInstance.Start(); err != nil { + logger.Fatal().Err(err).Msg("failed to start native instance") + } + + grpcLogger := logger.With().Str("socketPath", fmt.Sprintf("@%v", proxyOptions.CtrlUnixSocket)).Logger() + setProcTitle("starting gRPC server") + // Create gRPC server + grpcServer := NewGRPCServer(nativeInstance, &grpcLogger) + + logger.Info().Msg("starting gRPC server") + // Start gRPC server + server, lis, err := StartGRPCServer(grpcServer, fmt.Sprintf("@%v", proxyOptions.CtrlUnixSocket), &logger) + if err != nil { + logger.Fatal().Err(err).Msg("failed to start gRPC server") + } + setProcTitle("ready") + + if _, err := os.Stat(DebugModeFile); err == nil { + logger.Info().Msg("DEBUG mode: enabled") + go monitorCrashSignal(appCtx, &logger, nativeInstance) + } + + // Signal that we're ready by writing handshake message to stdout (for parent to read) + // Stdout.Write is used to avoid buffering the message + _, err = os.Stdout.Write([]byte(proxyOptions.HandshakeMessage + "\n")) + if err != nil { + logger.Fatal().Err(err).Msg("failed to write handshake message to stdout") + } + + // Set up signal handling + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT) + + // Wait for signal + sig := <-sigChan + logger.Info(). + Str("signal", sig.String()). + Msg("received termination signal") + + // Graceful shutdown might stuck forever, + // we will use Stop() instead to force quit the gRPC server, + // we can implement a graceful shutdown with a timeout in the future if needed + server.Stop() + lis.Close() + + logger.Info().Msg("native process exiting") +} diff --git a/internal/supervisor/consts.go b/internal/supervisor/consts.go new file mode 100644 index 000000000..5f2884895 --- /dev/null +++ b/internal/supervisor/consts.go @@ -0,0 +1,9 @@ +package supervisor + +const ( + EnvChildID = "JETKVM_CHILD_ID" // The child ID is the version of the app that is running + EnvSubcomponent = "JETKVM_SUBCOMPONENT" // The subcomponent is the component that is running + ErrorDumpDir = "/userdata/jetkvm/crashdump" // The error dump directory is the directory where the error dumps are stored + ErrorDumpLastFile = "last-crash.log" // The error dump last file is the last error dump file + ErrorDumpTemplate = "jetkvm-%s.log" // The error dump template is the template for the error dump file +) diff --git a/internal/utils/env.go b/internal/utils/env.go new file mode 100644 index 000000000..4040b961c --- /dev/null +++ b/internal/utils/env.go @@ -0,0 +1,92 @@ +package utils + +import ( + "fmt" + "reflect" + "strconv" +) + +func MarshalEnv(instance interface{}) ([]string, error) { + v := reflect.ValueOf(instance) + if v.Kind() == reflect.Ptr { + if v.IsNil() { + return nil, fmt.Errorf("instance is nil") + } + v = v.Elem() + } + + if v.Kind() != reflect.Struct { + return nil, fmt.Errorf("instance must be a struct or pointer to struct") + } + + t := v.Type() + var result []string + + for i := 0; i < v.NumField(); i++ { + field := t.Field(i) + fieldValue := v.Field(i) + + // Get the env tag + envTag := field.Tag.Get("env") + if envTag == "" || envTag == "-" { + continue + } + + // Skip unexported fields + if !fieldValue.CanInterface() { + continue + } + + var valueStr string + + // Handle different types + switch fieldValue.Kind() { + case reflect.Bool: + valueStr = strconv.FormatBool(fieldValue.Bool()) + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + valueStr = strconv.FormatUint(fieldValue.Uint(), 10) + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + valueStr = strconv.FormatInt(fieldValue.Int(), 10) + + case reflect.Float32, reflect.Float64: + valueStr = strconv.FormatFloat(fieldValue.Float(), 'f', -1, 64) + + case reflect.String: + valueStr = fieldValue.String() + + case reflect.Ptr: + if fieldValue.IsNil() { + continue // Skip nil pointers + } + elem := fieldValue.Elem() + // Handle *semver.Version and other pointer types + if elem.CanInterface() { + if stringer, ok := elem.Interface().(fmt.Stringer); ok { + valueStr = stringer.String() + } else { + valueStr = fmt.Sprintf("%v", elem.Interface()) + } + } else { + valueStr = fmt.Sprintf("%v", elem.Interface()) + } + + default: + // For other types, try to convert to string + if fieldValue.CanInterface() { + if stringer, ok := fieldValue.Interface().(fmt.Stringer); ok { + valueStr = stringer.String() + } else { + valueStr = fmt.Sprintf("%v", fieldValue.Interface()) + } + } else { + valueStr = fmt.Sprintf("%v", fieldValue.Interface()) + } + } + + result = append(result, fmt.Sprintf("%s=%s", envTag, valueStr)) + } + + return result, nil +} diff --git a/internal/utils/env_test.go b/internal/utils/env_test.go new file mode 100644 index 000000000..25313677b --- /dev/null +++ b/internal/utils/env_test.go @@ -0,0 +1,57 @@ +package utils + +import ( + "reflect" + "testing" + + "github.com/Masterminds/semver/v3" +) + +type nativeOptions struct { + Disable bool `env:"JETKVM_NATIVE_DISABLE"` + SystemVersion *semver.Version `env:"JETKVM_NATIVE_SYSTEM_VERSION"` + AppVersion *semver.Version `env:"JETKVM_NATIVE_APP_VERSION"` + DisplayRotation uint16 `env:"JETKVM_NATIVE_DISPLAY_ROTATION"` + DefaultQualityFactor float64 `env:"JETKVM_NATIVE_DEFAULT_QUALITY_FACTOR"` +} + +func TestMarshalEnv(t *testing.T) { + tests := []struct { + name string + instance interface{} + want []string + wantErr bool + }{ + { + name: "basic struct", + instance: nativeOptions{ + Disable: false, + SystemVersion: semver.MustParse("1.1.0"), + AppVersion: semver.MustParse("1111.0.0"), + DisplayRotation: 1, + DefaultQualityFactor: 1.0, + }, + want: []string{ + "JETKVM_NATIVE_DISABLE=false", + "JETKVM_NATIVE_SYSTEM_VERSION=1.1.0", + "JETKVM_NATIVE_APP_VERSION=1111.0.0", + "JETKVM_NATIVE_DISPLAY_ROTATION=1", + "JETKVM_NATIVE_DEFAULT_QUALITY_FACTOR=1", + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := MarshalEnv(tt.instance) + if (err != nil) != tt.wantErr { + t.Errorf("MarshalEnv() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("MarshalEnv() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/main.go b/main.go index da38d638d..a4d80fb74 100644 --- a/main.go +++ b/main.go @@ -2,23 +2,37 @@ package kvm import ( "context" + "fmt" "net/http" "os" "os/signal" "syscall" "time" + "github.com/erikdubbelboer/gspt" "github.com/gwatts/rootcerts" "github.com/jetkvm/kvm/internal/ota" ) var appCtx context.Context +var procPrefix string = "jetkvm: [app]" + +func setProcTitle(status string) { + if status != "" { + status = " " + status + } + title := fmt.Sprintf("%s%s", procPrefix, status) + gspt.SetProcTitle(title) +} func Main() { + setProcTitle("starting") + logger.Log().Msg("JetKVM Starting Up") checkFailsafeReason() if failsafeModeActive { + procPrefix = "jetkvm: [app+failsafe]" logger.Warn().Str("reason", failsafeModeReason).Msg("failsafe mode activated") } @@ -40,6 +54,12 @@ func Main() { go runWatchdog() + setProcTitle("initNative") + initNative(systemVersionLocal, appVersionLocal) + initDisplay() + + http.DefaultClient.Timeout = 1 * time.Minute + err = rootcerts.UpdateDefaultTransport() if err != nil { logger.Warn().Err(err).Msg("failed to load Root CA certificates") @@ -56,6 +76,7 @@ func Main() { http.DefaultClient.Timeout = 1 * time.Minute // Initialize network + setProcTitle("initNetwork") if err := initNetwork(); err != nil { logger.Error().Err(err).Msg("failed to initialize network") // TODO: reset config to default @@ -63,17 +84,21 @@ func Main() { } // Initialize time sync + setProcTitle("initTimeSync") initTimeSync() timeSync.Start() // Initialize mDNS + setProcTitle("initMdns") if err := initMdns(); err != nil { logger.Error().Err(err).Msg("failed to initialize mDNS") } + setProcTitle("initPrometheus") initPrometheus() // initialize usb gadget + setProcTitle("initUsbGadget") initUsbGadget() if err := setInitialVirtualMediaState(); err != nil { logger.Warn().Err(err).Msg("failed to set initial virtual media state") @@ -140,6 +165,9 @@ func Main() { initPublicIPState() initSerialPort() + + setProcTitle("ready") + sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) <-sigs diff --git a/native.go b/native.go index 81a0e50de..5f09ef83a 100644 --- a/native.go +++ b/native.go @@ -11,17 +11,28 @@ import ( ) var ( - nativeInstance *native.Native + nativeInstance native.NativeInterface nativeCmdLock = sync.Mutex{} ) func initNative(systemVersion *semver.Version, appVersion *semver.Version) { - nativeInstance = native.NewNative(native.NativeOptions{ - Disable: failsafeModeActive, + if failsafeModeActive { + nativeInstance = &native.EmptyNativeInterface{} + nativeLogger.Warn().Msg("failsafe mode active, using empty native interface") + return + } + + nativeLogger.Info().Msg("initializing native proxy") + var err error + nativeInstance, err = native.NewNativeProxy(native.NativeOptions{ SystemVersion: systemVersion, AppVersion: appVersion, DisplayRotation: config.GetDisplayRotation(), DefaultQualityFactor: config.VideoQualityFactor, + MaxRestartAttempts: config.NativeMaxRestart, + OnNativeRestart: func() { + configureDisplayOnNativeRestart() + }, OnVideoStateChange: func(state native.VideoState) { lastVideoState = state triggerVideoStateUpdate() @@ -63,8 +74,13 @@ func initNative(systemVersion *semver.Version, appVersion *semver.Version) { } }, }) + if err != nil { + nativeLogger.Fatal().Err(err).Msg("failed to create native proxy") + } - nativeInstance.Start() + if err := nativeInstance.Start(); err != nil { + nativeLogger.Fatal().Err(err).Msg("failed to start native proxy") + } go func() { if err := nativeInstance.VideoSetEDID(config.EdidString); err != nil { nativeLogger.Warn().Err(err).Msg("error setting EDID") diff --git a/scripts/build_cgo.sh b/scripts/build_cgo.sh index 87577e39f..d67fa7cda 100755 --- a/scripts/build_cgo.sh +++ b/scripts/build_cgo.sh @@ -4,6 +4,8 @@ set -e SCRIPT_PATH=$(realpath "$(dirname $(realpath "${BASH_SOURCE[0]}"))") source ${SCRIPT_PATH}/build_utils.sh +CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE:-Release} + CGO_PATH=$(realpath "${SCRIPT_PATH}/../internal/native/cgo") BUILD_DIR=${CGO_PATH}/build @@ -31,7 +33,7 @@ VERBOSE=1 cmake -B "${BUILD_DIR}" \ -DCONFIG_LV_BUILD_EXAMPLES=OFF \ -DCONFIG_LV_BUILD_DEMOS=OFF \ -DSKIP_GLIBC_NAMES=ON \ - -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ -DCMAKE_INSTALL_PREFIX="${TMP_DIR}" msg_info "▶ Copying built library and header files" diff --git a/scripts/configure_vscode.py b/scripts/configure_vscode.py new file mode 100755 index 000000000..cac54685d --- /dev/null +++ b/scripts/configure_vscode.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +import json +import os + +DEFAULT_C_INTELLISENSE_SETTINGS = { + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + # "compilerPath": "/opt/jetkvm-native-buildkit/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc", + "cStandard": "c17", + "cppStandard": "gnu++17", + "intelliSenseMode": "linux-gcc-arm", + "configurationProvider": "ms-vscode.cmake-tools" + } + ], + "version": 4 +} + +def configure_c_intellisense(): + settings_path = os.path.join('.vscode', 'c_cpp_properties.json') + settings = DEFAULT_C_INTELLISENSE_SETTINGS.copy() + + # open existing settings if they exist + if os.path.exists(settings_path): + with open(settings_path, 'r') as f: + settings = json.load(f) + + # update compiler path + settings['configurations'][0]['compilerPath'] = "/opt/jetkvm-native-buildkit/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc" + settings['configurations'][0]['configurationProvider'] = "ms-vscode.cmake-tools" + + with open(settings_path, 'w') as f: + json.dump(settings, f, indent=4) + + print("C/C++ IntelliSense configuration updated.") + + +if __name__ == "__main__": + configure_c_intellisense() \ No newline at end of file diff --git a/scripts/dev_deploy.sh b/scripts/dev_deploy.sh index 6c8b204c0..96e7cf60d 100755 --- a/scripts/dev_deploy.sh +++ b/scripts/dev_deploy.sh @@ -12,12 +12,14 @@ show_help() { echo echo "Optional:" echo " -u, --user Remote username (default: root)" + echo " --gdb-port GDB debug port (default: 2345)" echo " --run-go-tests Run go tests" echo " --run-go-tests-only Run go tests and exit" echo " --skip-ui-build Skip frontend/UI build" echo " --skip-native-build Skip native build" echo " --disable-docker Disable docker build" echo " --enable-sync-trace Enable sync trace (do not use in release builds)" + echo " --native-binary Build and deploy the native binary (FOR DEBUGGING ONLY)" echo " -i, --install Build for release and install the app" echo " --help Display this help message" echo @@ -58,6 +60,8 @@ REMOTE_PATH="/userdata/jetkvm/bin" SKIP_UI_BUILD=false SKIP_UI_BUILD_RELEASE=0 SKIP_NATIVE_BUILD=0 +GDB_DEBUG_PORT=2345 +BUILD_NATIVE_BINARY=false ENABLE_SYNC_TRACE=0 RESET_USB_HID_DEVICE=false LOG_TRACE_SCOPES="${LOG_TRACE_SCOPES:-jetkvm,cloud,websocket,native,jsonrpc}" @@ -79,6 +83,10 @@ while [[ $# -gt 0 ]]; do REMOTE_USER="$2" shift 2 ;; + --gdb-port) + GDB_DEBUG_PORT="$2" + shift 2 + ;; --skip-ui-build) SKIP_UI_BUILD=true shift @@ -113,6 +121,10 @@ while [[ $# -gt 0 ]]; do RUN_GO_TESTS=true shift ;; + --native-binary) + BUILD_NATIVE_BINARY=true + shift + ;; -i|--install) INSTALL_APP=true shift @@ -141,6 +153,10 @@ fi # Check device connectivity before proceeding check_ping "${REMOTE_HOST}" check_ssh "${REMOTE_USER}" "${REMOTE_HOST}" +function sshdev() { + ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "$@" + return $? +} # check if the current CPU architecture is x86_64 if [ "$(uname -m)" != "x86_64" ]; then @@ -152,6 +168,34 @@ if [ "$BUILD_IN_DOCKER" = true ]; then build_docker_image fi +if [ "$BUILD_NATIVE_BINARY" = true ]; then + msg_info "▶ Building native binary" + CMAKE_BUILD_TYPE=Debug make build_native + msg_info "▶ Checking if GDB is available on remote host" + if ! sshdev "command -v gdbserver > /dev/null 2>&1"; then + msg_warn "Error: gdbserver is not installed on the remote host" + tar -czf - -C /opt/jetkvm-native-buildkit/gdb/ . | sshdev "tar -xzf - -C /usr/bin" + msg_info "✓ gdbserver installed on remote host" + fi + msg_info "▶ Stopping any existing instances of jetkvm_native_debug on remote host" + sshdev "killall -9 jetkvm_app jetkvm_app_debug jetkvm_native_debug gdbserver || true >> /dev/null 2>&1" + sshdev "cat > ${REMOTE_PATH}/jetkvm_native_debug" < internal/native/cgo/build/jknative-bin + sshdev -t ash << EOF +set -e + +# Set the library path to include the directory where librockit.so is located +export LD_LIBRARY_PATH=/oem/usr/lib:\$LD_LIBRARY_PATH + +cd ${REMOTE_PATH} +killall -9 jetkvm_app jetkvm_app_debug jetkvm_native_debug || true +sleep 5 +echo 'V' > /dev/watchdog +chmod +x jetkvm_native_debug +gdbserver localhost:${GDB_DEBUG_PORT} ./jetkvm_native_debug +EOF + exit 0 +fi + # Build the development version on the host # When using `make build_release`, the frontend will be built regardless of the `SKIP_UI_BUILD` flag # check if static/index.html exists @@ -176,10 +220,10 @@ if [ "$RUN_GO_TESTS" = true ]; then make build_dev_test msg_info "▶ Copying device-tests.tar.gz to remote host" - ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "cat > /tmp/device-tests.tar.gz" < device-tests.tar.gz + sshdev "cat > /tmp/device-tests.tar.gz" < device-tests.tar.gz msg_info "▶ Running go tests" - ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" ash << 'EOF' + sshdev ash << 'EOF' set -e TMP_DIR=$(mktemp -d) cd ${TMP_DIR} @@ -222,10 +266,10 @@ then ENABLE_SYNC_TRACE=${ENABLE_SYNC_TRACE} # Copy the binary to the remote host as if we were the OTA updater. - ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "cat > /userdata/jetkvm/jetkvm_app.update" < bin/jetkvm_app + sshdev "cat > /userdata/jetkvm/jetkvm_app.update" < bin/jetkvm_app # Reboot the device, the new app will be deployed by the startup process. - ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "reboot" + sshdev "reboot" else msg_info "▶ Building development binary" do_make build_dev \ @@ -234,21 +278,21 @@ else ENABLE_SYNC_TRACE=${ENABLE_SYNC_TRACE} # Kill any existing instances of the application - ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "killall jetkvm_app_debug || true" + sshdev "killall jetkvm_app_debug || true" # Copy the binary to the remote host - ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "cat > ${REMOTE_PATH}/jetkvm_app_debug" < bin/jetkvm_app + sshdev "cat > ${REMOTE_PATH}/jetkvm_app_debug" < bin/jetkvm_app if [ "$RESET_USB_HID_DEVICE" = true ]; then msg_info "▶ Resetting USB HID device" msg_warn "The option has been deprecated and will be removed in a future version, as JetKVM will now reset USB gadget configuration when needed" # Remove the old USB gadget configuration - ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "rm -rf /sys/kernel/config/usb_gadget/jetkvm/configs/c.1/hid.usb*" - ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "ls /sys/class/udc > /sys/kernel/config/usb_gadget/jetkvm/UDC" + sshdev "rm -rf /sys/kernel/config/usb_gadget/jetkvm/configs/c.1/hid.usb*" + sshdev "ls /sys/class/udc > /sys/kernel/config/usb_gadget/jetkvm/UDC" fi # Deploy and run the application on the remote host - ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" ash << EOF + sshdev ash << EOF set -e # Set the library path to include the directory where librockit.so is located diff --git a/scripts/generate_proto.sh b/scripts/generate_proto.sh new file mode 100755 index 000000000..150bef94e --- /dev/null +++ b/scripts/generate_proto.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# Generate gRPC code from proto files + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +cd "$PROJECT_ROOT" + +# Check if protoc is installed +if ! command -v protoc &> /dev/null; then + echo "Error: protoc is not installed" + echo "Install it with:" + echo " apt-get install protobuf-compiler # Debian/Ubuntu" + echo " brew install protobuf # macOS" + exit 1 +fi + +# Check if protoc-gen-go is installed +if ! command -v protoc-gen-go &> /dev/null; then + echo "Error: protoc-gen-go is not installed" + echo "Install it with: go install google.golang.org/protobuf/cmd/protoc-gen-go@latest" + exit 1 +fi + +# Check if protoc-gen-go-grpc is installed +if ! command -v protoc-gen-go-grpc &> /dev/null; then + echo "Error: protoc-gen-go-grpc is not installed" + echo "Install it with: go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest" + exit 1 +fi + +# Generate code +echo "Generating gRPC code from proto files..." +protoc \ + --go_out=. \ + --go_opt=paths=source_relative \ + --go-grpc_out=. \ + --go-grpc_opt=paths=source_relative \ + internal/native/proto/native.proto + +echo "Done!" + diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100644 index 000000000..b6afef910 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,134 @@ +#!/bin/bash +set -eE +set -o pipefail + +SCRIPT_PATH=$(realpath "$(dirname $(realpath "${BASH_SOURCE[0]}"))") +source ${SCRIPT_PATH}/build_utils.sh + +# Function to display help message +show_help() { + echo "Usage: $0 [options] -v " + echo + echo "Required:" + echo " --app-version App version to release" + echo " --system-version System version to release" + echo + echo "Optional:" + echo " -u, --user Remote username (default: root)" + echo " --run-go-tests Run go tests" + echo " --run-go-tests-only Run go tests and exit" + echo " --skip-ui-build Skip frontend/UI build" + echo " --skip-native-build Skip native build" + echo " --disable-docker Disable docker build" + echo " -i, --install Build for release and install the app" + echo " --help Display this help message" + echo + echo "Example:" + echo " $0 --system-version 0.2.6" +} + + +BUILD_VERSION=$1 +R2_PATH="r2://jetkvm-update/system" +PACK_BIN_PATH="./tools/linux/Linux_Pack_Firmware" +UNPACK_BIN="${PACK_BIN_PATH}/mk-update_unpack.sh" + +# Create temporary directory for downloads +TEMP_DIR=$(mktemp -d) +msg_ok "Created temporary directory: $TEMP_DIR" + +# Cleanup function +cleanup() { + if [ -d "$TEMP_DIR" ]; then + msg_info "Cleaning up temporary directory: $TEMP_DIR" + rm -rf "$TEMP_DIR" + fi +} + +# Set trap to cleanup on exit +# trap cleanup EXIT + +mkdir -p ${TEMP_DIR}/extracted-update +${UNPACK_BIN} -i update.img -o ${TEMP_DIR}/extracted-update + +exit 0 +# Check if the version already exists +if rclone lsf $R2_PATH/$BUILD_VERSION/ | grep -q .; then + msg_err "Error: Version $BUILD_VERSION already exists in the remote storage." + exit 1 +fi + +# Check if the version exists in the github +RELEASE_URL="https://api.github.com/repos/jetkvm/rv1106-system/releases/tags/v$BUILD_VERSION" + +# Download the release JSON +RELEASE_JSON=$(curl -s $RELEASE_URL) + +# Check if the release has assets we need +if echo $RELEASE_JSON | jq -e '.assets | length == 0' > /dev/null; then + msg_err "Error: Version $BUILD_VERSION does not have assets we need." + exit 1 +fi + +function get_file_by_name() { + local file_name=$1 + local file_url=$(echo $RELEASE_JSON | jq -r ".assets[] | select(.name == \"$file_name\") | .browser_download_url") + if [ -z "$file_url" ]; then + msg_err "Error: File $file_name not found in the release." + exit 1 + fi + local digest=$(echo $RELEASE_JSON | jq -r ".assets[] | select(.name == \"$file_name\") | .digest") + local temp_file_path="$TEMP_DIR/$file_name" + + msg_info "Downloading $file_name: $file_url" + + # Download the file to temporary directory + curl -L -o "$temp_file_path" "$file_url" + + # Verify digest if available + if [ "$digest" != "null" ] && [ -n "$digest" ]; then + msg_info "Verifying digest for $file_name ..." + local calculated_digest=$(sha256sum "$temp_file_path" | cut -d' ' -f1) + # Strip "sha256:" prefix if present + local expected_digest=$(echo "$digest" | sed 's/^sha256://') + if [ "$calculated_digest" != "$expected_digest" ]; then + msg_err "🙅 Digest verification failed for $file_name" + msg_info "Expected: $expected_digest" + msg_info "Calculated: $calculated_digest" + exit 1 + fi + else + msg_warn "Warning: No digest available for $file_name, skipping verification" + fi + + msg_ok "✅ $file_name downloaded and verified." +} + +get_file_by_name "update_ota.tar" +get_file_by_name "update.img" + +strings -d bin/jetkvm_app | grep -x '0.4.8' + +# Ask for confirmation +msg_info "Do you want to continue with the release? (y/n)" +read -n 1 -s -r -p "Press y to continue, any other key to exit" +echo -ne "\n" +if [ "$REPLY" != "y" ]; then + msg_err "🙅 Release cancelled." + exit 1 +fi + +msg_info "Releasing $BUILD_VERSION..." + +sha256sum $TEMP_DIR/update_ota.tar | awk '{print $1}' > $TEMP_DIR/update_ota.tar.sha256 +sha256sum $TEMP_DIR/update.img | awk '{print $1}' > $TEMP_DIR/update.img.sha256 + +# Check if the version already exists +msg_info "Copying to $R2_PATH/$BUILD_VERSION/" + +rclone copyto --progress $TEMP_DIR/update_ota.tar $R2_PATH/$BUILD_VERSION/system.tar +rclone copyto --progress $TEMP_DIR/update_ota.tar.sha256 $R2_PATH/$BUILD_VERSION/system.tar.sha256 +rclone copyto --progress $TEMP_DIR/update.img $R2_PATH/$BUILD_VERSION/update.img +rclone copyto --progress $TEMP_DIR/update.img.sha256 $R2_PATH/$BUILD_VERSION/update.img.sha256 + +msg_ok "✅ $BUILD_VERSION released." \ No newline at end of file