From 837b75f83c508302cfae0ebe698dfec5f2690872 Mon Sep 17 00:00:00 2001 From: Siyuan Miao Date: Thu, 25 Sep 2025 12:26:15 +0000 Subject: [PATCH] feat: add reset config and reboot buttons to UI --- internal/native/cgo/ctrl.c | 12 +- internal/native/cgo/ctrl.h | 3 + internal/native/cgo/screen.c | 6 +- internal/native/cgo/video.c | 2 +- internal/native/cgo_linux.go | 11 + internal/native/chan.go | 8 + internal/native/eez/jetkvm.eez-project | 758 +++++++++++++++++- .../native/eez/jetkvm.eez-project-ui-state | 65 +- internal/native/eez/src/ui/actions.c | 63 +- internal/native/eez/src/ui/actions.h | 3 + internal/native/eez/src/ui/screens.c | 208 ++++- internal/native/eez/src/ui/screens.h | 12 + internal/native/eez/src/ui/ui.c | 13 + internal/native/eez/src/ui/ui.h | 7 + internal/native/lib/libjknative.a | Bin 3111426 -> 3117266 bytes internal/native/native.go | 11 + native.go | 12 + 17 files changed, 1105 insertions(+), 89 deletions(-) diff --git a/internal/native/cgo/ctrl.c b/internal/native/cgo/ctrl.c index 787e99bf..93a49872 100644 --- a/internal/native/cgo/ctrl.c +++ b/internal/native/cgo/ctrl.c @@ -18,7 +18,7 @@ jetkvm_video_state_t state; jetkvm_video_state_handler_t *video_state_handler = NULL; - +jetkvm_rpc_handler_t *rpc_handler = NULL; jetkvm_video_handler_t *video_handler = NULL; @@ -34,6 +34,16 @@ void jetkvm_set_indev_handler(jetkvm_indev_handler_t *handler) { lvgl_set_indev_handler(handler); } +void jetkvm_set_rpc_handler(jetkvm_rpc_handler_t *handler) { + rpc_handler = handler; +} + +void jetkvm_call_rpc_handler(const char *method, const char *params) { + if (rpc_handler != NULL) { + (*rpc_handler)(method, params); + } +} + const char *jetkvm_ui_event_code_to_name(int code) { lv_event_code_t cCode = (lv_event_code_t)code; return lv_event_code_get_name(code); diff --git a/internal/native/cgo/ctrl.h b/internal/native/cgo/ctrl.h index 8fca70a9..c23b01af 100644 --- a/internal/native/cgo/ctrl.h +++ b/internal/native/cgo/ctrl.h @@ -16,12 +16,15 @@ typedef struct typedef void (jetkvm_video_state_handler_t)(jetkvm_video_state_t *state); typedef void (jetkvm_log_handler_t)(int level, const char *filename, const char *funcname, int line, const char *message); +typedef void (jetkvm_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); void jetkvm_set_log_handler(jetkvm_log_handler_t *handler); void jetkvm_set_video_handler(jetkvm_video_handler_t *handler); void jetkvm_set_indev_handler(jetkvm_indev_handler_t *handler); +void jetkvm_set_rpc_handler(jetkvm_rpc_handler_t *handler); +void jetkvm_call_rpc_handler(const char *method, const char *params); void jetkvm_set_video_state_handler(jetkvm_video_state_handler_t *handler); void jetkvm_ui_set_var(const char *name, const char *value); diff --git a/internal/native/cgo/screen.c b/internal/native/cgo/screen.c index ecef3422..a468a6b3 100644 --- a/internal/native/cgo/screen.c +++ b/internal/native/cgo/screen.c @@ -9,6 +9,7 @@ // #include "st7789/lcd.h" #include "ui/ui.h" #include "ui_index.h" +#include "ctrl.h" #define DISP_BUF_SIZE (300 * 240 * 2) static lv_color_t buf[DISP_BUF_SIZE]; @@ -76,10 +77,9 @@ void lvgl_init(u_int16_t rotation) { ui_init(); + ui_set_rpc_handler((jetkvm_rpc_handler_t *)jetkvm_call_rpc_handler); + log_info("ui initalized"); - // lv_label_set_text(ui_Boot_Screen_Version, ""); - // lv_label_set_text(ui_Home_Content_Ip, "..."); - // lv_label_set_text(ui_Home_Header_Cloud_Status_Label, "0 active"); } void lvgl_tick(void) { diff --git a/internal/native/cgo/video.c b/internal/native/cgo/video.c index 0e67945b..14a5881c 100644 --- a/internal/native/cgo/video.c +++ b/internal/native/cgo/video.c @@ -323,7 +323,7 @@ void *run_video_stream(void *arg) { if (detected_signal == false) { - usleep(100000); + usleep(10000); // Reduced to 10ms for better responsiveness to streaming_flag changes continue; } diff --git a/internal/native/cgo_linux.go b/internal/native/cgo_linux.go index d108690b..5377cc17 100644 --- a/internal/native/cgo_linux.go +++ b/internal/native/cgo_linux.go @@ -37,6 +37,11 @@ extern void jetkvm_go_indev_handler(int code); static inline void jetkvm_cgo_setup_indev_handler() { jetkvm_set_indev_handler(&jetkvm_go_indev_handler); } + +extern void jetkvm_go_rpc_handler(cchar_t *method, cchar_t *params); +static inline void jetkvm_cgo_setup_rpc_handler() { + jetkvm_set_rpc_handler(&jetkvm_go_rpc_handler); +} */ import "C" @@ -75,6 +80,11 @@ func jetkvm_go_indev_handler(code C.int) { indevEventChan <- int(code) } +//export jetkvm_go_rpc_handler +func jetkvm_go_rpc_handler(method *C.cchar_t, params *C.cchar_t) { + rpcEventChan <- C.GoString(method) +} + var eventCodeToNameMap = map[int]string{} func uiEventCodeToName(code int) string { @@ -94,6 +104,7 @@ func setUpNativeHandlers() { C.jetkvm_cgo_setup_video_state_handler() C.jetkvm_cgo_setup_video_handler() C.jetkvm_cgo_setup_indev_handler() + C.jetkvm_cgo_setup_rpc_handler() } func uiInit(rotation uint16) { diff --git a/internal/native/chan.go b/internal/native/chan.go index a730689e..4162f260 100644 --- a/internal/native/chan.go +++ b/internal/native/chan.go @@ -11,6 +11,7 @@ var ( videoStateChan chan VideoState = make(chan VideoState) logChan chan nativeLogMessage = make(chan nativeLogMessage) indevEventChan chan int = make(chan int) + rpcEventChan chan string = make(chan string) ) func (n *Native) handleVideoFrameChan() { @@ -70,3 +71,10 @@ func (n *Native) handleIndevEventChan() { n.onIndevEvent(name) } } + +func (n *Native) handleRpcEventChan() { + for { + event := <-rpcEventChan + n.onRpcEvent(event) + } +} diff --git a/internal/native/eez/jetkvm.eez-project b/internal/native/eez/jetkvm.eez-project index efe179b6..81231dec 100644 --- a/internal/native/eez/jetkvm.eez-project +++ b/internal/native/eez/jetkvm.eez-project @@ -48,7 +48,7 @@ { "objID": "58af3ebb-96b3-494c-f4e3-9c23852e3e42", "fileName": "actions.c", - "template": "#include \"actions.h\"\n#include \"screens.h\"\n\nint handle_gesture_screen_switch(lv_event_t *e, lv_dir_t direction, int screenId) {\n lv_event_code_t event_code = lv_event_get_code(e);\n if (event_code != LV_EVENT_GESTURE) {\n return 0;\n }\n\n if (lv_indev_get_gesture_dir(lv_indev_get_act()) != direction) {\n return 0;\n }\n lv_indev_wait_release(lv_indev_get_act());\n loadScreen(screenId);\n return 1;\n}\n\nvoid action_switch_to_menu(lv_event_t *e) {\n loadScreen(SCREEN_ID_MENU_SCREEN);\n}\n\nvoid action_switch_to_advanced_menu(lv_event_t *e) {\n loadScreen(SCREEN_ID_MENU_ADVANCED_SCREEN);\n}\n\nvoid action_switch_to_status(lv_event_t *e) {\n loadScreen(SCREEN_ID_STATUS_SCREEN);\n}\n\nvoid action_switch_to_about(lv_event_t *e) {\n loadScreen(SCREEN_ID_ABOUT_SCREEN);\n}\n\nvoid action_menu_screen_gesture(lv_event_t * e) {\n handle_gesture_screen_switch(e, LV_DIR_RIGHT, SCREEN_ID_HOME_SCREEN);\n}\n\nvoid action_menu_advanced_screen_gesture(lv_event_t * e) {\n handle_gesture_screen_switch(e, LV_DIR_RIGHT, SCREEN_ID_MENU_SCREEN);\n}\n\nvoid action_reset_config_screen_gesture(lv_event_t * e) {\n handle_gesture_screen_switch(e, LV_DIR_RIGHT, SCREEN_ID_MENU_SCREEN);\n}\n\nvoid action_home_screen_gesture(lv_event_t * e) {\n handle_gesture_screen_switch(e, LV_DIR_LEFT, SCREEN_ID_MENU_SCREEN);\n}\n\nvoid action_about_screen_gesture(lv_event_t * e) {\n handle_gesture_screen_switch(e, LV_DIR_RIGHT, SCREEN_ID_MENU_SCREEN);\n}\n\nstatic const int RESET_LONG_PRESS_DURATION = 1000 * 20; // 20 seconds\n\nvoid action_reset_config(lv_event_t * e) \n lv_event_code_t event_code = lv_event_get_code(e);\n lv_obj_t *obj = lv_event_get_target(e);\n \n if (event_code == LV_EVENT_PRESSED) {\n // Button pressed - start timing\n reset_press_start_time = lv_tick_get();\n lv_obj_set_user_data(obj, (uint32_t) reset_press_start_time);\n }\n else if (event_code == LV_EVENT_PRESSING) {\n uint32_t reset_press_start_time = (uint32_t) lv_obj_get_user_data(obj);\n if (reset_press_start_time == 0) {\n return;\n }\n\n uint32_t current_time = lv_tick_get();\n if (current_time - reset_press_start_time >= RESET_LONG_PRESS_DURATION) {\n \n lv_obj_add_flag(objects.reset_config_spinner, LV_OBJ_FLAG_HIDDEN);\n }\n }\n}" + "template": "#include \"actions.h\"\n#include \"screens.h\"\n#include \"ui.h\"\n#include \n\nint handle_gesture_screen_switch(lv_event_t *e, lv_dir_t direction, int screenId) {\n lv_event_code_t event_code = lv_event_get_code(e);\n if (event_code != LV_EVENT_GESTURE) {\n return 0;\n }\n\n if (lv_indev_get_gesture_dir(lv_indev_get_act()) != direction) {\n return 0;\n }\n lv_indev_wait_release(lv_indev_get_act());\n loadScreen(screenId);\n return 1;\n}\n\nvoid action_switch_to_menu(lv_event_t *e) {\n loadScreen(SCREEN_ID_MENU_SCREEN);\n}\n\nvoid action_switch_to_advanced_menu(lv_event_t *e) {\n loadScreen(SCREEN_ID_MENU_ADVANCED_SCREEN);\n}\n\nvoid action_switch_to_status(lv_event_t *e) {\n loadScreen(SCREEN_ID_STATUS_SCREEN);\n}\n\nvoid action_switch_to_about(lv_event_t *e) {\n loadScreen(SCREEN_ID_ABOUT_SCREEN);\n}\n\nvoid action_switch_to_reset_config(lv_event_t *e) {\n loadScreen(SCREEN_ID_RESET_CONFIG_SCREEN);\n}\n\nvoid action_switch_to_reboot(lv_event_t *e) {\n loadScreen(SCREEN_ID_REBOOT_SCREEN);\n}\n\nvoid action_menu_screen_gesture(lv_event_t * e) {\n handle_gesture_screen_switch(e, LV_DIR_RIGHT, SCREEN_ID_HOME_SCREEN);\n}\n\nvoid action_menu_advanced_screen_gesture(lv_event_t * e) {\n handle_gesture_screen_switch(e, LV_DIR_RIGHT, SCREEN_ID_MENU_SCREEN);\n}\n\nvoid action_reset_config_screen_gesture(lv_event_t * e) {\n handle_gesture_screen_switch(e, LV_DIR_RIGHT, SCREEN_ID_MENU_SCREEN);\n}\n\nvoid action_home_screen_gesture(lv_event_t * e) {\n handle_gesture_screen_switch(e, LV_DIR_LEFT, SCREEN_ID_MENU_SCREEN);\n}\n\nvoid action_about_screen_gesture(lv_event_t * e) {\n handle_gesture_screen_switch(e, LV_DIR_RIGHT, SCREEN_ID_MENU_SCREEN);\n}\n\n// user_data doesn't seem to be working, so we use a global variable here\nstatic uint32_t t_reset_config;\nstatic uint32_t t_reboot;\nconst int RESET_CONFIG_HOLD_TIME = 10;\nconst int REBOOT_HOLD_TIME = 5;\n\nvoid action_reset_config(lv_event_t * e) {\n lv_event_code_t event_code = lv_event_get_code(e);\n lv_obj_t *obj = lv_event_get_target(e);\n \n if (event_code == LV_EVENT_PRESSED) {\n t_reset_config = lv_tick_get();\n }\n else if (event_code == LV_EVENT_PRESSING) {\n int remaining_time = RESET_CONFIG_HOLD_TIME * 1000 - lv_tick_elaps(t_reset_config);\n if (remaining_time <= 0) { \n lv_obj_add_flag(objects.reset_config_button, LV_OBJ_FLAG_HIDDEN);\n lv_obj_clear_flag(objects.reset_config_spinner, LV_OBJ_FLAG_HIDDEN); \n ui_call_rpc_handler(\"resetConfig\", NULL);\n } else {\n char buf[100];\n int remaining_time_seconds = remaining_time / 1000;\n if (remaining_time_seconds <= 1) {\n remaining_time_seconds = 1;\n }\n sprintf(buf, \"Press and hold for %d seconds\", remaining_time_seconds);\n lv_label_set_text(objects.reset_config_label, buf);\n }\n }\n}\n\nvoid action_reboot(lv_event_t * e) {\n lv_event_code_t event_code = lv_event_get_code(e);\n lv_obj_t *obj = lv_event_get_target(e);\n \n if (event_code == LV_EVENT_PRESSED) {\n t_reboot = lv_tick_get();\n }\n else if (event_code == LV_EVENT_PRESSING) {\n int remaining_time = REBOOT_HOLD_TIME * 1000 - lv_tick_elaps(t_reboot);\n if (remaining_time <= 0) {\n ui_call_rpc_handler(\"reboot\", NULL);\n } else {\n char buf[100];\n int remaining_time_seconds = remaining_time / 1000;\n if (remaining_time_seconds <= 1) {\n remaining_time_seconds = 1;\n }\n sprintf(buf, \"Press and hold for %d seconds\", remaining_time_seconds);\n lv_label_set_text(objects.reboot_label, buf);\n }\n }\n}" }, { "objID": "1dbd1b7e-7270-47f0-ee02-e80bdae287cf", @@ -93,12 +93,12 @@ { "objID": "118ba563-bf4b-4ce9-8f7d-67c550f7ebd2", "fileName": "ui.h", - "template": "#ifndef EEZ_LVGL_UI_GUI_H\n#define EEZ_LVGL_UI_GUI_H\n\n//${eez-studio LVGL_INCLUDE}\n\n//${eez-studio EEZ_FOR_LVGL_CHECK}\n\n#if defined(EEZ_FOR_LVGL)\n#include \n#endif\n\n#if !defined(EEZ_FOR_LVGL)\n#include \"screens.h\"\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n//${eez-studio GUI_ASSETS_DECL}\n\nvoid ui_init();\nvoid ui_tick();\n\n#if !defined(EEZ_FOR_LVGL)\nvoid loadScreen(enum ScreensEnum screenId);\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif // EEZ_LVGL_UI_GUI_H" + "template": "#ifndef EEZ_LVGL_UI_GUI_H\n#define EEZ_LVGL_UI_GUI_H\n\n//${eez-studio LVGL_INCLUDE}\n\ntypedef void (jetkvm_rpc_handler_t)(const char *method, const char *params);\n\nvoid ui_set_rpc_handler(jetkvm_rpc_handler_t *handler);\nvoid ui_call_rpc_handler(const char *method, const char *params);\n\n\n\n//${eez-studio EEZ_FOR_LVGL_CHECK}\n\n#if defined(EEZ_FOR_LVGL)\n#include \n#endif\n\n#if !defined(EEZ_FOR_LVGL)\n#include \"screens.h\"\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n//${eez-studio GUI_ASSETS_DECL}\n\nvoid ui_init();\nvoid ui_tick();\n\n#if !defined(EEZ_FOR_LVGL)\nvoid loadScreen(enum ScreensEnum screenId);\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif // EEZ_LVGL_UI_GUI_H" }, { "objID": "3697d44a-db04-48a2-f3e5-098172727346", "fileName": "ui.c", - "template": "#if defined(EEZ_FOR_LVGL)\n#include \n#endif\n\n#include \"ui.h\"\n#include \"screens.h\"\n#include \"images.h\"\n#include \"actions.h\"\n#include \"vars.h\"\n\n//${eez-studio GUI_ASSETS_DEF}\n\n//${eez-studio LVGL_NATIVE_VARS_TABLE_DEF}\n\n//${eez-studio LVGL_ACTIONS_ARRAY_DEF}\n\n#if defined(EEZ_FOR_LVGL)\n\nvoid ui_init() {\n eez_flow_init(assets, sizeof(assets), (lv_obj_t **)&objects, sizeof(objects), images, sizeof(images), actions);\n}\n\nvoid ui_tick() {\n eez_flow_tick();\n tick_screen(g_currentScreen);\n}\n\n#else\n\n#include \n\nstatic int16_t currentScreen = -1;\n\nstatic lv_obj_t *getLvglObjectFromIndex(int32_t index) {\n if (index == -1) {\n return 0;\n }\n return ((lv_obj_t **)&objects)[index];\n}\n\nvoid loadScreen(enum ScreensEnum screenId) {\n currentScreen = screenId - 1;\n lv_obj_t *screen = getLvglObjectFromIndex(currentScreen);\n lv_scr_load(screen);\n // lv_scr_load_anim(screen, LV_SCR_LOAD_ANIM_FADE_IN, 200, 0, false);\n}\n\nvoid ui_init() {\n create_screens();\n//${eez-studio LVGL_LOAD_FIRST_SCREEN}\n}\n\nvoid ui_tick() {\n tick_screen(currentScreen);\n}\n\n#endif\n" + "template": "#if defined(EEZ_FOR_LVGL)\n#include \n#endif\n\n#include \"ui.h\"\n#include \"screens.h\"\n#include \"images.h\"\n#include \"actions.h\"\n#include \"vars.h\"\n\n//${eez-studio GUI_ASSETS_DEF}\n\n//${eez-studio LVGL_NATIVE_VARS_TABLE_DEF}\n\n//${eez-studio LVGL_ACTIONS_ARRAY_DEF}\n\n\njetkvm_rpc_handler_t *ui_rpc_handler = NULL;\n\nvoid ui_set_rpc_handler(jetkvm_rpc_handler_t *handler) {\n ui_rpc_handler = handler;\n}\n\nvoid ui_call_rpc_handler(const char *method, const char *params) {\n if (ui_rpc_handler != NULL) {\n (*ui_rpc_handler)(method, params);\n }\n}\n\n#if defined(EEZ_FOR_LVGL)\n\nvoid ui_init() {\n eez_flow_init(assets, sizeof(assets), (lv_obj_t **)&objects, sizeof(objects), images, sizeof(images), actions);\n}\n\nvoid ui_tick() {\n eez_flow_tick();\n tick_screen(g_currentScreen);\n}\n\n#else\n\n#include \n\nstatic int16_t currentScreen = -1;\n\nstatic lv_obj_t *getLvglObjectFromIndex(int32_t index) {\n if (index == -1) {\n return 0;\n }\n return ((lv_obj_t **)&objects)[index];\n}\n\nvoid loadScreen(enum ScreensEnum screenId) {\n currentScreen = screenId - 1;\n lv_obj_t *screen = getLvglObjectFromIndex(currentScreen);\n lv_scr_load(screen);\n // lv_scr_load_anim(screen, LV_SCR_LOAD_ANIM_FADE_IN, 200, 0, false);\n}\n\nvoid ui_init() {\n create_screens();\n//${eez-studio LVGL_LOAD_FIRST_SCREEN}\n}\n\nvoid ui_tick() {\n tick_screen(currentScreen);\n}\n\n#endif\n" } ], "destinationFolder": "src\\ui", @@ -162,6 +162,14 @@ "userProperties": [], "name": "SwitchToAdvancedMenu" }, + { + "objID": "7a5c014d-5d9c-4401-b604-09417c95ea38", + "components": [], + "connectionLines": [], + "localVariables": [], + "userProperties": [], + "name": "SwitchToResetConfig" + }, { "objID": "50a38244-1805-40c3-ef63-4f0798e46498", "components": [], @@ -241,6 +249,22 @@ "localVariables": [], "userProperties": [], "name": "ResetConfig" + }, + { + "objID": "0fc0513e-bf14-4e77-83cc-34294b7c06a3", + "components": [], + "connectionLines": [], + "localVariables": [], + "userProperties": [], + "name": "Reboot" + }, + { + "objID": "8ca31236-6234-452d-b9b9-4354d0ea66ba", + "components": [], + "connectionLines": [], + "localVariables": [], + "userProperties": [], + "name": "SwitchToReboot" } ], "userPages": [ @@ -2993,7 +3017,7 @@ "left": 0, "top": 0, "width": 100, - "height": 224, + "height": 108, "customInputs": [], "customOutputs": [], "style": { @@ -3024,8 +3048,17 @@ "conditionalStyles": [], "childStyles": [] }, + "hiddenInEditor": true, "timeline": [], - "eventHandlers": [], + "eventHandlers": [ + { + "objID": "92167a24-7274-4cfe-8a9a-edeacccaeb21", + "eventName": "PRESSED", + "handlerType": "action", + "action": "ResetConfig", + "userData": 0 + } + ], "identifier": "MenuBtnAdvancedDeveloperMode", "leftUnit": "px", "topUnit": "px", @@ -3047,6 +3080,7 @@ "conditionalStyles": [], "childStyles": [] }, + "hiddenInEditor": true, "timeline": [], "eventHandlers": [], "identifier": "", @@ -3078,6 +3112,7 @@ } ], "widgetFlags": "CLICK_FOCUSABLE|GESTURE_BUBBLE|PRESS_LOCK|SCROLL_CHAIN_HOR|SCROLL_CHAIN_VER|SCROLL_ELASTIC|SCROLL_MOMENTUM|SCROLL_ON_FOCUS|SCROLL_WITH_ARROW|SNAPPABLE", + "hiddenFlag": true, "hiddenFlagType": "literal", "clickableFlag": true, "clickableFlagType": "literal", @@ -3110,6 +3145,7 @@ "conditionalStyles": [], "childStyles": [] }, + "hiddenInEditor": true, "timeline": [], "eventHandlers": [], "identifier": "MenuBtnAdvancedUSBEmulation", @@ -3133,6 +3169,7 @@ "conditionalStyles": [], "childStyles": [] }, + "hiddenInEditor": true, "timeline": [], "eventHandlers": [], "leftUnit": "px", @@ -3163,6 +3200,7 @@ } ], "widgetFlags": "CLICK_FOCUSABLE|GESTURE_BUBBLE|PRESS_LOCK|SCROLL_CHAIN_HOR|SCROLL_CHAIN_VER|SCROLL_ELASTIC|SCROLL_MOMENTUM|SCROLL_ON_FOCUS|SCROLL_WITH_ARROW|SNAPPABLE", + "hiddenFlag": true, "hiddenFlagType": "literal", "clickableFlag": true, "clickableFlagType": "literal", @@ -3195,9 +3233,18 @@ "conditionalStyles": [], "childStyles": [] }, + "locked": false, "hiddenInEditor": false, "timeline": [], - "eventHandlers": [], + "eventHandlers": [ + { + "objID": "ddf88376-fba1-4e5f-fc35-ab162c19cf9f", + "eventName": "PRESSED", + "handlerType": "action", + "action": "SwitchToReboot", + "userData": 0 + } + ], "identifier": "MenuBtnAdvancedReboot", "leftUnit": "px", "topUnit": "px", @@ -3250,6 +3297,7 @@ } ], "widgetFlags": "CLICK_FOCUSABLE|GESTURE_BUBBLE|PRESS_LOCK|SCROLL_CHAIN_HOR|SCROLL_CHAIN_VER|SCROLL_ELASTIC|SCROLL_MOMENTUM|SCROLL_ON_FOCUS|SCROLL_WITH_ARROW", + "hiddenFlag": false, "hiddenFlagType": "literal", "clickableFlag": true, "clickableFlagType": "literal", @@ -3283,7 +3331,15 @@ "childStyles": [] }, "timeline": [], - "eventHandlers": [], + "eventHandlers": [ + { + "objID": "cd3b5095-606c-4007-c053-a2b362818cc4", + "eventName": "PRESSED", + "handlerType": "action", + "action": "SwitchToResetConfig", + "userData": 0 + } + ], "identifier": "MenuBtnAdvancedResetConfig", "leftUnit": "px", "topUnit": "px", @@ -6557,7 +6613,7 @@ "type": "LVGLLabelWidget", "left": 0, "top": 0, - "width": 117, + "width": 116, "height": 20, "customInputs": [], "customOutputs": [], @@ -6586,7 +6642,7 @@ "objID": "a7abc8b4-5ce5-4506-8ecb-e4a3d059e35c" }, "groupIndex": 0, - "text": "Reset Device", + "text": "Reset Config", "textType": "literal", "longMode": "WRAP", "recolor": false @@ -6679,7 +6735,7 @@ }, "timeline": [], "eventHandlers": [], - "identifier": "ResetConfigLabel", + "identifier": "ResetConfigLabelContainer", "leftUnit": "px", "topUnit": "px", "widthUnit": "%", @@ -6702,7 +6758,7 @@ }, "timeline": [], "eventHandlers": [], - "identifier": "", + "identifier": "ResetConfigLabel", "leftUnit": "px", "topUnit": "px", "widthUnit": "%", @@ -6731,7 +6787,7 @@ }, "group": "", "groupIndex": 0, - "text": "Press and hold for\n20 seconds", + "text": "Press and hold for\n10 seconds", "textType": "literal", "longMode": "WRAP", "recolor": false, @@ -6767,7 +6823,7 @@ "groupIndex": 0 }, { - "objID": "cbac637a-7b3f-450b-c962-2ceca07a0a43", + "objID": "8a70fd05-3313-4713-9d38-f7d95f9f7648", "type": "LVGLContainerWidget", "left": 0, "top": 0, @@ -6776,12 +6832,12 @@ "customInputs": [], "customOutputs": [], "style": { - "objID": "5c719542-cd7f-4b66-c0b4-3b2812132e3f", + "objID": "3844c45f-c3ce-4eae-aa76-9fde7cca04b9", "useStyle": "default", "conditionalStyles": [], "childStyles": [] }, - "hiddenInEditor": true, + "hiddenInEditor": false, "timeline": [], "eventHandlers": [], "identifier": "ResetConfigSpinner", @@ -6791,7 +6847,7 @@ "heightUnit": "content", "children": [ { - "objID": "abf38156-7bc2-4c4e-93fc-e08d9ee999e7", + "objID": "7e07aff7-ba61-465d-c204-cb1d2f10c061", "type": "LVGLSpinnerWidget", "left": 0, "top": 0, @@ -6800,12 +6856,11 @@ "customInputs": [], "customOutputs": [], "style": { - "objID": "8f4f24d3-5bd6-433f-bac5-e15c05820742", + "objID": "b6389edb-790f-47ac-d2ec-cf7ad2262a8e", "useStyle": "default", "conditionalStyles": [], "childStyles": [] }, - "hiddenInEditor": true, "timeline": [], "eventHandlers": [], "leftUnit": "px", @@ -6824,15 +6879,16 @@ "disabledStateType": "literal", "states": "", "localStyles": { - "objID": "e8d49d0f-a7e9-44df-afa0-db6cde72f306" + "objID": "cdfe03f8-cdc5-4b9e-c4c0-1d6d4686431f" }, "group": "", "groupIndex": 0 } ], "widgetFlags": "CLICK_FOCUSABLE|GESTURE_BUBBLE|PRESS_LOCK|SCROLL_CHAIN_HOR|SCROLL_CHAIN_VER|SCROLL_ELASTIC|SCROLL_MOMENTUM|SCROLL_WITH_ARROW|SNAPPABLE", + "hiddenFlag": true, "hiddenFlagType": "literal", - "clickableFlag": true, + "clickableFlag": false, "clickableFlagType": "literal", "flagScrollbarMode": "", "flagScrollDirection": "", @@ -6843,7 +6899,7 @@ "states": "", "useStyle": "FlexColumnStart", "localStyles": { - "objID": "46a10ecd-c85a-4dfa-a7a8-412a90c09ee1", + "objID": "033be319-ac3b-4fa0-b8ab-fa37038c754a", "definition": { "MAIN": { "DEFAULT": { @@ -6900,8 +6956,15 @@ "timeline": [], "eventHandlers": [ { - "objID": "ecf97e0e-553c-4f12-d07d-75b8641d9374", - "eventName": "LONG_PRESSED", + "objID": "dd7fca79-3ff3-483a-9cbf-fdb14acf5979", + "eventName": "PRESSED", + "handlerType": "action", + "action": "ResetConfig", + "userData": 0 + }, + { + "objID": "c2e40610-5f7d-4c0e-e476-21a691336d09", + "eventName": "PRESSING", "handlerType": "action", "action": "ResetConfig", "userData": 0 @@ -6917,7 +6980,7 @@ "type": "LVGLLabelWidget", "left": 0, "top": 0, - "width": 94, + "width": 145, "height": 16, "customInputs": [], "customOutputs": [], @@ -6958,7 +7021,7 @@ }, "group": "", "groupIndex": 0, - "text": "Hold to reset", + "text": "Reset configuration", "textType": "literal", "longMode": "WRAP", "recolor": false @@ -7107,6 +7170,651 @@ "isUsedAsUserWidget": false, "createAtStart": true, "deleteOnScreenUnload": false + }, + { + "objID": "08c14f9e-dfe7-4b21-dd8e-fda8cf3c3050", + "components": [ + { + "objID": "07967261-bc17-488c-9a11-d19a01fbfe28", + "type": "LVGLScreenWidget", + "left": 0, + "top": 0, + "width": 300, + "height": 240, + "customInputs": [], + "customOutputs": [], + "style": { + "objID": "6f262fdf-8cf1-4c21-875d-5f1aa441f852", + "useStyle": "default", + "conditionalStyles": [], + "childStyles": [] + }, + "timeline": [], + "eventHandlers": [ + { + "objID": "eb60e3d5-4409-4497-878c-9a309b98d729", + "eventName": "GESTURE", + "handlerType": "action", + "action": "AboutScreenGesture", + "userData": 0 + } + ], + "leftUnit": "px", + "topUnit": "px", + "widthUnit": "px", + "heightUnit": "px", + "children": [ + { + "objID": "fbb29f7b-edbc-40fb-8bbe-d401d5baaef8", + "type": "LVGLContainerWidget", + "left": 0, + "top": 0, + "width": 100, + "height": 100, + "customInputs": [], + "customOutputs": [], + "style": { + "objID": "f2618f34-91a0-4377-88cd-964116e5dcbe", + "useStyle": "default", + "conditionalStyles": [], + "childStyles": [] + }, + "timeline": [], + "eventHandlers": [], + "identifier": "", + "leftUnit": "px", + "topUnit": "px", + "widthUnit": "%", + "heightUnit": "%", + "children": [ + { + "objID": "55459a31-97d4-44fa-9395-f049ee6160c8", + "type": "LVGLContainerWidget", + "left": 0, + "top": 0, + "width": 100, + "height": 32, + "customInputs": [], + "customOutputs": [], + "style": { + "objID": "2156ab89-2e70-48e0-f9b8-5df09832fd13", + "useStyle": "default", + "conditionalStyles": [], + "childStyles": [] + }, + "timeline": [], + "eventHandlers": [], + "identifier": "RebootHeader", + "leftUnit": "px", + "topUnit": "px", + "widthUnit": "%", + "heightUnit": "content", + "children": [ + { + "objID": "abdd9bd2-62de-41d8-ee18-da39bccf1844", + "type": "LVGLButtonWidget", + "left": 0, + "top": 0, + "width": 32, + "height": 32, + "customInputs": [], + "customOutputs": [], + "style": { + "objID": "30cd4c39-de2f-44cd-fbfb-6ea880df54b3", + "useStyle": "default", + "conditionalStyles": [], + "childStyles": [] + }, + "timeline": [], + "eventHandlers": [ + { + "objID": "6b2fe929-8516-45ad-f2d0-e6d4d5d2de91", + "eventName": "CLICKED", + "handlerType": "action", + "action": "SwitchToMenu", + "userData": 0 + } + ], + "leftUnit": "px", + "topUnit": "px", + "widthUnit": "px", + "heightUnit": "px", + "children": [ + { + "objID": "5b346529-992e-42bc-eae7-921ea24dd865", + "type": "LVGLImageWidget", + "left": -1, + "top": 2, + "width": 8, + "height": 12, + "customInputs": [], + "customOutputs": [], + "style": { + "objID": "9687a8bc-08a8-4eba-dc85-fa136ca66d5d", + "useStyle": "default", + "conditionalStyles": [], + "childStyles": [] + }, + "timeline": [], + "eventHandlers": [], + "leftUnit": "px", + "topUnit": "px", + "widthUnit": "content", + "heightUnit": "content", + "children": [], + "widgetFlags": "ADV_HITTEST|CLICK_FOCUSABLE|GESTURE_BUBBLE|PRESS_LOCK|SCROLLABLE|SCROLL_CHAIN_HOR|SCROLL_CHAIN_VER|SCROLL_ELASTIC|SCROLL_MOMENTUM|SCROLL_WITH_ARROW|SNAPPABLE", + "hiddenFlagType": "literal", + "clickableFlagType": "literal", + "flagScrollbarMode": "", + "flagScrollDirection": "", + "scrollSnapX": "", + "scrollSnapY": "", + "checkedStateType": "literal", + "disabledStateType": "literal", + "states": "", + "localStyles": { + "objID": "6b767ea7-aa49-4b55-b74d-e00e9093c807" + }, + "group": "", + "groupIndex": 0, + "image": "back-caret", + "setPivot": false, + "pivotX": 0, + "pivotY": 0, + "zoom": 256, + "angle": 0, + "innerAlign": "CENTER" + } + ], + "widgetFlags": "CLICK_FOCUSABLE|GESTURE_BUBBLE|PRESS_LOCK|SCROLL_CHAIN_HOR|SCROLL_CHAIN_VER|SCROLL_ELASTIC|SCROLL_MOMENTUM|SCROLL_ON_FOCUS|SCROLL_WITH_ARROW|SNAPPABLE", + "hiddenFlagType": "literal", + "clickableFlag": true, + "clickableFlagType": "literal", + "flagScrollbarMode": "", + "flagScrollDirection": "", + "scrollSnapX": "", + "scrollSnapY": "", + "checkedStateType": "literal", + "disabledStateType": "literal", + "states": "", + "useStyle": "BackButton", + "localStyles": { + "objID": "5ae1b220-a20e-4139-88d0-4306e9c7a8a4" + }, + "group": "", + "groupIndex": 0 + }, + { + "objID": "97d81954-5fc7-46f8-8805-1017eaab32f1", + "type": "LVGLLabelWidget", + "left": 0, + "top": 0, + "width": 131, + "height": 20, + "customInputs": [], + "customOutputs": [], + "style": { + "objID": "57bf65c3-724e-43cf-e62e-8020827485fc", + "useStyle": "default", + "conditionalStyles": [], + "childStyles": [] + }, + "timeline": [], + "eventHandlers": [], + "identifier": "", + "leftUnit": "%", + "topUnit": "%", + "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", + "hiddenFlagType": "literal", + "clickableFlagType": "literal", + "checkedStateType": "literal", + "disabledStateType": "literal", + "states": "", + "useStyle": "HeaderLink", + "localStyles": { + "objID": "e9d6f329-0eab-4410-f3e1-e192b9da3516" + }, + "groupIndex": 0, + "text": "Reboot Device", + "textType": "literal", + "longMode": "WRAP", + "recolor": false + } + ], + "widgetFlags": "CLICK_FOCUSABLE|GESTURE_BUBBLE|PRESS_LOCK|SCROLLABLE|SCROLL_CHAIN_HOR|SCROLL_CHAIN_VER|SCROLL_ELASTIC|SCROLL_MOMENTUM|SCROLL_WITH_ARROW|SNAPPABLE", + "hiddenFlagType": "literal", + "clickableFlag": true, + "clickableFlagType": "literal", + "flagScrollbarMode": "", + "flagScrollDirection": "", + "scrollSnapX": "", + "scrollSnapY": "", + "checkedStateType": "literal", + "disabledStateType": "literal", + "states": "", + "useStyle": "FlowRowSpaceBetween", + "localStyles": { + "objID": "8430c074-9478-4925-9cfc-ca66c40cebab", + "definition": { + "MAIN": { + "DEFAULT": { + "pad_right": 4 + } + } + } + }, + "group": "", + "groupIndex": 0 + }, + { + "objID": "786e6022-e155-4817-85c7-4fb14ba32abf", + "type": "LVGLContainerWidget", + "left": 0, + "top": 0, + "width": 100, + "height": 80, + "customInputs": [], + "customOutputs": [], + "style": { + "objID": "ae6ca605-56d9-4520-e1a9-aabd32d5a6e4", + "useStyle": "default", + "conditionalStyles": [], + "childStyles": [] + }, + "timeline": [], + "eventHandlers": [], + "identifier": "RebootContainer", + "leftUnit": "px", + "topUnit": "px", + "widthUnit": "%", + "heightUnit": "%", + "children": [ + { + "objID": "5b9d41c7-6ef0-4801-a451-03698a3cab62", + "type": "LVGLContainerWidget", + "left": 0, + "top": 0, + "width": 100, + "height": 118, + "customInputs": [], + "customOutputs": [], + "style": { + "objID": "887e0a83-0776-4fb3-b14c-0aca586acd1b", + "useStyle": "default", + "conditionalStyles": [], + "childStyles": [] + }, + "timeline": [], + "eventHandlers": [], + "leftUnit": "px", + "topUnit": "px", + "widthUnit": "%", + "heightUnit": "content", + "children": [ + { + "objID": "a4d2abb4-8fe3-427b-96ee-9374e1db7fbc", + "type": "LVGLContainerWidget", + "left": 0, + "top": 0, + "width": 100, + "height": 60, + "customInputs": [], + "customOutputs": [], + "style": { + "objID": "470bb18c-321e-4360-c405-151f213b5ece", + "useStyle": "default", + "conditionalStyles": [], + "childStyles": [] + }, + "timeline": [], + "eventHandlers": [], + "identifier": "RebootLabelContainer", + "leftUnit": "px", + "topUnit": "px", + "widthUnit": "%", + "heightUnit": "content", + "children": [ + { + "objID": "f4d2f437-87ee-4502-cf0c-c908b40df1b7", + "type": "LVGLLabelWidget", + "left": 0, + "top": 0, + "width": 100, + "height": 40, + "customInputs": [], + "customOutputs": [], + "style": { + "objID": "9b36d75b-68b5-472e-bbc4-f816a8e8f399", + "useStyle": "default", + "conditionalStyles": [], + "childStyles": [] + }, + "timeline": [], + "eventHandlers": [], + "identifier": "RebootLabel", + "leftUnit": "px", + "topUnit": "px", + "widthUnit": "%", + "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", + "hiddenFlagType": "literal", + "clickableFlagType": "literal", + "flagScrollbarMode": "", + "flagScrollDirection": "", + "scrollSnapX": "", + "scrollSnapY": "", + "checkedStateType": "literal", + "disabledStateType": "literal", + "states": "", + "useStyle": "InfoContentLabel", + "localStyles": { + "objID": "120486ec-a295-475b-c099-687261502c2a", + "definition": { + "MAIN": { + "DEFAULT": { + "text_font": "FontBook20" + } + } + } + }, + "group": "", + "groupIndex": 0, + "text": "Press and hold for\n5 seconds", + "textType": "literal", + "longMode": "WRAP", + "recolor": false, + "previewValue": "0.0.1" + } + ], + "widgetFlags": "CLICK_FOCUSABLE|GESTURE_BUBBLE|PRESS_LOCK|SCROLL_CHAIN_HOR|SCROLL_CHAIN_VER|SCROLL_ELASTIC|SCROLL_MOMENTUM|SCROLL_WITH_ARROW|SNAPPABLE", + "hiddenFlagType": "literal", + "clickableFlag": true, + "clickableFlagType": "literal", + "flagScrollbarMode": "", + "flagScrollDirection": "", + "scrollSnapX": "", + "scrollSnapY": "", + "checkedStateType": "literal", + "disabledStateType": "literal", + "states": "", + "useStyle": "FlexColumnStart", + "localStyles": { + "objID": "3a8dee11-73af-4910-f1c7-1468f5431a8a", + "definition": { + "MAIN": { + "DEFAULT": { + "pad_right": 10, + "pad_left": 10, + "pad_top": 10, + "pad_bottom": 10 + } + } + } + }, + "group": "", + "groupIndex": 0 + }, + { + "objID": "45ea864f-58c1-4d5d-e127-b97555add8bf", + "type": "LVGLContainerWidget", + "left": 0, + "top": 0, + "width": 100, + "height": 50, + "customInputs": [], + "customOutputs": [], + "style": { + "objID": "6ad1372a-50a8-40e1-f7d2-a2590f6e2165", + "useStyle": "default", + "conditionalStyles": [], + "childStyles": [] + }, + "hiddenInEditor": false, + "timeline": [], + "eventHandlers": [], + "identifier": "RebootConfigButton", + "leftUnit": "px", + "topUnit": "px", + "widthUnit": "%", + "heightUnit": "content", + "children": [ + { + "objID": "63f1761b-6e49-4dc6-b332-30433d40bacf", + "type": "LVGLButtonWidget", + "left": 0, + "top": 0, + "width": 100, + "height": 50, + "customInputs": [], + "customOutputs": [], + "style": { + "objID": "df54f6fe-592f-48bf-aa40-a4484f296e3e", + "useStyle": "default", + "conditionalStyles": [], + "childStyles": [] + }, + "hiddenInEditor": false, + "timeline": [], + "eventHandlers": [ + { + "objID": "a7796219-cfc4-45e7-bf90-9d374e674003", + "eventName": "PRESSED", + "handlerType": "action", + "action": "Reboot", + "userData": 0 + }, + { + "objID": "35cd8033-6cd0-4359-c995-e627dac3815c", + "eventName": "PRESSING", + "handlerType": "action", + "action": "Reboot", + "userData": 0 + } + ], + "leftUnit": "px", + "topUnit": "px", + "widthUnit": "%", + "heightUnit": "px", + "children": [ + { + "objID": "4fcf0c6c-39e7-48bd-8567-18093e91e6f2", + "type": "LVGLLabelWidget", + "left": 0, + "top": 0, + "width": 106, + "height": 16, + "customInputs": [], + "customOutputs": [], + "style": { + "objID": "06433a39-c46c-4c09-dd93-6521174405e0", + "useStyle": "default", + "conditionalStyles": [], + "childStyles": [] + }, + "hiddenInEditor": false, + "timeline": [], + "eventHandlers": [], + "leftUnit": "px", + "topUnit": "px", + "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", + "hiddenFlagType": "literal", + "clickableFlagType": "literal", + "flagScrollbarMode": "", + "flagScrollDirection": "", + "scrollSnapX": "", + "scrollSnapY": "", + "checkedStateType": "literal", + "disabledStateType": "literal", + "states": "", + "localStyles": { + "objID": "f88937ae-2537-4599-b0b3-6b41ad50c8cb", + "definition": { + "MAIN": { + "DEFAULT": { + "align": "CENTER", + "text_align": "LEFT" + } + } + } + }, + "group": "", + "groupIndex": 0, + "text": "Hold to reboot", + "textType": "literal", + "longMode": "WRAP", + "recolor": false + } + ], + "widgetFlags": "CLICK_FOCUSABLE|GESTURE_BUBBLE|PRESS_LOCK|SCROLL_CHAIN_HOR|SCROLL_CHAIN_VER|SCROLL_ELASTIC|SCROLL_MOMENTUM|SCROLL_ON_FOCUS|SCROLL_WITH_ARROW|SNAPPABLE", + "hiddenFlagType": "literal", + "clickableFlag": true, + "clickableFlagType": "literal", + "flagScrollbarMode": "", + "flagScrollDirection": "", + "scrollSnapX": "", + "scrollSnapY": "", + "checkedStateType": "literal", + "disabledStateType": "literal", + "states": "", + "useStyle": "", + "localStyles": { + "objID": "375a8cfa-03c1-4c33-8135-e3a841edb1df", + "definition": { + "MAIN": { + "DEFAULT": { + "bg_color": "DC2626", + "text_align": "LEFT", + "pad_right": 13 + } + } + } + }, + "group": "", + "groupIndex": 0 + } + ], + "widgetFlags": "CLICK_FOCUSABLE|GESTURE_BUBBLE|PRESS_LOCK|SCROLL_CHAIN_HOR|SCROLL_CHAIN_VER|SCROLL_ELASTIC|SCROLL_MOMENTUM|SCROLL_WITH_ARROW|SNAPPABLE", + "hiddenFlagType": "literal", + "clickableFlag": true, + "clickableFlagType": "literal", + "flagScrollbarMode": "", + "flagScrollDirection": "", + "scrollSnapX": "", + "scrollSnapY": "", + "checkedStateType": "literal", + "disabledStateType": "literal", + "states": "", + "useStyle": "FlexColumnStart", + "localStyles": { + "objID": "00264c51-c7f0-49f6-f367-f932e00a0ca5" + }, + "group": "", + "groupIndex": 0 + } + ], + "widgetFlags": "CLICK_FOCUSABLE|GESTURE_BUBBLE|PRESS_LOCK|SCROLL_CHAIN_HOR|SCROLL_CHAIN_VER|SCROLL_ELASTIC|SCROLL_MOMENTUM|SCROLL_WITH_ARROW|SNAPPABLE", + "hiddenFlagType": "literal", + "clickableFlag": true, + "clickableFlagType": "literal", + "flagScrollbarMode": "", + "flagScrollDirection": "", + "scrollSnapX": "", + "scrollSnapY": "", + "checkedStateType": "literal", + "disabledStateType": "literal", + "states": "", + "useStyle": "FlexColumnStart", + "localStyles": { + "objID": "91a33d82-74c3-48c4-b623-3cc82961c053", + "definition": { + "MAIN": { + "DEFAULT": { + "pad_right": 10 + } + } + } + }, + "group": "", + "groupIndex": 0 + } + ], + "widgetFlags": "CLICK_FOCUSABLE|GESTURE_BUBBLE|PRESS_LOCK|SCROLL_CHAIN_VER|SNAPPABLE|SCROLL_ELASTIC|SCROLL_WITH_ARROW|SCROLL_MOMENTUM|SCROLL_CHAIN_HOR|SCROLLABLE", + "hiddenFlagType": "literal", + "clickableFlag": true, + "clickableFlagType": "literal", + "flagScrollbarMode": "auto", + "flagScrollDirection": "ver", + "scrollSnapX": "start", + "scrollSnapY": "", + "checkedStateType": "literal", + "disabledStateType": "literal", + "states": "", + "useStyle": "FlexColumnStart", + "localStyles": { + "objID": "d8f53da5-0b49-44e7-bf3c-0dfaabf57ced", + "definition": { + "MAIN": { + "DEFAULT": { + "pad_right": 4 + } + } + } + }, + "group": "", + "groupIndex": 0 + } + ], + "widgetFlags": "CLICK_FOCUSABLE|GESTURE_BUBBLE|PRESS_LOCK|SCROLL_CHAIN_HOR|SCROLL_CHAIN_VER|SCROLL_MOMENTUM|SCROLL_WITH_ARROW|SNAPPABLE|SCROLL_ELASTIC", + "hiddenFlagType": "literal", + "clickableFlag": true, + "clickableFlagType": "literal", + "flagScrollbarMode": "", + "flagScrollDirection": "", + "scrollSnapX": "", + "scrollSnapY": "", + "checkedStateType": "literal", + "disabledStateType": "literal", + "states": "", + "useStyle": "FlexStart", + "localStyles": { + "objID": "a33e581f-4cdf-47d8-91cd-db93f2b49bf5" + }, + "group": "", + "groupIndex": 0 + } + ], + "widgetFlags": "CLICKABLE|PRESS_LOCK|CLICK_FOCUSABLE|GESTURE_BUBBLE|SNAPPABLE|SCROLLABLE|SCROLL_ELASTIC|SCROLL_MOMENTUM|SCROLL_CHAIN_HOR|SCROLL_CHAIN_VER", + "hiddenFlagType": "literal", + "clickableFlag": true, + "clickableFlagType": "literal", + "checkedStateType": "literal", + "disabledStateType": "literal", + "states": "", + "useStyle": "FlexScreenMenu", + "localStyles": { + "objID": "22536b90-1d84-4789-8229-4db6155fa9d0" + }, + "groupIndex": 0 + } + ], + "connectionLines": [], + "localVariables": [], + "userProperties": [], + "name": "RebootScreen", + "left": 0, + "top": 0, + "width": 300, + "height": 240, + "isUsedAsUserWidget": false, + "createAtStart": true, + "deleteOnScreenUnload": false } ], "userWidgets": [], diff --git a/internal/native/eez/jetkvm.eez-project-ui-state b/internal/native/eez/jetkvm.eez-project-ui-state index 6685cef6..897e3ad3 100644 --- a/internal/native/eez/jetkvm.eez-project-ui-state +++ b/internal/native/eez/jetkvm.eez-project-ui-state @@ -1,7 +1,7 @@ { "navigation": { "selectedUserPageObject": "[jetkvm.eez-project]:/userPages/8", - "selectedActionObject": "[jetkvm.eez-project]:/actions/5", + "selectedActionObject": "[jetkvm.eez-project]:/actions/12", "selectedGlobalVariableObject": "[jetkvm.eez-project]:/variables/globalVariables/1", "selectedStyleObject": "[jetkvm.eez-project]:/lvglStyles/styles/8", "selectedThemeObject": "[jetkvm.eez-project]:/themes/0", @@ -30,7 +30,7 @@ { "type": "border", "selected": 2, - "size": 296.5, + "size": 271.5, "location": "right", "children": [ { @@ -130,7 +130,6 @@ "type": "tabset", "id": "#2e3d9a08-c69c-42b5-b434-525109f2a5a7", "weight": 1, - "selected": 1, "enableClose": false, "children": [ { @@ -196,25 +195,25 @@ { "type": "tabset", "id": "EDITORS", - "weight": 56.255429602160085, - "selected": 2, + "weight": 52.479136828866125, + "selected": 5, "enableDeleteWhenEmpty": false, "enableClose": false, "children": [ { "type": "tab", - "id": "#4610a27a-ce31-4747-b3c0-488eafeb606b", - "name": "StatusScreen", + "id": "#2b774476-9ef3-4363-83f8-8b478f163b02", + "name": "MenuAdvancedScreen", "component": "editor", "config": { - "objectPath": "[jetkvm.eez-project]:/userPages/7", + "objectPath": "[jetkvm.eez-project]:/userPages/4", "permanent": false }, "icon": "svg:page" }, { "type": "tab", - "id": "#ef9d764d-e942-47f4-beec-136cbd40ffd7", + "id": "#7bbd8382-ea41-467d-8ad3-4312a2d47266", "name": "ResetConfigScreen", "component": "editor", "config": { @@ -223,21 +222,10 @@ }, "icon": "svg:page" }, - { - "type": "tab", - "id": "#aec56ae8-5b75-4a2f-ad81-f0d7d683c77a", - "name": "FontBook24", - "component": "editor", - "config": { - "objectPath": "[jetkvm.eez-project]:/fonts/4", - "permanent": false - }, - "icon": "material:font_download" - }, { "type": "tab", "id": "#c8dece00-e490-46b8-8a14-5dcfa8bbce36", - "name": "NoNetworkScreen", + "name": "StatusScreen", "component": "editor", "config": { "objectPath": "[jetkvm.eez-project]:/userPages/7", @@ -259,7 +247,7 @@ { "type": "tab", "id": "#f5a057a5-977c-46be-8702-5447d603a34f", - "name": "MenuScreen", + "name": "HomeScreen", "component": "editor", "config": { "objectPath": "[jetkvm.eez-project]:/userPages/2", @@ -285,7 +273,7 @@ { "type": "row", "id": "#ee319cf9-6145-49e4-b40e-1d999be897c8", - "weight": 18.007499872650474, + "weight": 21.78379264594443, "children": [ { "type": "tabset", @@ -1237,15 +1225,11 @@ "0": { "0": { "0": { - "0": { - "$selected": true - } + "0": {} }, "1": { "0": { - "0": {}, - "1": {}, - "2": {} + "3": {} } } } @@ -1253,8 +1237,8 @@ }, "transform": { "translate": { - "x": -176, - "y": -127 + "x": -181, + "y": -256.3828125 }, "scale": 1 }, @@ -1372,16 +1356,19 @@ "selection": { "0": { "0": { - "0": {}, + "0": { + "0": {} + }, "1": { "0": { - "0": {}, - "1": { + "0": { "0": { - "0": { - "$selected": true - } + "$selected": true } + }, + "1": {}, + "2": { + "0": {} } } } @@ -1418,10 +1405,8 @@ "lvglPart": "MAIN", "lvglState": "DEFAULT", "lvglExpandedPropertiesGroup": [ - "TEXT", - "ARC", "POSITION AND SIZE", - "PADDING" + "LAYOUT" ], "showInactiveFlowsInDebugger": true, "globalFlowZoom": true, diff --git a/internal/native/eez/src/ui/actions.c b/internal/native/eez/src/ui/actions.c index 257d4505..3deb1f1c 100644 --- a/internal/native/eez/src/ui/actions.c +++ b/internal/native/eez/src/ui/actions.c @@ -1,5 +1,7 @@ #include "actions.h" #include "screens.h" +#include "ui.h" +#include int handle_gesture_screen_switch(lv_event_t *e, lv_dir_t direction, int screenId) { lv_event_code_t event_code = lv_event_get_code(e); @@ -31,6 +33,14 @@ void action_switch_to_about(lv_event_t *e) { loadScreen(SCREEN_ID_ABOUT_SCREEN); } +void action_switch_to_reset_config(lv_event_t *e) { + loadScreen(SCREEN_ID_RESET_CONFIG_SCREEN); +} + +void action_switch_to_reboot(lv_event_t *e) { + loadScreen(SCREEN_ID_REBOOT_SCREEN); +} + void action_menu_screen_gesture(lv_event_t * e) { handle_gesture_screen_switch(e, LV_DIR_RIGHT, SCREEN_ID_HOME_SCREEN); } @@ -51,27 +61,56 @@ void action_about_screen_gesture(lv_event_t * e) { handle_gesture_screen_switch(e, LV_DIR_RIGHT, SCREEN_ID_MENU_SCREEN); } -static const int RESET_LONG_PRESS_DURATION = 1000 * 20; // 20 seconds +// user_data doesn't seem to be working, so we use a global variable here +static uint32_t t_reset_config; +static uint32_t t_reboot; +const int RESET_CONFIG_HOLD_TIME = 10; +const int REBOOT_HOLD_TIME = 5; void action_reset_config(lv_event_t * e) { lv_event_code_t event_code = lv_event_get_code(e); lv_obj_t *obj = lv_event_get_target(e); if (event_code == LV_EVENT_PRESSED) { - // Button pressed - start timing - uint32_t reset_press_start_time = lv_tick_get(); - lv_obj_set_user_data(obj, (uint32_t) reset_press_start_time); + t_reset_config = lv_tick_get(); } else if (event_code == LV_EVENT_PRESSING) { - uint32_t reset_press_start_time = (uint32_t) lv_obj_get_user_data(obj); - if (reset_press_start_time == 0) { - return; - } - - uint32_t current_time = lv_tick_get(); - if (current_time - reset_press_start_time >= RESET_LONG_PRESS_DURATION) { + int remaining_time = RESET_CONFIG_HOLD_TIME * 1000 - lv_tick_elaps(t_reset_config); + if (remaining_time <= 0) { lv_obj_add_flag(objects.reset_config_button, LV_OBJ_FLAG_HIDDEN); - lv_obj_clear_flag(objects.reset_config_spinner, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(objects.reset_config_spinner, LV_OBJ_FLAG_HIDDEN); + ui_call_rpc_handler("resetConfig", NULL); + } else { + char buf[100]; + int remaining_time_seconds = remaining_time / 1000; + if (remaining_time_seconds <= 1) { + remaining_time_seconds = 1; + } + sprintf(buf, "Press and hold for %d seconds", remaining_time_seconds); + lv_label_set_text(objects.reset_config_label, buf); + } + } +} + +void action_reboot(lv_event_t * e) { + lv_event_code_t event_code = lv_event_get_code(e); + lv_obj_t *obj = lv_event_get_target(e); + + if (event_code == LV_EVENT_PRESSED) { + t_reboot = lv_tick_get(); + } + else if (event_code == LV_EVENT_PRESSING) { + int remaining_time = REBOOT_HOLD_TIME * 1000 - lv_tick_elaps(t_reboot); + if (remaining_time <= 0) { + ui_call_rpc_handler("reboot", NULL); + } else { + char buf[100]; + int remaining_time_seconds = remaining_time / 1000; + if (remaining_time_seconds <= 1) { + remaining_time_seconds = 1; + } + sprintf(buf, "Press and hold for %d seconds", remaining_time_seconds); + lv_label_set_text(objects.reboot_label, buf); } } } \ No newline at end of file diff --git a/internal/native/eez/src/ui/actions.h b/internal/native/eez/src/ui/actions.h index 398ff723..f4a24e44 100644 --- a/internal/native/eez/src/ui/actions.h +++ b/internal/native/eez/src/ui/actions.h @@ -11,6 +11,7 @@ extern int handle_gesture_screen_switch(lv_event_t *e, lv_dir_t direction, int s extern void action_switch_to_menu(lv_event_t * e); extern void action_switch_to_advanced_menu(lv_event_t * e); +extern void action_switch_to_reset_config(lv_event_t * e); extern void action_switch_to_about(lv_event_t * e); extern void action_menu_screen_gesture(lv_event_t * e); extern void action_home_screen_gesture(lv_event_t * e); @@ -21,6 +22,8 @@ extern void action_switch_to_status(lv_event_t * e); extern void action_common_click_event(lv_event_t * e); extern void action_handle_common_press_event(lv_event_t * e); extern void action_reset_config(lv_event_t * e); +extern void action_reboot(lv_event_t * e); +extern void action_switch_to_reboot(lv_event_t * e); #ifdef __cplusplus diff --git a/internal/native/eez/src/ui/screens.c b/internal/native/eez/src/ui/screens.c index 6844b32a..9ee7fc60 100644 --- a/internal/native/eez/src/ui/screens.c +++ b/internal/native/eez/src/ui/screens.c @@ -829,6 +829,8 @@ void create_screen_menu_advanced_screen() { objects.menu_btn_advanced_developer_mode = obj; lv_obj_set_pos(obj, 0, 0); lv_obj_set_size(obj, LV_PCT(100), 50); + lv_obj_add_event_cb(obj, action_reset_config, LV_EVENT_PRESSED, (void *)0); + lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN); add_style_menu_button(obj); { lv_obj_t *parent_obj = obj; @@ -847,6 +849,7 @@ void create_screen_menu_advanced_screen() { objects.menu_btn_advanced_usb_emulation = obj; lv_obj_set_pos(obj, 0, 0); lv_obj_set_size(obj, LV_PCT(100), 50); + lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN); add_style_menu_button(obj); { lv_obj_t *parent_obj = obj; @@ -865,6 +868,7 @@ void create_screen_menu_advanced_screen() { objects.menu_btn_advanced_reboot = obj; lv_obj_set_pos(obj, 0, 0); lv_obj_set_size(obj, LV_PCT(100), 50); + lv_obj_add_event_cb(obj, action_switch_to_reboot, LV_EVENT_PRESSED, (void *)0); lv_obj_clear_flag(obj, LV_OBJ_FLAG_SNAPPABLE); add_style_menu_button(obj); { @@ -884,6 +888,7 @@ void create_screen_menu_advanced_screen() { objects.menu_btn_advanced_reset_config = obj; lv_obj_set_pos(obj, 0, 0); lv_obj_set_size(obj, LV_PCT(100), 50); + lv_obj_add_event_cb(obj, action_switch_to_reset_config, LV_EVENT_PRESSED, (void *)0); lv_obj_clear_flag(obj, LV_OBJ_FLAG_SNAPPABLE); add_style_menu_button(obj); lv_obj_set_style_bg_color(obj, lv_color_hex(0xffdc2626), LV_PART_MAIN | LV_STATE_DEFAULT); @@ -1780,7 +1785,7 @@ void create_screen_reset_config_screen() { lv_obj_set_pos(obj, LV_PCT(0), LV_PCT(0)); lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT); add_style_header_link(obj); - lv_label_set_text(obj, "Reset Device"); + lv_label_set_text(obj, "Reset Config"); } } } @@ -1819,9 +1824,9 @@ void create_screen_reset_config_screen() { { lv_obj_t *parent_obj = obj; { - // ResetConfigLabel + // ResetConfigLabelContainer lv_obj_t *obj = lv_obj_create(parent_obj); - objects.reset_config_label = obj; + objects.reset_config_label_container = obj; lv_obj_set_pos(obj, 0, 0); lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); @@ -1836,12 +1841,14 @@ void create_screen_reset_config_screen() { { lv_obj_t *parent_obj = obj; { + // ResetConfigLabel lv_obj_t *obj = lv_label_create(parent_obj); + objects.reset_config_label = obj; lv_obj_set_pos(obj, 0, 0); lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT); add_style_info_content_label(obj); lv_obj_set_style_text_font(obj, &ui_font_font_book20, LV_PART_MAIN | LV_STATE_DEFAULT); - lv_label_set_text(obj, "Press and hold for\n20 seconds"); + lv_label_set_text(obj, "Press and hold for\n10 seconds"); } } } @@ -1858,7 +1865,8 @@ void create_screen_reset_config_screen() { lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); - lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICKABLE|LV_OBJ_FLAG_SCROLLABLE); add_style_flex_column_start(obj); lv_obj_set_style_flex_main_place(obj, LV_FLEX_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_flex_cross_place(obj, LV_FLEX_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT); @@ -1895,7 +1903,8 @@ void create_screen_reset_config_screen() { objects.obj0 = obj; lv_obj_set_pos(obj, 0, 0); lv_obj_set_size(obj, LV_PCT(100), 50); - lv_obj_add_event_cb(obj, action_reset_config, LV_EVENT_LONG_PRESSED, (void *)0); + lv_obj_add_event_cb(obj, action_reset_config, LV_EVENT_PRESSED, (void *)0); + lv_obj_add_event_cb(obj, action_reset_config, LV_EVENT_PRESSING, (void *)0); lv_obj_set_style_bg_color(obj, lv_color_hex(0xffdc2626), LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_text_align(obj, LV_TEXT_ALIGN_LEFT, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_right(obj, 13, LV_PART_MAIN | LV_STATE_DEFAULT); @@ -1907,7 +1916,7 @@ void create_screen_reset_config_screen() { lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT); lv_obj_set_style_align(obj, LV_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_text_align(obj, LV_TEXT_ALIGN_LEFT, LV_PART_MAIN | LV_STATE_DEFAULT); - lv_label_set_text(obj, "Hold to reset"); + lv_label_set_text(obj, "Reset configuration"); } } } @@ -1927,6 +1936,189 @@ void create_screen_reset_config_screen() { void tick_screen_reset_config_screen() { } +void create_screen_reboot_screen() { + lv_obj_t *obj = lv_obj_create(0); + objects.reboot_screen = obj; + lv_obj_set_pos(obj, 0, 0); + lv_obj_set_size(obj, 300, 240); + lv_obj_add_event_cb(obj, action_about_screen_gesture, LV_EVENT_GESTURE, (void *)0); + add_style_flex_screen_menu(obj); + { + lv_obj_t *parent_obj = obj; + { + lv_obj_t *obj = lv_obj_create(parent_obj); + lv_obj_set_pos(obj, 0, 0); + lv_obj_set_size(obj, LV_PCT(100), LV_PCT(100)); + lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_right(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); + add_style_flex_start(obj); + { + lv_obj_t *parent_obj = obj; + { + // RebootHeader + lv_obj_t *obj = lv_obj_create(parent_obj); + objects.reboot_header = obj; + lv_obj_set_pos(obj, 0, 0); + lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT); + lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + add_style_flow_row_space_between(obj); + lv_obj_set_style_pad_right(obj, 4, LV_PART_MAIN | LV_STATE_DEFAULT); + { + lv_obj_t *parent_obj = obj; + { + lv_obj_t *obj = lv_button_create(parent_obj); + lv_obj_set_pos(obj, 0, 0); + lv_obj_set_size(obj, 32, 32); + lv_obj_add_event_cb(obj, action_switch_to_menu, LV_EVENT_CLICKED, (void *)0); + add_style_back_button(obj); + { + lv_obj_t *parent_obj = obj; + { + lv_obj_t *obj = lv_image_create(parent_obj); + lv_obj_set_pos(obj, -1, 2); + lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT); + lv_image_set_src(obj, &img_back_caret); + } + } + } + { + lv_obj_t *obj = lv_label_create(parent_obj); + lv_obj_set_pos(obj, LV_PCT(0), LV_PCT(0)); + lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT); + add_style_header_link(obj); + lv_label_set_text(obj, "Reboot Device"); + } + } + } + { + // RebootContainer + lv_obj_t *obj = lv_obj_create(parent_obj); + objects.reboot_container = obj; + lv_obj_set_pos(obj, 0, 0); + lv_obj_set_size(obj, LV_PCT(100), LV_PCT(80)); + lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_scrollbar_mode(obj, LV_SCROLLBAR_MODE_AUTO); + lv_obj_set_scroll_dir(obj, LV_DIR_VER); + lv_obj_set_scroll_snap_x(obj, LV_SCROLL_SNAP_START); + add_style_flex_column_start(obj); + lv_obj_set_style_pad_right(obj, 4, LV_PART_MAIN | LV_STATE_DEFAULT); + { + lv_obj_t *parent_obj = obj; + { + lv_obj_t *obj = lv_obj_create(parent_obj); + lv_obj_set_pos(obj, 0, 0); + lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT); + lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); + add_style_flex_column_start(obj); + lv_obj_set_style_pad_right(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT); + { + lv_obj_t *parent_obj = obj; + { + // RebootLabelContainer + lv_obj_t *obj = lv_obj_create(parent_obj); + objects.reboot_label_container = obj; + lv_obj_set_pos(obj, 0, 0); + lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT); + lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); + add_style_flex_column_start(obj); + lv_obj_set_style_pad_right(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_left(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_top(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_bottom(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT); + { + lv_obj_t *parent_obj = obj; + { + // RebootLabel + lv_obj_t *obj = lv_label_create(parent_obj); + objects.reboot_label = obj; + lv_obj_set_pos(obj, 0, 0); + lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT); + add_style_info_content_label(obj); + lv_obj_set_style_text_font(obj, &ui_font_font_book20, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_label_set_text(obj, "Press and hold for\n5 seconds"); + } + } + } + { + // RebootConfigButton + lv_obj_t *obj = lv_obj_create(parent_obj); + objects.reboot_config_button = obj; + lv_obj_set_pos(obj, 0, 0); + lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT); + lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_right(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); + add_style_flex_column_start(obj); + { + lv_obj_t *parent_obj = obj; + { + lv_obj_t *obj = lv_button_create(parent_obj); + objects.obj1 = obj; + lv_obj_set_pos(obj, 0, 0); + lv_obj_set_size(obj, LV_PCT(100), 50); + lv_obj_add_event_cb(obj, action_reboot, LV_EVENT_PRESSED, (void *)0); + lv_obj_add_event_cb(obj, action_reboot, LV_EVENT_PRESSING, (void *)0); + lv_obj_set_style_bg_color(obj, lv_color_hex(0xffdc2626), LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_text_align(obj, LV_TEXT_ALIGN_LEFT, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_right(obj, 13, LV_PART_MAIN | LV_STATE_DEFAULT); + { + lv_obj_t *parent_obj = obj; + { + lv_obj_t *obj = lv_label_create(parent_obj); + lv_obj_set_pos(obj, 0, 0); + lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT); + lv_obj_set_style_align(obj, LV_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_text_align(obj, LV_TEXT_ALIGN_LEFT, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_label_set_text(obj, "Hold to reboot"); + } + } + } + } + } + } + } + } + } + } + } + } + + tick_screen_reboot_screen(); +} + +void tick_screen_reboot_screen() { +} + typedef void (*tick_screen_func_t)(); @@ -1940,6 +2132,7 @@ tick_screen_func_t tick_screen_funcs[] = { tick_screen_about_screen, tick_screen_status_screen, tick_screen_reset_config_screen, + tick_screen_reboot_screen, }; void tick_screen(int screen_index) { tick_screen_funcs[screen_index](); @@ -1962,4 +2155,5 @@ void create_screens() { create_screen_about_screen(); create_screen_status_screen(); create_screen_reset_config_screen(); + create_screen_reboot_screen(); } diff --git a/internal/native/eez/src/ui/screens.h b/internal/native/eez/src/ui/screens.h index 3ae5941f..da05f543 100644 --- a/internal/native/eez/src/ui/screens.h +++ b/internal/native/eez/src/ui/screens.h @@ -17,6 +17,7 @@ typedef struct _objects_t { lv_obj_t *about_screen; lv_obj_t *status_screen; lv_obj_t *reset_config_screen; + lv_obj_t *reboot_screen; lv_obj_t *boot_logo; lv_obj_t *boot_screen_version; lv_obj_t *no_network_header_container; @@ -86,10 +87,17 @@ typedef struct _objects_t { lv_obj_t *cloud_domain; lv_obj_t *reset_config_header; lv_obj_t *reset_config_container; + lv_obj_t *reset_config_label_container; lv_obj_t *reset_config_label; lv_obj_t *reset_config_spinner; lv_obj_t *reset_config_button; lv_obj_t *obj0; + lv_obj_t *reboot_header; + lv_obj_t *reboot_container; + lv_obj_t *reboot_label_container; + lv_obj_t *reboot_label; + lv_obj_t *reboot_config_button; + lv_obj_t *obj1; } objects_t; extern objects_t objects; @@ -104,6 +112,7 @@ enum ScreensEnum { SCREEN_ID_ABOUT_SCREEN = 7, SCREEN_ID_STATUS_SCREEN = 8, SCREEN_ID_RESET_CONFIG_SCREEN = 9, + SCREEN_ID_REBOOT_SCREEN = 10, }; void create_screen_boot_screen(); @@ -132,6 +141,9 @@ void tick_screen_status_screen(); void create_screen_reset_config_screen(); void tick_screen_reset_config_screen(); + +void create_screen_reboot_screen(); +void tick_screen_reboot_screen(); void tick_screen_by_id(enum ScreensEnum screenId); void tick_screen(int screen_index); diff --git a/internal/native/eez/src/ui/ui.c b/internal/native/eez/src/ui/ui.c index 8d9c9c15..b1ef44a6 100644 --- a/internal/native/eez/src/ui/ui.c +++ b/internal/native/eez/src/ui/ui.c @@ -14,6 +14,19 @@ + +jetkvm_rpc_handler_t *ui_rpc_handler = NULL; + +void ui_set_rpc_handler(jetkvm_rpc_handler_t *handler) { + ui_rpc_handler = handler; +} + +void ui_call_rpc_handler(const char *method, const char *params) { + if (ui_rpc_handler != NULL) { + (*ui_rpc_handler)(method, params); + } +} + #if defined(EEZ_FOR_LVGL) void ui_init() { diff --git a/internal/native/eez/src/ui/ui.h b/internal/native/eez/src/ui/ui.h index de16057e..34d90416 100644 --- a/internal/native/eez/src/ui/ui.h +++ b/internal/native/eez/src/ui/ui.h @@ -3,6 +3,13 @@ #include +typedef void (jetkvm_rpc_handler_t)(const char *method, const char *params); + +void ui_set_rpc_handler(jetkvm_rpc_handler_t *handler); +void ui_call_rpc_handler(const char *method, const char *params); + + + #if defined(EEZ_FOR_LVGL) diff --git a/internal/native/lib/libjknative.a b/internal/native/lib/libjknative.a index ca20b69dfee19f83392835162f9c0917caecf587..2c8a4dde97f84c4779920b70202a82fc0827a282 100644 GIT binary patch delta 36958 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 zℜ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 diff --git a/internal/native/native.go b/internal/native/native.go index 7ba4a6d3..8cf51560 100644 --- a/internal/native/native.go +++ b/internal/native/native.go @@ -18,6 +18,7 @@ type Native struct { onVideoStateChange func(state VideoState) onVideoFrameReceived func(frame []byte, duration time.Duration) onIndevEvent func(event string) + onRpcEvent func(event string) videoLock sync.Mutex } @@ -28,6 +29,7 @@ type NativeOptions struct { OnVideoStateChange func(state VideoState) OnVideoFrameReceived func(frame []byte, duration time.Duration) OnIndevEvent func(event string) + OnRpcEvent func(event string) } func NewNative(opts NativeOptions) *Native { @@ -52,6 +54,13 @@ func NewNative(opts NativeOptions) *Native { } } + onRpcEvent := opts.OnRpcEvent + if onRpcEvent == nil { + onRpcEvent = func(event string) { + nativeLogger.Info().Str("event", event).Msg("rpc event") + } + } + return &Native{ ready: make(chan struct{}), l: nativeLogger, @@ -62,6 +71,7 @@ func NewNative(opts NativeOptions) *Native { onVideoStateChange: opts.OnVideoStateChange, onVideoFrameReceived: opts.OnVideoFrameReceived, onIndevEvent: opts.OnIndevEvent, + onRpcEvent: opts.OnRpcEvent, videoLock: sync.Mutex{}, } } @@ -76,6 +86,7 @@ func (n *Native) Start() { go n.handleVideoStateChan() go n.handleVideoFrameChan() go n.handleIndevEventChan() + go n.handleRpcEventChan() n.initUI() go n.tickUI() diff --git a/native.go b/native.go index ce36a230..4c272b9a 100644 --- a/native.go +++ b/native.go @@ -24,6 +24,18 @@ func initNative(systemVersion *semver.Version, appVersion *semver.Version) { nativeLogger.Trace().Str("event", event).Msg("indev event received") wakeDisplay(false, "indev_event") }, + OnRpcEvent: func(event string) { + nativeLogger.Trace().Str("event", event).Msg("rpc event received") + switch event { + case "resetConfig": + rpcResetConfig() + rpcReboot(true) + case "reboot": + rpcReboot(true) + default: + nativeLogger.Warn().Str("event", event).Msg("unknown rpc event received") + } + }, OnVideoFrameReceived: func(frame []byte, duration time.Duration) { if currentSession != nil { err := currentSession.VideoTrack.WriteSample(media.Sample{Data: frame, Duration: duration})