#include #include #include #include #include #include #include #include #include #include "video.h" #include "screen.h" #include "edid.h" #include "ctrl.h" #include #include "ui/vars.h" #include "log.h" #include "log_handler.h" jetkvm_video_state_t state; jetkvm_video_state_handler_t *video_state_handler = NULL; jetkvm_video_handler_t *video_handler = NULL; void jetkvm_set_log_handler(jetkvm_log_handler_t *handler) { log_set_handler(handler); } void jetkvm_set_video_handler(jetkvm_video_handler_t *handler) { video_handler = handler; } void video_report_format(bool ready, const char *error, u_int16_t width, u_int16_t height, double frame_per_second) { state.ready = ready; state.error = error; state.width = width; state.height = height; state.frame_per_second = frame_per_second; if (video_state_handler != NULL) { (*video_state_handler)(&state); } } int video_send_frame(const uint8_t *frame, ssize_t len) { if (video_handler != NULL) { (*video_handler)(frame, len); } else { log_error("video handler is not set"); } return 0; } /** * @brief Convert a hexadecimal string to an array of uint8_t bytes * * @param hex_str The input hexadecimal string * @param bytes The output byte array (must be pre-allocated) * @param max_len The maximum number of bytes that can be stored in the output array * @return int The number of bytes converted, or -1 on error */ int hex_to_bytes(const char *hex_str, uint8_t *bytes, size_t max_len) { size_t hex_len = strnlen(hex_str, 4096); if (hex_len % 2 != 0 || hex_len / 2 > max_len) { return -1; // Invalid input length or insufficient output buffer } for (size_t i = 0; i < hex_len; i += 2) { char byte_str[3] = {hex_str[i], hex_str[i + 1], '\0'}; char *end_ptr; long value = strtol(byte_str, &end_ptr, 16); if (*end_ptr != '\0' || value < 0 || value > 255) { return -1; // Invalid hexadecimal value } bytes[i / 2] = (uint8_t)value; } return hex_len / 2; } /** * @brief Convert an array of uint8_t bytes to a hexadecimal string, user must free the returned string * * @param bytes The input byte array * @param len The number of bytes in the input array * @return char* The output hexadecimal string (dynamically allocated, must be freed by the caller), or NULL on error */ const char *bytes_to_hex(const uint8_t *bytes, size_t len) { if (bytes == NULL || len == 0) { return NULL; } char *hex_str = malloc(2 * len + 1); // Each byte becomes 2 hex chars, plus null terminator if (hex_str == NULL) { return NULL; // Memory allocation failed } for (size_t i = 0; i < len; i++) { snprintf(hex_str + (2 * i), 3, "%02x", bytes[i]); } hex_str[2 * len] = '\0'; // Ensure null termination return hex_str; } lv_obj_flag_t str_to_lv_obj_flag(const char *flag) { if (strcmp(flag, "LV_OBJ_FLAG_HIDDEN") == 0) { return LV_OBJ_FLAG_HIDDEN; } else if (strcmp(flag, "LV_OBJ_FLAG_CLICKABLE") == 0) { return LV_OBJ_FLAG_CLICKABLE; } else if (strcmp(flag, "LV_OBJ_FLAG_SCROLLABLE") == 0) { return LV_OBJ_FLAG_SCROLLABLE; } else if (strcmp(flag, "LV_OBJ_FLAG_CLICK_FOCUSABLE") == 0) { return LV_OBJ_FLAG_CLICK_FOCUSABLE; } else if (strcmp(flag, "LV_OBJ_FLAG_SCROLL_ON_FOCUS") == 0) { return LV_OBJ_FLAG_SCROLL_ON_FOCUS; } else if (strcmp(flag, "LV_OBJ_FLAG_SCROLL_CHAIN") == 0) { return LV_OBJ_FLAG_SCROLL_CHAIN; } else if (strcmp(flag, "LV_OBJ_FLAG_PRESS_LOCK") == 0) { return LV_OBJ_FLAG_PRESS_LOCK; } else if (strcmp(flag, "LV_OBJ_FLAG_OVERFLOW_VISIBLE") == 0) { return LV_OBJ_FLAG_OVERFLOW_VISIBLE; } else { return 0; // Unknown flag } } void jetkvm_set_app_version(const char *version) { set_var_app_version(version); } void jetkvm_ui_init() { lvgl_init(); } void jetkvm_ui_tick() { lvgl_tick(); } void jetkvm_set_video_state_handler(jetkvm_video_state_handler_t *handler) { video_state_handler = handler; } void jetkvm_ui_set_rotation(u_int8_t rotation) { printf("setting rotation to %d\n", rotation); if (rotation == 90) { lv_disp_set_rotation(NULL, LV_DISP_ROT_90); } else if (rotation == 180) { lv_disp_set_rotation(NULL, LV_DISP_ROT_180); } else if (rotation == 270) { lv_disp_set_rotation(NULL, LV_DISP_ROT_270); } else { lv_disp_set_rotation(NULL, LV_DISP_ROT_NONE); } } const char *jetkvm_ui_get_current_screen() { return ui_get_current_screen(); } void jetkvm_ui_load_screen(const char *obj_name) { lv_obj_t *obj = ui_get_obj(obj_name); if (obj == NULL) { return; } if (lv_scr_act() != obj) { lv_scr_load(obj); } } int jetkvm_ui_set_text(const char *obj_name, const char *text) { lv_obj_t *obj = ui_get_obj(obj_name); if (obj == NULL) { return -1; } if (strcmp(lv_label_get_text(obj), text) == 0) { return 1; } lv_label_set_text(obj, text); return 0; } void jetkvm_ui_set_image(const char *obj_name, const char *image_name) { lv_obj_t *obj = ui_get_obj(obj_name); if (obj == NULL) { return; } lv_img_set_src(obj, image_name); } void jetkvm_ui_set_state(const char *obj_name, const char *state_name) { lv_obj_t *obj = ui_get_obj(obj_name); if (obj == NULL) { return; } lv_obj_add_state(obj, LV_STATE_USER_1); lv_state_t state_val = LV_STATE_DEFAULT; if (strcmp(state_name, "LV_STATE_USER_1") == 0) { state_val = LV_STATE_USER_1; } else if (strcmp(state_name, "LV_STATE_USER_2") == 0) { state_val = LV_STATE_USER_2; } else if (strcmp(state_name, "LV_STATE_USER_3") == 0) { state_val = LV_STATE_USER_3; } else if (strcmp(state_name, "LV_STATE_USER_4") == 0) { state_val = LV_STATE_USER_4; } else if (strcmp(state_name, "LV_STATE_DISABLED") == 0) { state_val = LV_STATE_DISABLED; } // TODO: use LV_STATE_USER_* once eez supports it lv_obj_clear_state(obj, LV_STATE_USER_1 | LV_STATE_USER_2 | LV_STATE_USER_3 | LV_STATE_USER_4 | LV_STATE_DISABLED); lv_obj_add_state(obj, state_val); } int jetkvm_ui_add_flag(const char *obj_name, const char *flag_name) { lv_obj_t *obj = ui_get_obj(obj_name); if (obj == NULL) { return -1; } lv_obj_flag_t flag_val = str_to_lv_obj_flag(flag_name); if (flag_val == 0) { return -2; } lv_obj_add_flag(obj, flag_val); return 0; } int jetkvm_ui_clear_flag(const char *obj_name, const char *flag_name) { lv_obj_t *obj = ui_get_obj(obj_name); if (obj == NULL) { return -1; } lv_obj_flag_t flag_val = str_to_lv_obj_flag(flag_name); if (flag_val == 0) { return -2; } lv_obj_clear_flag(obj, flag_val); return 0; } void jetkvm_ui_fade_in(const char *obj_name, u_int32_t duration) { lv_obj_t *obj = ui_get_obj(obj_name); if (obj == NULL) { return; } lv_obj_fade_in(obj, duration, 0); } void jetkvm_ui_fade_out(const char *obj_name, u_int32_t duration) { lv_obj_t *obj = ui_get_obj(obj_name); if (obj == NULL) { return; } lv_obj_fade_out(obj, duration, 0); } void jetkvm_ui_set_opacity(const char *obj_name, u_int8_t opacity) { lv_obj_t *obj = ui_get_obj(obj_name); if (obj == NULL) { return; } lv_obj_set_style_opa(obj, opacity, LV_PART_MAIN); } void jetkvm_video_start() { video_start_streaming(); } void jetkvm_video_stop() { video_stop_streaming(); } int jetkvm_video_set_quality_factor(float quality_factor) { if (quality_factor < 0 || quality_factor > 1) { return -1; } video_set_quality_factor(quality_factor); return 0; } float jetkvm_video_get_quality_factor() { return video_get_quality_factor(); } int jetkvm_video_set_edid(const char *edid_hex) { uint8_t edid[256]; int edid_len = hex_to_bytes(edid_hex, edid, 256); if (edid_len < 0) { return -1; } return set_edid(edid, edid_len); } char *jetkvm_video_get_edid_hex() { uint8_t edid[256]; int edid_len = get_edid(edid, 256); if (edid_len < 0) { return NULL; } return bytes_to_hex(edid, edid_len); } jetkvm_video_state_t *jetkvm_video_get_status() { return &state; } int jetkvm_video_init() { return video_init(); } void jetkvm_video_shutdown() { video_shutdown(); }