From 4997b13211622d31c3a0c09dfaf5db8979f24908 Mon Sep 17 00:00:00 2001 From: lixianjing Date: Sun, 5 Nov 2023 18:33:21 +0800 Subject: [PATCH] add remote_ui to ui auto test --- .gitignore | 3 + SConstruct | 1 + demos/demo_ui_old_app.c | 7 + docs/changes.md | 7 + src/SConscript | 4 + src/base/widget.c | 48 ++ src/base/widget.h | 2 + src/fscript_ext/fscript_widget.c | 64 +- src/remote_ui/README.md | 2 + src/remote_ui/client/remote_ui.c | 609 +++++++++++++ src/remote_ui/client/remote_ui.h | 398 +++++++++ src/remote_ui/service/remote_ui_service.c | 816 ++++++++++++++++++ src/remote_ui/service/remote_ui_service.h | 82 ++ src/remote_ui/shared/remote_ui_types_def.h | 465 ++++++++++ src/service/service.c | 147 ++++ src/service/service.h | 86 ++ tests/ui_test_data/demo_ui_old.ini | 68 ++ tests/ui_test_data/demo_ui_old_confirm.ini | 17 + .../ui_test_data/demo_ui_old_exec_fscript.ini | 16 + tests/ui_test_data/demo_ui_old_get_source.ini | 15 + tests/ui_test_data/demo_ui_old_info.ini | 17 + .../demo_ui_old_send_click_events.ini | 19 + .../demo_ui_old_send_key_events.ini | 19 + .../demo_ui_old_send_pointer_events.ini | 31 + .../ui_test_data/demo_ui_old_set_get_prop.ini | 21 + .../demo_ui_old_set_theme_language.ini | 17 + tests/ui_test_data/demo_ui_old_toast.ini | 17 + tests/ui_test_data/demo_ui_old_warn.ini | 17 + tests/widget_test.cc | 41 + tools/ui_test/SConscript | 6 + tools/ui_test/ui_test.c | 308 +++++++ 31 files changed, 3317 insertions(+), 53 deletions(-) create mode 100644 src/remote_ui/README.md create mode 100644 src/remote_ui/client/remote_ui.c create mode 100644 src/remote_ui/client/remote_ui.h create mode 100644 src/remote_ui/service/remote_ui_service.c create mode 100644 src/remote_ui/service/remote_ui_service.h create mode 100644 src/remote_ui/shared/remote_ui_types_def.h create mode 100644 src/service/service.c create mode 100644 src/service/service.h create mode 100644 tests/ui_test_data/demo_ui_old.ini create mode 100644 tests/ui_test_data/demo_ui_old_confirm.ini create mode 100644 tests/ui_test_data/demo_ui_old_exec_fscript.ini create mode 100644 tests/ui_test_data/demo_ui_old_get_source.ini create mode 100644 tests/ui_test_data/demo_ui_old_info.ini create mode 100644 tests/ui_test_data/demo_ui_old_send_click_events.ini create mode 100644 tests/ui_test_data/demo_ui_old_send_key_events.ini create mode 100644 tests/ui_test_data/demo_ui_old_send_pointer_events.ini create mode 100644 tests/ui_test_data/demo_ui_old_set_get_prop.ini create mode 100644 tests/ui_test_data/demo_ui_old_set_theme_language.ini create mode 100644 tests/ui_test_data/demo_ui_old_toast.ini create mode 100644 tests/ui_test_data/demo_ui_old_warn.ini create mode 100755 tools/ui_test/SConscript create mode 100644 tools/ui_test/ui_test.c diff --git a/.gitignore b/.gitignore index 472fb390a..1170b624d 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,6 @@ tools/idl_gen/idl.json tools/idl_gen/tkc.json t.* test.png +README.md.download +manifest.txt +test.xml diff --git a/SConstruct b/SConstruct index db46e38b9..c10700ebb 100644 --- a/SConstruct +++ b/SConstruct @@ -76,6 +76,7 @@ if complie_helper.get_value('BUILD_TOOLS', True) : 'tools/preview_ui/SConscript', 'tools/fdb/SConscript', 'tools/dltest/SConscript', + 'tools/ui_test/SConscript', 'src/hal/tools/network_shell/SConscript', ] diff --git a/demos/demo_ui_old_app.c b/demos/demo_ui_old_app.c index de9bdbb64..edb7125dc 100644 --- a/demos/demo_ui_old_app.c +++ b/demos/demo_ui_old_app.c @@ -27,6 +27,7 @@ #include "ext_widgets.h" #include "base/font_manager.h" #include "base/event_recorder_player.h" +#include "remote_ui/service/remote_ui_service.h" #define DEMOUI_MAIN_WINDOW_NAME "main" #define SCROLL_BAR_H_WIDGT_NAME "bar_h" @@ -1416,6 +1417,12 @@ ret_t application_init() { fs_get_user_storage_path(os_fs(), path); log_debug("user storage path:%s\n", path); +#ifndef REMOTE_UI_URL +#define REMOTE_UI_URL "tcp://localhost:2233" +#endif/*REMOTE_UI_URL*/ + + tk_service_start(main_loop_get_event_source_manager(main_loop()), REMOTE_UI_URL, remote_ui_service_create, NULL); + return show_preload_res_window(); } diff --git a/docs/changes.md b/docs/changes.md index dcbd226e7..6e59370fb 100644 --- a/docs/changes.md +++ b/docs/changes.md @@ -1,4 +1,11 @@ # 最新动态 +2023/11/5 + * 完善UI自动测试框架 src/remote\_ui + * 增加服务接口 src/service + +2023/11/4 + * 增加UI自动测试框架 src/remote\_ui + 2023/11/03 * 修复子进程继承句柄机制有概率导致父进程异常的问题(感谢智明提供补丁) * 修复ui\_loader\_load\_widget接口加载edit控件时打印大量警告的问题(感谢雨欣提供补丁) diff --git a/src/SConscript b/src/SConscript index bd5b65a5c..17cbc29fc 100644 --- a/src/SConscript +++ b/src/SConscript @@ -23,6 +23,10 @@ BASE_SOURCES = Glob('layouters/*.c') + \ Glob('widget_animators/*.c') + \ Glob('window_animators/*.c') + \ Glob('dialog_highlighters/*.c') + \ + Glob('service/*.c') + \ + Glob('remote_ui/service/*.c') + \ + Glob('remote_ui/client/*.c') + \ + Glob('remote_ui/shared/*.c') + \ Glob('window_manager/window_manager_default.c') if GRAPHIC_BUFFER == "default" : diff --git a/src/base/widget.c b/src/base/widget.c index 266ba853a..c2a4f3b94 100644 --- a/src/base/widget.c +++ b/src/base/widget.c @@ -5275,3 +5275,51 @@ ret_t widget_dispatch_model_event(widget_t* widget, const char* name, const char return RET_OK; } + +widget_t* widget_find_by_path(widget_t* widget, const char* path, bool_t recursive) { + bool_t is_first = TRUE; + tokenizer_t tokenizer; + widget_t* iter = widget; + tokenizer_t* t = NULL; + return_value_if_fail(widget != NULL && path != NULL, NULL); + if (strchr(path, '.') == NULL) { + const char* name = path; + if (tk_str_eq(name, STR_PROP_PARENT)) { + return widget->parent; + } else if (tk_str_eq(name, STR_PROP_SELF)) { + return widget; + } else if (tk_str_eq(name, STR_PROP_WINDOW)) { + return widget_get_window(widget); + } else if (tk_str_eq(name, STR_PROP_WINDOW_MANAGER)) { + return widget_get_window_manager(widget); + } else { + return widget_lookup(widget, name, recursive); + } + } + t = tokenizer_init(&tokenizer, path, strlen(path), "."); + return_value_if_fail(t != NULL, NULL); + + while (tokenizer_has_more(t) && iter != NULL) { + const char* name = tokenizer_next(t); + if (is_first) { + if (tk_str_eq(name, STR_PROP_PARENT)) { + iter = widget->parent; + } else if (tk_str_eq(name, STR_PROP_SELF)) { + iter = widget; + } else if (tk_str_eq(name, STR_PROP_WINDOW)) { + iter = widget_get_window(widget); + } else if (tk_str_eq(name, STR_PROP_WINDOW_MANAGER)) { + iter = widget_get_window_manager(widget); + } else { + iter = widget_child(iter, name); + } + is_first = FALSE; + } else { + iter = widget_child(iter, name); + } + } + tokenizer_deinit(t); + + return iter; +} + diff --git a/src/base/widget.h b/src/base/widget.h index 262f5426b..027de7b4c 100644 --- a/src/base/widget.h +++ b/src/base/widget.h @@ -3390,6 +3390,8 @@ ret_t widget_draw_image_with_region(widget_t* widget, canvas_t* c, bitmap_t* img const char* region, const rect_t* dst, image_draw_type_t draw_type); +widget_t* widget_find_by_path(widget_t* widget, const char* path, bool_t recursive); + END_C_DECLS #endif /*TK_WIDGET_H*/ diff --git a/src/fscript_ext/fscript_widget.c b/src/fscript_ext/fscript_widget.c index 018b3532f..bf4a8f2b3 100644 --- a/src/fscript_ext/fscript_widget.c +++ b/src/fscript_ext/fscript_widget.c @@ -31,54 +31,6 @@ #include "file_browser/file_dialog.h" #include "ui_loader/ui_builder_default.h" -static widget_t* find_target_widget(widget_t* widget, const char* path, uint32_t len, - bool_t recursive) { - bool_t is_first = TRUE; - tokenizer_t tokenizer; - widget_t* iter = widget; - tokenizer_t* t = NULL; - return_value_if_fail(widget != NULL && path != NULL, NULL); - if (strchr(path, '.') == NULL) { - const char* name = path; - if (tk_str_eq(name, STR_PROP_PARENT)) { - return widget->parent; - } else if (tk_str_eq(name, STR_PROP_SELF)) { - return widget; - } else if (tk_str_eq(name, STR_PROP_WINDOW)) { - return widget_get_window(widget); - } else if (tk_str_eq(name, STR_PROP_WINDOW_MANAGER)) { - return widget_get_window_manager(widget); - } else { - return widget_lookup(widget, name, recursive); - } - } - t = tokenizer_init(&tokenizer, path, len, "."); - return_value_if_fail(t != NULL, NULL); - - while (tokenizer_has_more(t) && iter != NULL) { - const char* name = tokenizer_next(t); - if (is_first) { - if (tk_str_eq(name, STR_PROP_PARENT)) { - iter = widget->parent; - } else if (tk_str_eq(name, STR_PROP_SELF)) { - iter = widget; - } else if (tk_str_eq(name, STR_PROP_WINDOW)) { - iter = widget_get_window(widget); - } else if (tk_str_eq(name, STR_PROP_WINDOW_MANAGER)) { - iter = widget_get_window_manager(widget); - } else { - iter = widget_child(iter, name); - } - is_first = FALSE; - } else { - iter = widget_child(iter, name); - } - } - tokenizer_deinit(t); - - return iter; -} - static widget_t* to_widget(fscript_t* fscript, const value_t* v) { widget_t* widget = NULL; if (v->type == VALUE_TYPE_STRING) { @@ -86,9 +38,9 @@ static widget_t* to_widget(fscript_t* fscript, const value_t* v) { const char* path = value_str(v); return_value_if_fail(path != NULL, NULL); - widget = find_target_widget(self, path, strlen(path), TRUE); + widget = widget_find_by_path(self, path, TRUE); if (widget == NULL) { - widget = find_target_widget(widget_get_window(self), path, strlen(path), TRUE); + widget = widget_find_by_path(widget_get_window(self), path, TRUE); } return widget; @@ -189,7 +141,10 @@ static ret_t widget_set(widget_t* self, const char* path, const value_t* v) { widget_t* widget = self; const char* prop = strrchr(path, '.'); if (prop != NULL) { - widget = find_target_widget(self, path, prop - path, TRUE); + char name[MAX_PATH+1] = {0}; + int32_t len = tk_min_int(prop - path, MAX_PATH); + tk_strncpy(name, path, len); + widget = widget_find_by_path(self, name, TRUE); prop++; } else { prop = path; @@ -204,7 +159,10 @@ static ret_t widget_get(widget_t* self, const char* path, value_t* v) { widget_t* widget = self; const char* prop = strrchr(path, '.'); if (prop != NULL) { - widget = find_target_widget(self, path, prop - path, TRUE); + char name[MAX_PATH+1] = {0}; + int32_t len = tk_min_int(prop - path, MAX_PATH); + tk_strncpy(name, path, len); + widget = widget_find_by_path(self, name, TRUE); prop++; } else { prop = path; @@ -286,7 +244,7 @@ static ret_t func_widget_lookup(fscript_t* fscript, fscript_args_t* args, value_ } FSCRIPT_FUNC_CHECK(widget != NULL && path != NULL, RET_BAD_PARAMS); - widget = find_target_widget(widget, path, strlen(path), recursive); + widget = widget_find_by_path(widget, path, recursive); if (widget == NULL) { result->type = VALUE_TYPE_INVALID; return RET_NOT_FOUND; diff --git a/src/remote_ui/README.md b/src/remote_ui/README.md new file mode 100644 index 000000000..be78dbdc1 --- /dev/null +++ b/src/remote_ui/README.md @@ -0,0 +1,2 @@ +# 自动化测试框架 + diff --git a/src/remote_ui/client/remote_ui.c b/src/remote_ui/client/remote_ui.c new file mode 100644 index 000000000..aedd4d9c5 --- /dev/null +++ b/src/remote_ui/client/remote_ui.c @@ -0,0 +1,609 @@ +/** + * File: remote_ui.c + * Author: AWTK Develop Team + * Brief: remote ui client + * + * Copyright (c) 2018 - 2023 Guangzhou ZHIYUAN Electronics Co.,Ltd. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * License file for more details. + * + */ + +/** + * History: + * ================================================================ + * 2023-11-04 Li XianJing created + * + */ + +#include "tkc/fs.h" +#include "tkc/mem.h" +#include "tkc/crc.h" +#include "tkc/utils.h" +#include "base/keys.h" +#include "base/events.h" +#include "conf_io/conf_ubjson.h" +#include "tkc/object_default.h" + +#include "remote_ui/client/remote_ui.h" +#include "remote_ui/shared/remote_ui_types_def.h" + +#define REMOTE_UI_ISTREAM_TIMEOUT 100 * 1000 + +remote_ui_t* remote_ui_create(tk_iostream_t* io) { + remote_ui_t* ui = NULL; + return_value_if_fail(io != NULL, NULL); + + ui = (remote_ui_t*)TKMEM_ZALLOC(remote_ui_t); + return_value_if_fail(ui != NULL, NULL); + + ui->io = io; + ui->event_handlers = object_default_create_ex(FALSE); + wbuffer_init_extendable(&(ui->wb)); + + return ui; +} + +static ret_t remote_ui_send_req(tk_iostream_t* io, uint32_t type, uint32_t data_type, + wbuffer_t* wb) { + int32_t ret = 0; + const void* data = NULL; + uint32_t size = 0; + uint32_t timeout = TK_OSTREAM_DEFAULT_TIMEOUT; + uint16_t crc_value = PPPINITFCS16; + remote_ui_msg_header_t header; + memset(&header, 0x00, sizeof(header)); + return_value_if_fail(io != NULL && wb != NULL, RET_BAD_PARAMS); + + size = wb->cursor; + data = wb->data; + if (size > 0) { + return_value_if_fail(data != NULL, RET_BAD_PARAMS); + } + + header.type = type; + header.size = size; + header.data_type = data_type; + + crc_value = tk_crc16(crc_value, &header, sizeof(header)); + if (data != NULL && size > 0) { + crc_value = tk_crc16(crc_value, data, size); + } + ret = tk_iostream_write_len(io, &header, sizeof(header), timeout); + return_value_if_fail(ret == sizeof(header), RET_IO); + + if (size > 0) { + timeout = TK_OSTREAM_DEFAULT_TIMEOUT * (size / 10240) + TK_OSTREAM_DEFAULT_TIMEOUT; + ret = tk_iostream_write_len(io, data, size, timeout); + return_value_if_fail(ret == size, RET_IO); + } + + ret = tk_iostream_write_len(io, &crc_value, sizeof(crc_value), TK_OSTREAM_DEFAULT_TIMEOUT); + return_value_if_fail(ret == sizeof(crc_value), RET_IO); + + return RET_OK; +} + +static ret_t remote_ui_read_resp(tk_iostream_t* io, remote_ui_msg_header_t* header, wbuffer_t* wb) { + int32_t ret = 0; + uint16_t crc_value = 0; + uint16_t real_crc_value = PPPINITFCS16; + return_value_if_fail(io != NULL && header != NULL && wb != NULL, RET_BAD_PARAMS); + + wbuffer_rewind(wb); + ret = tk_iostream_read_len(io, header, sizeof(*header), REMOTE_UI_ISTREAM_TIMEOUT); + return_value_if_fail(ret == sizeof(*header), RET_IO); + + real_crc_value = tk_crc16(real_crc_value, header, sizeof(*header)); + if (header->size > 0) { + return_value_if_fail(wbuffer_extend_capacity(wb, header->size) == RET_OK, RET_OOM); + ret = tk_iostream_read_len(io, wb->data, header->size, REMOTE_UI_ISTREAM_TIMEOUT); + return_value_if_fail(ret == header->size, RET_IO); + real_crc_value = tk_crc16(real_crc_value, wb->data, header->size); + } + + ret = tk_iostream_read_len(io, &crc_value, sizeof(crc_value), REMOTE_UI_ISTREAM_TIMEOUT); + return_value_if_fail(ret == sizeof(crc_value), RET_IO); + return_value_if_fail(real_crc_value == crc_value, RET_CRC); + + wb->cursor = header->size; + + return header->resp_code; +} + +static ret_t remote_ui_request(tk_iostream_t* io, uint32_t type, uint32_t data_type, + wbuffer_t* wb) { + ret_t ret = remote_ui_send_req(io, type, data_type, wb); + if (ret == RET_OK) { + remote_ui_msg_header_t header; + memset(&header, 0x00, sizeof(header)); + ret = remote_ui_read_resp(io, &header, wb); + } + + return ret; +} + +static ubjson_writer_t* remote_ui_client_get_writer(remote_ui_t* ui) { + wbuffer_t* wb = &(ui->wb); + ubjson_writer_t* writer = &(ui->writer); + + wb->cursor = 0; + return ubjson_writer_init(writer, (ubjson_write_callback_t)wbuffer_write_binary, wb); +} + +ret_t remote_ui_login(remote_ui_t* ui, const char* username, const char* password) { + ubjson_writer_t* writer = NULL; + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(username != NULL, RET_BAD_PARAMS); + return_value_if_fail(password != NULL, RET_BAD_PARAMS); + writer = remote_ui_client_get_writer(ui); + + ubjson_writer_write_object_begin(writer); + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_USERNAME, username); + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_PASSWORD, password); + ubjson_writer_write_object_end(writer); + + return remote_ui_request(ui->io, REMOTE_UI_REQ_LOGIN, REMOTE_UI_DATA_TYPE_UBJSON, &(ui->wb)); +} + +ret_t remote_ui_logout(remote_ui_t* ui) { + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + + wbuffer_rewind(&(ui->wb)); + return remote_ui_request(ui->io, REMOTE_UI_REQ_LOGOUT, REMOTE_UI_DATA_TYPE_NONE, &(ui->wb)); +} + +ret_t remote_ui_get_dev_info(remote_ui_t* ui, remote_ui_dev_info_t* info) { + ret_t ret = RET_FAIL; + wbuffer_t* wb = NULL; + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(info != NULL, RET_BAD_PARAMS); + + wb = &(ui->wb); + wbuffer_rewind(wb); + memset(info, 0x00, sizeof(*info)); + ret = remote_ui_request(ui->io, REMOTE_UI_REQ_GET_DEV_INFO, REMOTE_UI_DATA_TYPE_UBJSON, wb); + if (ret == RET_OK) { + tk_object_t* obj = conf_ubjson_load_from_buff(wb->data, wb->cursor, FALSE); + if (obj != NULL) { + const char* p = NULL; + p = tk_object_get_prop_str(obj, REMOTE_UI_KEY_DEV_NAME); + if (p != NULL) { + tk_strncpy(info->name, p, sizeof(info->name) - 1); + } + + p = tk_object_get_prop_str(obj, REMOTE_UI_KEY_DEV_VERSION); + if (p != NULL) { + tk_strncpy(info->version, p, sizeof(info->version) - 1); + } + + p = tk_object_get_prop_str(obj, REMOTE_UI_KEY_DEV_OS); + if (p != NULL) { + tk_strncpy(info->os, p, sizeof(info->os) - 1); + } + + p = tk_object_get_prop_str(obj, REMOTE_UI_KEY_DEV_ARCH); + if (p != NULL) { + tk_strncpy(info->arch, p, sizeof(info->arch) - 1); + } + + info->screen_width = tk_object_get_prop_int(obj, REMOTE_UI_KEY_DEV_SCREEN_WIDTH, 0); + info->screen_height = tk_object_get_prop_int(obj, REMOTE_UI_KEY_DEV_SCREEN_HEIGHT, 0); + info->dpi = tk_object_get_prop_int(obj, REMOTE_UI_KEY_DEV_DPI, 0); + + TK_OBJECT_UNREF(obj); + } + } + + return ret; +} + +ret_t remote_ui_reboot(remote_ui_t* ui, remote_ui_reboot_type_t reboot_type) { + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + + wbuffer_rewind(&(ui->wb)); + wbuffer_write_int32(&(ui->wb), reboot_type); + return remote_ui_request(ui->io, REMOTE_UI_REQ_REBOOT, REMOTE_UI_DATA_TYPE_NONE, &(ui->wb)); +} + +ret_t remote_ui_upload_file(remote_ui_t* ui, const char* remote_file, const char* local_file) { + wbuffer_t wb; + int32_t len = 0; + ret_t ret = RET_OK; + fs_file_t* file = NULL; + uint8_t buff[4096] = {0}; + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(remote_file != NULL, RET_BAD_PARAMS); + return_value_if_fail(local_file != NULL, RET_BAD_PARAMS); + + file = fs_open_file(os_fs(), local_file, "rb"); + return_value_if_fail(file != NULL, RET_BAD_PARAMS); + + wbuffer_init(&wb, (void*)remote_file, strlen(remote_file) + 1); + wb.cursor = wb.capacity; + ret = remote_ui_request(ui->io, REMOTE_UI_REQ_UPLOAD_FILE_BEGIN, REMOTE_UI_DATA_TYPE_STRING, &wb); + goto_error_if_fail(ret == RET_OK); + + while ((len = fs_file_read(file, buff, sizeof(buff))) > 0) { + wbuffer_init(&wb, buff, len); + wb.cursor = len; + ret = + remote_ui_request(ui->io, REMOTE_UI_REQ_UPLOAD_FILE_DATA, REMOTE_UI_DATA_TYPE_BINARY, &wb); + break_if_fail(ret == RET_OK); + } + + wbuffer_rewind(&wb); + ret = remote_ui_request(ui->io, REMOTE_UI_REQ_UPLOAD_FILE_END, REMOTE_UI_DATA_TYPE_NONE, &wb); + + fs_file_close(file); + + return ret; +error: + fs_file_close(file); + + return RET_FAIL; +} + +ret_t remote_ui_download_file(remote_ui_t* ui, const char* remote_file, const char* local_file) { + int32_t len = 0; + ret_t ret = RET_OK; + fs_file_t* file = NULL; + wbuffer_t* wb = NULL; + remote_ui_msg_header_t header; + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(remote_file != NULL, RET_BAD_PARAMS); + return_value_if_fail(local_file != NULL, RET_BAD_PARAMS); + + file = fs_open_file(os_fs(), local_file, "wb+"); + return_value_if_fail(file != NULL, RET_BAD_PARAMS); + + wb = &(ui->wb); + wbuffer_rewind(wb); + wbuffer_write_string(wb, remote_file); + ret = + remote_ui_request(ui->io, REMOTE_UI_REQ_DOWNLOAD_FILE_BEGIN, REMOTE_UI_DATA_TYPE_STRING, wb); + goto_error_if_fail(ret == RET_OK); + + memset(&header, 0x00, sizeof(header)); + + while ((ret = remote_ui_read_resp(ui->io, &header, wb)) == RET_OK) { + if (header.type == REMOTE_UI_RESP_DOWNLOAD_FILE_DATA) { + len = fs_file_write(file, wb->data, wb->cursor); + break_if_fail(len == wb->cursor); + } else if (header.type == REMOTE_UI_RESP_DOWNLOAD_FILE_END) { + ret = RET_OK; + break; + } else { + assert(!"impossible"); + ret = RET_FAIL; + break; + } + } + fs_file_close(file); + + return ret; +error: + fs_file_close(file); + + return ret; +} + +ret_t remote_ui_create_dir(remote_ui_t* ui, const char* remote_dir) { + ret_t ret = RET_OK; + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(remote_dir != NULL, RET_BAD_PARAMS); + + wbuffer_rewind(&(ui->wb)); + wbuffer_write_string(&(ui->wb), remote_dir); + ret = remote_ui_request(ui->io, REMOTE_UI_REQ_CREATE_DIR, REMOTE_UI_DATA_TYPE_STRING, &(ui->wb)); + + return ret; +} + +ret_t remote_ui_remove_dir(remote_ui_t* ui, const char* remote_dir) { + ret_t ret = RET_OK; + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(remote_dir != NULL, RET_BAD_PARAMS); + + wbuffer_rewind(&(ui->wb)); + wbuffer_write_string(&(ui->wb), remote_dir); + ret = remote_ui_request(ui->io, REMOTE_UI_REQ_REMOVE_DIR, REMOTE_UI_DATA_TYPE_STRING, &(ui->wb)); + + return ret; +} + +ret_t remote_ui_remove_file(remote_ui_t* ui, const char* remote_file) { + ret_t ret = RET_OK; + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(remote_file != NULL, RET_BAD_PARAMS); + + wbuffer_rewind(&(ui->wb)); + wbuffer_write_string(&(ui->wb), remote_file); + ret = remote_ui_request(ui->io, REMOTE_UI_REQ_REMOVE_FILE, REMOTE_UI_DATA_TYPE_STRING, &(ui->wb)); + + return ret; +} + +ret_t remote_ui_take_screen_shot(remote_ui_t* ui, const char* file) { + return remote_ui_download_file(ui, REMOTE_UI_FILE_SCREEN_SHOT, file); +} + +ret_t remote_ui_get_manifest(remote_ui_t* ui, const char* file) { + return remote_ui_download_file(ui, REMOTE_UI_FILE_MANIFEST, file); +} + +ret_t remote_ui_get_xml_source(remote_ui_t* ui, const char* file) { + return remote_ui_download_file(ui, REMOTE_UI_FILE_XML_SOURCE, file); +} + +ret_t remote_ui_on_event(remote_ui_t* ui, const char* target, uint32_t event, event_func_t func, + void* ctx) { + emitter_t* emitter = NULL; + ubjson_writer_t* writer = NULL; + return_value_if_fail(ui != NULL && ui->event_handlers != NULL, RET_BAD_PARAMS); + return_value_if_fail(target != NULL, RET_BAD_PARAMS); + return_value_if_fail(func != NULL, RET_BAD_PARAMS); + + emitter = tk_object_get_prop_pointer(ui->event_handlers, target); + if (emitter == NULL) { + emitter = emitter_create(); + return_value_if_fail(emitter != NULL, RET_OOM); + tk_object_set_prop_pointer_ex(ui->event_handlers, target, emitter, + (tk_destroy_t)emitter_destroy); + } + emitter_on(emitter, event, func, ctx); + + writer = remote_ui_client_get_writer(ui); + + ubjson_writer_write_object_begin(writer); + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_TARGET, target); + ubjson_writer_write_kv_int(writer, REMOTE_UI_KEY_EVENT, event); + ubjson_writer_write_object_end(writer); + + return remote_ui_request(ui->io, REMOTE_UI_REQ_ON_EVENT, REMOTE_UI_DATA_TYPE_UBJSON, &(ui->wb)); +} + +ret_t remote_ui_off_event(remote_ui_t* ui, const char* target, uint32_t event) { + emitter_t* emitter = NULL; + ubjson_writer_t* writer = NULL; + return_value_if_fail(ui != NULL && ui->event_handlers != NULL, RET_BAD_PARAMS); + return_value_if_fail(target != NULL, RET_BAD_PARAMS); + + emitter = tk_object_get_prop_pointer(ui->event_handlers, target); + if (emitter != NULL) { + emitter_off(emitter, event); + } + + writer = remote_ui_client_get_writer(ui); + ubjson_writer_write_object_begin(writer); + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_TARGET, target); + ubjson_writer_write_kv_int(writer, REMOTE_UI_KEY_EVENT, event); + ubjson_writer_write_object_end(writer); + + return remote_ui_request(ui->io, REMOTE_UI_REQ_OFF_EVENT, REMOTE_UI_DATA_TYPE_UBJSON, &(ui->wb)); +} + +ret_t remote_ui_send_event(remote_ui_t* ui, const char* target, event_t* event) { + ubjson_writer_t* writer = NULL; + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(target != NULL, RET_BAD_PARAMS); + return_value_if_fail(event != NULL, RET_BAD_PARAMS); + + writer = remote_ui_client_get_writer(ui); + ubjson_writer_write_object_begin(writer); + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_TARGET, target); + ubjson_writer_write_kv_int(writer, REMOTE_UI_KEY_EVENT, event->type); + switch (event->type) { + case EVT_CLICK: + case EVT_POINTER_DOWN: + case EVT_POINTER_UP: + case EVT_POINTER_MOVE: { + pointer_event_t* e = (pointer_event_t*)event; + ubjson_writer_write_kv_int(writer, REMOTE_UI_KEY_X, e->x); + ubjson_writer_write_kv_int(writer, REMOTE_UI_KEY_Y, e->y); + break; + } + case EVT_KEY_DOWN: + case EVT_KEY_UP: { + key_event_t* e = (key_event_t*)event; + ubjson_writer_write_kv_int(writer, REMOTE_UI_KEY_CODE, e->key); + break; + } + default: { + log_debug("not supported event type:%d\n", event->type); + return RET_NOT_IMPL; + } + } + ubjson_writer_write_object_end(writer); + + return remote_ui_request(ui->io, REMOTE_UI_REQ_SEND_EVENT, REMOTE_UI_DATA_TYPE_UBJSON, &(ui->wb)); +} + +ret_t remote_ui_open_window(remote_ui_t* ui, const char* name, const char* xml, + const char* init_json) { + ubjson_writer_t* writer = NULL; + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(name != NULL, RET_BAD_PARAMS); + + writer = remote_ui_client_get_writer(ui); + ubjson_writer_write_object_begin(writer); + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_NAME, name); + if (xml != NULL) { + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_XML, xml); + } + if (init_json != NULL) { + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_INIT, init_json); + } + ubjson_writer_write_object_end(writer); + + return remote_ui_request(ui->io, REMOTE_UI_REQ_OPEN_WINDOW, REMOTE_UI_DATA_TYPE_UBJSON, + &(ui->wb)); +} + +static ret_t remote_ui_show_dialog(remote_ui_t* ui, const char* type, const char* title, + const char* content, uint32_t duration) { + ubjson_writer_t* writer = NULL; + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(type != NULL, RET_BAD_PARAMS); + + writer = remote_ui_client_get_writer(ui); + ubjson_writer_write_object_begin(writer); + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_TYPE, type); + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_TITLE, title); + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_CONTENT, content); + ubjson_writer_write_kv_int(writer, REMOTE_UI_KEY_DURATION, duration); + ubjson_writer_write_object_end(writer); + + return remote_ui_request(ui->io, REMOTE_UI_REQ_OPEN_DIALOG, REMOTE_UI_DATA_TYPE_UBJSON, + &(ui->wb)); +} + +ret_t remote_ui_show_confirm(remote_ui_t* ui, const char* title, const char* content) { + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(title != NULL, RET_BAD_PARAMS); + return_value_if_fail(content != NULL, RET_BAD_PARAMS); + + return remote_ui_show_dialog(ui, REMOTE_UI_DIALOG_TYPE_CONFIRM, title, content, 0); +} + +ret_t remote_ui_show_warn(remote_ui_t* ui, const char* title, const char* content) { + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(title != NULL, RET_BAD_PARAMS); + return_value_if_fail(content != NULL, RET_BAD_PARAMS); + + return remote_ui_show_dialog(ui, REMOTE_UI_DIALOG_TYPE_WARN, title, content, 0); +} + +ret_t remote_ui_show_info(remote_ui_t* ui, const char* title, const char* content) { + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(title != NULL, RET_BAD_PARAMS); + return_value_if_fail(content != NULL, RET_BAD_PARAMS); + + return remote_ui_show_dialog(ui, NULL, title, content, 0); +} + +ret_t remote_ui_show_toast(remote_ui_t* ui, uint32_t duration, const char* content) { + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(content != NULL, RET_BAD_PARAMS); + + return remote_ui_show_dialog(ui, REMOTE_UI_DIALOG_TYPE_TOAST, "", content, duration); +} + +ret_t remote_ui_close_window(remote_ui_t* ui, const char* name) { + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(name != NULL, RET_BAD_PARAMS); + + wbuffer_rewind(&(ui->wb)); + wbuffer_write_string(&(ui->wb), name); + return remote_ui_request(ui->io, REMOTE_UI_REQ_CLOSE_WINDOW, REMOTE_UI_DATA_TYPE_STRING, + &(ui->wb)); +} + +ret_t remote_ui_back_to_prev(remote_ui_t* ui) { + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + + wbuffer_rewind(&(ui->wb)); + return remote_ui_request(ui->io, REMOTE_UI_REQ_BACK_TO_PREV, REMOTE_UI_DATA_TYPE_NONE, &(ui->wb)); +} + +ret_t remote_ui_back_to_home(remote_ui_t* ui) { + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + + wbuffer_rewind(&(ui->wb)); + return remote_ui_request(ui->io, REMOTE_UI_REQ_BACK_TO_HOME, REMOTE_UI_DATA_TYPE_NONE, &(ui->wb)); +} + +ret_t remote_ui_set_prop(remote_ui_t* ui, const char* target, const char* name, + const value_t* value) { + ubjson_writer_t* writer = NULL; + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(target != NULL, RET_BAD_PARAMS); + return_value_if_fail(name != NULL, RET_BAD_PARAMS); + return_value_if_fail(value != NULL, RET_BAD_PARAMS); + + writer = remote_ui_client_get_writer(ui); + ubjson_writer_write_object_begin(writer); + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_TARGET, target); + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_NAME, name); + ubjson_writer_write_kv_value(writer, REMOTE_UI_KEY_VALUE, value); + ubjson_writer_write_object_end(writer); + + return remote_ui_request(ui->io, REMOTE_UI_REQ_SET_PROP, REMOTE_UI_DATA_TYPE_UBJSON, &(ui->wb)); +} + +ret_t remote_ui_get_prop(remote_ui_t* ui, const char* target, const char* name, value_t* value) { + ret_t ret = RET_OK; + ubjson_writer_t* writer = NULL; + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(target != NULL, RET_BAD_PARAMS); + return_value_if_fail(name != NULL, RET_BAD_PARAMS); + return_value_if_fail(value != NULL, RET_BAD_PARAMS); + + writer = remote_ui_client_get_writer(ui); + ubjson_writer_write_object_begin(writer); + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_TARGET, target); + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_NAME, name); + ubjson_writer_write_kv_value(writer, REMOTE_UI_KEY_VALUE, value); + ubjson_writer_write_object_end(writer); + + ret = remote_ui_request(ui->io, REMOTE_UI_REQ_GET_PROP, REMOTE_UI_DATA_TYPE_UBJSON, &(ui->wb)); + return_value_if_fail(ret == RET_OK, ret); + + value_dup_str_with_len(value, (char*)(ui->wb.data), ui->wb.cursor); + + return RET_OK; +} + +ret_t remote_ui_set_theme(remote_ui_t* ui, const char* theme) { + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(theme != NULL, RET_BAD_PARAMS); + + wbuffer_rewind(&(ui->wb)); + wbuffer_write_string(&(ui->wb), theme); + return remote_ui_request(ui->io, REMOTE_UI_REQ_SET_THEME, REMOTE_UI_DATA_TYPE_STRING, &(ui->wb)); +} + +ret_t remote_ui_set_language(remote_ui_t* ui, const char* language) { + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(language != NULL, RET_BAD_PARAMS); + + wbuffer_rewind(&(ui->wb)); + wbuffer_write_string(&(ui->wb), language); + return remote_ui_request(ui->io, REMOTE_UI_REQ_SET_LANGUAGE, REMOTE_UI_DATA_TYPE_STRING, + &(ui->wb)); +} + +ret_t remote_ui_exec_fscript(remote_ui_t* ui, const char* script, str_t* str) { + ret_t ret = RET_OK; + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(script != NULL, RET_BAD_PARAMS); + + wbuffer_rewind(&(ui->wb)); + wbuffer_write_string(&(ui->wb), script); + ret = + remote_ui_request(ui->io, REMOTE_UI_REQ_EXEC_FSCRIPT, REMOTE_UI_DATA_TYPE_STRING, &(ui->wb)); + if (ret == RET_OK) { + str_set_with_len(str, (char*)(ui->wb.data), ui->wb.cursor); + } + + return ret; +} + +ret_t remote_ui_dispatch(remote_ui_t* ui) { + /*TODO*/ + return RET_OK; +} + +ret_t remote_ui_destroy(remote_ui_t* ui) { + return_value_if_fail(ui != NULL, RET_BAD_PARAMS); + + TK_OBJECT_UNREF(ui->io); + TK_OBJECT_UNREF(ui->event_handlers); + wbuffer_deinit(&(ui->wb)); + TKMEM_FREE(ui); + + return RET_OK; +} diff --git a/src/remote_ui/client/remote_ui.h b/src/remote_ui/client/remote_ui.h new file mode 100644 index 000000000..81b925197 --- /dev/null +++ b/src/remote_ui/client/remote_ui.h @@ -0,0 +1,398 @@ +/** + * File: remote_ui.h + * Author: AWTK Develop Team + * Brief: remote ui client + * + * Copyright (c) 2018 - 2023 Guangzhou ZHIYUAN Electronics Co.,Ltd. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * License file for more details. + * + */ + +/** + * History: + * ================================================================ + * 2023-11-04 Li XianJing created + * + */ + +#ifndef TK_REMOTE_UI_H +#define TK_REMOTE_UI_H + +#include "tkc/buffer.h" +#include "tkc/darray.h" +#include "tkc/iostream.h" +#include "ubjson/ubjson_writer.h" +#include "remote_ui/shared/remote_ui_types_def.h" + +BEGIN_C_DECLS + +/** + * @class remote_ui_t + * remote ui客户端。 +*/ +typedef struct _remote_ui_t { + /** + * @property {tk_iostream_t*} io + * @annotation ["readable"] + * IO stream。 + */ + tk_iostream_t* io; + + /*private*/ + wbuffer_t wb; + ubjson_writer_t writer; + tk_object_t* event_handlers; +} remote_ui_t; + +/** + * @method remote_ui_create + * 创建remote ui客户端。 + * + * @param {tk_iostream_t*} io IO流。 + * + * @return {remote_ui_t*} 返回remote ui客户端对象。 + */ +remote_ui_t* remote_ui_create(tk_iostream_t* io); + +/** + * @method remote_ui_login + * 登录。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} username 用户名。 + * @param {const char*} password 密码。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_login(remote_ui_t* ui, const char* username, const char* password); + +/** + * @method remote_ui_logout + * 登出。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_logout(remote_ui_t* ui); + +/** + * @method remote_ui_get_dev_info + * 获取设备信息。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {remote_ui_dev_info_t*} info 设备信息。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_get_dev_info(remote_ui_t* ui, remote_ui_dev_info_t* info); + +/** + * @method remote_ui_reboot + * @param {remote_ui_t*} ui remote ui客户端对象。 + * 重新启动。 + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_reboot(remote_ui_t* ui, remote_ui_reboot_type_t type); + +/** + * @method remote_ui_upload_file + * 上传文件。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} remote_file 远程文件。 + * @param {const char*} local_file 本地文件。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_upload_file(remote_ui_t* ui, const char* remote_file, const char* local_file); + +/** + * @method remote_ui_download_file + * 下载文件。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} remote_file 远程文件。 + * @param {const char*} local_file 本地文件。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_download_file(remote_ui_t* ui, const char* remote_file, const char* local_file); + +/** + * @method remote_ui_create_dir + * 创建目录。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} remote_dir 远程目录。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_create_dir(remote_ui_t* ui, const char* remote_dir); + +/** + * @method remote_ui_remove_dir + * 删除目录。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} remote_dir 远程目录。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_remove_dir(remote_ui_t* ui, const char* remote_dir); + +/** + * @method remote_ui_remove_file + * 删除文件。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} remote_file 远程文件。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_remove_file(remote_ui_t* ui, const char* remote_file); + +/** + * @method remote_ui_take_screen_shot + * 截屏。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} file 文件名。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_take_screen_shot(remote_ui_t* ui, const char* file); + +/** + * @method remote_ui_get_manifest + * 获取manifest。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} file 文件名。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_get_manifest(remote_ui_t* ui, const char* file); + +/** + * @method remote_ui_get_xml_source + * 获取当前窗口的XML源码。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} file 文件名。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_get_xml_source(remote_ui_t* ui, const char* file); + +/** + * @method remote_ui_on_event + * 注册事件。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} target 目标。 + * @param {uint32_t} event 事件。 + * @param {event_func_t} func 事件处理函数。 + * @param {void*} ctx 上下文。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_on_event(remote_ui_t* ui, const char* target, uint32_t event, event_func_t func, + void* ctx); + +/** + * @method remote_ui_off_event + * 注销事件。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} target 目标。 + * @param {uint32_t} event 事件。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_off_event(remote_ui_t* ui, const char* target, uint32_t event); + +/** + * @method remote_ui_send_event + * 发送事件。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} target 目标。 + * @param {event_t*} event 事件。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_send_event(remote_ui_t* ui, const char* target, event_t* event); + +/** + * @method remote_ui_open_window + * 打开窗口。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} name 窗口名称。 + * @param {const char*} xml 窗口XML。 + * @param {const char*} init_json 初始化JSON。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_open_window(remote_ui_t* ui, const char* name, const char* xml, + const char* init_json); + +/** + * @method remote_ui_show_confirm + * 显示确认对话框。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} title 标题。 + * @param {const char*} content 内容。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_show_confirm(remote_ui_t* ui, const char* title, + const char* content); +/** + * @method remote_ui_show_warn + * 显示警告对话框。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} title 标题。 + * @param {const char*} content 内容。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_show_warn(remote_ui_t* ui, const char* title, + const char* content); + +/** + * @method remote_ui_show_info + * 显示信息对话框。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} title 标题。 + * @param {const char*} content 内容。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_show_info(remote_ui_t* ui, const char* title, + const char* content); +/** + * @method remote_ui_show_toast + * 显示信息对话框。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {uint32_t} duration 时长。 + * @param {const char*} content 内容。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_show_toast(remote_ui_t* ui, uint32_t duration, + const char* content); +/** + * @method remote_ui_close_window + * 关闭窗口。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} name 窗口名称。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_close_window(remote_ui_t* ui, const char* name); + +/** + * @method remote_ui_back_to_prev + * 返回上一个窗口。 + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_back_to_prev(remote_ui_t* ui); + +/** + * @method remote_ui_back_to_home + * 返回主窗口。 + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 +*/ +ret_t remote_ui_back_to_home(remote_ui_t* ui); + +/** + * @method remote_ui_set_prop + * 设置属性。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} target 目标。 + * @param {const char*} name 属性名称。 + * @param {const value_t*} value 属性值。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_set_prop(remote_ui_t* ui, const char* target, const char* name, + const value_t* value); + +/** + * @method remote_ui_get_prop + * 获取属性。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} target 目标。 + * @param {const char*} name 属性名称。 + * @param {value_t*} value 属性值。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_get_prop(remote_ui_t* ui, const char* target, const char* name, value_t* value); + +/** + * @method remote_ui_set_theme + * 设置主题。 + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} theme 主题名称。 + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 +*/ +ret_t remote_ui_set_theme(remote_ui_t* ui, const char* theme); + +/** + * @method remote_ui_set_language + * 设置语言。 + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} language 语言名称。 + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 +*/ +ret_t remote_ui_set_language(remote_ui_t* ui, const char* language); + +/** + * @method remote_ui_exec_fscript + * 执行fscript脚本。 + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} script 脚本。 + * @param {str_t*} result 执行结果。 + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 +*/ +ret_t remote_ui_exec_fscript(remote_ui_t* ui, const char* script, str_t* result); + +/** + * @method remote_ui_dispatch + * 分发事件。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_dispatch(remote_ui_t* ui); + +/** + * @method remote_ui_destroy + * 销毁remote ui客户端。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_destroy(remote_ui_t* ui); + +END_C_DECLS + +#endif /*TK_REMOTE_UI_H*/ diff --git a/src/remote_ui/service/remote_ui_service.c b/src/remote_ui/service/remote_ui_service.c new file mode 100644 index 000000000..1d22deff8 --- /dev/null +++ b/src/remote_ui/service/remote_ui_service.c @@ -0,0 +1,816 @@ +/** + * File: remote_ui_service.c + * Author: AWTK Develop Team + * Brief: remote ui service + * + * Copyright (c) 2018 - 2023 Guangzhou ZHIYUAN Electronics Co.,Ltd. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * License file for more details. + * + */ + +/** + * History: + * ================================================================ + * 2023-11-04 Li XianJing created + * + */ + +#include "tkc/fs.h" +#include "tkc/mem.h" +#include "tkc/crc.h" +#include "tkc/path.h" +#include "tkc/utils.h" +#include "tkc/fscript.h" +#include "base/bitmap.h" +#include "base/window.h" +#include "base/window_manager.h" +#include "base/ui_loader.h" +#include "base/dialog.h" +#include "conf_io/conf_ubjson.h" +#include "tkc/object_default.h" +#include "ui_loader/ui_serializer.h" +#include "remote_ui/shared/remote_ui_types_def.h" +#include "remote_ui/service/remote_ui_service.h" + +#include "base/events.h" +#include "base/keys.h" + +static ret_t remote_ui_service_dispatch(remote_ui_service_t* ui); +static ret_t remote_ui_service_destroy(remote_ui_service_t* ui); + +tk_service_t* remote_ui_service_create(tk_iostream_t* io, void* args) { + remote_ui_service_t* ui = NULL; + remote_ui_service_args_t* service_args = (remote_ui_service_args_t*)args; + + return_value_if_fail(io != NULL, NULL); + + ui = (remote_ui_service_t*)TKMEM_ZALLOC(remote_ui_service_t); + return_value_if_fail(ui != NULL, NULL); + + ui->io = io; + ui->service.dispatch = (tk_service_dispatch_t)remote_ui_service_dispatch; + ui->service.destroy = (tk_service_destroy_t)remote_ui_service_destroy; + + wbuffer_init_extendable(&(ui->wb)); + if (service_args != NULL && service_args->auth != NULL) { + ui->auth = service_args->auth; + } + + return (tk_service_t*)ui; +} + +static ret_t remote_ui_service_send_resp(tk_iostream_t* io, uint32_t type, uint32_t data_type, + uint32_t resp_code, wbuffer_t* wb) { + int32_t ret = 0; + uint32_t size = 0; + const void* data = NULL; + remote_ui_msg_header_t header; + uint16_t crc_value = PPPINITFCS16; + uint32_t timeout = TK_OSTREAM_DEFAULT_TIMEOUT; + + memset(&header, 0x00, sizeof(header)); + return_value_if_fail(io != NULL && wb != NULL, RET_BAD_PARAMS); + + data = wb->data; + size = wb->cursor; + if (size > 0) { + return_value_if_fail(data != NULL, RET_BAD_PARAMS); + } + + header.type = type; + header.size = size; + header.data_type = data_type; + header.resp_code = resp_code; + + crc_value = tk_crc16(crc_value, &header, sizeof(header)); + if (data != NULL && size > 0) { + crc_value = tk_crc16(crc_value, data, size); + } + + ret = tk_iostream_write_len(io, &header, sizeof(header), timeout); + return_value_if_fail(ret == sizeof(header), RET_IO); + + if (size > 0) { + timeout = TK_OSTREAM_DEFAULT_TIMEOUT * (size / 10240) + TK_OSTREAM_DEFAULT_TIMEOUT; + ret = tk_iostream_write_len(io, data, size, timeout); + return_value_if_fail(ret == size, RET_IO); + } + + ret = tk_iostream_write_len(io, &crc_value, sizeof(crc_value), TK_OSTREAM_DEFAULT_TIMEOUT); + return_value_if_fail(ret == sizeof(crc_value), RET_IO); + + return RET_OK; +} + +static ret_t remote_ui_service_read_req(tk_iostream_t* io, remote_ui_msg_header_t* header, + wbuffer_t* wb) { + int32_t ret = 0; + uint16_t crc_value = 0; + uint16_t real_crc_value = PPPINITFCS16; + return_value_if_fail(io != NULL && header != NULL && wb != NULL, RET_BAD_PARAMS); + + wbuffer_rewind(wb); + ret = tk_iostream_read_len(io, header, sizeof(*header), TK_ISTREAM_DEFAULT_TIMEOUT); + if (ret == 0) { + return RET_IO; + } + return_value_if_fail(ret == sizeof(*header), RET_IO); + + real_crc_value = tk_crc16(real_crc_value, header, sizeof(*header)); + if (header->size > 0) { + return_value_if_fail(wbuffer_extend_capacity(wb, header->size) == RET_OK, RET_OOM); + ret = tk_iostream_read_len(io, wb->data, header->size, TK_ISTREAM_DEFAULT_TIMEOUT); + return_value_if_fail(ret == header->size, RET_IO); + real_crc_value = tk_crc16(real_crc_value, wb->data, header->size); + } + + ret = tk_iostream_read_len(io, &crc_value, sizeof(crc_value), TK_ISTREAM_DEFAULT_TIMEOUT); + return_value_if_fail(ret == sizeof(crc_value), RET_IO); + return_value_if_fail(crc_value == real_crc_value, RET_CRC); + + wb->cursor = header->size; + + return header->resp_code; +} + +static ubjson_writer_t* remote_ui_service_get_writer(remote_ui_service_t* ui) { + wbuffer_t* wb = &(ui->wb); + ubjson_writer_t* writer = &(ui->writer); + + wb->cursor = 0; + return ubjson_writer_init(writer, (ubjson_write_callback_t)wbuffer_write_binary, wb); +} + +static ret_t remote_ui_service_login(remote_ui_service_t* ui, const char* username, const char* password) { + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + if (ui->auth != NULL) { + if (ui->auth((tk_service_t*)ui, username, password) == RET_OK) { + ui->is_login = TRUE; + return RET_OK; + } else { + return RET_FAIL; + } + } else { + return RET_OK; + } +} + +static ret_t remote_ui_service_logout(remote_ui_service_t* ui) { + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + + ui->is_login = FALSE; + + return RET_OK; +} + +static ret_t remote_ui_service_get_dev_info(remote_ui_service_t* ui, remote_ui_dev_info_t* info) { + ret_t ret = RET_OK; + widget_t* wm = window_manager(); + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(info != NULL, RET_BAD_PARAMS); + + memset(info, 0x00, sizeof(*info)); + info->screen_width = wm->w; + info->screen_height = wm->h; + /*TODO*/ + return ret; +} + +static ret_t remote_ui_service_reboot(remote_ui_service_t* ui, remote_ui_reboot_type_t reboot_type) { + /*TODO*/ + return RET_OK; +} + +static ret_t remote_ui_service_upload_file(remote_ui_service_t* ui, const char* filename) { + int32_t len = 0; + ret_t ret = RET_OK; + fs_file_t* file = NULL; + wbuffer_t* wb = NULL; + remote_ui_msg_header_t header; + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(filename != NULL, RET_BAD_PARAMS); + + wb = &(ui->wb); + wbuffer_rewind(wb); + file = fs_open_file(os_fs(), filename, "wb+"); + if (file != NULL) { + remote_ui_service_send_resp(ui->io, REMOTE_UI_RESP_UPLOAD_FILE_BEGIN, REMOTE_UI_DATA_TYPE_NONE, + RET_OK, wb); + } else { + remote_ui_service_send_resp(ui->io, REMOTE_UI_RESP_UPLOAD_FILE_BEGIN, REMOTE_UI_DATA_TYPE_NONE, + RET_FAIL, wb); + } + return_value_if_fail(file != NULL, RET_BAD_PARAMS); + + memset(&header, 0x00, sizeof(header)); + while ((ret = remote_ui_service_read_req(ui->io, &header, wb)) == RET_OK) { + if (header.type == REMOTE_UI_REQ_UPLOAD_FILE_DATA) { + len = fs_file_write(file, wb->data, wb->cursor); + ret = (len == wb->cursor) ? RET_OK : RET_FAIL; + remote_ui_service_send_resp(ui->io, REMOTE_UI_RESP_UPLOAD_FILE_DATA, REMOTE_UI_DATA_TYPE_NONE, + ret, wb); + break_if_fail(ret == RET_OK); + } else if (header.type == REMOTE_UI_REQ_UPLOAD_FILE_END) { + ret = RET_OK; + ret = remote_ui_service_send_resp(ui->io, REMOTE_UI_RESP_UPLOAD_FILE_END, + REMOTE_UI_DATA_TYPE_NONE, ret, wb); + break_if_fail(ret == RET_OK); + break; + } else { + assert(!"impossible"); + ret = RET_FAIL; + remote_ui_service_send_resp(ui->io, REMOTE_UI_RESP_UPLOAD_FILE_END, REMOTE_UI_DATA_TYPE_NONE, + ret, wb); + break; + } + } + fs_file_close(file); + + return RET_OK; +} + +static ret_t remote_ui_service_download_file(remote_ui_service_t* ui, const char* filename) { + wbuffer_t wb; + int32_t len = 0; + ret_t ret = RET_OK; + fs_file_t* file = NULL; + uint8_t buff[4096] = {0}; + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(filename != NULL, RET_BAD_PARAMS); + + wbuffer_init(&wb, buff, sizeof(buff)); + file = fs_open_file(os_fs(), filename, "rb"); + if (file != NULL) { + remote_ui_service_send_resp(ui->io, REMOTE_UI_RESP_DOWNLOAD_FILE_BEGIN, + REMOTE_UI_DATA_TYPE_NONE, RET_OK, &wb); + } else { + remote_ui_service_send_resp(ui->io, REMOTE_UI_RESP_DOWNLOAD_FILE_BEGIN, + REMOTE_UI_DATA_TYPE_NONE, RET_FAIL, &wb); + } + return_value_if_fail(file != NULL, RET_BAD_PARAMS); + + while ((len = fs_file_read(file, buff, sizeof(buff))) > 0) { + wbuffer_init(&wb, buff, len); + wb.cursor = len; + ret = remote_ui_service_send_resp(ui->io, REMOTE_UI_RESP_DOWNLOAD_FILE_DATA, + REMOTE_UI_DATA_TYPE_BINARY, RET_OK, &wb); + break_if_fail(ret == RET_OK); + } + + wbuffer_rewind(&wb); + ret = remote_ui_service_send_resp(ui->io, REMOTE_UI_RESP_DOWNLOAD_FILE_END, + REMOTE_UI_DATA_TYPE_NONE, ret, &wb); + + fs_file_close(file); + + return RET_OK; +} + +static ret_t remote_ui_service_create_dir(remote_ui_service_t* ui, const char* path) { + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(path != NULL, RET_BAD_PARAMS); + + return fs_create_dir_r(os_fs(), path); +} + +static ret_t remote_ui_service_remove_dir(remote_ui_service_t* ui, const char* path) { + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(path != NULL, RET_BAD_PARAMS); + + return fs_remove_dir_r(os_fs(), path); +} + +static ret_t remote_ui_service_remove_file(remote_ui_service_t* ui, const char* filename) { + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(filename != NULL, RET_BAD_PARAMS); + + return fs_remove_file(os_fs(), filename); +} + +static ret_t remote_ui_service_get_manifest(remote_ui_service_t* ui, str_t* result) { + ret_t ret = RET_OK; + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(result != NULL, RET_BAD_PARAMS); + + str_set(result, "todo"); + + /*TODO*/ + return ret; +} + +static ret_t remote_ui_service_take_screen_shot(remote_ui_service_t* ui, const char* filename) { + ret_t ret = RET_OK; + bitmap_t* image = NULL; + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(filename != NULL, RET_BAD_PARAMS); + + image = widget_take_snapshot(window_manager()); + if (image != NULL) { + ret = bitmap_save_png(image, filename); + } else { + fs_remove_file(os_fs(), filename); + } + + return ret; +} + +static ret_t remote_ui_service_prepare_manifest(remote_ui_service_t* ui, const char* filename) { + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(filename != NULL, RET_BAD_PARAMS); + + return file_write(filename, "TODO", 5); +} + +static ret_t remote_ui_service_prepare_xml_source(remote_ui_service_t* ui, const char* filename) { + str_t str; + ret_t ret = RET_OK; + widget_t* win = window_manager_get_top_window(window_manager()); + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(filename != NULL, RET_BAD_PARAMS); + + str_init(&str, 10000); + widget_to_xml(win, &str); + ret = file_write(filename, str.str, str.size); + str_reset(&str); + + return ret; +} + +static ret_t remote_ui_service_on_event_func(void* ctx, event_t* e) { + remote_ui_service_t* ui = (remote_ui_service_t*)ctx; + return_value_if_fail(ui != NULL, RET_BAD_PARAMS); + /*TODO*/ + return RET_OK; +} + + +static ret_t remote_ui_service_on_event(remote_ui_service_t* ui, const char* target, uint32_t event) { + widget_t* win = window_manager_get_top_window(window_manager()); + widget_t* widget = widget_find_by_path(win, target, TRUE); + return_value_if_fail(widget != NULL, RET_BAD_PARAMS); + + widget_on(widget, event, remote_ui_service_on_event_func, ui); + + return RET_OK; +} + +static ret_t remote_ui_service_off_event(remote_ui_service_t* ui, const char* target, uint32_t event) { + widget_t* win = window_manager_get_top_window(window_manager()); + widget_t* widget = widget_find_by_path(win, target, TRUE); + return_value_if_fail(widget != NULL, RET_BAD_PARAMS); + + widget_off_by_func(widget, event, remote_ui_service_on_event_func, ui); + + return RET_OK; +} + +static ret_t remote_ui_service_send_event(remote_ui_service_t* ui, const char* target, event_t* event) { + widget_t* win = window_manager_get_top_window(window_manager()); + widget_t* widget = widget_find_by_path(win, target, TRUE); + return_value_if_fail(widget != NULL, RET_BAD_PARAMS); + + switch (event->type) { + case EVT_CLICK: { + event->type = EVT_POINTER_DOWN; + widget_on_pointer_down(widget, pointer_event_cast(event)); + + event->type = EVT_POINTER_UP; + return widget_on_pointer_up(widget, pointer_event_cast(event)); + } + case EVT_POINTER_DOWN: { + return widget_on_pointer_down(widget, pointer_event_cast(event)); + } + case EVT_POINTER_MOVE: { + return widget_on_pointer_move(widget, pointer_event_cast(event)); + } + case EVT_POINTER_UP: { + return widget_on_pointer_up(widget, pointer_event_cast(event)); + } + case EVT_KEY_DOWN: { + return widget_on_keydown(widget, key_event_cast(event)); + } + case EVT_KEY_UP: { + return widget_on_keyup(widget, key_event_cast(event)); + } + default: { + break; + } + } + + return RET_FAIL; +} + +static ret_t remote_ui_service_open_dialog(remote_ui_service_t* ui, const char* type, const char* title, + const char* content, uint32_t duration) { + if (tk_str_eq(type, REMOTE_UI_DIALOG_TYPE_CONFIRM)) { + return dialog_confirm(title, content); + } else if (tk_str_eq(type, REMOTE_UI_DIALOG_TYPE_INFO)) { + return dialog_info(title, content); + } else if (tk_str_eq(type, REMOTE_UI_DIALOG_TYPE_WARN)) { + return dialog_warn(title, content); + } else { + return dialog_toast(content, duration); + } + + return RET_OK; +} + +static ret_t remote_ui_service_open_window(remote_ui_service_t* ui, const char* name, const char* xml, + const char* init_json) { + widget_t* win = NULL; + if (TK_STR_IS_NOT_EMPTY(xml)) { + win = ui_loader_load_widget_from_xml(NULL, xml, strlen(xml)); + } else { + win = window_open(name); + } + + return_value_if_fail(win != NULL, RET_BAD_PARAMS); + + if (init_json != NULL) { + /*TODO*/ + } + + return RET_OK; +} + +static ret_t remote_ui_service_close_window(remote_ui_service_t* ui, const char* name) { + widget_t* win = widget_child(window_manager(), name); + return_value_if_fail(win != NULL, RET_BAD_PARAMS); + + return window_manager_close_window(window_manager(), win); +} + +static ret_t remote_ui_service_back_to_prev(remote_ui_service_t* ui) { + window_manager_back(window_manager()); + + return RET_OK; +} + +static ret_t remote_ui_service_back_to_home(remote_ui_service_t* ui) { + window_manager_back_to_home(window_manager()); + + return RET_OK; +} + +static ret_t remote_ui_service_set_prop(remote_ui_service_t* ui, const char* target, const char* name, + const value_t* value) { + widget_t* win = window_manager_get_top_window(window_manager()); + widget_t* widget = widget_find_by_path(win, target, TRUE); + return_value_if_fail(widget != NULL, RET_BAD_PARAMS); + + return widget_set_prop(widget, name, value); +} + +static ret_t remote_ui_service_get_prop(remote_ui_service_t* ui, const char* target, const char* name, + value_t* value) { + widget_t* win = window_manager_get_top_window(window_manager()); + widget_t* widget = widget_find_by_path(win, target, TRUE); + return_value_if_fail(widget != NULL, RET_BAD_PARAMS); + + return widget_get_prop(widget, name, value); +} + +static ret_t remote_ui_service_set_theme(remote_ui_service_t* ui, const char* theme) { + return widget_set_theme(window_manager(), theme); +} + +static ret_t remote_ui_service_exec_script(remote_ui_service_t* ui, const char* script, value_t* v) { + tk_object_t* obj = object_default_create(); + ret_t ret = fscript_eval(obj, script, v); + TK_OBJECT_UNREF(obj); + + return ret; +} + +static ret_t remote_ui_service_set_language(remote_ui_service_t* ui, const char* language) { + const char* p = NULL; + char lang[TK_NAME_LEN + 1] = {0}; + char country[TK_NAME_LEN + 1] = {0}; + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(language != NULL, RET_BAD_PARAMS); + + p = strchr(language, '_'); + if (p != NULL) { + tk_strncpy(lang, language, p - language); + tk_strcpy(country, p + 1); + } else { + tk_strcpy(lang, language); + } + + return locale_info_change(locale_info(), lang, country); +} + +static ret_t remote_ui_dev_info_write(ubjson_writer_t* writer, remote_ui_dev_info_t* info) { + return_value_if_fail(writer != NULL && info != NULL, RET_BAD_PARAMS); + + ubjson_writer_write_object_begin(writer); + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_DEV_NAME, info->name); + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_DEV_OS, info->os); + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_DEV_VERSION, info->version); + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_DEV_ARCH, info->arch); + ubjson_writer_write_kv_int(writer, REMOTE_UI_KEY_DEV_SCREEN_WIDTH, info->screen_width); + ubjson_writer_write_kv_int(writer, REMOTE_UI_KEY_DEV_SCREEN_HEIGHT, info->screen_height); + ubjson_writer_write_kv_int(writer, REMOTE_UI_KEY_DEV_DPI, info->dpi); + ubjson_writer_write_object_end(writer); + + return RET_OK; +} + +static ret_t remote_ui_service_dispatch_impl(remote_ui_service_t* ui, remote_ui_msg_header_t* req, + wbuffer_t* wb) { + value_t v; + char buff[1024] = {0}; + ret_t ret = RET_FAIL; + tk_object_t* obj = NULL; + ubjson_writer_t* writer = NULL; + remote_ui_msg_header_t resp; + char local_file[MAX_PATH + 1] = {0}; + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(req != NULL && wb != NULL, RET_BAD_PARAMS); + + memset(&resp, 0x00, sizeof(resp)); + if (req->data_type == REMOTE_UI_DATA_TYPE_UBJSON) { + obj = conf_ubjson_load_from_buff(wb->data, wb->cursor, FALSE); + } + + switch (req->type) { + case REMOTE_UI_REQ_LOGIN: { + const char* username = tk_object_get_prop_str(obj, "username"); + const char* password = tk_object_get_prop_str(obj, "password"); + resp.resp_code = remote_ui_service_login(ui, username, password); + resp.data_type = REMOTE_UI_DATA_TYPE_NONE; + resp.type = REMOTE_UI_RESP_LOGIN; + wbuffer_rewind(wb); + break; + } + case REMOTE_UI_REQ_LOGOUT: { + resp.resp_code = remote_ui_service_logout(ui); + resp.data_type = REMOTE_UI_DATA_TYPE_NONE; + resp.type = REMOTE_UI_RESP_LOGOUT; + wbuffer_rewind(wb); + break; + } + case REMOTE_UI_REQ_GET_DEV_INFO: { + remote_ui_dev_info_t info; + memset(&info, 0x00, sizeof(info)); + resp.resp_code = remote_ui_service_get_dev_info(ui, &info); + resp.data_type = REMOTE_UI_DATA_TYPE_UBJSON; + resp.type = REMOTE_UI_RESP_GET_DEV_INFO; + writer = remote_ui_service_get_writer(ui); + ret = remote_ui_dev_info_write(writer, &info); + break; + } + case REMOTE_UI_REQ_REBOOT: { + rbuffer_t rb; + uint32_t reboot_type = REMOTE_UI_REBOOT_DEFAULT; + rbuffer_init(&rb, wb->data, wb->cursor); + + rbuffer_read_uint32(&rb, &reboot_type); + resp.resp_code = remote_ui_service_reboot(ui, reboot_type); + resp.data_type = REMOTE_UI_DATA_TYPE_NONE; + resp.type = REMOTE_UI_RESP_REBOOT; + wbuffer_rewind(wb); + break; + } + case REMOTE_UI_REQ_UPLOAD_FILE_BEGIN: { + const char* filename = (const char*)(wb->data); + filename = path_prepend_app_root(local_file, filename); + resp.resp_code = remote_ui_service_upload_file(ui, filename); + return RET_OK; + break; + } + case REMOTE_UI_REQ_DOWNLOAD_FILE_BEGIN: { + const char* filename = (const char*)(wb->data); + + if (tk_str_eq(filename, REMOTE_UI_FILE_SCREEN_SHOT)) { + filename = path_prepend_temp_path(local_file, filename); + resp.resp_code = remote_ui_service_take_screen_shot(ui, filename); + } else if (tk_str_eq(filename, REMOTE_UI_FILE_MANIFEST)) { + filename = path_prepend_temp_path(local_file, filename); + resp.resp_code = remote_ui_service_prepare_manifest(ui, filename); + } else if (tk_str_eq(filename, REMOTE_UI_FILE_XML_SOURCE)) { + filename = path_prepend_temp_path(local_file, filename); + resp.resp_code = remote_ui_service_prepare_xml_source(ui, filename); + } else { + filename = path_prepend_app_root(local_file, filename); + } + + resp.resp_code = remote_ui_service_download_file(ui, filename); + return RET_OK; + } + case REMOTE_UI_REQ_CREATE_DIR: { + const char* filename = (const char*)(wb->data); + filename = path_prepend_app_root(local_file, filename); + + resp.resp_code = remote_ui_service_create_dir(ui, filename); + resp.data_type = REMOTE_UI_DATA_TYPE_NONE; + resp.type = REMOTE_UI_RESP_CREATE_DIR; + wbuffer_rewind(wb); + break; + } + case REMOTE_UI_REQ_REMOVE_DIR: { + const char* filename = (const char*)(wb->data); + filename = path_prepend_app_root(local_file, filename); + + resp.resp_code = remote_ui_service_remove_dir(ui, filename); + resp.data_type = REMOTE_UI_DATA_TYPE_NONE; + resp.type = REMOTE_UI_RESP_REMOVE_DIR; + wbuffer_rewind(wb); + break; + } + case REMOTE_UI_REQ_REMOVE_FILE: { + const char* filename = (const char*)(wb->data); + filename = path_prepend_app_root(local_file, filename); + resp.resp_code = remote_ui_service_remove_file(ui, filename); + resp.data_type = REMOTE_UI_DATA_TYPE_NONE; + resp.type = REMOTE_UI_RESP_REMOVE_FILE; + wbuffer_rewind(wb); + break; + } + case REMOTE_UI_REQ_OPEN_WINDOW: { + const char* name = tk_object_get_prop_str(obj, REMOTE_UI_KEY_NAME); + const char* xml = tk_object_get_prop_str(obj, REMOTE_UI_KEY_XML); + const char* init_json = tk_object_get_prop_str(obj, REMOTE_UI_KEY_INIT); + resp.resp_code = remote_ui_service_open_window(ui, name, xml, init_json); + resp.data_type = REMOTE_UI_DATA_TYPE_NONE; + resp.type = REMOTE_UI_RESP_OPEN_WINDOW; + wbuffer_rewind(wb); + break; + } + case REMOTE_UI_REQ_OPEN_DIALOG: { + const char* type = tk_object_get_prop_str(obj, REMOTE_UI_KEY_TYPE); + const char* title = tk_object_get_prop_str(obj, REMOTE_UI_KEY_TITLE); + const char* content = tk_object_get_prop_str(obj, REMOTE_UI_KEY_CONTENT); + uint32_t duration = tk_object_get_prop_uint32(obj, REMOTE_UI_KEY_DURATION, 3000); + + resp.resp_code = remote_ui_service_open_dialog(ui, type, title, content, duration); + resp.data_type = REMOTE_UI_DATA_TYPE_NONE; + resp.type = REMOTE_UI_RESP_OPEN_WINDOW; + wbuffer_rewind(wb); + break; + } + case REMOTE_UI_REQ_BACK_TO_PREV: { + resp.resp_code = remote_ui_service_back_to_prev(ui); + resp.data_type = REMOTE_UI_DATA_TYPE_NONE; + resp.type = REMOTE_UI_RESP_BACK_TO_PREV; + wbuffer_rewind(wb); + break; + } + case REMOTE_UI_REQ_BACK_TO_HOME: { + resp.resp_code = remote_ui_service_back_to_home(ui); + resp.data_type = REMOTE_UI_DATA_TYPE_NONE; + resp.type = REMOTE_UI_RESP_BACK_TO_HOME; + wbuffer_rewind(wb); + break; + } + case REMOTE_UI_REQ_CLOSE_WINDOW: { + const char* name = (const char*)(wb->data); + resp.resp_code = remote_ui_service_close_window(ui, name); + resp.data_type = REMOTE_UI_DATA_TYPE_NONE; + resp.type = REMOTE_UI_RESP_CLOSE_WINDOW; + wbuffer_rewind(wb); + break; + } + case REMOTE_UI_REQ_SET_PROP: { + value_t v; + const char* target = tk_object_get_prop_str(obj, REMOTE_UI_KEY_TARGET); + const char* name = tk_object_get_prop_str(obj, REMOTE_UI_KEY_NAME); + + if (tk_object_get_prop(obj, REMOTE_UI_KEY_VALUE, &v) == RET_OK) { + resp.resp_code = remote_ui_service_set_prop(ui, target, name, &v); + } else { + resp.resp_code = RET_FAIL; + } + resp.data_type = REMOTE_UI_DATA_TYPE_NONE; + wbuffer_rewind(wb); + break; + } + case REMOTE_UI_REQ_GET_PROP: { + const char* str = NULL; + const char* target = tk_object_get_prop_str(obj, REMOTE_UI_KEY_TARGET); + const char* name = tk_object_get_prop_str(obj, REMOTE_UI_KEY_NAME); + + value_set_int(&v, 0); + resp.resp_code = remote_ui_service_get_prop(ui, target, name, &v); + resp.data_type = REMOTE_UI_DATA_TYPE_STRING; + str = value_str_ex(&v, buff, sizeof(buff)); + wbuffer_rewind(wb); + wbuffer_write_string(wb, str); + break; + } + case REMOTE_UI_REQ_SET_LANGUAGE: { + const char* language = (const char*)(wb->data); + resp.resp_code = remote_ui_service_set_language(ui, language); + resp.data_type = REMOTE_UI_DATA_TYPE_NONE; + resp.type = REMOTE_UI_RESP_SET_LANGUAGE; + wbuffer_rewind(wb); + break; + } + case REMOTE_UI_REQ_SET_THEME: { + const char* theme = (const char*)(wb->data); + resp.resp_code = remote_ui_service_set_theme(ui, theme); + resp.data_type = REMOTE_UI_DATA_TYPE_NONE; + resp.type = REMOTE_UI_RESP_SET_THEME; + wbuffer_rewind(wb); + break; + } + case REMOTE_UI_REQ_EXEC_FSCRIPT: { + const char* script = (const char*)(wb->data); + + value_set_int(&v, 0); + resp.resp_code = remote_ui_service_exec_script(ui, script, &v); + resp.data_type = REMOTE_UI_DATA_TYPE_NONE; + resp.type = REMOTE_UI_RESP_EXEC_FSCRIPT; + wbuffer_rewind(wb); + wbuffer_write_string(wb, value_str_ex(&v, buff, sizeof(buff))); + + break; + } + case REMOTE_UI_REQ_ON_EVENT: { + const char* target = tk_object_get_prop_str(obj, REMOTE_UI_KEY_TARGET); + uint32_t event_type = tk_object_get_prop_int(obj, REMOTE_UI_KEY_EVENT, 0); + resp.resp_code = remote_ui_service_on_event(ui, target, event_type); + resp.data_type = REMOTE_UI_DATA_TYPE_NONE; + resp.type = REMOTE_UI_RESP_ON_EVENT; + wbuffer_rewind(wb); + break; + } + case REMOTE_UI_REQ_OFF_EVENT: { + const char* target = tk_object_get_prop_str(obj, REMOTE_UI_KEY_TARGET); + uint32_t event_type = tk_object_get_prop_int(obj, REMOTE_UI_KEY_EVENT, 0); + resp.resp_code = remote_ui_service_off_event(ui, target, event_type); + resp.data_type = REMOTE_UI_DATA_TYPE_NONE; + resp.type = REMOTE_UI_RESP_OFF_EVENT; + wbuffer_rewind(wb); + break; + } + case REMOTE_UI_REQ_SEND_EVENT: { + event_t* e = NULL; + const char* target = tk_object_get_prop_str(obj, REMOTE_UI_KEY_TARGET); + uint32_t event_type = tk_object_get_prop_int(obj, REMOTE_UI_KEY_EVENT, 0); + + switch (event_type) { + case EVT_CLICK: + case EVT_POINTER_DOWN: + case EVT_POINTER_UP: + case EVT_POINTER_MOVE: { + pointer_event_t evt; + int x = tk_object_get_prop_int(obj, REMOTE_UI_KEY_X, 0); + int y = tk_object_get_prop_int(obj, REMOTE_UI_KEY_Y, 0); + e = pointer_event_init(&evt, event_type, NULL, x, y); + break; + } + case EVT_KEY_DOWN: + case EVT_KEY_UP: { + key_event_t evt; + uint32_t key = tk_object_get_prop_int(obj, REMOTE_UI_KEY_CODE, 0); + e = key_event_init(&evt, event_type, NULL, key); + break; + } + default: + break; + } + if (e != NULL) { + resp.resp_code = remote_ui_service_send_event(ui, target, e); + } else { + resp.resp_code = RET_FAIL; + } + resp.data_type = REMOTE_UI_DATA_TYPE_NONE; + resp.type = REMOTE_UI_RESP_SEND_EVENT; + wbuffer_rewind(wb); + break; + } + default: { + ret = RET_NOT_IMPL; + break; + } + } + TK_OBJECT_UNREF(obj); + + return remote_ui_service_send_resp(ui->io, resp.type, resp.data_type, resp.resp_code, wb); +} + +static ret_t remote_ui_service_dispatch(remote_ui_service_t* ui) { + ret_t ret = RET_OK; + remote_ui_msg_header_t header; + return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + + memset(&header, 0x00, sizeof(header)); + ret = remote_ui_service_read_req(ui->io, &header, &(ui->wb)); + return_value_if_fail(ret == RET_OK, ret); + + return remote_ui_service_dispatch_impl(ui, &header, &(ui->wb)); +} + +static ret_t remote_ui_service_destroy(remote_ui_service_t* ui) { + return_value_if_fail(ui != NULL, RET_BAD_PARAMS); + + wbuffer_deinit(&(ui->wb)); + TKMEM_FREE(ui); + + return RET_OK; +} diff --git a/src/remote_ui/service/remote_ui_service.h b/src/remote_ui/service/remote_ui_service.h new file mode 100644 index 000000000..54968c1d5 --- /dev/null +++ b/src/remote_ui/service/remote_ui_service.h @@ -0,0 +1,82 @@ +/** + * File: remote_ui_service.h + * Author: AWTK Develop Team + * Brief: remote ui service + * + * Copyright (c) 2018 - 2023 Guangzhou ZHIYUAN Electronics Co.,Ltd. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * License file for more details. + * + */ + +/** + * History: + * ================================================================ + * 2023-11-04 Li XianJing created + * + */ + +#ifndef TK_REMOTE_UI_SERVICE_H +#define TK_REMOTE_UI_SERVICE_H + +#include "tkc/buffer.h" +#include "tkc/darray.h" +#include "tkc/iostream.h" +#include "ubjson/ubjson_writer.h" +#include "service/service.h" +#include "remote_ui/shared/remote_ui_types_def.h" + +BEGIN_C_DECLS + +/** + * @class remote_ui_service_args_t + * remote ui服务端启动参数。 +*/ +typedef struct _remote_ui_service_args_t { + /** + * @property {tk_service_auth_t} auth + * @annotation ["readable"] + * 登陆认证函数。 + */ + tk_service_auth_t auth; +} remote_ui_service_args_t; + +/** + * @class remote_ui_service_t + * remote ui服务端。 +*/ +typedef struct _remote_ui_service_t { + tk_service_t service; + + /** + * @property {tk_iostream_t*} io + * @annotation ["readable"] + * IO stream。 + */ + tk_iostream_t* io; + + /*private*/ + wbuffer_t wb; + ubjson_writer_t writer; + tk_object_t* event_handlers; + bool_t is_login; + tk_service_auth_t auth; +} remote_ui_service_t; + +/** + * @method remote_ui_service_create + * 创建remote ui客户端。 + * + * @param {tk_iostream_t*} io IO流(由service释放)。 + * @param {void*} args 参数。 + * + * @return {tk_service_t*} 返回service对象。 + */ +tk_service_t* remote_ui_service_create(tk_iostream_t* io, void* args); + +END_C_DECLS + +#endif /*TK_REMOTE_UI_SERVICE_H*/ diff --git a/src/remote_ui/shared/remote_ui_types_def.h b/src/remote_ui/shared/remote_ui_types_def.h new file mode 100644 index 000000000..aac731f5f --- /dev/null +++ b/src/remote_ui/shared/remote_ui_types_def.h @@ -0,0 +1,465 @@ +/** + * File: remote_ui_types_def.h + * Author: AWTK Develop Team + * Brief: remote ui types def + * + * Copyright (c) 2018 - 2023 Guangzhou ZHIYUAN Electronics Co.,Ltd. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * License file for more details. + * + */ + +/** + * History: + * ================================================================ + * 2023-11-04 Li XianJing created + * + */ + +#ifndef TK_REMOTE_UI_TYPES_DEF_H +#define TK_REMOTE_UI_TYPES_DEF_H + +#include "tkc/types_def.h" + +BEGIN_C_DECLS + +/** + * @enum remote_ui_msg_code_t + * @prefix REMOTE_UI_ + * 消息类型。 + * + */ +typedef enum _remote_ui_msg_code_t { + /** + * @const REMOTE_UI_MSG_NONE + * 无效消息。 + */ + REMOTE_UI_MSG_NONE = 0, + /** + * @const REMOTE_UI_REQ_LOGIN + * 登录请求。 + */ + REMOTE_UI_REQ_LOGIN, + /** + * @const REMOTE_UI_REQ_LOGOUT + * 登出请求。 + */ + REMOTE_UI_REQ_LOGOUT, + /** + * @const REMOTE_UI_REQ_GET_DEV_INFO + * 获取设备信息。 + */ + REMOTE_UI_REQ_GET_DEV_INFO, + /** + * @const REMOTE_UI_REQ_REBOOT + * 重新加载请求。 + */ + REMOTE_UI_REQ_REBOOT, + /** + * @const REMOTE_UI_REQ_UPLOAD_FILE_BEGIN + * 上传文件请求开始。 + */ + REMOTE_UI_REQ_UPLOAD_FILE_BEGIN, + /** + * @const REMOTE_UI_REQ_UPLOAD_FILE_DATA + * 上传文件请求数据。 + */ + REMOTE_UI_REQ_UPLOAD_FILE_DATA, + /** + * @const REMOTE_UI_REQ_UPLOAD_FILE_END + * 上传文件请求结束。 + */ + REMOTE_UI_REQ_UPLOAD_FILE_END, + /** + * @const REMOTE_UI_REQ_DOWNLOAD_FILE_BEGIN + * 下载文件请求。 + */ + REMOTE_UI_REQ_DOWNLOAD_FILE_BEGIN, + /** + * @const REMOTE_UI_REQ_CREATE_DIR + * 创建目录请求。 + */ + REMOTE_UI_REQ_CREATE_DIR, + /** + * @const REMOTE_UI_REQ_REMOVE_DIR + * 删除目录请求。 + */ + REMOTE_UI_REQ_REMOVE_DIR, + /** + * @const REMOTE_UI_REQ_REMOVE_FILE + * 删除文件请求。 + */ + REMOTE_UI_REQ_REMOVE_FILE, + /** + * @const REMOTE_UI_REQ_ON_EVENT + * 注册事件请求。 + */ + REMOTE_UI_REQ_ON_EVENT, + /** + * @const REMOTE_UI_REQ_OFF_EVENT + * 注销事件请求。 + */ + REMOTE_UI_REQ_OFF_EVENT, + /** + * @const REMOTE_UI_REQ_SEND_EVENT + * 发送事件请求。 + */ + REMOTE_UI_REQ_SEND_EVENT, + /** + * @const REMOTE_UI_REQ_OPEN_WINDOW + * 打开窗口请求。 + */ + REMOTE_UI_REQ_OPEN_WINDOW, + /** + * @const REMOTE_UI_REQ_OPEN_DIALOG + * 打开基本对话框请求。 + */ + REMOTE_UI_REQ_OPEN_DIALOG, + /** + * @const REMOTE_UI_REQ_CLOSE_WINDOW + * 关闭窗口请求。 + */ + REMOTE_UI_REQ_CLOSE_WINDOW, + /** + * @const REMOTE_UI_REQ_BACK_TO_PREV + * 返回请求。 + */ + REMOTE_UI_REQ_BACK_TO_PREV, + /** + * @const REMOTE_UI_REQ_BACK_TO_HOME + * 返回主页请求。 + */ + REMOTE_UI_REQ_BACK_TO_HOME, + /** + * @const REMOTE_UI_REQ_SET_PROP + * 设置属性请求。 + */ + REMOTE_UI_REQ_SET_PROP, + /** + * @const REMOTE_UI_REQ_GET_PROP + * 获取属性请求。 + */ + REMOTE_UI_REQ_GET_PROP, + /** + * @const REMOTE_UI_REQ_SET_THEME + * 设置主题请求。 + */ + REMOTE_UI_REQ_SET_THEME, + /** + * @const REMOTE_UI_REQ_SET_LANGUAGE + * 设置语言请求。 + */ + REMOTE_UI_REQ_SET_LANGUAGE, + /** + * @const REMOTE_UI_REQ_GET_XML_SOURCE + * 获取xml源码请求。 + */ + REMOTE_UI_REQ_GET_XML_SOURCE, + /** + * @const REMOTE_UI_REQ_EXEC_FSCRIPT + * 执行脚本请求。 + */ + REMOTE_UI_REQ_EXEC_FSCRIPT, + + /** + * @const REMOTE_UI_RESP_LOGIN + * 登录响应。 + */ + REMOTE_UI_RESP_LOGIN, + /** + * @const REMOTE_UI_RESP_LOGOUT + * 登出响应。 + */ + REMOTE_UI_RESP_LOGOUT, + /** + * @const REMOTE_UI_RESP_GET_DEV_INFO + * 获取设备信息响应。 + */ + REMOTE_UI_RESP_GET_DEV_INFO, + /** + * @const REMOTE_UI_RESP_REBOOT + * 重新加载响应。 + */ + REMOTE_UI_RESP_REBOOT, + /** + * @const REMOTE_UI_RESP_UPLOAD_FILE_BEGIN + * 上传文件开始响应。 + */ + REMOTE_UI_RESP_UPLOAD_FILE_BEGIN, + /** + * @const REMOTE_UI_RESP_UPLOAD_FILE_DATA + * 上传文件数据响应。 + */ + REMOTE_UI_RESP_UPLOAD_FILE_DATA, + /** + * @const REMOTE_UI_RESP_UPLOAD_FILE_END + * 上传文件结束响应。 + */ + REMOTE_UI_RESP_UPLOAD_FILE_END, + /** + * @const REMOTE_UI_RESP_DOWNLOAD_FILE_BEGIN + * 下载文件开始响应。 + */ + REMOTE_UI_RESP_DOWNLOAD_FILE_BEGIN, + /** + * @const REMOTE_UI_RESP_DOWNLOAD_FILE_DATA + * 下载文件数据响应。 + */ + REMOTE_UI_RESP_DOWNLOAD_FILE_DATA, + /** + * @const REMOTE_UI_RESP_DOWNLOAD_FILE_END + * 下载文件数据响应。 + */ + REMOTE_UI_RESP_DOWNLOAD_FILE_END, + /** + * @const REMOTE_UI_RESP_CREATE_DIR + * 创建目录响应。 + */ + REMOTE_UI_RESP_CREATE_DIR, + /** + * @const REMOTE_UI_RESP_REMOVE_DIR + * 删除目录响应。 + */ + REMOTE_UI_RESP_REMOVE_DIR, + /** + * @const REMOTE_UI_RESP_REMOVE_FILE + * 删除文件响应。 + */ + REMOTE_UI_RESP_REMOVE_FILE, + /** + * @const REMOTE_UI_RESP_ON_EVENT + * 注册事件响应。 + */ + REMOTE_UI_RESP_ON_EVENT, + /** + * @const REMOTE_UI_RESP_OFF_EVENT + * 注销事件响应。 + */ + REMOTE_UI_RESP_OFF_EVENT, + /** + * @const REMOTE_UI_RESP_SEND_EVENT + * 发送事件响应。 + */ + REMOTE_UI_RESP_SEND_EVENT, + /** + * @const REMOTE_UI_RESP_OPEN_WINDOW + * 打开窗口响应。 + */ + REMOTE_UI_RESP_OPEN_WINDOW, + /** + * @const REMOTE_UI_RESP_OPEN_DIALOG + * 打开基本对话框响应。 + */ + REMOTE_UI_RESP_OPEN_DIALOG, + /** + * @const REMOTE_UI_RESP_CLOSE_WINDOW + * 关闭窗口响应。 + */ + REMOTE_UI_RESP_CLOSE_WINDOW, + /** + * @const REMOTE_UI_RESP_BACK_TO_PREV + * 返回响应。 + */ + REMOTE_UI_RESP_BACK_TO_PREV, + /** + * @const REMOTE_UI_RESP_BACK_TO_HOME + * 返回主页响应。 + */ + REMOTE_UI_RESP_BACK_TO_HOME, + /** + * @const REMOTE_UI_RESP_SET_PROP + * 设置属性响应。 + */ + REMOTE_UI_RESP_SET_PROP, + /** + * @const REMOTE_UI_RESP_GET_PROP + * 获取属性响应。 + */ + REMOTE_UI_RESP_GET_PROP, + /** + * @const REMOTE_UI_RESP_SET_THEME + * 设置主题响应。 + */ + REMOTE_UI_RESP_SET_THEME, + /** + * @const REMOTE_UI_RESP_SET_LANGUAGE + * 设置语言响应。 + */ + REMOTE_UI_RESP_SET_LANGUAGE, + /** + * @const REMOTE_UI_RESP_GET_XML_SOURCE + * 获取xml源码响应。 + */ + REMOTE_UI_RESP_GET_XML_SOURCE, + /** + * @const REMOTE_UI_RESP_EXEC_FSCRIPT + * 执行脚本响应。 + */ + REMOTE_UI_RESP_EXEC_FSCRIPT, + /** + * @const REMOTE_UI_NOTIFY + * 事件通知。 + */ + REMOTE_UI_NOTIFY, +} remote_ui_msg_code_t; + +/** + * @enum remote_ui_data_type_t + * @prefix REMOTE_UI_DATA_TYPE_ + * 数据类型。 +*/ +typedef enum _remote_ui_data_type_t { + /** + * @const REMOTE_UI_DATA_TYPE_NONE + * 无效数据类型。 + */ + REMOTE_UI_DATA_TYPE_NONE = 0, + /** + * @const REMOTE_UI_DATA_TYPE_UBJSON + * JSON数据类型。 + */ + REMOTE_UI_DATA_TYPE_UBJSON, + /** + * @const REMOTE_UI_DATA_TYPE_STRING + * 字符串数据类型。 + */ + REMOTE_UI_DATA_TYPE_STRING, + /** + * @const REMOTE_UI_DATA_TYPE_BINARY + * 二进制数据类型。 + */ + REMOTE_UI_DATA_TYPE_BINARY +} remote_ui_data_type_t; + +/** + * @class remote_ui_msg_header_t + * 消息头。 +*/ +typedef struct _remote_ui_msg_header_t { + /** + * @property {uint32_t} size + * 消息体的大小。 + */ + uint32_t size; + /** + * @property {uint16_t} type + * 消息类型。 + */ + uint16_t type; + /** + * @property {uint8_t} data_type + * 数据类型。 + */ + uint8_t data_type; + /** + * @property {uint8_t} resp_code + * 响应码(仅适用于resp)。 + */ + uint8_t resp_code; +} remote_ui_msg_header_t; + +/** + * @class remote_ui_dev_info_t + * 设备信息。 +*/ +typedef struct _remote_ui_dev_info_t { + /** + * @property {char*} name + * 设备名称。 + */ + char name[64]; + /** + * @property {char*} version + * 设备版本。 + */ + char version[64]; + /** + * @property {char*} os + * 操作系统。 + */ + char os[16]; + + /** + * @property {char*} arch + * CPU架构。 + */ + char arch[16]; + + /** + * @property {uint32_t} screen_width + * 屏幕宽度。 + */ + uint32_t screen_width; + /** + * @property {uint32_t} screen_height + * 屏幕高度。 + */ + uint32_t screen_height; + /** + * @property {uint32_t} dpi + * 屏幕DPI。 + */ + uint32_t dpi; +} remote_ui_dev_info_t; + +/** + * @enum remote_ui_reboot_type_t + * @prefix REMOTE_UI_REBOOT_ + * 重启类型。 +*/ +typedef enum _remote_ui_reboot_type_t { + /** + * @const REMOTE_UI_REBOOT_DEFAULT + * 重启。 + */ + REMOTE_UI_REBOOT_DEFAULT = 0, + /** + * @const REMOTE_UI_REBOOT_RELOAD + * 重新加载。 + */ + REMOTE_UI_REBOOT_RELOAD, + /** + * @const REMOTE_UI_REBOOT_POWEROFF + * 关机。 + */ + REMOTE_UI_REBOOT_POWEROFF +} remote_ui_reboot_type_t; + +#define REMOTE_UI_KEY_USERNAME "username" +#define REMOTE_UI_KEY_PASSWORD "password" + +#define REMOTE_UI_KEY_DEV_NAME "dev_name" +#define REMOTE_UI_KEY_DEV_OS "dev_os" +#define REMOTE_UI_KEY_DEV_ARCH "dev_arch" +#define REMOTE_UI_KEY_DEV_VERSION "dev_version" +#define REMOTE_UI_KEY_DEV_SCREEN_WIDTH "dev_screen_width" +#define REMOTE_UI_KEY_DEV_SCREEN_HEIGHT "dev_screen_height" +#define REMOTE_UI_KEY_DEV_DPI "dev_dpi" +#define REMOTE_UI_KEY_TARGET "target" +#define REMOTE_UI_KEY_NAME "name" +#define REMOTE_UI_KEY_VALUE "value" +#define REMOTE_UI_KEY_EVENT "event" +#define REMOTE_UI_KEY_XML "xml" +#define REMOTE_UI_KEY_INIT "init" +#define REMOTE_UI_KEY_X "x" +#define REMOTE_UI_KEY_Y "y" +#define REMOTE_UI_KEY_CODE "code" +#define REMOTE_UI_KEY_TYPE "type" +#define REMOTE_UI_KEY_TITLE "title" +#define REMOTE_UI_KEY_CONTENT "content" +#define REMOTE_UI_KEY_DURATION "duration" +#define REMOTE_UI_FILE_MANIFEST "__manifest__.txt" +#define REMOTE_UI_FILE_XML_SOURCE "__xml_source__.xml" +#define REMOTE_UI_FILE_SCREEN_SHOT "__screen_shot__.png" + +#define REMOTE_UI_DIALOG_TYPE_CONFIRM "confirm" +#define REMOTE_UI_DIALOG_TYPE_WARN "warn" +#define REMOTE_UI_DIALOG_TYPE_INFO "info" +#define REMOTE_UI_DIALOG_TYPE_TOAST "toast" + +END_C_DECLS + +#endif /*TK_REMOTE_UI_TYPES_DEF_H*/ diff --git a/src/service/service.c b/src/service/service.c new file mode 100644 index 000000000..638a30cc3 --- /dev/null +++ b/src/service/service.c @@ -0,0 +1,147 @@ +/** + * File: service.h + * Author: AWTK Develop Team + * Brief: service interface + * + * Copyright (c) 2018 - 2023 Guangzhou ZHIYUAN Electronics Co.,Ltd. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * License file for more details. + * + */ + +/** + * History: + * ================================================================ + * 2023-11-05 Li XianJing created + * + */ +#include "tkc/url.h" +#include "tkc/utils.h" +#include "service/service.h" +#include "tkc/event_source_fd.h" +#include "streams/inet/iostream_tcp.h" +#include "streams/serial/iostream_serial.h" + +#include "tkc/socket_helper.h" + +ret_t tk_service_dispatch(tk_service_t* service) { + return_value_if_fail(service != NULL && service->dispatch != NULL, RET_BAD_PARAMS); + + return service->dispatch(service); +} + +ret_t tk_service_destroy(tk_service_t* service) { + return_value_if_fail(service != NULL && service->destroy != NULL, RET_BAD_PARAMS); + + return service->destroy(service); +} + +static ret_t tk_service_on_data(event_source_t* source) { + event_source_fd_t* event_source_fd = (event_source_fd_t*)source; + tk_service_t* service = (tk_service_t*)(event_source_fd->ctx); + + if (tk_service_dispatch(service) != RET_OK) { + tk_service_destroy(service); + log_debug("client disconnected\n"); + return RET_REMOVE; + } else { + return RET_OK; + } +} + +static ret_t tk_service_tcp_on_client(event_source_t* source) { + event_source_fd_t* event_source_fd = (event_source_fd_t*)source; + tk_service_create_t create = (tk_service_create_t)(event_source_fd->ctx); + + int listen_sock = event_source_get_fd(source); + int sock = tcp_accept(listen_sock); + + if (sock >= 0) { + log_debug("client connected:%d\n", sock); + tk_iostream_t* io = tk_iostream_tcp_create(sock); + if (io != NULL) { + tk_service_t* service = create(io, event_source_fd->ctx2); + if (service != NULL) { + event_source_manager_t* esm = source->manager; + event_source_t* client_source = event_source_fd_create(sock, tk_service_on_data, service); + event_source_manager_add(esm, client_source); + TK_OBJECT_UNREF(client_source); + } else { + TK_OBJECT_UNREF(io); + } + } else { + log_debug("oom! disconnected:%d\n", sock); + tk_socket_close(sock); + } + } else { + log_debug("error disconnected:%d\n", sock); + tk_socket_close(sock); + } + + return RET_OK; +} + +static ret_t tk_service_start_tcp(event_source_manager_t* esm, const char* url, + tk_service_create_t create, void* args) { + int port = 0; + int listen_sock = -1; + event_source_t* source = NULL; + url_t* aurl = url_create(url); + return_value_if_fail(esm != NULL && aurl != NULL && create != NULL, RET_BAD_PARAMS); + + port = aurl->port; + listen_sock = tcp_listen(port); + url_destroy(aurl); + return_value_if_fail(listen_sock >= 0, RET_BAD_PARAMS); + + log_debug("listen on %d listen_sock=%d\n", port, listen_sock); + + source = event_source_fd_create(listen_sock, tk_service_tcp_on_client, create); + return_value_if_fail(source != NULL, RET_OOM); + EVENT_SOURCE_FD(source)->ctx2 = args; + event_source_manager_add(esm, source); + OBJECT_UNREF(source); + log_debug("service start: %s\n", url); + + return RET_OK; +} + +static ret_t tk_service_start_serial(event_source_manager_t* esm, const char* url, + tk_service_create_t create, void* args) { + tk_iostream_t* io = NULL; + tk_service_t* service = NULL; + return_value_if_fail(esm != NULL && create != NULL, RET_BAD_PARAMS); + + io = tk_iostream_serial_create_ex(url); + return_value_if_fail(io != NULL, RET_BAD_PARAMS); + + service = create(io, args); + if (service != NULL) { + int fd = tk_object_get_prop_int(TK_OBJECT(io), TK_STREAM_PROP_FD, -1); + event_source_t* client_source = event_source_fd_create(fd, tk_service_on_data, service); + event_source_manager_add(esm, client_source); + TK_OBJECT_UNREF(client_source); + log_debug("service start: %s\n", url); + } else { + TK_OBJECT_UNREF(io); + } + + return RET_OK; +} + +ret_t tk_service_start(event_source_manager_t* esm, const char* url, tk_service_create_t create, + void* args) { + return_value_if_fail(esm != NULL && create != NULL, RET_BAD_PARAMS); + + if (tk_str_start_with(url, STR_SCHEMA_TCP)) { + return tk_service_start_tcp(esm, url, create, args); + } else if (tk_str_start_with(url, STR_SCHEMA_SERIAL)) { + return tk_service_start_serial(esm, url, create, args); + } else { + log_debug("not supported: %s\n", url); + return RET_NOT_IMPL; + } +} diff --git a/src/service/service.h b/src/service/service.h new file mode 100644 index 000000000..5fa4a391d --- /dev/null +++ b/src/service/service.h @@ -0,0 +1,86 @@ +/** + * File: service.h + * Author: AWTK Develop Team + * Brief: service interface + * + * Copyright (c) 2018 - 2023 Guangzhou ZHIYUAN Electronics Co.,Ltd. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * License file for more details. + * + */ + +/** + * History: + * ================================================================ + * 2023-11-05 Li XianJing created + * + */ + +#ifndef TK_SERVICE_H +#define TK_SERVICE_H + +#include "tkc/iostream.h" +#include "tkc/event_source_manager.h" + +BEGIN_C_DECLS + +struct _tk_service_t; +typedef struct _tk_service_t tk_service_t; + +typedef tk_service_t* (*tk_service_create_t)(tk_iostream_t* io, void* args); +typedef ret_t (*tk_service_dispatch_t)(tk_service_t* service); +typedef ret_t (*tk_service_destroy_t)(tk_service_t* service); + +typedef ret_t (*tk_service_auth_t)(tk_service_t* service, const char* username, + const char* password); + +/** + * @class tk_service_t + * 服务接口。 + */ +struct _tk_service_t { + tk_service_dispatch_t dispatch; + tk_service_destroy_t destroy; + tk_iostream_t* io; +}; + +/** + * @method tk_service_dispatch + * 处理服务器请求。 + * + * @param {tk_service_t*} service 服务对象。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t tk_service_dispatch(tk_service_t* service); + +/** + * @method tk_service_destroy + * 销毁服务对象。 + * + * @param {tk_service_t*} service 服务对象。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t tk_service_destroy(tk_service_t* service); + +/** + * @method tk_service_start + * 启动服务。 + * + * @param {event_source_manager_t*} esm 事件源管理器。 + * @param {const char*} url 服务地址。 + * @param {tk_service_create_t} create 创建服务对象的函数。 + * @param {void*} args 参数。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t tk_service_start(event_source_manager_t* esm, const char* url, tk_service_create_t create, + void* args); + +END_C_DECLS + +#endif /*TK_SERVICE_H*/ diff --git a/tests/ui_test_data/demo_ui_old.ini b/tests/ui_test_data/demo_ui_old.ini new file mode 100644 index 000000000..cd6a79131 --- /dev/null +++ b/tests/ui_test_data/demo_ui_old.ini @@ -0,0 +1,68 @@ +[create] + url=tcp://localhost:2233 +[login] + username=admin + password=admin +[get_dev_info] + +[create_dir] + path=abc/123 + +[upload] + remote = abc/123/test.txt + local = README.md + +[download] + remote = abc/123/test.txt + local = README.md.download + ret=RET_OK + +[remove_file] + filename=abc/123/test.txt +[remove_dir] + path=abc/123 + +[take_screen_shot] + filename=test.png + +[get_manifest] + +[open_window] + name=button + +[sleep] + time=2000 + +[back] + +[open_window] + name=edit + +[sleep] + time=2000 + +[home] + +[open_window] + name=rich_text + +[sleep] + time=2000 + +[close_window] + name=rich_text + +[set_prop] + target=open:button + name=text + value=Hello + +[get_prop] + target=open:button + name=text + value=Hello + +[logout] + +[close] + diff --git a/tests/ui_test_data/demo_ui_old_confirm.ini b/tests/ui_test_data/demo_ui_old_confirm.ini new file mode 100644 index 000000000..2e7074a92 --- /dev/null +++ b/tests/ui_test_data/demo_ui_old_confirm.ini @@ -0,0 +1,17 @@ +[create] + url=tcp://localhost:2233 + +[login] + username=admin + password=admin + +[confirm] + title=title + content=content + +[back] + +[logout] + +[close] + diff --git a/tests/ui_test_data/demo_ui_old_exec_fscript.ini b/tests/ui_test_data/demo_ui_old_exec_fscript.ini new file mode 100644 index 000000000..8055ab40b --- /dev/null +++ b/tests/ui_test_data/demo_ui_old_exec_fscript.ini @@ -0,0 +1,16 @@ +[create] + url=tcp://localhost:2233 + +[login] + username=admin + password=admin + +[exec_fscript] + fscript=print("hello"); + +[back] + +[logout] + +[close] + diff --git a/tests/ui_test_data/demo_ui_old_get_source.ini b/tests/ui_test_data/demo_ui_old_get_source.ini new file mode 100644 index 000000000..bb14889a3 --- /dev/null +++ b/tests/ui_test_data/demo_ui_old_get_source.ini @@ -0,0 +1,15 @@ +[create] + url=tcp://localhost:2233 + +[login] + username=admin + password=admin + +[get_source] + filename=test.xml +[back] + +[logout] + +[close] + diff --git a/tests/ui_test_data/demo_ui_old_info.ini b/tests/ui_test_data/demo_ui_old_info.ini new file mode 100644 index 000000000..c4909dcb1 --- /dev/null +++ b/tests/ui_test_data/demo_ui_old_info.ini @@ -0,0 +1,17 @@ +[create] + url=tcp://localhost:2233 + +[login] + username=admin + password=admin + +[info] + title=title + content=content + +[back] + +[logout] + +[close] + diff --git a/tests/ui_test_data/demo_ui_old_send_click_events.ini b/tests/ui_test_data/demo_ui_old_send_click_events.ini new file mode 100644 index 000000000..9993e7270 --- /dev/null +++ b/tests/ui_test_data/demo_ui_old_send_click_events.ini @@ -0,0 +1,19 @@ +[create] + url=tcp://localhost:2233 + +[login] + username=admin + password=admin + +[send_event] + target=window + type=click + x = 100 + y = 300 + +[back] + +[logout] + +[close] + diff --git a/tests/ui_test_data/demo_ui_old_send_key_events.ini b/tests/ui_test_data/demo_ui_old_send_key_events.ini new file mode 100644 index 000000000..b8ab23347 --- /dev/null +++ b/tests/ui_test_data/demo_ui_old_send_key_events.ini @@ -0,0 +1,19 @@ +[create] + url=tcp://localhost:2233 + +[login] + username=admin + password=admin + +[send_event] + type=key_down + key=RIGHT + +[send_event] + type=key_up + key=RIGHT + +[logout] + +[close] + diff --git a/tests/ui_test_data/demo_ui_old_send_pointer_events.ini b/tests/ui_test_data/demo_ui_old_send_pointer_events.ini new file mode 100644 index 000000000..8302ab863 --- /dev/null +++ b/tests/ui_test_data/demo_ui_old_send_pointer_events.ini @@ -0,0 +1,31 @@ +[create] + url=tcp://localhost:2233 + +[login] + username=admin + password=admin + +[send_event] + target=window + type=pointer_down + x = 100 + y = 300 + +[send_event] + target=window + type=pointer_move + x = 101 + y = 301 + +[send_event] + target=window + type=pointer_up + x = 100 + y = 300 + +[back] + +[logout] + +[close] + diff --git a/tests/ui_test_data/demo_ui_old_set_get_prop.ini b/tests/ui_test_data/demo_ui_old_set_get_prop.ini new file mode 100644 index 000000000..9cf2b6d02 --- /dev/null +++ b/tests/ui_test_data/demo_ui_old_set_get_prop.ini @@ -0,0 +1,21 @@ +[create] + url=tcp://localhost:2233 + +[login] + username=admin + password=admin + +[set_prop] + target=open:button + name=text + value=Hello + +[get_prop] + target=open:button + name=text + value=Hello + +[logout] + +[close] + diff --git a/tests/ui_test_data/demo_ui_old_set_theme_language.ini b/tests/ui_test_data/demo_ui_old_set_theme_language.ini new file mode 100644 index 000000000..58fd98564 --- /dev/null +++ b/tests/ui_test_data/demo_ui_old_set_theme_language.ini @@ -0,0 +1,17 @@ +[create] + url=tcp://localhost:2233 + +[login] + username=admin + password=admin + +[set_theme] + theme=default + +[set_language] + language=zh_CN + +[logout] + +[close] + diff --git a/tests/ui_test_data/demo_ui_old_toast.ini b/tests/ui_test_data/demo_ui_old_toast.ini new file mode 100644 index 000000000..bf0816fa2 --- /dev/null +++ b/tests/ui_test_data/demo_ui_old_toast.ini @@ -0,0 +1,17 @@ +[create] + url=tcp://localhost:2233 + +[login] + username=admin + password=admin + +[toast] + content=content + duration=3000 + +[back] + +[logout] + +[close] + diff --git a/tests/ui_test_data/demo_ui_old_warn.ini b/tests/ui_test_data/demo_ui_old_warn.ini new file mode 100644 index 000000000..bfd1065ad --- /dev/null +++ b/tests/ui_test_data/demo_ui_old_warn.ini @@ -0,0 +1,17 @@ +[create] + url=tcp://localhost:2233 + +[login] + username=admin + password=admin + +[warn] + title=title + content=content + +[back] + +[logout] + +[close] + diff --git a/tests/widget_test.cc b/tests/widget_test.cc index 81966a12c..44c42b6d6 100644 --- a/tests/widget_test.cc +++ b/tests/widget_test.cc @@ -21,6 +21,7 @@ #include "base/theme_xml.h" #include "base/style_factory.h" #include "base/window.h" +#include "base/window_manager.h" using std::string; #include "common.h" @@ -68,6 +69,46 @@ TEST(Widget, move_to_center) { ASSERT_EQ(b->x, 150); ASSERT_EQ(b->y, 120); + + widget_destroy(w); +} + +TEST(Widget, find_by_path) { + widget_t* w = window_create(NULL, 0, 0, 400, 300); + widget_t* b = button_create(w, 0, 0, 100, 60); + widget_t* label = label_create(b, 0, 0, 100, 60); + + widget_set_name(w, "win"); + ASSERT_EQ(widget_find_by_path(w, STR_PROP_SELF, TRUE), w); + ASSERT_EQ(widget_find_by_path(w, STR_PROP_WINDOW, TRUE), w); + ASSERT_EQ(widget_find_by_path(w, STR_PROP_WINDOW_MANAGER, TRUE), window_manager()); + + widget_set_name(b, "foo"); + ASSERT_EQ(widget_find_by_path(w, STR_PROP_SELF".foo", TRUE), b); + ASSERT_EQ(widget_find_by_path(w, STR_PROP_WINDOW".foo", TRUE), b); + + ASSERT_EQ(widget_find_by_path(w, "foo", TRUE), b); + ASSERT_EQ(widget_find_by_path(w, "foo", TRUE), b); + + widget_set_name(label, "bar"); + + ASSERT_EQ(widget_find_by_path(w, STR_PROP_SELF".bar", TRUE), (widget_t*)NULL); + ASSERT_EQ(widget_find_by_path(w, STR_PROP_WINDOW".bar", TRUE), (widget_t*)NULL); + + ASSERT_EQ(widget_find_by_path(w, "foo.bar", TRUE), label); + ASSERT_EQ(widget_find_by_path(w, "foo.bar", TRUE), label); + + ASSERT_EQ(widget_find_by_path(w, "foo.bar", FALSE), label); + ASSERT_EQ(widget_find_by_path(w, "foo.bar", FALSE), label); + + ASSERT_EQ(widget_find_by_path(w, "bar", TRUE), label); + ASSERT_EQ(widget_find_by_path(w, "bar", TRUE), label); + + ASSERT_EQ(widget_find_by_path(w, "bar", FALSE), (widget_t*)NULL); + ASSERT_EQ(widget_find_by_path(w, "bar", FALSE), (widget_t*)NULL); + + ASSERT_EQ(widget_find_by_path(w, STR_PROP_WINDOW_MANAGER ".win.foo.bar", TRUE), label); + ASSERT_EQ(widget_find_by_path(w, STR_PROP_WINDOW_MANAGER ".win.foo.bar", TRUE), label); widget_destroy(w); } diff --git a/tools/ui_test/SConscript b/tools/ui_test/SConscript new file mode 100755 index 000000000..93da47998 --- /dev/null +++ b/tools/ui_test/SConscript @@ -0,0 +1,6 @@ +import os + +env=DefaultEnvironment().Clone() +BIN_DIR=os.environ['BIN_DIR']; + +env.Program(os.path.join(BIN_DIR, 'ui_test'), Glob('ui_test.c')) diff --git a/tools/ui_test/ui_test.c b/tools/ui_test/ui_test.c new file mode 100644 index 000000000..9b3e99b57 --- /dev/null +++ b/tools/ui_test/ui_test.c @@ -0,0 +1,308 @@ +/** + * File: ui_test.c + * Author: AWTK Develop Team + * Brief: ui test + * + * Copyright (c) 2023 - 2023 Guangzhou ZHIYUAN Electronics Co.,Ltd. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * License file for more details. + * + */ + +/** + * History: + * ================================================================ + * 2023-11-04 Li XianJing created + * + */ + +#include "tkc.h" +#include "base/enums.h" +#include "base/events.h" +#include "base/widget_consts.h" + +#include "conf_io/conf_ini.h" +#include "conf_io/conf_node.h" +#include "remote_ui/client/remote_ui.h" +#include "streams/stream_factory.h" + +static const char* fix_str(const char* str) { + return str != NULL ? str : ""; +} + +static void check_return_code(ret_t ret, const char* expected_ret, const char* name, + const char* arg1, const char* arg2, const char* arg3) { + if (expected_ret != NULL) { + if (ret != ret_code_from_name(expected_ret)) { + log_debug("%s failed(%s != %s)\n", name, ret_code_to_name(ret), expected_ret); + } else { + log_debug("%s (%s %s %s) ok\n", name, fix_str(arg1), fix_str(arg2), fix_str(arg3)); + } + } else { + if (ret == RET_OK) { + log_debug("%s (%s %s %s) ok\n", name, fix_str(arg1), fix_str(arg2), fix_str(arg3)); + } else { + log_debug("%s (%s %s %s) fail\n", name, fix_str(arg1), fix_str(arg2), fix_str(arg3)); + } + } +} + +static void run_script(conf_doc_t* doc, uint32_t times) { + ret_t ret = RET_OK; + remote_ui_t* ui = NULL; + const char* expected_ret = NULL; + conf_node_t* iter = conf_node_get_first_child(doc->root); + + while (iter != NULL) { + const char* name = conf_node_get_name(iter); + + if (tk_str_eq(name, "create")) { + const char* url = conf_node_get_child_value_str(iter, "url", "tcp://localhost:2233"); + tk_iostream_t* io = tk_stream_factory_create_iostream(url); + + break_if_fail(io != NULL); + if (ui != NULL) { + remote_ui_destroy(ui); + } + ui = remote_ui_create(io); + iter = iter->next; + continue; + } + + if (ui == NULL) { + log_debug("ui is null.\n"); + break; + } + + expected_ret = conf_node_get_child_value_str(iter, "ret", NULL); + if (tk_str_eq(name, "login")) { + const char* user = conf_node_get_child_value_str(iter, "user", "admin"); + const char* password = conf_node_get_child_value_str(iter, "password", "admin"); + ret = remote_ui_login(ui, user, password); + check_return_code(ret, expected_ret, name, user, NULL, NULL); + } else if (tk_str_eq(name, "create_dir")) { + const char* path = conf_node_get_child_value_str(iter, "path", NULL); + ret = remote_ui_create_dir(ui, path); + check_return_code(ret, expected_ret, name, path, NULL, NULL); + } else if (tk_str_eq(name, "remove_dir")) { + const char* path = conf_node_get_child_value_str(iter, "path", NULL); + ret = remote_ui_remove_dir(ui, path); + check_return_code(ret, expected_ret, name, path, NULL, NULL); + } else if (tk_str_eq(name, "remove_file")) { + const char* filename = conf_node_get_child_value_str(iter, "filename", NULL); + ret = remote_ui_remove_file(ui, filename); + check_return_code(ret, expected_ret, name, filename, NULL, NULL); + } else if (tk_str_eq(name, "upload")) { + const char* remote = conf_node_get_child_value_str(iter, "remote", NULL); + const char* local = conf_node_get_child_value_str(iter, "local", NULL); + ret = remote_ui_upload_file(ui, remote, local); + check_return_code(ret, expected_ret, name, remote, local, NULL); + } else if (tk_str_eq(name, "download")) { + const char* remote = conf_node_get_child_value_str(iter, "remote", NULL); + const char* local = conf_node_get_child_value_str(iter, "local", NULL); + ret = remote_ui_download_file(ui, remote, local); + check_return_code(ret, expected_ret, name, remote, local, NULL); + } else if (tk_str_eq(name, "get_dev_info")) { + remote_ui_dev_info_t info; + ret = remote_ui_get_dev_info(ui, &info); + check_return_code(ret, expected_ret, name, info.name, info.os, info.arch); + log_debug("width=%d height=%d\n", info.screen_width, info.screen_height); + } else if (tk_str_eq(name, "take_screen_shot")) { + const char* filename = conf_node_get_child_value_str(iter, "filename", NULL); + ret = remote_ui_take_screen_shot(ui, filename); + check_return_code(ret, expected_ret, name, filename, NULL, NULL); + } else if (tk_str_eq(name, "get_source")) { + const char* filename = conf_node_get_child_value_str(iter, "filename", NULL); + ret = remote_ui_get_xml_source(ui, filename); + check_return_code(ret, expected_ret, name, filename, NULL, NULL); + } else if (tk_str_eq(name, "get_manifest")) { + const char* filename = conf_node_get_child_value_str(iter, "filename", "manifest.txt"); + ret = remote_ui_get_manifest(ui, filename); + check_return_code(ret, expected_ret, name, filename, NULL, NULL); + } else if (tk_str_eq(name, "open_window")) { + const char* wname = conf_node_get_child_value_str(iter, "name", NULL); + const char* xml = conf_node_get_child_value_str(iter, "xml", NULL); + const char* init = conf_node_get_child_value_str(iter, "init", NULL); + ret = remote_ui_open_window(ui, wname, xml, init); + check_return_code(ret, expected_ret, name, wname, NULL, NULL); + } else if (tk_str_eq(name, "close_window")) { + const char* wname = conf_node_get_child_value_str(iter, "name", NULL); + ret = remote_ui_close_window(ui, wname); + check_return_code(ret, expected_ret, name, wname, NULL, NULL); + } else if (tk_str_eq(name, "back")) { + ret = remote_ui_back_to_prev(ui); + check_return_code(ret, expected_ret, name, NULL, NULL, NULL); + } else if (tk_str_eq(name, "home")) { + ret = remote_ui_back_to_home(ui); + check_return_code(ret, expected_ret, name, NULL, NULL, NULL); + } else if (tk_str_eq(name, "logout")) { + ret = remote_ui_logout(ui); + check_return_code(ret, expected_ret, name, NULL, NULL, NULL); + } else if (tk_str_eq(name, "set_prop")) { + value_t v; + const char* target = conf_node_get_child_value_str(iter, "target", NULL); + const char* prop = conf_node_get_child_value_str(iter, "name", NULL); + const char* value = conf_node_get_child_value_str(iter, "value", NULL); + + value_set_str(&v, value); + ret = remote_ui_set_prop(ui, target, prop, &v); + check_return_code(ret, expected_ret, name, target, prop, value); + } else if (tk_str_eq(name, "get_prop")) { + value_t v; + const char* target = conf_node_get_child_value_str(iter, "target", NULL); + const char* prop = conf_node_get_child_value_str(iter, "name", NULL); + const char* value = conf_node_get_child_value_str(iter, "value", NULL); + value_set_str(&v, NULL); + ret = remote_ui_get_prop(ui, target, prop, &v); + if (value != NULL) { + if (!tk_str_eq(value, value_str(&v))) { + ret = RET_FAIL; + } + } + check_return_code(ret, expected_ret, name, target, prop, value_str(&v)); + } else if (tk_str_eq(name, "set_theme")) { + const char* theme = conf_node_get_child_value_str(iter, "theme", NULL); + ret = remote_ui_set_theme(ui, theme); + check_return_code(ret, expected_ret, name, theme, NULL, NULL); + } else if (tk_str_eq(name, "set_language")) { + const char* language = conf_node_get_child_value_str(iter, "language", NULL); + ret = remote_ui_set_language(ui, language); + check_return_code(ret, expected_ret, name, language, NULL, NULL); + } else if (tk_str_eq(name, "exec_fscript")) { + str_t str; + const char* fscript = conf_node_get_child_value_str(iter, "fscript", NULL); + str_init(&str, 1000); + ret = remote_ui_exec_fscript(ui, fscript, &str); + check_return_code(ret, expected_ret, name, fscript, str.str, NULL); + str_reset(&str); + } else if (tk_str_eq(name, "send_event")) { + event_t* e = NULL; + const char* target = conf_node_get_child_value_str(iter, "target", NULL); + const char* type = conf_node_get_child_value_str(iter, "type", NULL); + const char* key = conf_node_get_child_value_str(iter, "key", NULL); + const char* x = conf_node_get_child_value_str(iter, "x", NULL); + const char* y = conf_node_get_child_value_str(iter, "y", NULL); + break_if_fail(type != NULL); + + if (target == NULL) { + target = STR_PROP_WINDOW; + } + + if (strstr(type, "pointer") != NULL || strstr(type, "click") != NULL) { + pointer_event_t event; + if (strstr(type, "down") != NULL) { + e = pointer_event_init(&event, EVT_POINTER_DOWN, NULL, tk_atoi(x), tk_atoi(y)); + } else if (strstr(type, "up") != NULL) { + e = pointer_event_init(&event, EVT_POINTER_UP, NULL, tk_atoi(x), tk_atoi(y)); + } else if (strstr(type, "click") != NULL) { + e = pointer_event_init(&event, EVT_CLICK, NULL, tk_atoi(x), tk_atoi(y)); + } else { + e = pointer_event_init(&event, EVT_POINTER_MOVE, NULL, tk_atoi(x), tk_atoi(y)); + } + ret = remote_ui_send_event(ui, target, e); + } else if (strstr(type, "key") != NULL) { + key_event_t event; + const key_type_value_t* kv = keys_type_find(key); + break_if_fail(kv != NULL); + if (strstr(type, "down") != NULL) { + e = key_event_init(&event, EVT_KEY_DOWN, NULL, kv->value); + } else { + e = key_event_init(&event, EVT_KEY_UP, NULL, kv->value); + } + ret = remote_ui_send_event(ui, target, e); + } + check_return_code(ret, expected_ret, name, target, type, NULL); + } else if (tk_str_eq(name, "confirm")) { + const char* title = conf_node_get_child_value_str(iter, "title", NULL); + const char* content = conf_node_get_child_value_str(iter, "content", NULL); + ret = remote_ui_show_confirm(ui, title, content); + check_return_code(ret, expected_ret, name, title, content, NULL); + } else if (tk_str_eq(name, "warn")) { + const char* title = conf_node_get_child_value_str(iter, "title", NULL); + const char* content = conf_node_get_child_value_str(iter, "content", NULL); + ret = remote_ui_show_warn(ui, title, content); + check_return_code(ret, expected_ret, name, title, content, NULL); + } else if (tk_str_eq(name, "info")) { + const char* title = conf_node_get_child_value_str(iter, "title", NULL); + const char* content = conf_node_get_child_value_str(iter, "content", NULL); + ret = remote_ui_show_info(ui, title, content); + check_return_code(ret, expected_ret, name, title, content, NULL); + } else if (tk_str_eq(name, "toast")) { + const char* content = conf_node_get_child_value_str(iter, "content", NULL); + int32_t duration = conf_node_get_child_value_int32(iter, "duration", 3000); + ret = remote_ui_show_toast(ui, duration, content); + check_return_code(ret, expected_ret, name, content, NULL, NULL); + } else if (tk_str_eq(name, "sleep")) { + int32_t time_ms = conf_node_get_child_value_int32(iter, "time", 1000); + sleep_ms(time_ms); + } else if (tk_str_eq(name, "close")) { + remote_ui_destroy(ui); + ui = NULL; + } + + iter = iter->next; + + if (iter == NULL) { + iter = conf_node_get_first_child(doc->root); + times--; + log_debug("=============%u===============\n", times); + } + + if (times == 0) { + break; + } + } + + if (ui != NULL) { + remote_ui_destroy(ui); + ui = NULL; + } +} + +#include "tkc/data_reader_factory.h" +#include "tkc/data_writer_factory.h" +#include "tkc/data_writer_file.h" +#include "tkc/data_writer_wbuffer.h" +#include "tkc/data_reader_file.h" +#include "tkc/data_reader_mem.h" + +int main(int argc, char* argv[]) { + char* data = NULL; + conf_doc_t* doc = NULL; + const char* input = argc > 1 ? argv[1] : "data/fs_default.ini"; + uint32_t times = argc > 2 ? tk_atoi(argv[2]) : 1; + + platform_prepare(); + data_writer_factory_set(data_writer_factory_create()); + data_reader_factory_set(data_reader_factory_create()); + data_writer_factory_register(data_writer_factory(), "file", data_writer_file_create); + data_reader_factory_register(data_reader_factory(), "file", data_reader_file_create); + data_reader_factory_register(data_reader_factory(), "mem", data_reader_mem_create); + data_writer_factory_register(data_writer_factory(), "wbuffer", data_writer_wbuffer_create); + + if (argc < 2) { + log_debug("Usage: %s config times\n", argv[0]); + log_debug(" ex: %s data/tcp.ini\n", argv[0]); + log_debug(" ex: %s data/tcp.ini 10\n", argv[0]); + return 0; + } + + tk_socket_init(); + + data = (char*)file_read(input, NULL); + if (data != NULL) { + doc = conf_doc_load_ini(data); + if (doc != NULL) { + run_script(doc, times); + conf_doc_destroy(doc); + } + TKMEM_FREE(data); + } + + tk_socket_deinit(); + + return 0; +}