From 8123900f2680bfce9fb960f6786eadf717bd5cf5 Mon Sep 17 00:00:00 2001 From: Siyuan Miao Date: Thu, 25 Sep 2025 09:51:59 +0000 Subject: [PATCH] add locks and missing comments --- Makefile | 4 +- dev_deploy.sh | 3 +- internal/native/cgo/edid.c | 2 +- internal/native/cgo/log_handler.h | 17 ++++ internal/native/cgo/screen.c | 2 +- internal/native/cgo/screen.h | 27 ++++++ internal/native/cgo/video.c | 117 +++++++++++++------------ internal/native/cgo/video.h | 35 ++++++++ internal/native/eez/jetkvm.eez-project | 106 ++++++++++++++++++++-- internal/native/eez/src/ui/actions.c | 25 ++++++ internal/native/eez/src/ui/screens.c | 46 +++++++--- internal/native/eez/src/ui/screens.h | 10 +-- internal/native/lib/libjknative.a | Bin 3101834 -> 3111426 bytes 13 files changed, 311 insertions(+), 83 deletions(-) diff --git a/Makefile b/Makefile index 7461f59e..f043a28c 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ KVM_PKG_NAME := github.com/jetkvm/kvm BUILDKIT_FLAVOR := arm-rockchip830-linux-uclibcgnueabihf BUILDKIT_PATH ?= /opt/jetkvm-native-buildkit -SKIP_NATIVE_IF_EXISTS ?= 1 +SKIP_NATIVE_IF_EXISTS ?= 0 GO_BUILD_ARGS := -tags netgo -tags timetzdata @@ -111,7 +111,7 @@ dev_release: frontend build_dev rclone copyto bin/jetkvm_app r2://jetkvm-update/app/$(VERSION_DEV)/jetkvm_app rclone copyto bin/jetkvm_app.sha256 r2://jetkvm-update/app/$(VERSION_DEV)/jetkvm_app.sha256 -build_release: frontend +build_release: frontend build_native @echo "Building release..." $(GO_CMD) build \ -ldflags="$(GO_LDFLAGS) -X $(KVM_PKG_NAME).builtAppVersion=$(VERSION)" \ diff --git a/dev_deploy.sh b/dev_deploy.sh index 9a1f8e02..c793efd7 100755 --- a/dev_deploy.sh +++ b/dev_deploy.sh @@ -104,7 +104,8 @@ if [ -z "$REMOTE_HOST" ]; then fi # Build the development version on the host -if [ "$SKIP_UI_BUILD" = false ]; then +# When using `make build_release`, the frontend will be built regardless of the `SKIP_UI_BUILD` flag +if [[ "$SKIP_UI_BUILD" = false && "$INSTALL_APP" = false ]]; then msg_info "▶ Building frontend" make frontend fi diff --git a/internal/native/cgo/edid.c b/internal/native/cgo/edid.c index 6bc11c35..15160f7b 100644 --- a/internal/native/cgo/edid.c +++ b/internal/native/cgo/edid.c @@ -173,7 +173,7 @@ const char *videoc_log_status() } else { - log_error("Failed to read kernel log\n"); + log_error("Failed to read kernel log"); return NULL; } diff --git a/internal/native/cgo/log_handler.h b/internal/native/cgo/log_handler.h index e019aa93..62b071c2 100644 --- a/internal/native/cgo/log_handler.h +++ b/internal/native/cgo/log_handler.h @@ -2,8 +2,25 @@ #define LOG_HANDLER_H typedef void (jetkvm_log_handler_t)(int level, const char *filename, const char *funcname, const int line, const char *message); + +/** + * @brief Log a message + * + * @param level The level of the message + * @param filename The filename of the message + * @param funcname The function name of the message + * @param line The line number of the message + * @param message The message to log + * @return void + */ void log_message(int level, const char *filename, const char *funcname, const int line, const char *message); +/** + * @brief Set the log handler + * + * @param handler The handler to set + * @return void + */ void log_set_handler(jetkvm_log_handler_t *handler); #endif \ No newline at end of file diff --git a/internal/native/cgo/screen.c b/internal/native/cgo/screen.c index 2db3cfe3..ecef3422 100644 --- a/internal/native/cgo/screen.c +++ b/internal/native/cgo/screen.c @@ -188,7 +188,7 @@ lv_img_dsc_t *ui_get_image(const char *name) { void ui_set_text(const char *name, const char *text) { lv_obj_t *obj = ui_get_obj(name); if(obj == NULL) { - log_error("ui_set_text %s %s, obj not found\n", name, text); + log_error("ui_set_text %s %s, obj not found", name, text); return; } lv_label_set_text(obj, text); diff --git a/internal/native/cgo/screen.h b/internal/native/cgo/screen.h index 532ef616..4aaabeaf 100644 --- a/internal/native/cgo/screen.h +++ b/internal/native/cgo/screen.h @@ -12,10 +12,37 @@ void lvgl_tick(void); void lvgl_set_rotation(lv_display_t *disp, u_int16_t rotation); +/** + * @brief Set the text of an object + * + * @param name The name of the object + * @param text The text to set + * @return void + */ void ui_set_text(const char *name, const char *text); +/** + * @brief Get the object with the given name + * + * @param name The name of the object + * @return lv_obj_t* The object with the given name + */ lv_obj_t *ui_get_obj(const char *name); + +/** + * @brief Get the style with the given name + * + * @param name The name of the style + * @return lv_style_t* The style with the given name + */ lv_style_t *ui_get_style(const char *name); + +/** + * @brief Get the image with the given name + * + * @param name The name of the image + * @return lv_img_dsc_t* The image with the given name + */ lv_img_dsc_t *ui_get_image(const char *name); #endif // SCREEN_H diff --git a/internal/native/cgo/video.c b/internal/native/cgo/video.c index 5f82c422..0e67945b 100644 --- a/internal/native/cgo/video.c +++ b/internal/native/cgo/video.c @@ -268,12 +268,12 @@ static void *venc_read_stream(void *arg) stFrame.pstPack = malloc(sizeof(VENC_PACK_S)); while (venc_running) { - log_trace("RK_MPI_VENC_GetStream\n"); + log_trace("RK_MPI_VENC_GetStream"); s32Ret = RK_MPI_VENC_GetStream(VENC_CHANNEL, &stFrame, 200); // blocks max 200ms if (s32Ret == RK_SUCCESS) { RK_U64 nowUs = get_us(); - log_trace("chn:0, loopCount:%d enc->seq:%d wd:%d pts=%llu delay=%lldus\n", + log_trace("chn:0, loopCount:%d enc->seq:%d wd:%d pts=%llu delay=%lldus", loopCount, stFrame.u32Seq, stFrame.pstPack->u32Len, stFrame.pstPack->u64PTS, nowUs - stFrame.pstPack->u64PTS); pData = RK_MPI_MB_Handle2VirAddr(stFrame.pstPack->pMbBlk); @@ -348,7 +348,7 @@ void *run_video_stream(void *arg) if (ioctl(video_dev_fd, VIDIOC_S_FMT, &fmt) < 0) { - log_error("Set format fail"); + log_error("Set format fail: %s", strerror(errno)); usleep(100000); // Sleep for 100 milliseconds close(video_dev_fd); continue; @@ -363,7 +363,8 @@ void *run_video_stream(void *arg) if (ioctl(video_dev_fd, VIDIOC_REQBUFS, &req) < 0) { - log_error("VIDIOC_REQBUFS failed"); + log_error("VIDIOC_REQBUFS failed: %s", strerror(errno)); + close(video_dev_fd); return errno; } log_info("VIDIOC_REQBUFS successful"); @@ -385,22 +386,24 @@ void *run_video_stream(void *arg) if (-1 == ioctl(video_dev_fd, VIDIOC_QUERYBUF, &buf)) { - log_error("VIDIOC_QUERYBUF failed"); + log_error("VIDIOC_QUERYBUF failed: %s", strerror(errno)); req.count = i; + close(video_dev_fd); return errno; } - log_info("VIDIOC_QUERYBUF successful for buffer %d\n", i); + log_info("VIDIOC_QUERYBUF successful for buffer %d", i); - log_info("plane: length = %d\n", planes_buffer->length); - log_info("plane: offset = %d\n", planes_buffer->m.mem_offset); + log_info("plane: length = %d", planes_buffer->length); + log_info("plane: offset = %d", planes_buffer->m.mem_offset); MB_BLK blk = RK_MPI_MB_GetMB(memPool, (planes_buffer)->length, RK_TRUE); if (blk == NULL) { log_error("get mb blk failed!"); + close(video_dev_fd); return -1; } - log_info("Got memory block for buffer %d\n", i); + log_info("Got memory block for buffer %d", i); buffers[i].mb_blk = blk; @@ -408,9 +411,10 @@ void *run_video_stream(void *arg) if (buf_fd < 0) { log_error("RK_MPI_MB_Handle2Fd failed!"); + close(video_dev_fd); return -1; } - log_info("Converted memory block to file descriptor for buffer %d\n", i); + log_info("Converted memory block to file descriptor for buffer %d", i); planes_buffer->m.fd = buf_fd; } @@ -425,15 +429,16 @@ void *run_video_stream(void *arg) buf.m.planes = &buffers[i].plane_buffer; if (ioctl(video_dev_fd, VIDIOC_QBUF, &buf) < 0) { - log_error("VIDIOC_QBUF failed"); + log_error("VIDIOC_QBUF failed: %s", strerror(errno)); + close(video_dev_fd); return errno; } - log_info("VIDIOC_QBUF successful for buffer %d\n", i); + log_info("VIDIOC_QBUF successful for buffer %d", i); } if (ioctl(video_dev_fd, VIDIOC_STREAMON, &type) < 0) { - log_error("VIDIOC_STREAMON failed"); + log_error("VIDIOC_STREAMON failed: %s", strerror(errno)); goto cleanup; } @@ -464,7 +469,7 @@ void *run_video_stream(void *arg) r = select(video_dev_fd + 1, &fds, NULL, NULL, &tv); if (r == 0) { - log_info("select timeout \n"); + log_info("select timeout"); break; } if (r == -1) @@ -483,10 +488,10 @@ void *run_video_stream(void *arg) buf.length = 1; if (ioctl(video_dev_fd, VIDIOC_DQBUF, &buf) < 0) { - log_error("VIDIOC_DQBUF failed"); + log_error("VIDIOC_DQBUF failed: %s", strerror(errno)); break; } - log_trace("got frame, bytesused = %d\n", tmp_plane.bytesused); + log_trace("got frame, bytesused = %d", tmp_plane.bytesused); memset(&stFrame, 0, sizeof(VIDEO_FRAME_INFO_S)); MB_BLK blk = RK_NULL; blk = RK_MPI_MMZ_Fd2Handle(tmp_plane.m.fd); @@ -504,16 +509,6 @@ void *run_video_stream(void *arg) stFrame.stVFrame.u32FrameFlag |= 0; stFrame.stVFrame.enCompressMode = COMPRESS_MODE_NONE; bool retried = false; - // if (num == 100) { - // RK_VOID *pData = RK_MPI_MB_Handle2VirAddr(stFrame.stVFrame.pMbBlk); - // if (pData) { - // size_t frameSize = tmp_plane.bytesused; // Use the actual size reported by the driver - // write_buffer_to_file(pData, frameSize, "/userdata/banana.raw"); - // log_trace("Frame 100 written to /userdata/banana.raw\n"); - // } else { - // log_trace("Failed to get virtual address for frame 100\n"); - // } - // } retry_send_frame: if (RK_MPI_VENC_SendFrame(VENC_CHANNEL, &stFrame, 2000) != RK_SUCCESS) { @@ -533,12 +528,13 @@ void *run_video_stream(void *arg) num++; if (ioctl(video_dev_fd, VIDIOC_QBUF, &buf) < 0) - log_error("failture VIDIOC_QBUF\n"); + log_error("failure VIDIOC_QBUF: %s", strerror(errno)); } cleanup: + log_info("cleaning up video capture device %s", VIDEO_DEV); if (ioctl(video_dev_fd, VIDIOC_STREAMOFF, &type) < 0) { - log_error("VIDIOC_STREAMOFF failed"); + log_error("VIDIOC_STREAMOFF failed: %s", strerror(errno)); } venc_stop(); @@ -551,9 +547,11 @@ void *run_video_stream(void *arg) } } + log_info("closing video capture device %s", VIDEO_DEV); close(video_dev_fd); } + log_info("video stream thread exiting"); return NULL; } @@ -561,41 +559,26 @@ void video_shutdown() { if (should_exit == true) { - log_info("shutting down in progress already\n"); + log_info("shutting down in progress already"); return; } video_stop_streaming(); - // if (buffers != NULL) { - // for (int i = 0; i < input_buffer_count; i++) { - // if ((buffers + i)->mb_blk != NULL) { - // RK_MPI_MB_ReleaseMB((buffers + i)->mb_blk); - // } - // free((buffers + i)->planes_buffer); - // } - // free(buffers); - // } + should_exit = true; if (sub_dev_fd > 0) { shutdown(sub_dev_fd, SHUT_RDWR); - // close(sub_dev_fd); - log_info("Closed sub_dev_fd\n"); + log_info("Closed sub_dev_fd"); } if (memPool != MB_INVALID_POOLID) { RK_MPI_MB_DestroyPool(memPool); } - log_info("Destroyed memory pool\n"); + log_info("Destroyed memory pool"); pthread_mutex_destroy(&streaming_mutex); - log_info("Destroyed streaming mutex\n"); - // if (format_thread != NULL) { - // pthread_join(*format_thread, NULL); - // free(format_thread); - // format_thread = NULL; - // } - // printf("Joined format detection thread\n"); + log_info("Destroyed streaming mutex"); } @@ -605,14 +588,31 @@ void video_start_streaming() if (streaming_thread != NULL) { log_warn("video streaming already started"); - pthread_mutex_unlock(&streaming_mutex); - return; + goto cleanup; } - streaming_thread = malloc(sizeof(pthread_t)); - assert(streaming_thread != NULL); + + pthread_t *new_thread = malloc(sizeof(pthread_t)); + if (new_thread == NULL) + { + log_error("Failed to allocate memory for streaming thread"); + goto cleanup; + } + streaming_flag = true; - pthread_create(streaming_thread, NULL, run_video_stream, NULL); + int result = pthread_create(new_thread, NULL, run_video_stream, NULL); + if (result != 0) + { + log_error("Failed to create streaming thread: %s", strerror(result)); + streaming_flag = false; + free(new_thread); + goto cleanup; + } + + // Only set streaming_thread after successful creation, and before unlocking the mutex + streaming_thread = new_thread; +cleanup: pthread_mutex_unlock(&streaming_mutex); + return; } void video_stop_streaming() @@ -621,6 +621,10 @@ void video_stop_streaming() if (streaming_thread != NULL) { streaming_flag = false; + log_info("stopping video streaming"); + // wait 100ms for the thread to exit + usleep(1000000); + log_info("waiting for video streaming thread to exit"); pthread_join(*streaming_thread, NULL); free(streaming_thread); streaming_thread = NULL; @@ -640,7 +644,6 @@ void *run_detect_format(void *arg) if (ioctl(sub_dev_fd, VIDIOC_SUBSCRIBE_EVENT, &sub) == -1) { log_error("cannot subscribe to event"); - log_error("Cannot subscribe to event"); goto exit; } @@ -665,12 +668,12 @@ void *run_detect_format(void *arg) else if (errno == ERANGE) { // Timings were found, but they are out of range of the hardware capabilities. - log_warn("HDMI status: out of range\n"); + log_warn("HDMI status: out of range"); video_report_format(false, "out_of_range", 0, 0, 0); } else { - log_error("error VIDIOC_QUERY_DV_TIMINGS"); + log_error("error VIDIOC_QUERY_DV_TIMINGS: %s", strerror(errno)); sleep(1); continue; } @@ -706,7 +709,7 @@ void *run_detect_format(void *arg) memset(&ev, 0, sizeof(ev)); if (ioctl(sub_dev_fd, VIDIOC_DQEVENT, &ev) != 0) { - log_error("failed to VIDIOC_DQEVENT"); + log_error("failed to VIDIOC_DQEVENT: %s", strerror(errno)); break; } log_info("New event of type %u", ev.type); diff --git a/internal/native/cgo/video.h b/internal/native/cgo/video.h index e5e1d1a9..e9309be4 100644 --- a/internal/native/cgo/video.h +++ b/internal/native/cgo/video.h @@ -1,13 +1,48 @@ #ifndef VIDEO_DAEMON_VIDEO_H #define VIDEO_DAEMON_VIDEO_H +/** + * @brief Initialize the video subsystem + * + * @return int 0 on success, -1 on failure + */ int video_init(); + +/** + * @brief Shutdown the video subsystem + */ void video_shutdown(); + +/** + * @brief Run the detect format thread + * + * @param arg The argument to pass to the thread + * @return void* The result of the thread + */ void *run_detect_format(void *arg); + +/** + * @brief Start the video streaming + */ void video_start_streaming(); + +/** + * @brief Stop the video streaming + */ void video_stop_streaming(); +/** + * @brief Set the quality factor of the video + * + * @param factor The quality factor to set + */ void video_set_quality_factor(float factor); + +/** + * @brief Get the quality factor of the video + * + * @return float The quality factor of the video + */ float video_get_quality_factor(); #endif //VIDEO_DAEMON_VIDEO_H diff --git a/internal/native/eez/jetkvm.eez-project b/internal/native/eez/jetkvm.eez-project index 465cdea9..efe179b6 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}" + "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}" }, { "objID": "1dbd1b7e-7270-47f0-ee02-e80bdae287cf", @@ -6452,7 +6452,7 @@ }, "timeline": [], "eventHandlers": [], - "identifier": "AboutHeaderContainer_1", + "identifier": "ResetConfigHeader", "leftUnit": "px", "topUnit": "px", "widthUnit": "%", @@ -6634,7 +6634,7 @@ }, "timeline": [], "eventHandlers": [], - "identifier": "AboutItemsContainer_1", + "identifier": "ResetConfigContainer", "leftUnit": "px", "topUnit": "px", "widthUnit": "%", @@ -6679,7 +6679,7 @@ }, "timeline": [], "eventHandlers": [], - "identifier": "systemVersionContainer_1", + "identifier": "ResetConfigLabel", "leftUnit": "px", "topUnit": "px", "widthUnit": "%", @@ -6702,7 +6702,7 @@ }, "timeline": [], "eventHandlers": [], - "identifier": "systemVersion_1", + "identifier": "", "leftUnit": "px", "topUnit": "px", "widthUnit": "%", @@ -6766,6 +6766,97 @@ "group": "", "groupIndex": 0 }, + { + "objID": "cbac637a-7b3f-450b-c962-2ceca07a0a43", + "type": "LVGLContainerWidget", + "left": 0, + "top": 0, + "width": 100, + "height": 80, + "customInputs": [], + "customOutputs": [], + "style": { + "objID": "5c719542-cd7f-4b66-c0b4-3b2812132e3f", + "useStyle": "default", + "conditionalStyles": [], + "childStyles": [] + }, + "hiddenInEditor": true, + "timeline": [], + "eventHandlers": [], + "identifier": "ResetConfigSpinner", + "leftUnit": "px", + "topUnit": "px", + "widthUnit": "%", + "heightUnit": "content", + "children": [ + { + "objID": "abf38156-7bc2-4c4e-93fc-e08d9ee999e7", + "type": "LVGLSpinnerWidget", + "left": 0, + "top": 0, + "width": 80, + "height": 80, + "customInputs": [], + "customOutputs": [], + "style": { + "objID": "8f4f24d3-5bd6-433f-bac5-e15c05820742", + "useStyle": "default", + "conditionalStyles": [], + "childStyles": [] + }, + "hiddenInEditor": true, + "timeline": [], + "eventHandlers": [], + "leftUnit": "px", + "topUnit": "px", + "widthUnit": "px", + "heightUnit": "px", + "children": [], + "widgetFlags": "CLICK_FOCUSABLE|GESTURE_BUBBLE|PRESS_LOCK|SCROLL_ELASTIC|SCROLL_MOMENTUM|SCROLL_WITH_ARROW|SNAPPABLE", + "hiddenFlagType": "literal", + "clickableFlagType": "literal", + "flagScrollbarMode": "", + "flagScrollDirection": "", + "scrollSnapX": "", + "scrollSnapY": "", + "checkedStateType": "literal", + "disabledStateType": "literal", + "states": "", + "localStyles": { + "objID": "e8d49d0f-a7e9-44df-afa0-db6cde72f306" + }, + "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": "46a10ecd-c85a-4dfa-a7a8-412a90c09ee1", + "definition": { + "MAIN": { + "DEFAULT": { + "flex_main_place": "CENTER", + "flex_cross_place": "CENTER", + "flex_track_place": "CENTER" + } + } + } + }, + "group": "", + "groupIndex": 0 + }, { "objID": "e3c027f5-a51d-49ee-c7d6-74eca94fbf6a", "type": "LVGLContainerWidget", @@ -6781,9 +6872,10 @@ "conditionalStyles": [], "childStyles": [] }, + "hiddenInEditor": false, "timeline": [], "eventHandlers": [], - "identifier": "appVersionContainer_1", + "identifier": "ResetConfigButton", "leftUnit": "px", "topUnit": "px", "widthUnit": "%", @@ -6804,6 +6896,7 @@ "conditionalStyles": [], "childStyles": [] }, + "hiddenInEditor": false, "timeline": [], "eventHandlers": [ { @@ -6834,6 +6927,7 @@ "conditionalStyles": [], "childStyles": [] }, + "hiddenInEditor": false, "timeline": [], "eventHandlers": [], "leftUnit": "px", diff --git a/internal/native/eez/src/ui/actions.c b/internal/native/eez/src/ui/actions.c index bfa33da1..257d4505 100644 --- a/internal/native/eez/src/ui/actions.c +++ b/internal/native/eez/src/ui/actions.c @@ -49,4 +49,29 @@ void action_home_screen_gesture(lv_event_t * e) { 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 + +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); + } + 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) { + lv_obj_add_flag(objects.reset_config_button, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(objects.reset_config_spinner, LV_OBJ_FLAG_HIDDEN); + } + } } \ No newline at end of file diff --git a/internal/native/eez/src/ui/screens.c b/internal/native/eez/src/ui/screens.c index bead8c86..6844b32a 100644 --- a/internal/native/eez/src/ui/screens.c +++ b/internal/native/eez/src/ui/screens.c @@ -1744,9 +1744,9 @@ void create_screen_reset_config_screen() { { lv_obj_t *parent_obj = obj; { - // AboutHeaderContainer_1 + // ResetConfigHeader lv_obj_t *obj = lv_obj_create(parent_obj); - objects.about_header_container_1 = obj; + objects.reset_config_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); @@ -1785,9 +1785,9 @@ void create_screen_reset_config_screen() { } } { - // AboutItemsContainer_1 + // ResetConfigContainer lv_obj_t *obj = lv_obj_create(parent_obj); - objects.about_items_container_1 = obj; + objects.reset_config_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); @@ -1819,9 +1819,9 @@ void create_screen_reset_config_screen() { { lv_obj_t *parent_obj = obj; { - // systemVersionContainer_1 + // ResetConfigLabel lv_obj_t *obj = lv_obj_create(parent_obj); - objects.system_version_container_1 = obj; + objects.reset_config_label = 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,9 +1836,7 @@ void create_screen_reset_config_screen() { { lv_obj_t *parent_obj = obj; { - // systemVersion_1 lv_obj_t *obj = lv_label_create(parent_obj); - objects.system_version_1 = 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); @@ -1848,9 +1846,37 @@ void create_screen_reset_config_screen() { } } { - // appVersionContainer_1 + // ResetConfigSpinner lv_obj_t *obj = lv_obj_create(parent_obj); - objects.app_version_container_1 = obj; + objects.reset_config_spinner = 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_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); + lv_obj_set_style_flex_track_place(obj, LV_FLEX_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT); + { + lv_obj_t *parent_obj = obj; + { + lv_obj_t *obj = lv_spinner_create(parent_obj); + lv_obj_set_pos(obj, 0, 0); + lv_obj_set_size(obj, 80, 80); + lv_spinner_set_anim_params(obj, 1000, 60); + } + } + } + { + // ResetConfigButton + lv_obj_t *obj = lv_obj_create(parent_obj); + objects.reset_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); diff --git a/internal/native/eez/src/ui/screens.h b/internal/native/eez/src/ui/screens.h index 711ae743..3ae5941f 100644 --- a/internal/native/eez/src/ui/screens.h +++ b/internal/native/eez/src/ui/screens.h @@ -84,11 +84,11 @@ typedef struct _objects_t { lv_obj_t *app_version_1; lv_obj_t *cloud_domain_container; lv_obj_t *cloud_domain; - lv_obj_t *about_header_container_1; - lv_obj_t *about_items_container_1; - lv_obj_t *system_version_container_1; - lv_obj_t *system_version_1; - lv_obj_t *app_version_container_1; + lv_obj_t *reset_config_header; + lv_obj_t *reset_config_container; + lv_obj_t *reset_config_label; + lv_obj_t *reset_config_spinner; + lv_obj_t *reset_config_button; lv_obj_t *obj0; } objects_t; diff --git a/internal/native/lib/libjknative.a b/internal/native/lib/libjknative.a index 50210510856a76fee19392e5d8efe4f33fda1dd0..ca20b69dfee19f83392835162f9c0917caecf587 100644 GIT binary patch delta 42567 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 shW!(o@Qj$B&>+}9b%7 delta 35490 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