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 50210510..ca20b69d 100644 Binary files a/internal/native/lib/libjknative.a and b/internal/native/lib/libjknative.a differ