From 47d471f0998bb8fa7e4067e82d4fc2d6d2f3d514 Mon Sep 17 00:00:00 2001 From: Liu Date: Fri, 9 Feb 2024 20:30:23 +0800 Subject: [PATCH] feat(ui): improve the event and loading logic of image loader --- lib/ui/include/ui/image.h | 95 +++++- lib/ui/src/ui_image.c | 326 +++++++++++------- lib/ui/src/ui_widget_style.c | 43 ++- src/lcui_ui.c | 622 +++++++++++++++++++---------------- 4 files changed, 654 insertions(+), 432 deletions(-) diff --git a/lib/ui/include/ui/image.h b/lib/ui/include/ui/image.h index 5422c85ba..39ea15c5c 100644 --- a/lib/ui/include/ui/image.h +++ b/lib/ui/include/ui/image.h @@ -15,11 +15,36 @@ #include "common.h" #include "types.h" -#define ui_image_on_event ui_image_add_event_listener -#define ui_image_off_event ui_image_remove_event_listener - -typedef struct ui_image_t ui_image_t; -typedef void (*ui_image_event_handler_t)(ui_image_t *, void *); +#define ui_image_on ui_image_add_event_listener +#define ui_image_off ui_image_remove_event_listener + +typedef enum ui_image_state_t { + UI_IMAGE_STATE_PENDING, + UI_IMAGE_STATE_LOADING, + UI_IMAGE_STATE_COMPLETE +} ui_image_state_t; + +typedef enum ui_image_event_type_t { + UI_IMAGE_EVENT_LOAD, + UI_IMAGE_EVENT_PROGRESS, + UI_IMAGE_EVENT_ERROR, +} ui_image_event_type_t; + +typedef struct ui_image_t { + pd_canvas_t data; + pd_error_t error; + ui_image_state_t state; + char *path; + float progress; +} ui_image_t; + +typedef struct ui_image_event_t { + ui_image_event_type_t type; + ui_image_t *image; + void *data; +} ui_image_event_t; + +typedef void (*ui_image_event_handler_t)(ui_image_event_t *); LIBUI_BEGIN_DECLS @@ -31,21 +56,59 @@ LIBUI_PUBLIC ui_image_t *ui_get_image(const char *path); **/ LIBUI_PUBLIC ui_image_t *ui_image_create(const char *path); -/** - * 获取已加载的图像数据 - */ -LIBUI_PUBLIC pd_canvas_t *ui_image_get_data(ui_image_t *image); - -LIBUI_PUBLIC const char *ui_image_get_path(ui_image_t *image); - -LIBUI_PUBLIC bool ui_image_is_loaded(ui_image_t *image); - LIBUI_PUBLIC void ui_image_destroy(ui_image_t *image); LIBUI_PUBLIC int ui_image_add_event_listener(ui_image_t *image, + ui_image_event_type_t type, ui_image_event_handler_t handler, void *data); LIBUI_PUBLIC int ui_image_remove_event_listener( - ui_image_t *image, ui_image_event_handler_t handler, void *data); + ui_image_t *image, ui_image_event_type_t type, + ui_image_event_handler_t handler, void *data); + +LIBUI_INLINE int ui_image_on_load(ui_image_t *image, + ui_image_event_handler_t handler, void *data) +{ + return ui_image_add_event_listener(image, UI_IMAGE_EVENT_LOAD, handler, + data); +} + +LIBUI_INLINE int ui_image_on_error(ui_image_t *image, + ui_image_event_handler_t handler, void *data) +{ + return ui_image_add_event_listener(image, UI_IMAGE_EVENT_ERROR, handler, + data); +} + +LIBUI_INLINE int ui_image_on_progress(ui_image_t *image, + ui_image_event_handler_t handler, + void *data) +{ + return ui_image_add_event_listener(image, UI_IMAGE_EVENT_PROGRESS, + handler, data); +} + +LIBUI_INLINE int ui_image_off_load(ui_image_t *image, + ui_image_event_handler_t handler, void *data) +{ + return ui_image_remove_event_listener(image, UI_IMAGE_EVENT_LOAD, + handler, data); +} + +LIBUI_INLINE int ui_image_off_error(ui_image_t *image, + ui_image_event_handler_t handler, + void *data) +{ + return ui_image_remove_event_listener(image, UI_IMAGE_EVENT_ERROR, + handler, data); +} + +LIBUI_INLINE int ui_image_off_progress(ui_image_t *image, + ui_image_event_handler_t handler, + void *data) +{ + return ui_image_remove_event_listener(image, UI_IMAGE_EVENT_PROGRESS, + handler, data); +} /** * 为所有 UIImage 对象加载数据 @@ -65,6 +128,8 @@ LIBUI_PUBLIC void ui_process_image_events(void); */ LIBUI_PUBLIC void ui_clear_images(void); +LIBUI_PUBLIC void ui_set_image_loader_callback(void (*callback)(ui_image_t *)); + LIBUI_END_DECLS #endif diff --git a/lib/ui/src/ui_image.c b/lib/ui/src/ui_image.c index 66f389a77..fb5a5ac98 100644 --- a/lib/ui/src/ui_image.c +++ b/lib/ui/src/ui_image.c @@ -16,90 +16,88 @@ #include #include +#define UI_IMAGE_PROGRESS_EVENT_INTERVAL 100 + typedef struct ui_image_event_listener_t { - list_node_t node; + ui_image_event_type_t type; void *data; ui_image_event_handler_t handler; } ui_image_event_listener_t; -struct ui_image_t { - pd_canvas_t data; - bool loaded; - int error; - char *path; +typedef struct ui_image_source_t { + ui_image_t image; size_t refs_count; + pd_image_reader_t *reader; /** list_t */ list_t listeners; -}; + list_node_t node; +} ui_image_source_t; -typedef struct ui_image_event_t { +typedef struct ui_image_mutation_t { + ui_image_event_type_t type; + ui_image_source_t *src; list_node_t node; - void *data; - ui_image_t *image; - ui_image_event_handler_t handler; -} ui_image_event_t; +} ui_image_mutation_t; typedef struct ui_image_loader_t { - /** dict_t */ + int64_t progress_tick_time; + void (*callback)(ui_image_t *); + + /** dict_t */ dict_t *cache; dict_type_t dict_type; - /** list_t */ - list_t events; + /** list_t */ + list_t mutations; - /** list_t */ + /** list_t */ list_t images; + + bool changed; } ui_image_loader_t; static ui_image_loader_t ui_image_loader; -static void ui_image_force_destroy(void *privdata, void *data) +static void ui_image_force_destroy(ui_image_source_t *src) { - ui_image_t *image = data; + list_destroy_without_node(&src->listeners, free); + pd_image_reader_destroy(src->reader); + pd_canvas_destroy(&src->image.data); + free(src->image.path); + src->reader = NULL; + src->refs_count = 0; + src->image.path = NULL; + free(src); +} - pd_canvas_destroy(&image->data); - free(image->path); - image->refs_count = 0; - list_destroy_without_node(&image->listeners, free); - image->path = NULL; - free(image); +static void ui_image_loader_add_mutation(ui_image_source_t *src, + ui_event_type_t type) +{ + ui_image_mutation_t *mutation; + + mutation = malloc(sizeof(ui_image_mutation_t)); + mutation->type = type; + mutation->src = src; + mutation->node.data = mutation; + list_append_node(&ui_image_loader.mutations, &mutation->node); } -static void ui_image_dispatch_events(ui_image_t *image) +static void ui_image_loader_process_mutation(ui_image_mutation_t *mutation) { - list_t events, listeners; - list_node_t *node; - ui_image_event_t *e; + list_node_t *node, *next; ui_image_event_listener_t *listener; + ui_image_event_t e = { .image = &mutation->src->image, + .type = mutation->type }; - list_create(&events); - list_create(&listeners); - list_concat(&listeners, &image->listeners); - for (list_each(node, &listeners)) { + for (node = list_get_first_node(&mutation->src->listeners); node; + node = next) { + next = node->next; listener = node->data; - e = malloc(sizeof(ui_image_event_t)); - e->image = NULL; - if (pd_canvas_is_valid(&image->data)) { - e->image = image; + if (listener->type == mutation->type) { + e.image = &mutation->src->image; + e.data = listener->data; + listener->handler(&e); } - e->data = listener->data; - e->handler = listener->handler; - e->node.data = e; - list_append_node(&events, &e->node); - } - list_destroy_without_node(&listeners, free); - list_concat(&ui_image_loader.events, &events); -} - -static void ui_image_load(ui_image_t *image) -{ - if (image->loaded) { - return; - } - pd_canvas_init(&image->data); - image->error = pd_read_image_from_file(image->path, &image->data); - if (image->error == PD_OK) { - image->loaded = true; } } @@ -110,125 +108,206 @@ ui_image_t *ui_get_image(const char *path) ui_image_t *ui_image_create(const char *path) { - ui_image_t *image; - - image = ui_get_image(path); - if (image) { - image->refs_count++; - return image; + ui_image_source_t *src; + + src = (ui_image_source_t *)ui_get_image(path); + if (src) { + src->refs_count++; + list_unlink(&ui_image_loader.images, &src->node); + } else { + src = malloc(sizeof(ui_image_source_t)); + if (!src) { + return NULL; + } + src->refs_count = 1; + src->image.state = UI_IMAGE_STATE_PENDING; + src->image.path = strdup2(path); + src->node.data = src; + pd_canvas_init(&src->image.data); + list_create(&src->listeners); + dict_add(ui_image_loader.cache, src->image.path, src); } - image = malloc(sizeof(ui_image_t)); - if (!image) { - return NULL; + list_insert_node(&ui_image_loader.images, 0, &src->node); + ui_image_loader.changed = true; + ui_image_loader.progress_tick_time = get_time_ms(); + if (ui_image_loader.callback) { + ui_image_loader.callback(&src->image); } - image->loaded = false; - image->refs_count = 1; - image->path = strdup2(path); - pd_canvas_init(&image->data); - list_create(&image->listeners); - list_append(&ui_image_loader.images, image); - dict_add(ui_image_loader.cache, image->path, image); - return image; -} - -pd_canvas_t *ui_image_get_data(ui_image_t *image) -{ - return &image->data; -} - -const char *ui_image_get_path(ui_image_t *image) -{ - return image->path; -} - -bool ui_image_is_loaded(ui_image_t *image) -{ - return image != NULL && image->loaded; + return &src->image; } void ui_image_destroy(ui_image_t *image) { - assert(image->refs_count > 0); - image->refs_count--; + assert(((ui_image_source_t *)image)->refs_count > 0); + ((ui_image_source_t *)image)->refs_count--; } -int ui_image_add_event_listener(ui_image_t *image, +int ui_image_add_event_listener(ui_image_t *image, ui_image_event_type_t type, ui_image_event_handler_t handler, void *data) { + ui_image_event_t ev = { .type = type, .image = image, .data = data }; ui_image_event_listener_t *listener; - if (image->loaded) { - handler(image, data); + if (image->state == UI_IMAGE_STATE_COMPLETE) { + if ((image->error == PD_OK && type == UI_IMAGE_EVENT_LOAD) || + (image->error != PD_OK && type == UI_IMAGE_EVENT_ERROR)) { + handler(&ev); + } return 0; } listener = malloc(sizeof(ui_image_event_listener_t)); if (listener == NULL) { return -1; } + listener->type = type; listener->handler = handler; listener->data = data; - listener->node.data = listener; - list_append_node(&image->listeners, &listener->node); + list_append(&((ui_image_source_t *)image)->listeners, listener); return 0; } int ui_image_remove_event_listener(ui_image_t *image, + ui_image_event_type_t type, ui_image_event_handler_t handler, void *data) { - list_node_t *node; + list_node_t *node, *next; + ui_image_source_t *src = (ui_image_source_t *)image; ui_image_event_listener_t *listener; - for (list_each(node, &image->listeners)) { + for (node = list_get_first_node(&src->listeners); node; + node = next) { + next = node->next; listener = node->data; - if (listener->handler == handler && listener->data == data) { - list_unlink(&image->listeners, node); + if (listener->type == type && listener->handler == handler && + listener->data == data) { + list_unlink(&src->listeners, node); free(listener); - return 0; } } return -1; } +static bool ui_image_loader_create_reader(ui_image_source_t *src) +{ + src->reader = pd_image_reader_create_from_file(src->image.path); + if (!src->reader) { + src->image.error = PD_ERROR_NOT_FOUND; + return false; + } + src->image.error = pd_image_reader_read_header(src->reader); + if (src->image.error != PD_OK) { + return false; + } + src->image.error = + pd_image_reader_create_buffer(src->reader, &src->image.data); + if (src->image.error != PD_OK) { + return false; + } + return true; +} + +static void ui_image_loader_reject(ui_image_source_t *src) +{ + src->image.state = UI_IMAGE_STATE_COMPLETE; + ui_image_loader_add_mutation(src, UI_IMAGE_EVENT_ERROR); +} + +static void ui_image_loader_load(ui_image_source_t *src) +{ + if (src->image.state == UI_IMAGE_STATE_COMPLETE) { + return; + } + if (src->image.state == UI_IMAGE_STATE_PENDING) { + if (!ui_image_loader_create_reader(src)) { + ui_image_loader_reject(src); + return; + } + src->image.state = UI_IMAGE_STATE_LOADING; + pd_image_reader_start(src->reader); + } + if (setjmp(*pd_image_reader_jmpbuf(src->reader))) { + src->image.error = PD_ERROR_IMAGE_READING; + ui_image_loader_reject(src); + return; + } + while (src->image.state != UI_IMAGE_STATE_COMPLETE && + !ui_image_loader.changed) { + pd_image_reader_read_row(src->reader, &src->image.data); + src->image.progress = 100.f * src->reader->read_row_index / + src->reader->header.height * + (src->reader->pass + 1) / + src->reader->passes; + if (src->reader->read_row_index >= src->reader->header.height) { + src->reader->read_row_index = 0; + src->reader->pass++; + } + if (src->reader->pass >= src->reader->passes) { + pd_image_reader_finish(src->reader); + src->image.state = UI_IMAGE_STATE_COMPLETE; + src->image.progress = 100; + ui_image_loader_add_mutation(src, UI_IMAGE_EVENT_LOAD); + } + } +} + void ui_load_images(void) { - list_t list; list_node_t *node; - list_create(&list); - list_concat(&list, &ui_image_loader.images); - for (list_each(node, &list)) { - ui_image_load(node->data); - ui_image_dispatch_events(node->data); + for (list_each(node, &ui_image_loader.images)) { + if (ui_image_loader.changed) { + ui_image_loader.changed = false; + node = list_get_first_node(&ui_image_loader.images); + } + ui_image_loader_load(node->data); } } void ui_process_image_events(void) { + int64_t now; list_node_t *node; - list_t events; - - list_create(&events); - list_concat(&events, &ui_image_loader.events); - for (list_each(node, &events)) { - ui_image_event_t *e = node->data; - e->handler(e->image, e->data); + list_t mutations; + ui_image_source_t *src; + ui_image_mutation_t mutation = { .type = UI_IMAGE_EVENT_PROGRESS }; + + now = get_time_ms(); + list_create(&mutations); + list_concat(&mutations, &ui_image_loader.mutations); + for (list_each(node, &mutations)) { + ui_image_loader_process_mutation(node->data); + } + list_destroy_without_node(&mutations, free); + if (now < ui_image_loader.progress_tick_time) { + return; + } + ui_image_loader.progress_tick_time = + now + UI_IMAGE_PROGRESS_EVENT_INTERVAL; + for (list_each(node, &ui_image_loader.images)) { + src = node->data; + if (src->image.state == UI_IMAGE_STATE_LOADING) { + mutation.src = src; + ui_image_loader_process_mutation(&mutation); + } } - list_destroy_without_node(&events, free); } void ui_clear_images(void) { - ui_image_t *image; + ui_image_source_t *src; dict_entry_t *entry; dict_iterator_t *iter; iter = dict_get_safe_iterator(ui_image_loader.cache); while ((entry = dict_next(iter))) { - image = dict_get_val(entry); - if (image->refs_count < 1) { - logger_debug("[ui-image] free unused image: %s\n", - image->path); - dict_delete(ui_image_loader.cache, image->path); + src = dict_get_val(entry); + if (src->refs_count < 1) { + logger_debug( + "[ui-image-loader] free unused image: %s\n", + src->image.path); + dict_delete(ui_image_loader.cache, src->image.path); + list_unlink(&ui_image_loader.images, &src->node); + ui_image_force_destroy(src); } } dict_destroy_iterator(iter); @@ -237,14 +316,21 @@ void ui_clear_images(void) void ui_init_image_loader(void) { dict_init_string_key_type(&ui_image_loader.dict_type); - ui_image_loader.dict_type.val_destructor = ui_image_force_destroy; ui_image_loader.cache = dict_create(&ui_image_loader.dict_type, NULL); + ui_image_loader.progress_tick_time = get_time_ms(); list_create(&ui_image_loader.images); } void ui_destroy_image_loader(void) { - list_destroy(&ui_image_loader.images, NULL); + list_destroy_without_node( + &ui_image_loader.images, + (list_item_destructor_t)ui_image_force_destroy); dict_destroy(ui_image_loader.cache); ui_image_loader.cache = NULL; } + +void ui_set_image_loader_callback(void (*callback)(ui_image_t *)) +{ + ui_image_loader.callback = callback; +} diff --git a/lib/ui/src/ui_widget_style.c b/lib/ui/src/ui_widget_style.c index 2a859d4ca..151221f8e 100644 --- a/lib/ui/src/ui_widget_style.c +++ b/lib/ui/src/ui_widget_style.c @@ -1,4 +1,4 @@ -/* +/* * lib/ui/src/ui_widget_style.c * * Copyright (c) 2023-2024, Liu Chao All rights reserved. @@ -254,16 +254,13 @@ void ui_widget_set_style_keyword_value(ui_widget_t *w, int key, ui_widget_set_style(w, key, &v); } -static void ui_widget_on_image_load(ui_image_t *loaded_image, void *data) +static void ui_widget_on_image_load(ui_image_event_t *e) { float scale; - ui_widget_t *w = data; - pd_canvas_t *img = (pd_canvas_t *)loaded_image; + ui_widget_t *w = e->data; + pd_canvas_t *img = &e->image->data; css_computed_style_t *s = &w->computed_style; - if (!loaded_image) { - return; - } CSS_COPY_LENGTH(s, &w->specified_style, background_width); CSS_COPY_LENGTH(s, &w->specified_style, background_height); CSS_COPY_LENGTH(s, &w->specified_style, background_position_x); @@ -333,6 +330,17 @@ static void ui_widget_on_image_load(ui_image_t *loaded_image, void *data) ui_widget_mark_dirty_rect(w, NULL, UI_BOX_TYPE_BORDER_BOX); } +static void ui_widget_on_image_load_start(ui_image_event_t *e) +{ + ui_widget_on_image_load(e); + ui_image_off_progress(e->image, ui_widget_on_image_load_start, e->data); +} + +static void ui_widget_on_image_progress(ui_image_event_t *e) +{ + ui_widget_mark_dirty_rect(e->data, NULL, UI_BOX_TYPE_GRAPH_BOX); +} + static void ui_widget_destroy_background_style(ui_widget_t *w) { ui_image_t *image; @@ -340,7 +348,11 @@ static void ui_widget_destroy_background_style(ui_widget_t *w) if (w->computed_style.background_image) { image = ui_get_image(w->computed_style.background_image); if (image) { - ui_image_off_event(image, ui_widget_on_image_load, w); + ui_image_off_load(image, ui_widget_on_image_load, w); + ui_image_off_progress(image, + ui_widget_on_image_load_start, w); + ui_image_off_progress(image, + ui_widget_on_image_progress, w); ui_image_destroy(image); } } @@ -349,6 +361,7 @@ static void ui_widget_destroy_background_style(ui_widget_t *w) void ui_widget_compute_style(ui_widget_t *w) { ui_image_t *image; + ui_image_event_t e; css_computed_style_t *s = &w->computed_style; if (w->parent) { @@ -357,12 +370,19 @@ void ui_widget_compute_style(ui_widget_t *w) } if (s->background_image) { image = ui_get_image(s->background_image); - ui_image_on_event(image, ui_widget_on_image_load, w); + if (image->state == UI_IMAGE_STATE_COMPLETE && + image->error == PD_OK) { + e.type = UI_IMAGE_EVENT_LOAD; + e.data = w; + e.image = image; + ui_widget_on_image_load(&e); + } } } void ui_widget_update_style(ui_widget_t *w) { + ui_image_t *image; css_computed_style_t *s = &w->specified_style; css_style_decl_t *style; @@ -380,7 +400,10 @@ void ui_widget_update_style(ui_widget_t *w) w->computed_style = *s; // 增加 UIImage 的引用次数,让它在部件销毁和背景图改变前一直可用 if (s->background_image) { - ui_image_create(s->background_image); + image = ui_image_create(s->background_image); + ui_image_on_load(image, ui_widget_on_image_load, w); + ui_image_on_progress(image, ui_widget_on_image_load_start, w); + ui_image_on_progress(image, ui_widget_on_image_progress, w); } ui_widget_compute_style(w); ui_widget_update_box_size(w); diff --git a/src/lcui_ui.c b/src/lcui_ui.c index ea6e0cb28..7de80e138 100644 --- a/src/lcui_ui.c +++ b/src/lcui_ui.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -21,280 +22,291 @@ #define DEFAULT_WINDOW_WIDTH 800 #define DEFAULT_WINDOW_HEIGHT 600 +typedef struct { + bool active; + thread_t thread; + thread_mutex_t mutex; + thread_cond_t cond; +} lcui_ui_image_loader_t; + static struct lcui_ui_t { - int image_loading_timer; - lcui_display_mode_t mode; - ui_mutation_observer_t *observer; + lcui_ui_image_loader_t image_loader; + lcui_display_mode_t mode; + ui_mutation_observer_t *observer; - /** list_t */ - list_t windows; + /** list_t */ + list_t windows; } lcui_ui; static void lcui_dispatch_ui_mouse_event(ui_event_type_t type, - app_event_t *app_evt) + app_event_t *app_evt) { - ui_event_t e = { 0 }; - float scale = ui_metrics.scale; + ui_event_t e = { 0 }; + float scale = ui_metrics.scale; - e.type = type; - e.mouse.y = (float)round(app_evt->mouse.y / scale); - e.mouse.x = (float)round(app_evt->mouse.x / scale); - ui_dispatch_event(&e); + e.type = type; + e.mouse.y = (float)round(app_evt->mouse.y / scale); + e.mouse.x = (float)round(app_evt->mouse.x / scale); + ui_dispatch_event(&e); } static void lcui_dispatch_ui_keyboard_event(ui_event_type_t type, - app_event_t *app_evt) + app_event_t *app_evt) { - ui_event_t e = { 0 }; - - e.type = type; - e.key.code = app_evt->key.code; - e.key.is_composing = app_evt->key.is_composing; - e.key.alt_key = app_evt->key.alt_key; - e.key.shift_key = app_evt->key.shift_key; - e.key.ctrl_key = app_evt->key.ctrl_key; - e.key.meta_key = app_evt->key.meta_key; - ui_dispatch_event(&e); + ui_event_t e = { 0 }; + + e.type = type; + e.key.code = app_evt->key.code; + e.key.is_composing = app_evt->key.is_composing; + e.key.alt_key = app_evt->key.alt_key; + e.key.shift_key = app_evt->key.shift_key; + e.key.ctrl_key = app_evt->key.ctrl_key; + e.key.meta_key = app_evt->key.meta_key; + ui_dispatch_event(&e); } static void lcui_dispatch_ui_touch_event(app_touch_event_t *touch) { - size_t i; - float scale; - ui_event_t e = { 0 }; - - scale = ui_metrics.scale; - e.type = UI_EVENT_TOUCH; - e.touch.n_points = touch->n_points; - e.touch.points = malloc(sizeof(ui_touch_point_t) * e.touch.n_points); - for (i = 0; i < e.touch.n_points; ++i) { - switch (touch->points[i].state) { - case APP_EVENT_TOUCHDOWN: - e.touch.points[i].state = UI_EVENT_TOUCHDOWN; - break; - case APP_EVENT_TOUCHUP: - e.touch.points[i].state = UI_EVENT_TOUCHUP; - break; - case APP_EVENT_TOUCHMOVE: - e.touch.points[i].state = UI_EVENT_TOUCHMOVE; - break; - default: - break; - } - e.touch.points[i].x = (float)round(touch->points[i].x / scale); - e.touch.points[i].y = (float)round(touch->points[i].y / scale); - } - ui_dispatch_event(&e); - ui_event_destroy(&e); + size_t i; + float scale; + ui_event_t e = { 0 }; + + scale = ui_metrics.scale; + e.type = UI_EVENT_TOUCH; + e.touch.n_points = touch->n_points; + e.touch.points = malloc(sizeof(ui_touch_point_t) * e.touch.n_points); + for (i = 0; i < e.touch.n_points; ++i) { + switch (touch->points[i].state) { + case APP_EVENT_TOUCHDOWN: + e.touch.points[i].state = UI_EVENT_TOUCHDOWN; + break; + case APP_EVENT_TOUCHUP: + e.touch.points[i].state = UI_EVENT_TOUCHUP; + break; + case APP_EVENT_TOUCHMOVE: + e.touch.points[i].state = UI_EVENT_TOUCHMOVE; + break; + default: + break; + } + e.touch.points[i].x = (float)round(touch->points[i].x / scale); + e.touch.points[i].y = (float)round(touch->points[i].y / scale); + } + ui_dispatch_event(&e); + ui_event_destroy(&e); } static void lcui_dispatch_ui_textinput_event(app_event_t *app_evt) { - ui_event_t e = { 0 }; + ui_event_t e = { 0 }; - e.type = UI_EVENT_TEXTINPUT; - e.text.length = app_evt->text.length; - e.text.text = wcsdup2(app_evt->text.text); - ui_dispatch_event(&e); - ui_event_destroy(&e); + e.type = UI_EVENT_TEXTINPUT; + e.text.length = app_evt->text.length; + e.text.text = wcsdup2(app_evt->text.text); + ui_dispatch_event(&e); + ui_event_destroy(&e); } static void lcui_dispatch_ui_wheel_event(app_wheel_event_t *wheel) { - ui_event_t e = { 0 }; + ui_event_t e = { 0 }; - // TODO: - e.type = UI_EVENT_WHEEL; - e.wheel.delta_mode = UI_WHEEL_DELTA_PIXEL; - e.wheel.delta_y = wheel->delta_y; - ui_dispatch_event(&e); + // TODO: + e.type = UI_EVENT_WHEEL; + e.wheel.delta_mode = UI_WHEEL_DELTA_PIXEL; + e.wheel.delta_y = wheel->delta_y; + ui_dispatch_event(&e); } void lcui_dispatch_ui_event(app_event_t *app_event) { - switch (app_event->type) { - case APP_EVENT_KEYDOWN: - lcui_dispatch_ui_keyboard_event(UI_EVENT_KEYDOWN, app_event); - break; - case APP_EVENT_KEYUP: - lcui_dispatch_ui_keyboard_event(UI_EVENT_KEYUP, app_event); - break; - case APP_EVENT_KEYPRESS: - lcui_dispatch_ui_keyboard_event(UI_EVENT_KEYPRESS, app_event); - break; - case APP_EVENT_MOUSEDOWN: - lcui_dispatch_ui_mouse_event(UI_EVENT_MOUSEDOWN, app_event); - break; - case APP_EVENT_MOUSEUP: - lcui_dispatch_ui_mouse_event(UI_EVENT_MOUSEUP, app_event); - break; - case APP_EVENT_MOUSEMOVE: - lcui_dispatch_ui_mouse_event(UI_EVENT_MOUSEMOVE, app_event); - break; - case APP_EVENT_TOUCH: - lcui_dispatch_ui_touch_event(&app_event->touch); - break; - case APP_EVENT_WHEEL: - lcui_dispatch_ui_wheel_event(&app_event->wheel); - break; - case APP_EVENT_COMPOSITION: - lcui_dispatch_ui_textinput_event(app_event); - break; - default: - break; - } + switch (app_event->type) { + case APP_EVENT_KEYDOWN: + lcui_dispatch_ui_keyboard_event(UI_EVENT_KEYDOWN, app_event); + break; + case APP_EVENT_KEYUP: + lcui_dispatch_ui_keyboard_event(UI_EVENT_KEYUP, app_event); + break; + case APP_EVENT_KEYPRESS: + lcui_dispatch_ui_keyboard_event(UI_EVENT_KEYPRESS, app_event); + break; + case APP_EVENT_MOUSEDOWN: + lcui_dispatch_ui_mouse_event(UI_EVENT_MOUSEDOWN, app_event); + break; + case APP_EVENT_MOUSEUP: + lcui_dispatch_ui_mouse_event(UI_EVENT_MOUSEUP, app_event); + break; + case APP_EVENT_MOUSEMOVE: + lcui_dispatch_ui_mouse_event(UI_EVENT_MOUSEMOVE, app_event); + break; + case APP_EVENT_TOUCH: + lcui_dispatch_ui_touch_event(&app_event->touch); + break; + case APP_EVENT_WHEEL: + lcui_dispatch_ui_wheel_event(&app_event->wheel); + break; + case APP_EVENT_COMPOSITION: + lcui_dispatch_ui_textinput_event(app_event); + break; + default: + break; + } } size_t lcui_render_ui(void) { - return ui_server_render(); + return ui_server_render(); } void lcui_update_ui(void) { - ui_process_image_events(); - ui_process_events(); - ui_update(); - ui_clear_images(); + ui_process_image_events(); + ui_process_events(); + ui_update(); + + thread_mutex_lock(&lcui_ui.image_loader.mutex); + ui_clear_images(); + thread_mutex_unlock(&lcui_ui.image_loader.mutex); } static void lcui_process_ui_mutation(ui_mutation_record_t *mutation) { - list_node_t *node; - - if (mutation->type != UI_MUTATION_RECORD_TYPE_CHILD_LIST || - lcui_ui.mode != LCUI_DISPLAY_MODE_SEAMLESS) { - return; - } - for (list_each(node, &mutation->removed_widgets)) { - ui_server_disconnect(node->data, NULL); - } - for (list_each(node, &mutation->added_widgets)) { - ui_server_connect(node->data, NULL); - } + list_node_t *node; + + if (mutation->type != UI_MUTATION_RECORD_TYPE_CHILD_LIST || + lcui_ui.mode != LCUI_DISPLAY_MODE_SEAMLESS) { + return; + } + for (list_each(node, &mutation->removed_widgets)) { + ui_server_disconnect(node->data, NULL); + } + for (list_each(node, &mutation->added_widgets)) { + ui_server_connect(node->data, NULL); + } } static void lcui_on_ui_mutation(ui_mutation_list_t *mutation_list, - ui_mutation_observer_t *observer, void *arg) + ui_mutation_observer_t *observer, void *arg) { - list_node_t *node; + list_node_t *node; - for (list_each(node, mutation_list)) { - lcui_process_ui_mutation(node->data); - } + for (list_each(node, mutation_list)) { + lcui_process_ui_mutation(node->data); + } } static void lcui_on_window_destroy(app_event_t *e, void *arg) { - list_node_t *node; - - for (list_each(node, &lcui_ui.windows)) { - if (node->data == e->window) { - list_delete_node(&lcui_ui.windows, node); - break; - } - } + list_node_t *node; + + for (list_each(node, &lcui_ui.windows)) { + if (node->data == e->window) { + list_delete_node(&lcui_ui.windows, node); + break; + } + } } static void lcui_close_window(void *arg) { - app_window_close(arg); + app_window_close(arg); } void lcui_set_ui_display_mode(lcui_display_mode_t mode) { - float scale; - app_window_t *wnd; - list_node_t *node; - ui_mutation_observer_init_t options = { 0 }; - - if (mode == LCUI_DISPLAY_MODE_DEFAULT) { - mode = LCUI_DISPLAY_MODE_WINDOWED; - } - if (lcui_ui.observer) { - ui_mutation_observer_disconnect(lcui_ui.observer); - ui_mutation_observer_destroy(lcui_ui.observer); - lcui_ui.observer = NULL; - } - list_destroy(&lcui_ui.windows, lcui_close_window); - switch (lcui_ui.mode) { - case LCUI_DISPLAY_MODE_FULLSCREEN: - case LCUI_DISPLAY_MODE_WINDOWED: - ui_server_disconnect(ui_root(), NULL); - break; - case LCUI_DISPLAY_MODE_SEAMLESS: - for (list_each(node, &ui_root()->children)) { - ui_server_disconnect(node->data, NULL); - } - default: - break; - } - switch (mode) { - case LCUI_DISPLAY_MODE_FULLSCREEN: - scale = ui_metrics.scale; - // TODO: set window fullscreen style - wnd = app_window_create(NULL, 0, 0, 0, 0, NULL); - ui_widget_resize(ui_root(), app_get_screen_width() / scale, - app_get_screen_height() / scale); - list_append(&lcui_ui.windows, wnd); - ui_server_connect(ui_root(), wnd); - break; - case LCUI_DISPLAY_MODE_SEAMLESS: - options.child_list = TRUE; - lcui_ui.observer = - ui_mutation_observer_create(lcui_on_ui_mutation, NULL); - ui_mutation_observer_observe(lcui_ui.observer, ui_root(), - options); - for (list_each(node, &ui_root()->children)) { - wnd = app_window_create(NULL, 0, 0, 0, 0, NULL); - ui_server_connect(node->data, wnd); - } - break; - case LCUI_DISPLAY_MODE_WINDOWED: - default: - wnd = app_window_create(NULL, 0, 0, 0, 0, NULL); - ui_server_connect(ui_root(), wnd); - break; - } - lcui_ui.mode = mode; + float scale; + app_window_t *wnd; + list_node_t *node; + ui_mutation_observer_init_t options = { 0 }; + + if (mode == LCUI_DISPLAY_MODE_DEFAULT) { + mode = LCUI_DISPLAY_MODE_WINDOWED; + } + if (lcui_ui.observer) { + ui_mutation_observer_disconnect(lcui_ui.observer); + ui_mutation_observer_destroy(lcui_ui.observer); + lcui_ui.observer = NULL; + } + list_destroy(&lcui_ui.windows, lcui_close_window); + switch (lcui_ui.mode) { + case LCUI_DISPLAY_MODE_FULLSCREEN: + case LCUI_DISPLAY_MODE_WINDOWED: + ui_server_disconnect(ui_root(), NULL); + break; + case LCUI_DISPLAY_MODE_SEAMLESS: + for (list_each(node, &ui_root()->children)) { + ui_server_disconnect(node->data, NULL); + } + default: + break; + } + switch (mode) { + case LCUI_DISPLAY_MODE_FULLSCREEN: + scale = ui_metrics.scale; + // TODO: set window fullscreen style + wnd = app_window_create(NULL, 0, 0, 0, 0, NULL); + ui_widget_resize(ui_root(), app_get_screen_width() / scale, + app_get_screen_height() / scale); + list_append(&lcui_ui.windows, wnd); + ui_server_connect(ui_root(), wnd); + break; + case LCUI_DISPLAY_MODE_SEAMLESS: + options.child_list = TRUE; + lcui_ui.observer = + ui_mutation_observer_create(lcui_on_ui_mutation, NULL); + ui_mutation_observer_observe(lcui_ui.observer, ui_root(), + options); + for (list_each(node, &ui_root()->children)) { + wnd = app_window_create(NULL, 0, 0, 0, 0, NULL); + ui_server_connect(node->data, wnd); + } + break; + case LCUI_DISPLAY_MODE_WINDOWED: + default: + wnd = app_window_create(NULL, 0, 0, 0, 0, NULL); + ui_server_connect(ui_root(), wnd); + break; + } + lcui_ui.mode = mode; } void lcui_init_ui_preset_widgets(void) { - ui_register_text(); - ui_register_canvas(); - ui_register_anchor(); - ui_register_button(); - ui_register_scrollbar(); - ui_register_textcaret(); - ui_register_textinput(); + ui_register_text(); + ui_register_canvas(); + ui_register_anchor(); + ui_register_button(); + ui_register_scrollbar(); + ui_register_textcaret(); + ui_register_textinput(); } void lcui_destroy_ui_preset_widgets(void) { - ui_unregister_text(); - ui_unregister_anchor(); + ui_unregister_text(); + ui_unregister_anchor(); } #ifdef LIBPLAT_WIN32 static void lcui_load_fonts_for_windows(void) { - size_t i; - int *ids = NULL; - const char *names[] = { "Consola", "Simsun", "Microsoft YaHei", NULL }; - const char *fonts[] = { "C:/Windows/Fonts/consola.ttf", - "C:/Windows/Fonts/simsun.ttc", - "C:/Windows/Fonts/msyh.ttf", - "C:/Windows/Fonts/msyh.ttc" }; - - for (i = 0; i < sizeof(fonts) / sizeof(char *); ++i) { - pd_font_library_load_file(fonts[i]); - } - i = pd_font_library_query(&ids, PD_FONT_STYLE_NORMAL, PD_FONT_WEIGHT_NORMAL, names); - if (i > 0) { - pd_font_library_set_default_font(ids[i - 1]); - } - free(ids); + size_t i; + int *ids = NULL; + const char *names[] = { "Consola", "Simsun", "Microsoft YaHei", NULL }; + const char *fonts[] = { "C:/Windows/Fonts/consola.ttf", + "C:/Windows/Fonts/simsun.ttc", + "C:/Windows/Fonts/msyh.ttf", + "C:/Windows/Fonts/msyh.ttc" }; + + for (i = 0; i < sizeof(fonts) / sizeof(char *); ++i) { + pd_font_library_load_file(fonts[i]); + } + i = pd_font_library_query(&ids, PD_FONT_STYLE_NORMAL, + PD_FONT_WEIGHT_NORMAL, names); + if (i > 0) { + pd_font_library_set_default_font(ids[i - 1]); + } + free(ids); } #else @@ -303,69 +315,71 @@ static void lcui_load_fonts_for_windows(void) static void lcui_load_fonts_by_font_config(void) { - size_t i; - char *path; - int *ids = NULL; - const char *names[] = { "Noto Sans CJK", "Ubuntu", - "WenQuanYi Micro Hei", NULL }; - const char *fonts[] = { "Ubuntu", "Noto Sans CJK SC", - "WenQuanYi Micro Hei" }; - - for (i = 0; i < sizeof(fonts) / sizeof(char *); ++i) { - path = pd_font_library_get_font_path(fonts[i]); - pd_font_library_load_file(path); - free(path); - } - i = pd_font_library_query(&ids, PD_FONT_STYLE_NORMAL, PD_FONT_WEIGHT_NORMAL, names); - if (i > 0) { - pd_font_library_set_default_font(ids[i - 1]); - } - free(ids); + size_t i; + char *path; + int *ids = NULL; + const char *names[] = { "Noto Sans CJK", "Ubuntu", + "WenQuanYi Micro Hei", NULL }; + const char *fonts[] = { "Ubuntu", "Noto Sans CJK SC", + "WenQuanYi Micro Hei" }; + + for (i = 0; i < sizeof(fonts) / sizeof(char *); ++i) { + path = pd_font_library_get_font_path(fonts[i]); + pd_font_library_load_file(path); + free(path); + } + i = pd_font_library_query(&ids, PD_FONT_STYLE_NORMAL, + PD_FONT_WEIGHT_NORMAL, names); + if (i > 0) { + pd_font_library_set_default_font(ids[i - 1]); + } + free(ids); } #else static void lcui_load_fonts_for_linux(void) { - size_t i; - int *ids = NULL; - const char *names[] = { "Noto Sans CJK SC", "Ubuntu", "Ubuntu Mono", - "WenQuanYi Micro Hei", NULL }; - const char *fonts[] = { - "/usr/share/fonts/truetype/ubuntu/Ubuntu-BI.ttf", - "/usr/share/fonts/truetype/ubuntu/Ubuntu-B.ttf", - "/usr/share/fonts/truetype/ubuntu/Ubuntu-C.ttf", - "/usr/share/fonts/truetype/ubuntu/Ubuntu-LI.ttf", - "/usr/share/fonts/truetype/ubuntu/Ubuntu-L.ttf", - "/usr/share/fonts/truetype/ubuntu/Ubuntu-MI.ttf", - "/usr/share/fonts/truetype/ubuntu/UbuntuMono-BI.ttf", - "/usr/share/fonts/truetype/ubuntu/UbuntuMono-B.ttf", - "/usr/share/fonts/truetype/ubuntu/UbuntuMono-RI.ttf", - "/usr/share/fonts/truetype/ubuntu/UbuntuMono-R.ttf", - "/usr/share/fonts/truetype/ubuntu/Ubuntu-M.ttf", - "/usr/share/fonts/truetype/ubuntu/Ubuntu-RI.ttf", - "/usr/share/fonts/truetype/ubuntu/Ubuntu-R.ttf", - "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-R.ttf", - "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-RI.ttf", - "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-B.ttf", - "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-BI.ttf", - "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-M.ttf", - "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-MI.ttf", - "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-L.ttf", - "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-LI.ttf", - "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", - "/usr/share/fonts/opentype/noto/NotoSansCJK.ttc", - "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc" - }; - - for (i = 0; i < sizeof(fonts) / sizeof(char *); ++i) { - pd_font_library_load_file(fonts[i]); - } - i = pd_font_library_query(&ids, PD_FONT_STYLE_NORMAL, PD_FONT_WEIGHT_NORMAL, names); - if (i > 0) { - pd_font_library_set_default_font(ids[i - 1]); - } - free(ids); + size_t i; + int *ids = NULL; + const char *names[] = { "Noto Sans CJK SC", "Ubuntu", "Ubuntu Mono", + "WenQuanYi Micro Hei", NULL }; + const char *fonts[] = { + "/usr/share/fonts/truetype/ubuntu/Ubuntu-BI.ttf", + "/usr/share/fonts/truetype/ubuntu/Ubuntu-B.ttf", + "/usr/share/fonts/truetype/ubuntu/Ubuntu-C.ttf", + "/usr/share/fonts/truetype/ubuntu/Ubuntu-LI.ttf", + "/usr/share/fonts/truetype/ubuntu/Ubuntu-L.ttf", + "/usr/share/fonts/truetype/ubuntu/Ubuntu-MI.ttf", + "/usr/share/fonts/truetype/ubuntu/UbuntuMono-BI.ttf", + "/usr/share/fonts/truetype/ubuntu/UbuntuMono-B.ttf", + "/usr/share/fonts/truetype/ubuntu/UbuntuMono-RI.ttf", + "/usr/share/fonts/truetype/ubuntu/UbuntuMono-R.ttf", + "/usr/share/fonts/truetype/ubuntu/Ubuntu-M.ttf", + "/usr/share/fonts/truetype/ubuntu/Ubuntu-RI.ttf", + "/usr/share/fonts/truetype/ubuntu/Ubuntu-R.ttf", + "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-R.ttf", + "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-RI.ttf", + "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-B.ttf", + "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-BI.ttf", + "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-M.ttf", + "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-MI.ttf", + "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-L.ttf", + "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-LI.ttf", + "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", + "/usr/share/fonts/opentype/noto/NotoSansCJK.ttc", + "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc" + }; + + for (i = 0; i < sizeof(fonts) / sizeof(char *); ++i) { + pd_font_library_load_file(fonts[i]); + } + i = pd_font_library_query(&ids, PD_FONT_STYLE_NORMAL, + PD_FONT_WEIGHT_NORMAL, names); + if (i > 0) { + pd_font_library_set_default_font(ids[i - 1]); + } + free(ids); } #endif @@ -374,45 +388,79 @@ static void lcui_load_fonts_for_linux(void) static void lcui_load_default_fonts(void) { #ifdef LIBPLAT_WIN32 - lcui_load_fonts_for_windows(); + lcui_load_fonts_for_windows(); #elif defined(HAVE_FONTCONFIG) - logger_debug("[font] fontconfig enabled\n"); - lcui_load_fonts_by_font_config(); + logger_debug("[font] fontconfig enabled\n"); + lcui_load_fonts_by_font_config(); #else - lcui_load_fonts_for_linux(); + lcui_load_fonts_for_linux(); #endif } -static void lcui_ui_image_loading_timer(void *arg) +static void lcui_ui_image_loader_refresh(void) +{ + thread_mutex_lock(&lcui_ui.image_loader.mutex); + thread_cond_signal(&lcui_ui.image_loader.cond); + thread_mutex_unlock(&lcui_ui.image_loader.mutex); +} + +static void lcui_ui_on_load_image(ui_image_t *unused) { - ui_load_images(); + lcui_ui_image_loader_refresh(); +} + +static void lcui_ui_image_loader_thread(void *unused) +{ + while (lcui_ui.image_loader.active) { + ui_load_images(); + thread_mutex_lock(&lcui_ui.image_loader.mutex); + thread_cond_timedwait(&lcui_ui.image_loader.cond, + &lcui_ui.image_loader.mutex, 1000); + thread_mutex_unlock(&lcui_ui.image_loader.mutex); + } + thread_exit(NULL); } void lcui_init_ui(void) { - ui_init(); - ui_server_init(); - ui_widget_resize(ui_root(), DEFAULT_WINDOW_WIDTH, - DEFAULT_WINDOW_HEIGHT); - list_create(&lcui_ui.windows); - lcui_set_ui_display_mode(LCUI_DISPLAY_MODE_DEFAULT); - lcui_init_ui_preset_widgets(); - lcui_load_default_fonts(); - app_on_event(APP_EVENT_CLOSE, lcui_on_window_destroy, NULL); - lcui_ui.image_loading_timer = lcui_set_interval(100, lcui_ui_image_loading_timer, NULL); + thread_cond_init(&lcui_ui.image_loader.cond); + thread_mutex_init(&lcui_ui.image_loader.mutex); + lcui_ui.image_loader.active = true; + if (thread_create(&lcui_ui.image_loader.thread, + lcui_ui_image_loader_thread, NULL) != 0) { + logger_error( + "[lcui-ui] image loader thread creation failed!\n"); + } + ui_set_image_loader_callback(lcui_ui_on_load_image); + + ui_init(); + ui_server_init(); + ui_widget_resize(ui_root(), DEFAULT_WINDOW_WIDTH, + DEFAULT_WINDOW_HEIGHT); + list_create(&lcui_ui.windows); + lcui_set_ui_display_mode(LCUI_DISPLAY_MODE_DEFAULT); + lcui_init_ui_preset_widgets(); + lcui_load_default_fonts(); + app_on_event(APP_EVENT_CLOSE, lcui_on_window_destroy, NULL); + } void lcui_destroy_ui(void) { - lcui_destroy_timer(lcui_ui.image_loading_timer); - lcui_destroy_ui_preset_widgets(); - app_off_event(APP_EVENT_CLOSE, lcui_on_window_destroy); - list_destroy(&lcui_ui.windows, lcui_close_window); - if (lcui_ui.observer) { - ui_mutation_observer_disconnect(lcui_ui.observer); - ui_mutation_observer_destroy(lcui_ui.observer); - lcui_ui.observer = NULL; - } - ui_server_destroy(); - ui_destroy(); + lcui_ui.image_loader.active = false; + lcui_ui_image_loader_refresh(); + thread_join(lcui_ui.image_loader.thread, NULL); + thread_mutex_destroy(&lcui_ui.image_loader.mutex); + thread_cond_destroy(&lcui_ui.image_loader.cond); + + lcui_destroy_ui_preset_widgets(); + app_off_event(APP_EVENT_CLOSE, lcui_on_window_destroy); + list_destroy(&lcui_ui.windows, lcui_close_window); + if (lcui_ui.observer) { + ui_mutation_observer_disconnect(lcui_ui.observer); + ui_mutation_observer_destroy(lcui_ui.observer); + lcui_ui.observer = NULL; + } + ui_server_destroy(); + ui_destroy(); }