diff --git a/display.go b/display.go index f236d906..063803dc 100644 --- a/display.go +++ b/display.go @@ -38,17 +38,17 @@ func updateDisplay() { if usbState == "configured" { nativeInstance.UpdateLabelIfChanged("usb_status_label", "Connected") - _, _ = nativeInstance.UIObjSetState("usb_status", "LV_STATE_DEFAULT") + _, _ = nativeInstance.UIObjAddState("usb_status_label", "LV_STATE_CHECKED") } else { nativeInstance.UpdateLabelIfChanged("usb_status_label", "Disconnected") - _, _ = nativeInstance.UIObjSetState("usb_status", "LV_STATE_DISABLED") + _, _ = nativeInstance.UIObjClearState("usb_status_label", "LV_STATE_CHECKED") } if lastVideoState.Ready { nativeInstance.UpdateLabelIfChanged("hdmi_status_label", "Connected") - _, _ = nativeInstance.UIObjSetState("hdmi_status", "LV_STATE_DEFAULT") + _, _ = nativeInstance.UIObjAddState("hdmi_status_label", "LV_STATE_CHECKED") } else { nativeInstance.UpdateLabelIfChanged("hdmi_status_label", "Disconnected") - _, _ = nativeInstance.UIObjSetState("hdmi_status", "LV_STATE_DISABLED") + _, _ = nativeInstance.UIObjClearState("hdmi_status_label", "LV_STATE_CHECKED") } nativeInstance.UpdateLabelIfChanged("cloud_status_label", fmt.Sprintf("%d active", actionSessions)) diff --git a/internal/native/cgo/ctrl.c b/internal/native/cgo/ctrl.c index 93a49872..b98adb06 100644 --- a/internal/native/cgo/ctrl.c +++ b/internal/native/cgo/ctrl.c @@ -247,36 +247,55 @@ void jetkvm_ui_set_image(const char *obj_name, const char *image_name) { lv_img_set_src(obj, image_name); } -void jetkvm_ui_set_state(const char *obj_name, const char *state_name) { +lv_state_t str_to_lv_state(const char *state_name) { + if (strcmp(state_name, "LV_STATE_USER_1") == 0) { + return LV_STATE_USER_1; + } + else if (strcmp(state_name, "LV_STATE_USER_2") == 0) { + return LV_STATE_USER_2; + } + else if (strcmp(state_name, "LV_STATE_USER_3") == 0) { + return LV_STATE_USER_3; + } + else if (strcmp(state_name, "LV_STATE_USER_4") == 0) { + return LV_STATE_USER_4; + } + else if (strcmp(state_name, "LV_STATE_DISABLED") == 0) { + return LV_STATE_DISABLED; + } + else if (strcmp(state_name, "LV_STATE_DEFAULT") == 0) { + return LV_STATE_DEFAULT; + } + else if (strcmp(state_name, "LV_STATE_CHECKED") == 0) { + return LV_STATE_CHECKED; + } + else if (strcmp(state_name, "LV_STATE_FOCUSED") == 0) { + return LV_STATE_FOCUSED; + } + return LV_STATE_DEFAULT; +} + +void jetkvm_ui_add_state(const char *obj_name, const char *state_name) { lv_obj_t *obj = ui_get_obj(obj_name); if (obj == NULL) { return; } - lv_obj_add_state(obj, LV_STATE_USER_1); - lv_state_t state_val = LV_STATE_DEFAULT; - if (strcmp(state_name, "LV_STATE_USER_1") == 0) - { - state_val = LV_STATE_USER_1; - } - else if (strcmp(state_name, "LV_STATE_USER_2") == 0) - { - state_val = LV_STATE_USER_2; - } - else if (strcmp(state_name, "LV_STATE_USER_3") == 0) - { - state_val = LV_STATE_USER_3; - } - else if (strcmp(state_name, "LV_STATE_USER_4") == 0) - { - state_val = LV_STATE_USER_4; - } - else if (strcmp(state_name, "LV_STATE_DISABLED") == 0) - { - state_val = LV_STATE_DISABLED; - } - // TODO: use LV_STATE_USER_* once eez supports it - lv_obj_clear_state(obj, LV_STATE_USER_1 | LV_STATE_USER_2 | LV_STATE_USER_3 | LV_STATE_USER_4 | LV_STATE_DISABLED); + lv_state_t state_val = str_to_lv_state(state_name); lv_obj_add_state(obj, state_val); + + lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY); +} + +void jetkvm_ui_clear_state(const char *obj_name, const char *state_name) { + lv_obj_t *obj = ui_get_obj(obj_name); + if (obj == NULL) { + return; + } + lv_state_t state_val = str_to_lv_state(state_name); + lv_obj_clear_state(obj, state_val); + + log_info("cleared state %s from object %s: %d", state_name, obj_name, state_val); + lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY); } int jetkvm_ui_add_flag(const char *obj_name, const char *flag_name) { diff --git a/internal/native/cgo/ctrl.h b/internal/native/cgo/ctrl.h index c23b01af..254fdf7b 100644 --- a/internal/native/cgo/ctrl.h +++ b/internal/native/cgo/ctrl.h @@ -39,7 +39,8 @@ const char *jetkvm_ui_get_current_screen(); void jetkvm_ui_load_screen(const char *obj_name); int jetkvm_ui_set_text(const char *obj_name, const char *text); void jetkvm_ui_set_image(const char *obj_name, const char *image_name); -void jetkvm_ui_set_state(const char *obj_name, const char *state_name); +void jetkvm_ui_add_state(const char *obj_name, const char *state_name); +void jetkvm_ui_clear_state(const char *obj_name, const char *state_name); void jetkvm_ui_fade_in(const char *obj_name, u_int32_t duration); void jetkvm_ui_fade_out(const char *obj_name, u_int32_t duration); void jetkvm_ui_set_opacity(const char *obj_name, u_int8_t opacity); diff --git a/internal/native/cgo_linux.go b/internal/native/cgo_linux.go index 5377cc17..54d57b55 100644 --- a/internal/native/cgo_linux.go +++ b/internal/native/cgo_linux.go @@ -4,6 +4,7 @@ package native import ( "fmt" + "sync" "unsafe" "github.com/rs/zerolog" @@ -45,6 +46,8 @@ static inline void jetkvm_cgo_setup_rpc_handler() { */ import "C" +var cgoLock sync.Mutex + //export jetkvm_go_video_state_handler func jetkvm_go_video_state_handler(state *C.jetkvm_video_state_t) { videoState := VideoState{ @@ -100,6 +103,9 @@ func uiEventCodeToName(code int) string { } func setUpNativeHandlers() { + cgoLock.Lock() + defer cgoLock.Unlock() + C.jetkvm_cgo_setup_log_handler() C.jetkvm_cgo_setup_video_state_handler() C.jetkvm_cgo_setup_video_handler() @@ -108,6 +114,9 @@ func setUpNativeHandlers() { } func uiInit(rotation uint16) { + cgoLock.Lock() + defer cgoLock.Unlock() + cRotation := C.u_int16_t(rotation) defer C.free(unsafe.Pointer(&cRotation)) @@ -115,10 +124,16 @@ func uiInit(rotation uint16) { } func uiTick() { + cgoLock.Lock() + defer cgoLock.Unlock() + C.jetkvm_ui_tick() } func videoInit() error { + cgoLock.Lock() + defer cgoLock.Unlock() + ret := C.jetkvm_video_init() if ret != 0 { return fmt.Errorf("failed to initialize video: %d", ret) @@ -127,18 +142,30 @@ func videoInit() error { } func videoShutdown() { + cgoLock.Lock() + defer cgoLock.Unlock() + C.jetkvm_video_shutdown() } func videoStart() { + cgoLock.Lock() + defer cgoLock.Unlock() + C.jetkvm_video_start() } func videoStop() { + cgoLock.Lock() + defer cgoLock.Unlock() + C.jetkvm_video_stop() } func uiSetVar(name string, value string) { + cgoLock.Lock() + defer cgoLock.Unlock() + nameCStr := C.CString(name) defer C.free(unsafe.Pointer(nameCStr)) @@ -149,6 +176,9 @@ func uiSetVar(name string, value string) { } func uiGetVar(name string) string { + cgoLock.Lock() + defer cgoLock.Unlock() + nameCStr := C.CString(name) defer C.free(unsafe.Pointer(nameCStr)) @@ -156,31 +186,58 @@ func uiGetVar(name string) string { } func uiSwitchToScreen(screen string) { + cgoLock.Lock() + defer cgoLock.Unlock() + screenCStr := C.CString(screen) defer C.free(unsafe.Pointer(screenCStr)) C.jetkvm_ui_load_screen(screenCStr) } func uiGetCurrentScreen() string { + cgoLock.Lock() + defer cgoLock.Unlock() + screenCStr := C.jetkvm_ui_get_current_screen() return C.GoString(screenCStr) } -func uiObjSetState(objName string, state string) (bool, error) { +func uiObjAddState(objName string, state string) (bool, error) { + cgoLock.Lock() + defer cgoLock.Unlock() + objNameCStr := C.CString(objName) defer C.free(unsafe.Pointer(objNameCStr)) stateCStr := C.CString(state) defer C.free(unsafe.Pointer(stateCStr)) - C.jetkvm_ui_set_state(objNameCStr, stateCStr) + C.jetkvm_ui_add_state(objNameCStr, stateCStr) + return true, nil +} + +func uiObjClearState(objName string, state string) (bool, error) { + cgoLock.Lock() + defer cgoLock.Unlock() + + objNameCStr := C.CString(objName) + defer C.free(unsafe.Pointer(objNameCStr)) + stateCStr := C.CString(state) + defer C.free(unsafe.Pointer(stateCStr)) + C.jetkvm_ui_clear_state(objNameCStr, stateCStr) return true, nil } func uiGetLVGLVersion() string { + cgoLock.Lock() + defer cgoLock.Unlock() + return C.GoString(C.jetkvm_ui_get_lvgl_version()) } // 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) { + cgoLock.Lock() + defer cgoLock.Unlock() + objNameCStr := C.CString(objName) defer C.free(unsafe.Pointer(objNameCStr)) flagCStr := C.CString(flag) @@ -190,6 +247,9 @@ func uiObjAddFlag(objName string, flag string) (bool, error) { } func uiObjClearFlag(objName string, flag string) (bool, error) { + cgoLock.Lock() + defer cgoLock.Unlock() + objNameCStr := C.CString(objName) defer C.free(unsafe.Pointer(objNameCStr)) flagCStr := C.CString(flag) @@ -207,6 +267,9 @@ func uiObjShow(objName string) (bool, error) { } func uiObjSetOpacity(objName string, opacity int) (bool, error) { + cgoLock.Lock() + defer cgoLock.Unlock() + objNameCStr := C.CString(objName) defer C.free(unsafe.Pointer(objNameCStr)) @@ -215,6 +278,9 @@ func uiObjSetOpacity(objName string, opacity int) (bool, error) { } func uiObjFadeIn(objName string, duration uint32) (bool, error) { + cgoLock.Lock() + defer cgoLock.Unlock() + objNameCStr := C.CString(objName) defer C.free(unsafe.Pointer(objNameCStr)) @@ -224,6 +290,9 @@ func uiObjFadeIn(objName string, duration uint32) (bool, error) { } func uiObjFadeOut(objName string, duration uint32) (bool, error) { + cgoLock.Lock() + defer cgoLock.Unlock() + objNameCStr := C.CString(objName) defer C.free(unsafe.Pointer(objNameCStr)) @@ -233,6 +302,9 @@ func uiObjFadeOut(objName string, duration uint32) (bool, error) { } func uiLabelSetText(objName string, text string) (bool, error) { + cgoLock.Lock() + defer cgoLock.Unlock() + objNameCStr := C.CString(objName) defer C.free(unsafe.Pointer(objNameCStr)) @@ -247,6 +319,9 @@ func uiLabelSetText(objName string, text string) (bool, error) { } func uiImgSetSrc(objName string, src string) (bool, error) { + cgoLock.Lock() + defer cgoLock.Unlock() + objNameCStr := C.CString(objName) defer C.free(unsafe.Pointer(objNameCStr)) @@ -259,6 +334,9 @@ func uiImgSetSrc(objName string, src string) (bool, error) { } func uiDispSetRotation(rotation uint16) (bool, error) { + cgoLock.Lock() + defer cgoLock.Unlock() + nativeLogger.Info().Uint16("rotation", rotation).Msg("setting rotation") cRotation := C.u_int16_t(rotation) @@ -269,21 +347,33 @@ func uiDispSetRotation(rotation uint16) (bool, error) { } func videoGetStreamQualityFactor() (float64, error) { + cgoLock.Lock() + defer cgoLock.Unlock() + factor := C.jetkvm_video_get_quality_factor() return float64(factor), nil } func videoSetStreamQualityFactor(factor float64) error { + cgoLock.Lock() + defer cgoLock.Unlock() + C.jetkvm_video_set_quality_factor(C.float(factor)) return nil } func videoGetEDID() (string, error) { + cgoLock.Lock() + defer cgoLock.Unlock() + edidCStr := C.jetkvm_video_get_edid_hex() return C.GoString(edidCStr), nil } func videoSetEDID(edid string) error { + cgoLock.Lock() + defer cgoLock.Unlock() + edidCStr := C.CString(edid) defer C.free(unsafe.Pointer(edidCStr)) C.jetkvm_video_set_edid(edidCStr) diff --git a/internal/native/cgo_notlinux.go b/internal/native/cgo_notlinux.go index 77d75301..1b23ab5e 100644 --- a/internal/native/cgo_notlinux.go +++ b/internal/native/cgo_notlinux.go @@ -28,7 +28,12 @@ func uiGetCurrentScreen() string { return "" } -func uiObjSetState(objName string, state string) (bool, error) { +func uiObjAddState(objName string, state string) (bool, error) { + panicPlatformNotSupported() + return false, nil +} + +func uiObjClearState(objName string, state string) (bool, error) { panicPlatformNotSupported() return false, nil } diff --git a/internal/native/display.go b/internal/native/display.go index 925a0a10..de9b9a01 100644 --- a/internal/native/display.go +++ b/internal/native/display.go @@ -47,9 +47,14 @@ func (n *Native) UIGetVar(name string) string { return uiGetVar(name) } -// UIObjSetState clears the state then adds the new state -func (n *Native) UIObjSetState(objName string, state string) (bool, error) { - return uiObjSetState(objName, state) +// UIObjAddState adds the state to the object +func (n *Native) UIObjAddState(objName string, state string) (bool, error) { + return uiObjAddState(objName, state) +} + +// UIObjClearState clears the state from the object +func (n *Native) UIObjClearState(objName string, state string) (bool, error) { + return uiObjClearState(objName, state) } // UIObjAddFlag adds the flag to the object diff --git a/internal/native/eez/jetkvm.eez-project b/internal/native/eez/jetkvm.eez-project index 0ef12470..66b9c955 100644 --- a/internal/native/eez/jetkvm.eez-project +++ b/internal/native/eez/jetkvm.eez-project @@ -1577,7 +1577,7 @@ "type": "LVGLLabelWidget", "left": 0, "top": 0, - "width": 81, + "width": 68, "height": 17, "customInputs": [], "customOutputs": [], @@ -1595,9 +1595,10 @@ "widthUnit": "content", "heightUnit": "content", "children": [], - "widgetFlags": "CLICK_FOCUSABLE|GESTURE_BUBBLE|PRESS_LOCK|SCROLLABLE|SCROLL_CHAIN_HOR|SCROLL_CHAIN_VER|SCROLL_ELASTIC|SCROLL_MOMENTUM|SCROLL_WITH_ARROW|SNAPPABLE", + "widgetFlags": "CLICK_FOCUSABLE|GESTURE_BUBBLE|PRESS_LOCK|SCROLLABLE|SCROLL_CHAIN_HOR|SCROLL_CHAIN_VER|SCROLL_ELASTIC|SCROLL_MOMENTUM|SCROLL_WITH_ARROW|SNAPPABLE|CHECKABLE", "hiddenFlagType": "literal", "clickableFlagType": "literal", + "checkedState": false, "checkedStateType": "literal", "disabledStateType": "literal", "states": "", @@ -1607,16 +1608,16 @@ "definition": { "MAIN": { "DEFAULT": { - "text_color": "22C55E" - }, - "DISABLED": { "text_color": "808080" + }, + "CHECKED": { + "text_color": "22C55E" } } } }, "groupIndex": 0, - "text": "Connected", + "text": "Unknown", "textType": "literal", "longMode": "WRAP", "recolor": false @@ -1795,7 +1796,7 @@ "type": "LVGLLabelWidget", "left": 0, "top": 0, - "width": 100, + "width": 68, "height": 17, "customInputs": [], "customOutputs": [], @@ -1813,11 +1814,12 @@ "widthUnit": "content", "heightUnit": "content", "children": [], - "widgetFlags": "CLICK_FOCUSABLE|GESTURE_BUBBLE|PRESS_LOCK|SCROLLABLE|SCROLL_CHAIN_HOR|SCROLL_CHAIN_VER|SCROLL_ELASTIC|SCROLL_MOMENTUM|SCROLL_WITH_ARROW|SNAPPABLE", + "widgetFlags": "CLICK_FOCUSABLE|GESTURE_BUBBLE|PRESS_LOCK|SCROLLABLE|SCROLL_CHAIN_HOR|SCROLL_CHAIN_VER|SCROLL_ELASTIC|SCROLL_MOMENTUM|SCROLL_WITH_ARROW|SNAPPABLE|CHECKABLE", "hiddenFlagType": "literal", "clickableFlagType": "literal", + "checkedState": false, "checkedStateType": "literal", - "disabledState": true, + "disabledState": false, "disabledStateType": "literal", "states": "", "useStyle": "LabelFont16", @@ -1825,17 +1827,17 @@ "objID": "00742432-4b32-47f4-a4c5-5bc75f6b458d", "definition": { "MAIN": { - "DEFAULT": { + "CHECKED": { "text_color": "22C55E" }, - "DISABLED": { + "DEFAULT": { "text_color": "808080" } } } }, "groupIndex": 0, - "text": "Disconnected", + "text": "Unknown", "textType": "literal", "longMode": "WRAP", "recolor": false diff --git a/internal/native/eez/src/ui/screens.c b/internal/native/eez/src/ui/screens.c index de01cd66..2c108906 100644 --- a/internal/native/eez/src/ui/screens.c +++ b/internal/native/eez/src/ui/screens.c @@ -432,10 +432,11 @@ void create_screen_home_screen() { objects.usb_status_label = obj; lv_obj_set_pos(obj, LV_PCT(0), LV_PCT(0)); lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT); + lv_obj_add_flag(obj, LV_OBJ_FLAG_CHECKABLE); add_style_label_font16(obj); - lv_obj_set_style_text_color(obj, lv_color_hex(0xff22c55e), LV_PART_MAIN | LV_STATE_DEFAULT); - lv_obj_set_style_text_color(obj, lv_color_hex(0xff808080), LV_PART_MAIN | LV_STATE_DISABLED); - lv_label_set_text(obj, "Connected"); + lv_obj_set_style_text_color(obj, lv_color_hex(0xff808080), LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_text_color(obj, lv_color_hex(0xff22c55e), LV_PART_MAIN | LV_STATE_CHECKED); + lv_label_set_text(obj, "Unknown"); } } } @@ -494,11 +495,11 @@ void create_screen_home_screen() { objects.hdmi_status_label = obj; lv_obj_set_pos(obj, LV_PCT(0), LV_PCT(0)); lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT); - lv_obj_add_state(obj, LV_STATE_DISABLED); + lv_obj_add_flag(obj, LV_OBJ_FLAG_CHECKABLE); add_style_label_font16(obj); - lv_obj_set_style_text_color(obj, lv_color_hex(0xff22c55e), LV_PART_MAIN | LV_STATE_DEFAULT); - lv_obj_set_style_text_color(obj, lv_color_hex(0xff808080), LV_PART_MAIN | LV_STATE_DISABLED); - lv_label_set_text(obj, "Disconnected"); + lv_obj_set_style_text_color(obj, lv_color_hex(0xff22c55e), LV_PART_MAIN | LV_STATE_CHECKED); + lv_obj_set_style_text_color(obj, lv_color_hex(0xff808080), LV_PART_MAIN | LV_STATE_DEFAULT); + lv_label_set_text(obj, "Unknown"); } } } diff --git a/internal/native/lib/libjknative.a b/internal/native/lib/libjknative.a index 646eefda..35ccc811 100644 Binary files a/internal/native/lib/libjknative.a and b/internal/native/lib/libjknative.a differ diff --git a/internal/native/native.go b/internal/native/native.go index 8cf51560..ba152f48 100644 --- a/internal/native/native.go +++ b/internal/native/native.go @@ -20,6 +20,7 @@ type Native struct { onIndevEvent func(event string) onRpcEvent func(event string) videoLock sync.Mutex + screenLock sync.Mutex } type NativeOptions struct { @@ -73,6 +74,7 @@ func NewNative(opts NativeOptions) *Native { onIndevEvent: opts.OnIndevEvent, onRpcEvent: opts.OnRpcEvent, videoLock: sync.Mutex{}, + screenLock: sync.Mutex{}, } }