From beaddc72e08eea8791b6d5aed9acec35d01852b9 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Mon, 4 Jul 2022 19:33:18 +0900 Subject: [PATCH 01/16] fix the typo Change-Id: Ie2407006a7d008b8a1c5d32413cf9a64669693e5 --- src/dpms/dpms.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dpms/dpms.c b/src/dpms/dpms.c index a85834a..833005f 100644 --- a/src/dpms/dpms.c +++ b/src/dpms/dpms.c @@ -29,7 +29,7 @@ struct ds_tizen_dpms static void dpms_handle_display_destroy(struct wl_listener *listener, void *data); static void dpms_bind(struct wl_client *wl_client, void *data, - uint32_t verison, uint32_t id); + uint32_t version, uint32_t id); WL_EXPORT struct ds_tizen_dpms * ds_tizen_dpms_create(struct wl_display *display) -- 2.7.4 From 0f72718246529b64ae641457786f47d3d5c734e3 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Wed, 6 Jul 2022 14:10:33 +0900 Subject: [PATCH 02/16] add missing files Change-Id: Ifd75994c3ae705441947e2b225adfb08bacc0466 --- include/libds-tizen/input_devicemgr.h | 44 + include/libds-tizen/tbm_server.h | 33 + src/input_devicemgr/input_devicemgr.c | 1551 +++++++++++++++++++++++++++++++++ src/input_devicemgr/input_devicemgr.h | 78 ++ src/input_devicemgr/meson.build | 32 + src/tbm_server/meson.build | 32 + src/tbm_server/pixel_format.c | 61 ++ src/tbm_server/pixel_format.h | 10 + src/tbm_server/tbm_server.c | 268 ++++++ src/tbm_server/tbm_server.h | 34 + tests/test_backend.c | 80 ++ tests/test_compositor.c | 63 ++ tests/test_subsurface.c | 211 +++++ tests/test_surface.c | 162 ++++ 14 files changed, 2659 insertions(+) create mode 100644 include/libds-tizen/input_devicemgr.h create mode 100644 include/libds-tizen/tbm_server.h create mode 100644 src/input_devicemgr/input_devicemgr.c create mode 100644 src/input_devicemgr/input_devicemgr.h create mode 100644 src/input_devicemgr/meson.build create mode 100644 src/tbm_server/meson.build create mode 100644 src/tbm_server/pixel_format.c create mode 100644 src/tbm_server/pixel_format.h create mode 100644 src/tbm_server/tbm_server.c create mode 100644 src/tbm_server/tbm_server.h create mode 100644 tests/test_backend.c create mode 100644 tests/test_compositor.c create mode 100644 tests/test_subsurface.c create mode 100644 tests/test_surface.c diff --git a/include/libds-tizen/input_devicemgr.h b/include/libds-tizen/input_devicemgr.h new file mode 100644 index 0000000..fc1060e --- /dev/null +++ b/include/libds-tizen/input_devicemgr.h @@ -0,0 +1,44 @@ +#ifndef LIBDS_TIZEN_INPUT_DEVICEMGR_H +#define LIBDS_TIZEN_INPUT_DEVICEMGR_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct ds_tizen_input_devicemgr; +struct ds_backend; +struct ds_seat; + +struct ds_tizen_input_devicemgr_keymap_data +{ + char *name; + int keycode; + + struct wl_list link; +}; + +struct ds_tizen_input_devicemgr * +ds_tizen_input_devicemgr_create(struct ds_backend *backend, + struct ds_seat *seat); + +void +ds_tizen_input_devicemgr_add_destroy_listener( + struct ds_tizen_input_devicemgr *devicemgr, + struct wl_listener *listener); + +bool +ds_tizen_input_devicemgr_set_keymap_list( + struct ds_tizen_input_devicemgr *devicemgr, + struct wl_list *list); + +bool +ds_tizen_input_devicemgr_set_output_width_height( + struct ds_tizen_input_devicemgr *devicemgr, + uint32_t width, uint32_t height); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libds-tizen/tbm_server.h b/include/libds-tizen/tbm_server.h new file mode 100644 index 0000000..6e3b148 --- /dev/null +++ b/include/libds-tizen/tbm_server.h @@ -0,0 +1,33 @@ +#ifndef LIBDS_TIZEN_TBM_SERVER_H +#define LIBDS_TIZEN_TBM_SERVER_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct ds_tbm_server; + +struct ds_tbm_client_buffer; + +struct ds_tbm_server * +ds_tbm_server_create(struct wl_display *display); + +void +ds_tbm_server_add_destroy_listener(struct ds_tbm_server *tbm, + struct wl_listener *listener); + +struct ds_tbm_client_buffer * +ds_tbm_client_buffer_from_buffer(struct ds_buffer *ds_buffer); + +tbm_surface_h +ds_tbm_client_buffer_get_tbm_surface(struct ds_tbm_client_buffer *buffer); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/input_devicemgr/input_devicemgr.c b/src/input_devicemgr/input_devicemgr.c new file mode 100644 index 0000000..f690fe7 --- /dev/null +++ b/src/input_devicemgr/input_devicemgr.c @@ -0,0 +1,1551 @@ +#include +#include // gettimeofday() +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "security.h" +#include "input_devicemgr.h" + +#define TIZEN_INPUT_DEVICEMGR_VERSION 4 +#define TIZEN_PRIV_INPUT_GENERATOR "http://tizen.org/privilege/inputgenerator" +#define TIZEN_PRIV_INPUT_BLOCK "http://tizen.org/privilege/internal/inputdevice.block" +#define TIZEN_INPUT_DEVICEMGR_MAX_BTN 16 + +static const struct ds_keyboard_grab_interface devicemgr_keyboard_grab_iface; + +//listeners +static void +backend_handle_destroy(struct wl_listener *listener, void *data); +static void +backend_handle_input_device_add(struct wl_listener *listener, void *data); +static void +seat_handle_destroy(struct wl_listener *listener, void *data); + +//tizen_input_device_manager bind/unbind +static void +device_manager_handle_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id); +static void +device_manager_client_handle_resource_destroy(struct wl_resource *resource); + +//tizen_input_device_manager's handlers for requests +static void +device_manager_handle_block_events(struct wl_client *client, + struct wl_resource *resource, uint32_t serial, + uint32_t clas, uint32_t duration); +static void +device_manager_handle_unblock_events(struct wl_client *client, + struct wl_resource *resource, uint32_t serial); +static void +device_manager_handle_init_generator(struct wl_client *client, + struct wl_resource *resource, uint32_t clas); +static void +device_manager_handle_init_generator_with_name(struct wl_client *client, + struct wl_resource *resource, uint32_t clas, const char *name); +static void +device_manager_handle_deinit_generator(struct wl_client *client, + struct wl_resource *resource, uint32_t clas); +static void +device_manager_handle_generate_key(struct wl_client *client, + struct wl_resource *resource, + const char *keyname, uint32_t pressed); +static void +device_manager_handle_generate_touch(struct wl_client *client, + struct wl_resource *resource, uint32_t type, uint32_t x, uint32_t y, + uint32_t finger); +static void +device_manager_handle_generate_pointer(struct wl_client *client, + struct wl_resource *resource, uint32_t type, uint32_t x, uint32_t y, + uint32_t button); +static void +device_manager_handle_destroy(struct wl_client *client, + struct wl_resource *resource); + +// +static void tz_devicemgr_destroy(struct ds_tizen_input_devicemgr *tz_devicemgr); +static int +tz_devicemgr_init_generator(struct ds_tizen_input_devicemgr *tz_devicemgr, + struct wl_resource *resource, uint32_t clas, const char *name); +static int +tz_devicemgr_deinit_generator(struct ds_tizen_input_devicemgr *tz_devicemgr, + struct wl_resource *resource); +static bool +tz_devicemgr_generate_key(struct ds_input_device *device, int keycode, + int pressed); +static bool +tz_devicemgr_pressed_keys_update(struct ds_tizen_input_devicemgr *tz_devicemgr, + int keycode, bool pressed); +static void +tz_devicemgr_pressed_keys_cleanup(struct ds_tizen_input_devicemgr *tz_devicemgr); +static void +tz_devicemgr_keymap_list_cleanup(struct ds_tizen_input_devicemgr *tz_devicemgr); +static int +tz_devicemgr_keyname_to_keycode(struct wl_list *list, const char *name); + +static bool +tz_devicemgr_generate_touch_move(struct ds_input_device *device, double x, double y, + uint32_t finger); +static bool +tz_devicemgr_generate_touch_down(struct ds_input_device *device, double x, double y, + uint32_t finger); +static bool +tz_devicemgr_generate_touch_up(struct ds_input_device *device, uint32_t finger); +static bool +tz_devicemgr_generate_mouse_move(struct ds_input_device *device, double x, double y); +static bool +tz_devicemgr_generate_mouse_button(struct ds_input_device *device, uint32_t button, bool state); + +static bool +tz_devicemgr_check_privilege(struct ds_tizen_input_devicemgr *tz_devicemgr, + struct wl_client *client, const char *rule); + +static void +tz_devicemgr_grab_keyboard(struct ds_tizen_input_devicemgr *tz_devicemgr); +static void +tz_devicemgr_ungrab_keyboard(struct ds_tizen_input_devicemgr *tz_devicemgr); +static void +tz_devicemgr_ungrab_keyboard_check(struct ds_tizen_input_devicemgr *tz_devicemgr); +static void +tz_devicemgr_blocked_keys_cleanup(struct ds_tizen_input_devicemgr *tz_devicemgr); + +WL_EXPORT struct ds_tizen_input_devicemgr * +ds_tizen_input_devicemgr_create(struct ds_backend *backend, + struct ds_seat *seat) +{ + struct ds_tizen_input_devicemgr *tz_devicemgr; + + tz_devicemgr = calloc(1, sizeof *tz_devicemgr); + if (!tz_devicemgr) { + ds_err("Fail to allocate ds_tizen_input_devicemgr"); + return NULL; + } + + tz_devicemgr->backend = backend; + tz_devicemgr->backend_destroy.notify = backend_handle_destroy; + ds_backend_add_destroy_listener(backend, &tz_devicemgr->backend_destroy); + + tz_devicemgr->seat = seat; + tz_devicemgr->seat_destroy.notify = seat_handle_destroy; + ds_seat_add_destroy_listener(seat, &tz_devicemgr->seat_destroy); + + tz_devicemgr->new_input.notify = backend_handle_input_device_add; + ds_backend_add_new_input_listener(backend, &tz_devicemgr->new_input); + + tz_devicemgr->global = wl_global_create(backend->display, + &tizen_input_device_manager_interface, + TIZEN_INPUT_DEVICEMGR_VERSION, + tz_devicemgr, device_manager_handle_bind); + if (!tz_devicemgr->global) { + goto err_global; + } + + tz_devicemgr->devices.kbd = calloc(1, + sizeof(struct ds_tizen_input_devicemgr_device)); + if (!tz_devicemgr->devices.kbd) { + goto err_kbd; + } + tz_devicemgr->devices.ptr = calloc(1, + sizeof(struct ds_tizen_input_devicemgr_device)); + if (!tz_devicemgr->devices.ptr) { + goto err_ptr; + } + tz_devicemgr->devices.touch = calloc(1, + sizeof(struct ds_tizen_input_devicemgr_device)); + if (!tz_devicemgr->devices.touch) { + goto err_touch; + } + tz_devicemgr->touch_max_count = 5;//TODO: make this variable configurable + + tz_devicemgr->grab = ds_seat_create_keyboard_grab( + tz_devicemgr->seat, &devicemgr_keyboard_grab_iface, tz_devicemgr); + if (!tz_devicemgr->grab) { + goto err_grab; + } + + wl_signal_init(&tz_devicemgr->events.destroy); + wl_list_init(&tz_devicemgr->clients); + wl_list_init(&tz_devicemgr->devices.kbd->key.pressed); + wl_list_init(&tz_devicemgr->keymap_list); + wl_list_init(&tz_devicemgr->blocked_keys); + + if (!tizen_security_init()) { + ds_inf("tizen_security_init() is failed. go on without security"); + } + + ds_inf("Global created: ds_tizen_input_devicemgr(%p) ", tz_devicemgr); + return tz_devicemgr; + +err_grab: + free(tz_devicemgr->devices.touch); +err_touch: + free(tz_devicemgr->devices.ptr); +err_ptr: + free(tz_devicemgr->devices.kbd); +err_kbd: + wl_global_destroy(tz_devicemgr->global); +err_global: + wl_list_remove(&tz_devicemgr->backend_destroy.link); + wl_list_remove(&tz_devicemgr->seat_destroy.link); + wl_list_remove(&tz_devicemgr->new_input.link); + free(tz_devicemgr); + return NULL; +} + +WL_EXPORT void +ds_tizen_input_devicemgr_add_destroy_listener( + struct ds_tizen_input_devicemgr *tz_devicemgr, + struct wl_listener *listener) +{ + wl_signal_add(&tz_devicemgr->events.destroy, listener); +} + +WL_EXPORT bool +ds_tizen_input_devicemgr_set_keymap_list( + struct ds_tizen_input_devicemgr *tz_devicemgr, struct wl_list *list) +{ + struct ds_tizen_input_devicemgr_keymap_data *data, *new_data; + + if (!tz_devicemgr || !list) { + ds_err("Please insert correct data\n"); + return false; + } + + wl_list_for_each(data, list, link) { + new_data = calloc(1, sizeof *data); + if (!new_data) { + ds_err("Failed to alloc memory"); + return false; + } + new_data->name = strdup(data->name); + new_data->keycode = data->keycode; + wl_list_insert(&tz_devicemgr->keymap_list, &new_data->link); + } + ds_inf("keymap set. length:%d", + wl_list_length(&tz_devicemgr->keymap_list)); + + return true; +} + +WL_EXPORT bool +ds_tizen_input_devicemgr_set_output_width_height( + struct ds_tizen_input_devicemgr *tz_devicemgr, + uint32_t width, uint32_t height) +{ + if (!tz_devicemgr) return false; + + tz_devicemgr->output.width = width; + tz_devicemgr->output.height = height; + + ds_inf("output's width: %d, height:%d", width, height); + + return true; +} + +static void +tz_devicemgr_destroy(struct ds_tizen_input_devicemgr *tz_devicemgr) +{ + struct ds_tizen_input_devicemgr_client *client_data, *tmp; + + tizen_security_finish(); + + tz_devicemgr_keymap_list_cleanup(tz_devicemgr); + tz_devicemgr_blocked_keys_cleanup(tz_devicemgr); + tz_devicemgr_ungrab_keyboard(tz_devicemgr); + ds_seat_keyboard_destroy_grab(tz_devicemgr->grab); + + wl_signal_emit(&tz_devicemgr->events.destroy, tz_devicemgr); + wl_list_remove(&tz_devicemgr->backend_destroy.link); + wl_list_remove(&tz_devicemgr->seat_destroy.link); + wl_list_remove(&tz_devicemgr->new_input.link); + + wl_global_destroy(tz_devicemgr->global); + + wl_list_for_each_safe(client_data, tmp, &tz_devicemgr->clients, link) { + wl_list_remove(&client_data->link); + tz_devicemgr_deinit_generator(tz_devicemgr, client_data->resource); + + wl_resource_set_user_data(client_data->resource, NULL); + free(client_data); + } + + free(tz_devicemgr->devices.touch); + free(tz_devicemgr->devices.ptr); + free(tz_devicemgr->devices.kbd); + free(tz_devicemgr); +} + +static void +backend_handle_destroy(struct wl_listener *listener, void *data) +{ + struct ds_tizen_input_devicemgr *tz_devicemgr; + + tz_devicemgr = wl_container_of(listener, tz_devicemgr, backend_destroy); + + ds_inf("Global destroy: ds_tizen_input_devicemgr(%p)", tz_devicemgr); + + tz_devicemgr_destroy(tz_devicemgr); +} + +static void +seat_handle_destroy(struct wl_listener *listener, void *data) +{ + struct ds_tizen_input_devicemgr *tz_devicemgr; + + tz_devicemgr = wl_container_of(listener, tz_devicemgr, seat_destroy); + + wl_list_remove(&tz_devicemgr->seat_destroy.link); + wl_list_init(&tz_devicemgr->seat_destroy.link); + tz_devicemgr->seat = NULL; +} + +static void +tz_devicemgr_device_close(struct ds_tizen_input_devicemgr_device *dev) +{ + if (!dev->input_device) return; + ds_input_device_destroy(dev->input_device); + dev->input_device = NULL; +} + +static void +backend_handle_input_device_add(struct wl_listener *listener, void *data) +{ + struct ds_input_device *dev = data; + struct ds_tizen_input_devicemgr *tz_devicemgr; + enum ds_input_device_type dev_type; + + tz_devicemgr = wl_container_of(listener, tz_devicemgr, new_input); + + dev_type = ds_input_device_get_type(dev); + if (dev_type == DS_INPUT_DEVICE_KEYBOARD) { + if (tz_devicemgr->devices.kbd->input_device) return; + ds_inf("devicemgr's kbd device is set to dev(%p)", dev); + tz_devicemgr->devices.kbd->input_device = dev; + } + else if (dev_type == DS_INPUT_DEVICE_POINTER) { + if (tz_devicemgr->devices.ptr->input_device) return; + ds_inf("devicemgr's ptr device is set to dev(%p)", dev); + tz_devicemgr->devices.ptr->input_device = dev; + } + else if (dev_type == DS_INPUT_DEVICE_TOUCH) { + if (tz_devicemgr->devices.touch->input_device) return; + ds_inf("devicemgr's touch device is set to dev(%p)", dev); + tz_devicemgr->devices.touch->input_device = dev; + } +} + +static const struct tizen_input_device_manager_interface _devicemgr_impl = { + .block_events = device_manager_handle_block_events, + .unblock_events = device_manager_handle_unblock_events, + .init_generator = device_manager_handle_init_generator, + .deinit_generator = device_manager_handle_deinit_generator, + .generate_key = device_manager_handle_generate_key, + .generate_pointer = device_manager_handle_generate_pointer, + .generate_touch = device_manager_handle_generate_touch, + .pointer_warp = NULL, + .init_generator_with_name = + device_manager_handle_init_generator_with_name, // v2 + .destroy = device_manager_handle_destroy, // v3 + .generate_axis = NULL, // v3 + .set_touch_count = NULL, // v4 +}; + +static void +device_manager_client_handle_resource_destroy(struct wl_resource *resource) +{ + struct ds_tizen_input_devicemgr *tz_devicemgr; + struct ds_tizen_input_devicemgr_client *client_data, *tmp; + + tz_devicemgr = wl_resource_get_user_data(resource); + + tz_devicemgr_deinit_generator(tz_devicemgr, resource); + + if (resource == tz_devicemgr->block_resource) { + tz_devicemgr_ungrab_keyboard_check(tz_devicemgr); + if (tz_devicemgr->timer) { + wl_event_source_remove(tz_devicemgr->timer); + tz_devicemgr->timer = NULL; + } + } + + wl_list_for_each_safe(client_data, tmp, &tz_devicemgr->clients, link) { + if (client_data->resource == resource) { + wl_list_remove(&client_data->link); + free(client_data); + } + } +} + +static void +device_manager_handle_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) +{ + struct ds_tizen_input_devicemgr *tz_devicemgr = data; + struct ds_tizen_input_devicemgr_client *client_data; + + client_data = calloc(1, sizeof *client_data); + if (!client_data) { + ds_err("Failed to allocate memory !\n"); + wl_client_post_no_memory(client); + return; + } + + client_data->resource = wl_resource_create(client, + &tizen_input_device_manager_interface, MIN(version,4), id); + if (!client_data->resource) { + ds_err("Failed to create resource! (ver. :%d, id:%d)", version, id); + free(client_data); + wl_client_post_no_memory(client); + return; + } + + client_data->init = false; + wl_list_init(&client_data->link); + wl_list_insert(&tz_devicemgr->clients, &client_data->link); + + wl_resource_set_implementation(client_data->resource, &_devicemgr_impl, + tz_devicemgr, device_manager_client_handle_resource_destroy); +} + +static void +device_manager_handle_init_generator(struct wl_client *client, + struct wl_resource *resource, uint32_t clas) +{ + struct ds_tizen_input_devicemgr *tz_devicemgr; + int ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NO_SYSTEM_RESOURCES; + + tz_devicemgr = wl_resource_get_user_data(resource); + + if (!tz_devicemgr_check_privilege(tz_devicemgr, client, + TIZEN_PRIV_INPUT_GENERATOR)) { + ds_err("No permission to input generate"); + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NO_PERMISSION; + goto finish; + } + + ret = tz_devicemgr_init_generator(tz_devicemgr, resource, clas, + "Input Generator"); + if (ret != TIZEN_INPUT_DEVICE_MANAGER_ERROR_NONE) { + ds_err("Failed to init input generator\n"); + goto finish; + } + +finish: + tizen_input_device_manager_send_error(resource, ret); +} + +static void +device_manager_handle_init_generator_with_name(struct wl_client *client, + struct wl_resource *resource, uint32_t clas, const char *name) +{ + struct ds_tizen_input_devicemgr *tz_devicemgr; + int ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NO_SYSTEM_RESOURCES; + + tz_devicemgr = wl_resource_get_user_data(resource); + + if (!name) { + ds_err("no name for device"); + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_INVALID_PARAMETER; + goto finish; + } + + if (!tz_devicemgr_check_privilege(tz_devicemgr, client, + TIZEN_PRIV_INPUT_GENERATOR)) { + ds_err("No permission to input generate"); + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NO_PERMISSION; + goto finish; + } + + ret = tz_devicemgr_init_generator(tz_devicemgr, resource, clas, + name); + if (ret != TIZEN_INPUT_DEVICE_MANAGER_ERROR_NONE) { + ds_err("Failed to init input generator\n"); + goto finish; + } + +finish: + tizen_input_device_manager_send_error(resource, ret); +} + +static void +device_manager_handle_deinit_generator(struct wl_client *client, + struct wl_resource *resource, uint32_t clas __attribute__((unused))) +{ + struct ds_tizen_input_devicemgr *tz_devicemgr; + int ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NO_SYSTEM_RESOURCES; + + tz_devicemgr = wl_resource_get_user_data(resource); + + if (!tz_devicemgr_check_privilege(tz_devicemgr, client, + TIZEN_PRIV_INPUT_GENERATOR)) { + ds_err("No permission to input generate"); + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NO_PERMISSION; + goto finish; + } + + ret = tz_devicemgr_deinit_generator(tz_devicemgr, resource); + if (ret != TIZEN_INPUT_DEVICE_MANAGER_ERROR_NONE) { + ds_err("Failed to deinit input generator\n"); + goto finish; + } + +finish: + tizen_input_device_manager_send_error(resource, ret); +} + +struct keycode_map{ + xkb_keysym_t keysym; + xkb_keycode_t keycode; +}; + +static void +find_keycode(struct xkb_keymap *keymap, xkb_keycode_t key, void *data) +{ + struct keycode_map *found_keycodes = (struct keycode_map *)data; + xkb_keysym_t keysym = found_keycodes->keysym; + int nsyms = 0; + const xkb_keysym_t *syms_out = NULL; + + if (found_keycodes->keycode) return; + + nsyms = xkb_keymap_key_get_syms_by_level(keymap, key, 0, 0, &syms_out); + if (nsyms && syms_out) { + if (*syms_out == keysym) { + found_keycodes->keycode = key; + } + } +} + +static void +tz_devicemgr_xkb_keycode_from_keysym(struct xkb_keymap *keymap, + xkb_keysym_t keysym, xkb_keycode_t *keycode) +{ + struct keycode_map found_keycodes = {0,}; + found_keycodes.keysym = keysym; + xkb_keymap_key_for_each(keymap, find_keycode, &found_keycodes); + + *keycode = found_keycodes.keycode; +} + +static int +tz_devicemgr_xkb_keyname_to_keycode(struct xkb_keymap *keymap, + const char *name) +{ + xkb_keysym_t keysym = 0x0; + xkb_keycode_t keycode = 0; + + if (!strncmp(name, "Keycode-", sizeof("Keycode-")-1)) { + keycode = atoi(name + 8); + } else { + keysym = xkb_keysym_from_name(name, XKB_KEYSYM_NO_FLAGS); + tz_devicemgr_xkb_keycode_from_keysym(keymap, keysym, &keycode); + } + + return keycode; +} + +static uint32_t +tz_devicemgr_client_get_device_clas(struct ds_tizen_input_devicemgr *tz_devicemgr, + struct wl_resource *resource) +{ + struct ds_tizen_input_devicemgr_client *client_data; + + wl_list_for_each(client_data, &tz_devicemgr->clients, link) { + if (client_data->resource == resource) + return client_data->clas; + } + return 0; +} + +static void +device_manager_handle_generate_key(struct wl_client *client, + struct wl_resource *resource, const char *keyname, uint32_t pressed) +{ + struct ds_tizen_input_devicemgr *tz_devicemgr; + int ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NO_SYSTEM_RESOURCES; + int keycode = 0; + bool res; + struct ds_keyboard *kbd; + uint32_t clas; + + tz_devicemgr = wl_resource_get_user_data(resource); + + clas = tz_devicemgr_client_get_device_clas(tz_devicemgr, resource); + if ((clas & TIZEN_INPUT_DEVICE_MANAGER_CLAS_KEYBOARD) == 0) + { + ds_err("Keyboard generator is not initialized by client"); + goto finish; + } + + if (!tz_devicemgr->devices.kbd || + !tz_devicemgr->devices.kbd->input_device) { + ds_err("Keyboard device is not initialized\n"); + goto finish; + } + + if (!tz_devicemgr_check_privilege(tz_devicemgr, client, + TIZEN_PRIV_INPUT_GENERATOR)) { + ds_err("No permission to input generate"); + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NO_PERMISSION; + goto finish; + } + + // keyname to keycode using xkb_info + kbd = ds_input_device_get_keyboard( + tz_devicemgr->devices.kbd->input_device); + if (kbd->keymap) { + keycode = tz_devicemgr_xkb_keyname_to_keycode(kbd->keymap, keyname); + } + + if (keycode <= 0) { + keycode = tz_devicemgr_keyname_to_keycode(&tz_devicemgr->keymap_list, + keyname); + } + if (keycode <= 0) + goto finish; + + res = tz_devicemgr_generate_key(tz_devicemgr->devices.kbd->input_device, + keycode, pressed); + if (!res) { + ds_err("Generating key is failed. key: %s, pressed: %d", + keyname, pressed); + goto finish; + } + res = tz_devicemgr_pressed_keys_update(tz_devicemgr, keycode, pressed); + if (!res) { + ds_err("Updating pressed keys is failed. key: %s, pressed: %d", + keyname, pressed); + goto finish; + } + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NONE; + +finish: + tizen_input_device_manager_send_error(resource, ret); +} + +static void +device_manager_handle_generate_pointer(struct wl_client *client, + struct wl_resource *resource, uint32_t type, uint32_t x, uint32_t y, + uint32_t button) +{ + struct ds_tizen_input_devicemgr *tz_devicemgr; + int ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NO_SYSTEM_RESOURCES; + bool res; + bool state; + uint32_t clas; + + tz_devicemgr = wl_resource_get_user_data(resource); + + clas = tz_devicemgr_client_get_device_clas(tz_devicemgr, resource); + if ((clas & TIZEN_INPUT_DEVICE_MANAGER_CLAS_MOUSE) == 0) + { + ds_err("Pointer generator is not initialized by client"); + goto finish; + } + + if (!tz_devicemgr->devices.ptr || + !tz_devicemgr->devices.ptr->input_device) { + ds_err("Pointer device is not initialized\n"); + goto finish; + } + + if (!tz_devicemgr_check_privilege(tz_devicemgr, client, + TIZEN_PRIV_INPUT_GENERATOR)) { + ds_err("No permission to input generate"); + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NO_PERMISSION; + goto finish; + } + + if (button <= 0 || button >= TIZEN_INPUT_DEVICEMGR_MAX_BTN) + { + ds_err("Invalid button: %d\n", button); + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_INVALID_PARAMETER; + goto finish; + } + + if (type == TIZEN_INPUT_DEVICE_MANAGER_POINTER_EVENT_TYPE_UPDATE) { + res = tz_devicemgr_generate_mouse_move( + tz_devicemgr->devices.ptr->input_device, (double)x, (double)y); + if (!res) goto finish; + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NONE; + } + else { + state = (type == TIZEN_INPUT_DEVICE_MANAGER_POINTER_EVENT_TYPE_BEGIN) ? + true : false; + res = tz_devicemgr_generate_mouse_button( + tz_devicemgr->devices.ptr->input_device, button, state); + if (!res) goto finish; + if (state) tz_devicemgr->devices.ptr->mouse.pressed |= 1 << (button - 1); + else tz_devicemgr->devices.ptr->mouse.pressed &= ~(1 << (button - 1)); + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NONE; + } + +finish: + tizen_input_device_manager_send_error(resource, ret); +} + +static void +device_manager_handle_generate_touch(struct wl_client *client, + struct wl_resource *resource, uint32_t type, uint32_t x, uint32_t y, + uint32_t finger) +{ + struct ds_tizen_input_devicemgr *tz_devicemgr; + double transformed_x = .0, transformed_y = .0; + int ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NO_SYSTEM_RESOURCES; + bool res; + uint32_t clas; + + tz_devicemgr = wl_resource_get_user_data(resource); + + clas = tz_devicemgr_client_get_device_clas(tz_devicemgr, resource); + if ((clas & TIZEN_INPUT_DEVICE_MANAGER_CLAS_TOUCHSCREEN) == 0) + { + ds_err("Touch generator is not initialized by client"); + goto finish; + } + + if (!tz_devicemgr->devices.touch || + !tz_devicemgr->devices.touch->input_device) { + ds_err("Touch device is not initialized\n"); + goto finish; + } + + if (!tz_devicemgr_check_privilege(tz_devicemgr, client, + TIZEN_PRIV_INPUT_GENERATOR)) { + ds_err("No permission to input generate"); + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NO_PERMISSION; + goto finish; + } + + if (finger >= tz_devicemgr->touch_max_count) + { + ds_err("Invalid fingers: %d\n", finger); + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_INVALID_PARAMETER; + goto finish; + } + + if (tz_devicemgr->output.width != 0 && tz_devicemgr->output.height != 0) { + transformed_x = x / (double)tz_devicemgr->output.width; + transformed_y = y / (double)tz_devicemgr->output.height; + } + + switch(type) { + case TIZEN_INPUT_DEVICE_MANAGER_POINTER_EVENT_TYPE_BEGIN: // 0 + res = tz_devicemgr_generate_touch_move( + tz_devicemgr->devices.touch->input_device, transformed_x, transformed_y, finger); + if (!res) break; + res= tz_devicemgr_generate_touch_down( + tz_devicemgr->devices.touch->input_device, transformed_x, transformed_y, finger); + if (!res) break; + tz_devicemgr->devices.touch->touch.pressed |= 1 << finger; + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NONE; + break; + case TIZEN_INPUT_DEVICE_MANAGER_POINTER_EVENT_TYPE_END: // 2 + res = tz_devicemgr_generate_touch_up( + tz_devicemgr->devices.touch->input_device, finger); + if (!res) break; + tz_devicemgr->devices.touch->touch.pressed &= ~(1 << finger); + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NONE; + break; + case TIZEN_INPUT_DEVICE_MANAGER_POINTER_EVENT_TYPE_UPDATE: // 1 + res = tz_devicemgr_generate_touch_move( + tz_devicemgr->devices.touch->input_device, transformed_x, transformed_y, finger); + if (!res) break; + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NONE; + break; + default: + break; + } + +finish: + tizen_input_device_manager_send_error(resource, ret); +} + +static void +device_manager_handle_destroy(struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static bool +tz_devicemgr_check_privilege(struct ds_tizen_input_devicemgr *tz_devicemgr, + struct wl_client *client, const char *rule) +{ + pid_t pid = 0; + uid_t uid = 0; + gid_t gid = 0; + + if (!client) return false; + + wl_client_get_credentials(client, &pid, &uid, &gid); + + return tizen_security_check_privilege(pid, uid, rule); +} + +static const struct ds_input_device_interface input_device_iface = +{ + .destroy = NULL, +}; + +static struct ds_keyboard * +create_ds_keyboard() +{ + struct ds_keyboard *kbd; + kbd = calloc(1, sizeof *kbd); + if (!kbd) { + ds_err("Could not allocate memory"); + return NULL; + } + ds_keyboard_init(kbd, NULL); + + return kbd; +} + +static struct ds_pointer * +create_ds_pointer() +{ + struct ds_pointer *pointer; + pointer = calloc(1, sizeof *pointer); + if (!pointer) { + ds_err("Could not allocate memory"); + return NULL; + } + ds_pointer_init(pointer, NULL); + + return pointer; +} + +static struct ds_touch * +create_ds_touch() +{ + struct ds_touch *touch; + touch = calloc(1, sizeof *touch); + if (!touch) { + ds_err("Could not allocate memory"); + return NULL; + } + ds_touch_init(touch, NULL); + + return touch; +} + +static int +tz_devicemgr_create_device(struct ds_tizen_input_devicemgr_device *dev, + uint32_t clas, const char *name) +{ + int ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NO_SYSTEM_RESOURCES; + const char *dev_name; + + if (dev->name && strlen(dev->name) > 0) { + ds_inf("device already has name. name:%s", dev->name); + return TIZEN_INPUT_DEVICE_MANAGER_ERROR_NONE; + } + + if (dev->input_device) { + dev_name = ds_input_device_get_name(dev->input_device); + ds_inf("device was already set. name:%s", dev_name); + dev->name = strdup(dev_name); + return TIZEN_INPUT_DEVICE_MANAGER_ERROR_NONE; + } + + //input_device create + dev->input_device = calloc(1, sizeof(struct ds_input_device)); + if(!dev->input_device) { + ds_err("Failed to create input device !\n"); + return ret; + } + + if (clas & TIZEN_INPUT_DEVICE_MANAGER_CLAS_KEYBOARD) + { + ds_input_device_init(dev->input_device, DS_INPUT_DEVICE_KEYBOARD, + &input_device_iface, name, -1, -1); + dev->input_device->keyboard = create_ds_keyboard(); + } + else if (clas & TIZEN_INPUT_DEVICE_MANAGER_CLAS_MOUSE) + { + ds_input_device_init(dev->input_device, DS_INPUT_DEVICE_POINTER, + &input_device_iface, name, -1, -1); + dev->input_device->pointer = create_ds_pointer(); + } + else if (clas & TIZEN_INPUT_DEVICE_MANAGER_CLAS_TOUCHSCREEN) + { + ds_input_device_init(dev->input_device, DS_INPUT_DEVICE_TOUCH, + &input_device_iface, name, -1, -1); + dev->input_device->touch = create_ds_touch(); + } + + dev->created = true; + dev->name = strdup(name); + + return TIZEN_INPUT_DEVICE_MANAGER_ERROR_NONE; +} + +static int +tz_devicemgr_init_generator(struct ds_tizen_input_devicemgr *tz_devicemgr, + struct wl_resource *resource, uint32_t clas, const char *name) +{ + int ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NO_SYSTEM_RESOURCES; + struct ds_tizen_input_devicemgr_client *client_data; + struct ds_tizen_input_devicemgr_device *dev; + bool inited = false; + + ds_inf("Init generator. name:%s", name); + + if (clas & TIZEN_INPUT_DEVICE_MANAGER_CLAS_KEYBOARD) { + dev = tz_devicemgr->devices.kbd; + ret = tz_devicemgr_create_device(dev, clas, name); + if (ret == TIZEN_INPUT_DEVICE_MANAGER_ERROR_NONE) { + inited = true; + tz_devicemgr->devices.kbd->ref++; + } + if (dev->created) { + wl_signal_emit(&tz_devicemgr->backend->events.new_input, + dev->input_device); + } + } + if (clas & TIZEN_INPUT_DEVICE_MANAGER_CLAS_MOUSE) { + dev = tz_devicemgr->devices.ptr; + ret = tz_devicemgr_create_device(dev, clas, name); + if (ret == TIZEN_INPUT_DEVICE_MANAGER_ERROR_NONE) { + inited = true; + tz_devicemgr->devices.ptr->ref++; + } + if (dev->created) { + wl_signal_emit(&tz_devicemgr->backend->events.new_input, + dev->input_device); + } + } + if (clas & TIZEN_INPUT_DEVICE_MANAGER_CLAS_TOUCHSCREEN) { + dev = tz_devicemgr->devices.touch; + ret = tz_devicemgr_create_device(dev, clas, name); + if (ret == TIZEN_INPUT_DEVICE_MANAGER_ERROR_NONE) { + inited = true; + tz_devicemgr->devices.touch->ref++; + } + if (dev->created) { + wl_signal_emit(&tz_devicemgr->backend->events.new_input, + dev->input_device); + } + } + + if (inited) { + wl_list_for_each(client_data, &tz_devicemgr->clients, link) { + if (client_data->resource == resource) { + if (client_data->init == false) { + client_data->init = true; + client_data->clas = clas; + } + break; + } + } + } + + return ret; +} + +static int +tz_devicemgr_deinit_generator(struct ds_tizen_input_devicemgr *tz_devicemgr, + struct wl_resource *resource) +{ + int ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NO_SYSTEM_RESOURCES; + struct ds_tizen_input_devicemgr_client *client_data; + struct ds_tizen_input_devicemgr_device *dev; + uint32_t clas = 0; + int i = 0; + bool res; + + ds_inf("Deinit generator."); + wl_list_for_each(client_data, &tz_devicemgr->clients, link) { + if (client_data->resource == resource) { + if (client_data->init == true) { + client_data->init = false; + clas = client_data->clas; + break; + } else { + return ret; + } + } + } + if (clas & TIZEN_INPUT_DEVICE_MANAGER_CLAS_KEYBOARD) { + dev = tz_devicemgr->devices.kbd; + dev->ref--; + if (dev->ref<= 0) { + dev->ref = 0; + tz_devicemgr_pressed_keys_cleanup(tz_devicemgr); + + if (dev->created) { + tz_devicemgr_device_close(dev); + dev->created = false; + } + free(dev->name); + dev->name = NULL; + } + } + if (clas & TIZEN_INPUT_DEVICE_MANAGER_CLAS_MOUSE) { + dev = tz_devicemgr->devices.ptr; + dev->ref--; + if (dev->ref <= 0) { + dev->ref = 0; + while(dev->mouse.pressed) + { + if (dev->mouse.pressed & (1 << i)) + { + res = tz_devicemgr_generate_mouse_button(dev->input_device, + i + 1, false); + if (!res) break; + dev->mouse.pressed &= ~(1 << i); + } + i++; + if (i >= TIZEN_INPUT_DEVICEMGR_MAX_BTN) break; + } + + if (dev->created) + { + tz_devicemgr_device_close(dev); + dev->created = false; + } + free(dev->name); + dev->name = NULL; + } + } + if (clas & TIZEN_INPUT_DEVICE_MANAGER_CLAS_TOUCHSCREEN) { + dev = tz_devicemgr->devices.touch; + dev->ref--; + if (dev->ref <= 0) { + dev->ref = 0; + while(dev->touch.pressed) + { + if (dev->touch.pressed & (1 << i)) + { + res = tz_devicemgr_generate_touch_up(dev->input_device, + i); + if (!res) break; + dev->touch.pressed &= ~(1 << i); + } + i++; + if (i >= tz_devicemgr->touch_max_count) break; + } + + if (dev->created) + { + tz_devicemgr_device_close(dev); + dev->created = false; + } + free(dev->name); + dev->name = NULL; + } + } + + return TIZEN_INPUT_DEVICE_MANAGER_ERROR_NONE; +} + +static int +tz_devicemgr_keyname_to_keycode(struct wl_list *list, const char *name) +{ + struct ds_tizen_input_devicemgr_keymap_data *data; + + if (!wl_list_empty(list)) { + wl_list_for_each(data, list, link) { + if (!strcmp(data->name, name)) { + return data->keycode; + } + } + } + + return 0; +} + +static bool +tz_devicemgr_generate_key(struct ds_input_device *device, int keycode, + int pressed) +{ + struct ds_event_keyboard_key ds_event; + struct timeval time; + unsigned int timestamp; + struct ds_keyboard *kbd; + + kbd = ds_input_device_get_keyboard(device); + if (!kbd) { + ds_err("No ds_keyboard to notify event"); + return false; + } + + gettimeofday(&time, NULL); + timestamp = time.tv_sec * 1000 + time.tv_usec / 1000; + + ds_event.time_msec = timestamp; + ds_event.keycode = keycode - 8; + if (pressed) + ds_event.state = WL_KEYBOARD_KEY_STATE_PRESSED; + else + ds_event.state = WL_KEYBOARD_KEY_STATE_RELEASED; + + ds_inf("Generate key. kbd:%p, key:%d, state:%s", kbd, ds_event.keycode, + (ds_event.state == WL_KEYBOARD_KEY_STATE_PRESSED) ? + "PRESSED" : "RELEASED"); + + ds_keyboard_notify_key(kbd, &ds_event); + + return true; +} + +static bool +tz_devicemgr_pressed_keys_update(struct ds_tizen_input_devicemgr *tz_devicemgr, + int keycode, bool pressed) +{ + struct ds_tizen_input_devicemgr_key_info *key, *tmp; + + if (pressed) { + key = calloc(1, sizeof(*key)); + if (!key) { + ds_err("Failed to alloc keydata memory.\n"); + return false; + } + key->keycode = keycode; + wl_list_init(&key->link); + wl_list_insert(&tz_devicemgr->devices.kbd->key.pressed, &key->link); + } + else { + wl_list_for_each_safe(key, tmp, &tz_devicemgr->devices.kbd->key.pressed, link) { + if (key->keycode == keycode) { + wl_list_remove(&key->link); + free(key); + break; + } + } + } + + ds_inf("Update pressed keys. length: %d, keycode:%d, pressed:%d", + wl_list_length(&tz_devicemgr->devices.kbd->key.pressed), keycode, pressed); + + return true; +} + +static void +tz_devicemgr_pressed_keys_cleanup(struct ds_tizen_input_devicemgr *tz_devicemgr) +{ + struct ds_tizen_input_devicemgr_key_info *keydata, *tmp; + + ds_inf("Clean up the kbd.pressed_keys. length: %d", + wl_list_length(&tz_devicemgr->devices.kbd->key.pressed)); + + wl_list_for_each_safe(keydata, tmp, &tz_devicemgr->devices.kbd->key.pressed, link) { + if (tz_devicemgr->devices.kbd) + tz_devicemgr_generate_key(tz_devicemgr->devices.kbd->input_device, + keydata->keycode, false); + wl_list_remove(&keydata->link); + free(keydata); + } +} + +static void +tz_devicemgr_keymap_list_cleanup(struct ds_tizen_input_devicemgr *tz_devicemgr) +{ + struct ds_tizen_input_devicemgr_keymap_data *keymap, *tmp; + + ds_inf("Clean up the keymap_list. length: %d", + wl_list_length(&tz_devicemgr->keymap_list)); + + wl_list_for_each_safe(keymap, tmp, &tz_devicemgr->keymap_list, link) { + free(keymap->name); + wl_list_remove(&keymap->link); + free(keymap); + } +} + +static bool +tz_devicemgr_generate_touch_move(struct ds_input_device *device, double x, double y, + uint32_t finger) +{ + struct ds_event_touch_motion ds_event; + struct timeval time; + unsigned int timestamp; + struct ds_touch *touch; + + touch = ds_input_device_get_touch(device); + if (!touch) { + ds_err("No ds_touch to notify event"); + return false; + } + + gettimeofday(&time, NULL); + timestamp = time.tv_sec * 1000 + time.tv_usec / 1000; + + ds_event.time_msec = timestamp; + ds_event.id = finger; + ds_event.x = x; + ds_event.y = y; + ds_inf("Generate touch motion. touch:%p, id:%d (%d, %d)", touch, ds_event.id, x, y); + + wl_signal_emit(&touch->events.motion, &ds_event); + + return true; +} + +static bool +tz_devicemgr_generate_touch_down(struct ds_input_device *device, double x, double y, + uint32_t finger) +{ + struct ds_event_touch_down ds_event; + struct timeval time; + unsigned int timestamp; + struct ds_touch *touch; + + touch = ds_input_device_get_touch(device); + if (!touch) { + ds_err("No ds_touch to notify event"); + return false; + } + + gettimeofday(&time, NULL); + timestamp = time.tv_sec * 1000 + time.tv_usec / 1000; + + ds_event.time_msec = timestamp; + ds_event.id = finger; + ds_event.x = x; + ds_event.y = y; + ds_inf("Generate touch down. touch:%p, id:%d (%d, %d)", touch, ds_event.id, x, y); + + wl_signal_emit(&touch->events.down, &ds_event); + + return true; +} + +static bool +tz_devicemgr_generate_touch_up(struct ds_input_device *device, uint32_t finger) +{ + struct ds_event_touch_up ds_event; + struct timeval time; + unsigned int timestamp; + struct ds_touch *touch; + + touch = ds_input_device_get_touch(device); + if (!touch) { + ds_err("No ds_touch to notify event"); + return false; + } + + gettimeofday(&time, NULL); + timestamp = time.tv_sec * 1000 + time.tv_usec / 1000; + + ds_event.time_msec = timestamp; + ds_event.id = finger; + ds_inf("Generate touch up. touch:%p, id:%d", touch, ds_event.id); + + wl_signal_emit(&touch->events.up, &ds_event); + + return true; +} + +static bool +tz_devicemgr_generate_mouse_move(struct ds_input_device *device, double x, double y) +{ + struct ds_event_pointer_motion ds_event; + struct timeval time; + unsigned int timestamp; + struct ds_pointer *pointer; + + pointer = ds_input_device_get_pointer(device); + if (!pointer) { + ds_err("No ds_pointer to notify event"); + return false; + } + + gettimeofday(&time, NULL); + timestamp = time.tv_sec * 1000 + time.tv_usec / 1000; + + ds_event.time_msec = timestamp; + ds_event.delta_x = x; + ds_event.delta_y = y; + ds_inf("Generate mouse motion. pointer:%p, x:%f, y:%f", pointer, ds_event.delta_x, ds_event.delta_y); + + wl_signal_emit(&pointer->events.motion, &ds_event); + + return true; +} + +static bool +tz_devicemgr_generate_mouse_button(struct ds_input_device *device, uint32_t button, bool state) +{ + struct ds_event_pointer_button ds_event; + struct timeval time; + unsigned int timestamp; + struct ds_pointer *pointer; + + pointer = ds_input_device_get_pointer(device); + if (!pointer) { + ds_err("No ds_pointer to notify event"); + return false; + } + + gettimeofday(&time, NULL); + timestamp = time.tv_sec * 1000 + time.tv_usec / 1000; + + ds_event.time_msec = timestamp; + ds_event.button = button; + if (state) + ds_event.state = DS_BUTTON_PRESSED; + else + ds_event.state = DS_BUTTON_RELEASED; + ds_inf("Generate mouse button. pointer:%p, button:%d, state:%s", pointer, ds_event.button, state ? "PRESSED" : "RELEASED"); + + wl_signal_emit(&pointer->events.button, &ds_event); + + return true; +} + +static void +devicemgr_keyboard_grab_iface_enter(struct ds_seat_keyboard_grab *grab, + struct ds_surface *surface, uint32_t keycodes[], + size_t num_keycodes, struct ds_keyboard_modifiers *modifiers) +{ + ds_inf("devicemgr. keyboard_grab_iface_enter"); +} + +static void +devicemgr_keyboard_grab_iface_clear_focus(struct ds_seat_keyboard_grab *grab) +{ + ds_inf("devicemgr. keyboard_grab_iface_clear_focus"); +} + +static void +tz_devicemgr_blocked_keys_cleanup(struct ds_tizen_input_devicemgr *tz_devicemgr) +{ + struct ds_tizen_input_devicemgr_key_info *keydata, *tmp; + + ds_inf("Clean up the blocked keys. length: %d", + wl_list_length(&tz_devicemgr->blocked_keys)); + + wl_list_for_each_safe(keydata, tmp, &tz_devicemgr->blocked_keys, link) { + wl_list_remove(&keydata->link); + free(keydata); + } +} + +static void +devicemgr_keyboard_grab_iface_key(struct ds_seat_keyboard_grab *grab, + uint32_t time_msec, uint32_t key, uint32_t state) +{ + struct ds_tizen_input_devicemgr *devicemgr; + struct ds_tizen_input_devicemgr_key_info *keydata, *tmp; + bool key_blocked = false; + + ds_inf("devicemgr. keyboard_grab_iface_key"); + + devicemgr = ds_seat_keyboard_grab_get_data(grab); + if (!devicemgr->block_resource) { + if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { + goto finish; + } + else { + wl_list_for_each_safe(keydata, tmp, &devicemgr->blocked_keys, link) { + if (keydata->keycode == (int)key) { + wl_list_remove(&keydata->link); + free(keydata); + key_blocked = true; + break; + } + } + if (wl_list_empty(&devicemgr->blocked_keys)) { + tz_devicemgr_ungrab_keyboard(devicemgr); + } + if (key_blocked) { + goto finish; + } + } + } + + if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { + keydata = calloc(1, sizeof (*keydata)); + if (!keydata) + goto finish; + keydata->keycode = key; + wl_list_init(&keydata->link); + wl_list_insert(&devicemgr->blocked_keys, &keydata->link); + key_blocked = true; + } + else { + if (wl_list_empty(&devicemgr->blocked_keys)) + goto finish; + wl_list_for_each_safe(keydata, tmp, &devicemgr->blocked_keys, link) { + if (keydata->keycode == (int)key) { + wl_list_remove(&keydata->link); + free(keydata); + key_blocked = true; + } + } + } + +finish: + if (!key_blocked) + ds_inf("block key event: (%d %s)\n", key, (state ? "press" : "release")); +} + +static void +devicemgr_modifiers_grab_iface_key(struct ds_seat_keyboard_grab *grab, + struct ds_keyboard_modifiers *modifiers) +{ + ds_inf("devicemgr. modifiers_grab_iface_key"); +} + +static void +devicemgr_cancel_grab_iface_key(struct ds_seat_keyboard_grab *grab) +{ + ds_inf("devicemgr. cancel_grab_iface_key"); +} + +static const struct ds_keyboard_grab_interface devicemgr_keyboard_grab_iface = { + .enter = devicemgr_keyboard_grab_iface_enter, + .clear_focus = devicemgr_keyboard_grab_iface_clear_focus, + .key = devicemgr_keyboard_grab_iface_key, + .modifiers = devicemgr_modifiers_grab_iface_key, + .cancel = devicemgr_cancel_grab_iface_key, +}; + +static void +tz_devicemgr_grab_keyboard(struct ds_tizen_input_devicemgr *tz_devicemgr) +{ + ds_seat_keyboard_start_grab(tz_devicemgr->seat, tz_devicemgr->grab); +} + +static void +tz_devicemgr_ungrab_keyboard(struct ds_tizen_input_devicemgr *tz_devicemgr) +{ + ds_seat_keyboard_end_grab(tz_devicemgr->seat); +} + +static void +tz_devicemgr_ungrab_keyboard_check(struct ds_tizen_input_devicemgr *tz_devicemgr) +{ + if (wl_list_empty(&tz_devicemgr->blocked_keys)) + tz_devicemgr_ungrab_keyboard(tz_devicemgr); + + tz_devicemgr->block_resource = NULL; +} + +static bool +devicemgr_add_timer(struct ds_tizen_input_devicemgr *tz_devicemgr, + wl_event_loop_timer_func_t func, int time) +{ + struct wl_event_loop *event_loop; + + event_loop = wl_display_get_event_loop(tz_devicemgr->backend->display); + if (!event_loop) { + ds_err("Failed to get event_loop from display: %p", + tz_devicemgr->backend->display); + return false; + } + + tz_devicemgr->timer = wl_event_loop_add_timer(event_loop, func, + tz_devicemgr); + if (!tz_devicemgr->timer) { + ds_err("Failed to timer"); + return false; + } + wl_event_source_timer_update(tz_devicemgr->timer, time); + + return true; +} + +static int +devicemgr_block_timer(void *data) +{ + struct ds_tizen_input_devicemgr *devicemgr = data; + + tizen_input_device_manager_send_block_expired(devicemgr->block_resource); + + tz_devicemgr_ungrab_keyboard_check(devicemgr); + + wl_event_source_remove(devicemgr->timer); + devicemgr->timer = NULL; + + return 1; +} + +static void +device_manager_handle_block_events(struct wl_client *client, + struct wl_resource *resource, uint32_t serial, uint32_t clas, + uint32_t duration) +{ + struct ds_tizen_input_devicemgr *tz_devicemgr; + int ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NO_SYSTEM_RESOURCES; + bool res; + + tz_devicemgr = wl_resource_get_user_data(resource); + + if (!tz_devicemgr_check_privilege(tz_devicemgr, client, + TIZEN_PRIV_INPUT_BLOCK)) { + ds_err("No permission to input generate"); + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NO_PERMISSION; + goto finish; + } + + if (clas != TIZEN_INPUT_DEVICE_MANAGER_CLAS_KEYBOARD) { + ds_err("only support keyboard device. (requested: 0x%x)\n", clas); + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_INVALID_PARAMETER; + goto finish; + } + + if(tz_devicemgr->block_resource) { + ds_err("currently the input system is already blocked\n"); + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_INVALID_PARAMETER; + goto finish; + } + + res = devicemgr_add_timer(tz_devicemgr, devicemgr_block_timer, duration); + if (!res) { + ds_err("Failed to add a timer\n"); + goto finish; + } + + tz_devicemgr_grab_keyboard(tz_devicemgr); + tz_devicemgr->block_resource = resource; + ds_inf("Block events. clas: %d, duration:%d", clas, duration); + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NONE; + +finish: + tizen_input_device_manager_send_error(resource, ret); +} + +static void +device_manager_handle_unblock_events(struct wl_client *client, + struct wl_resource *resource, uint32_t serial) +{ + struct ds_tizen_input_devicemgr *tz_devicemgr; + int ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NO_SYSTEM_RESOURCES; + + tz_devicemgr = wl_resource_get_user_data(resource); + + if (!tz_devicemgr_check_privilege(tz_devicemgr, client, + TIZEN_PRIV_INPUT_BLOCK)) { + ds_err("No permission to input generate"); + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NO_PERMISSION; + goto finish; + } + + if (tz_devicemgr->block_resource != resource) { + ds_err("currently the input system is blocked by another resource"); + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_INVALID_PARAMETER; + goto finish; + } + + tz_devicemgr_ungrab_keyboard_check(tz_devicemgr); + tz_devicemgr->block_resource = NULL; + ds_inf("Unblock events."); + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NONE; + + if (tz_devicemgr->timer) { + wl_event_source_remove(tz_devicemgr->timer); + tz_devicemgr->timer = NULL; + } + +finish: + tizen_input_device_manager_send_error(resource, ret); +} + diff --git a/src/input_devicemgr/input_devicemgr.h b/src/input_devicemgr/input_devicemgr.h new file mode 100644 index 0000000..a4428ee --- /dev/null +++ b/src/input_devicemgr/input_devicemgr.h @@ -0,0 +1,78 @@ +#ifndef DS_TIZEN_DEVICEMGR_H +#define DS_TIZEN_DEVICEMGR_H + +#include +#include +#include +#include +#include +#include + +struct ds_tizen_input_devicemgr_device { + char *name; + struct ds_input_device *input_device; + int ref; + + bool created; + + struct { + struct wl_list pressed; + } key; + + struct { + unsigned int pressed; + } touch; + + struct { + unsigned int pressed; + } mouse; +}; + +struct ds_tizen_input_devicemgr { + struct wl_global *global; + struct wl_display *display; + struct ds_backend *backend; + struct ds_seat *seat; + + struct { + struct wl_signal destroy; + } events; + + struct wl_listener new_input; + struct wl_listener backend_destroy; + struct wl_listener seat_destroy; + struct { + struct ds_tizen_input_devicemgr_device *kbd; + struct ds_tizen_input_devicemgr_device *ptr; + struct ds_tizen_input_devicemgr_device *touch; + } devices; + + struct wl_list clients; + + struct wl_list keymap_list; + + struct wl_list blocked_keys; + struct wl_resource *block_resource; + struct wl_event_source *timer; + struct ds_seat_keyboard_grab *grab; + int touch_max_count; + + struct { + uint32_t width; + uint32_t height; + } output; +}; + +struct ds_tizen_input_devicemgr_client { + struct wl_resource *resource; + bool init; + uint32_t clas; + struct wl_list link; // ds_tizen_input_devicemgr::clients +}; + +struct ds_tizen_input_devicemgr_key_info { + int keycode; + struct wl_list link; // ds_tizen_input_devicemgr::pressed_keys; +}; + +#endif diff --git a/src/input_devicemgr/meson.build b/src/input_devicemgr/meson.build new file mode 100644 index 0000000..ea8a94f --- /dev/null +++ b/src/input_devicemgr/meson.build @@ -0,0 +1,32 @@ +libds_tizen_input_devicemgr_files = [ + tizen_security_files, + 'input_devicemgr.c', +] + +libds_tizen_input_devicemgr_deps = [ + deps_libds_tizen, + deps_tizen_security, + dependency('tizen-extension-server', required: true), + dependency('xkbcommon', required: true), +] + +lib_libds_tizen_input_devicemgr = shared_library('ds-tizen-input-devicemgr', libds_tizen_input_devicemgr_files, + dependencies: libds_tizen_input_devicemgr_deps, + include_directories: [ common_inc, include_directories('.'), include_directories('..') ], + version: meson.project_version(), + install: true +) + +deps_libds_tizen_input_devicemgr = declare_dependency( + link_with: lib_libds_tizen_input_devicemgr, + dependencies: libds_tizen_input_devicemgr_deps, + include_directories: [ common_inc, include_directories('.') ], +) + +pkgconfig = import('pkgconfig') +pkgconfig.generate(lib_libds_tizen_input_devicemgr, + version: meson.project_version(), + filebase: 'libds-tizen-input-devicemgr', + name: 'libds-tizen-input-devicemgr', + description: 'tizen input devicemgr extension of libds-tizen for tizen platform', +) diff --git a/src/tbm_server/meson.build b/src/tbm_server/meson.build new file mode 100644 index 0000000..795576a --- /dev/null +++ b/src/tbm_server/meson.build @@ -0,0 +1,32 @@ +libds_tizen_tbm_server_files = [ + 'pixel_format.c', + 'tbm_server.c', +] + +libds_tizen_tbm_server_deps = [ + deps_libds_tizen, + dependency('libdrm', required: true), + dependency('libtbm', required: true), + dependency('wayland-tbm-server', required: true), +] + +lib_libds_tizen_tbm_server = shared_library('ds-tizen-tbm-server', libds_tizen_tbm_server_files, + dependencies: libds_tizen_tbm_server_deps, + include_directories: [ common_inc, include_directories('.'), include_directories('..') ], + version: meson.project_version(), + install: true +) + +deps_libds_tizen_tbm_server = declare_dependency( + link_with: lib_libds_tizen_tbm_server, + dependencies: libds_tizen_tbm_server_deps, + include_directories: [ common_inc, include_directories('.') ], +) + +pkgconfig = import('pkgconfig') +pkgconfig.generate(lib_libds_tizen_tbm_server, + version: meson.project_version(), + filebase: 'libds-tizen-tbm-server', + name: 'libds-tizen-tbm-server', + description: 'wayland-tbm extension of libds-tizen for tizen platform', +) diff --git a/src/tbm_server/pixel_format.c b/src/tbm_server/pixel_format.c new file mode 100644 index 0000000..030f211 --- /dev/null +++ b/src/tbm_server/pixel_format.c @@ -0,0 +1,61 @@ +#include +#include +#include + +#include +#include "pixel_format.h" + +#ifdef ARRAY_LENGTH +#undef ARRAY_LENGTH +#endif + +#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) + +struct ds_tbm_format +{ + uint32_t drm_format; + uint32_t tbm_format; +}; + +static const struct ds_tbm_format formats[] = +{ + { + .drm_format = DRM_FORMAT_ARGB8888, + .tbm_format = TBM_FORMAT_ARGB8888, + }, + { + .drm_format = DRM_FORMAT_XRGB8888, + .tbm_format = TBM_FORMAT_XRGB8888, + }, + /* TODO more format */ +}; + +uint32_t +convert_drm_format_to_tbm(uint32_t fmt) +{ + size_t i; + + for (i = 0; i < ARRAY_LENGTH(formats); i++) { + if (formats[i].drm_format == fmt) + return formats[i].tbm_format; + } + + ds_err("DRM format 0x%"PRIX32" has no TBM equivalent", fmt); + + return 0; +} + +uint32_t +convert_tbm_format_to_drm(uint32_t fmt) +{ + size_t i; + + for (i = 0; i < ARRAY_LENGTH(formats); i++) { + if (formats[i].tbm_format == fmt) + return formats[i].drm_format; + } + + ds_err("TBM format 0x%"PRIX32" has no DRM equivalent", fmt); + + return 0; +} diff --git a/src/tbm_server/pixel_format.h b/src/tbm_server/pixel_format.h new file mode 100644 index 0000000..a63d096 --- /dev/null +++ b/src/tbm_server/pixel_format.h @@ -0,0 +1,10 @@ +#ifndef DS_TIZEN_PIXEL_FORMAT_H +#define DS_TIZEN_PIXEL_FORMAT_H + +#include + +uint32_t convert_drm_format_to_tbm(uint32_t fmt); + +uint32_t convert_tbm_format_to_drm(uint32_t fmt); + +#endif diff --git a/src/tbm_server/tbm_server.c b/src/tbm_server/tbm_server.c new file mode 100644 index 0000000..0fd25cd --- /dev/null +++ b/src/tbm_server/tbm_server.c @@ -0,0 +1,268 @@ +#include +#include +#include + +#include + +#include +#include "pixel_format.h" +#include "tbm_server.h" + +static const struct ds_buffer_resource_interface tbm_buffer_resource_iface; +static const struct ds_buffer_interface tbm_client_buffer_iface; + +static void tbm_server_handle_display_destroy(struct wl_listener *listener, + void *data); + +WL_EXPORT struct ds_tbm_server * +ds_tbm_server_create(struct wl_display *display) +{ + struct ds_tbm_server *tbm; + tbm_bufmgr bufmgr; + + tbm = calloc(1, sizeof *tbm); + if (!tbm) + return NULL; + + wl_signal_init(&tbm->events.destroy); + + tbm->wl_tbm = wayland_tbm_server_init(display, NULL, -1, 0); + if (!tbm->wl_tbm) { + goto err_wl_tbm; + } + + bufmgr = wayland_tbm_server_get_bufmgr(tbm->wl_tbm); + if (!bufmgr) { + goto err_bind; + } + + if (!tbm_bufmgr_bind_native_display(bufmgr, (void *)display)) { + goto err_bind; + } + + tbm->display_destroy.notify = tbm_server_handle_display_destroy; + wl_display_add_destroy_listener(display, &tbm->display_destroy); + + ds_buffer_register_resource_interface(&tbm_buffer_resource_iface); + + return tbm; + +err_bind: + wayland_tbm_server_deinit(tbm->wl_tbm); +err_wl_tbm: + free(tbm); + + return NULL; +} + +WL_EXPORT void +ds_tbm_server_add_destroy_listener(struct ds_tbm_server *tbm, + struct wl_listener *listener) +{ + wl_signal_add(&tbm->events.destroy, listener); +} + +WL_EXPORT struct ds_tbm_client_buffer * +ds_tbm_client_buffer_from_buffer(struct ds_buffer *ds_buffer) +{ + if (ds_buffer->iface != &tbm_client_buffer_iface) + return NULL; + return (struct ds_tbm_client_buffer *)ds_buffer; +} + +WL_EXPORT tbm_surface_h +ds_tbm_client_buffer_get_tbm_surface(struct ds_tbm_client_buffer *buffer) +{ + if (buffer->base.iface != &tbm_client_buffer_iface) + return NULL; + return buffer->surface; +} + +static void +tbm_server_handle_display_destroy(struct wl_listener *listener, void *data) +{ + struct ds_tbm_server *tbm; + + tbm = wl_container_of(listener, tbm, display_destroy); + + wl_signal_emit(&tbm->events.destroy, tbm); + + wayland_tbm_server_deinit(tbm->wl_tbm); + free(tbm); +} + +static void +tbm_client_buffer_handle_release(struct wl_listener *listener, void *data) +{ + struct ds_tbm_client_buffer *buffer; + + buffer = wl_container_of(listener, buffer, buffer_release); + if (buffer->resource) + wl_buffer_send_release(buffer->resource); +} + +static void +tbm_client_buffer_handle_resource_destroy(struct wl_listener *listener, + void *data) +{ + struct ds_tbm_client_buffer *buffer; + + buffer = wl_container_of(listener, buffer, resource_destroy); + + buffer->resource = NULL; + buffer->surface = NULL; + wl_list_remove(&buffer->resource_destroy.link); + wl_list_init(&buffer->resource_destroy.link); + + ds_buffer_drop(&buffer->base); +} + +static struct ds_tbm_client_buffer * +tbm_client_buffer_from_buffer(struct ds_buffer *ds_buffer) +{ + assert(ds_buffer->iface == &tbm_client_buffer_iface); + return (struct ds_tbm_client_buffer *)ds_buffer; +} + +static void +tbm_client_buffer_iface_destroy(struct ds_buffer *ds_buffer) +{ + struct ds_tbm_client_buffer *buffer; + + buffer = tbm_client_buffer_from_buffer(ds_buffer); + + ds_inf("Destroy TBM client buffer(%p)", buffer); + + wl_list_remove(&buffer->resource_destroy.link); + wl_list_remove(&buffer->buffer_release.link); + free(buffer); +} + +static bool +tbm_client_buffer_iface_begin_data_ptr_access(struct ds_buffer *ds_buffer, + enum ds_buffer_data_ptr_access_flag flags, void **data, + uint32_t *format, size_t *stride) +{ + struct ds_tbm_client_buffer *buffer; + tbm_surface_info_s info; + tbm_bo_access_option op = TBM_OPTION_NONE; + int err; + + buffer = tbm_client_buffer_from_buffer(ds_buffer); + + if (flags & DS_BUFFER_DATA_PTR_ACCESS_READ) + op |= TBM_OPTION_READ; + + if (flags & DS_BUFFER_DATA_PTR_ACCESS_WRITE) + op |= TBM_OPTION_WRITE; + + err = tbm_surface_map(buffer->surface, op, &info); + if (err != TBM_SURFACE_ERROR_NONE) { + ds_err("Failed tbm_surface_map()"); + return false; + } + + *format = convert_tbm_format_to_drm(buffer->format); + *stride = info.planes[0].stride; + *data = info.planes[0].ptr; + + return true; +} + +static void +tbm_client_buffer_iface_end_ptr_access(struct ds_buffer *ds_buffer) +{ + struct ds_tbm_client_buffer *buffer; + + buffer = tbm_client_buffer_from_buffer(ds_buffer); + + tbm_surface_unmap(buffer->surface); +} + +static const struct ds_buffer_interface tbm_client_buffer_iface = { + .destroy = tbm_client_buffer_iface_destroy, + .begin_data_ptr_access = tbm_client_buffer_iface_begin_data_ptr_access, + .end_data_ptr_access = tbm_client_buffer_iface_end_ptr_access, +}; + +static struct ds_tbm_client_buffer * +tbm_client_buffer_create(struct wl_resource *resource) +{ + struct ds_tbm_client_buffer *buffer; + tbm_surface_h surface; + int32_t width, height; + + surface = wayland_tbm_server_get_surface(NULL, resource); + if (!surface) { + ds_err("Could not get tbm_surface from wl_resource@%d", + wl_resource_get_id(resource)); + return NULL; + } + + width = tbm_surface_get_width(surface); + height = tbm_surface_get_height(surface); + + buffer = calloc(1, sizeof *buffer); + if (!buffer) + return NULL; + + ds_buffer_init(&buffer->base, &tbm_client_buffer_iface, width, height); + + buffer->resource = resource; + buffer->surface = surface; + buffer->format = tbm_surface_get_format(surface); + + buffer->buffer_release.notify = tbm_client_buffer_handle_release; + ds_buffer_add_release_listener(&buffer->base, &buffer->buffer_release); + + buffer->resource_destroy.notify = + tbm_client_buffer_handle_resource_destroy; + wl_resource_add_destroy_listener(resource, &buffer->resource_destroy); + + ds_inf("TBM client buffer(%p) created", buffer); + + return buffer; +} + +static struct ds_tbm_client_buffer * +tbm_client_buffer_get_or_create(struct wl_resource *resource) +{ + struct ds_tbm_client_buffer *buffer; + struct wl_listener *resource_destroy_listener; + + resource_destroy_listener = wl_resource_get_destroy_listener(resource, + tbm_client_buffer_handle_resource_destroy);; + if (resource_destroy_listener) { + buffer = wl_container_of(resource_destroy_listener, + buffer, resource_destroy); + return buffer; + } + + return tbm_client_buffer_create(resource); +} + +static bool +tbm_buffer_resource_iface_is_instance(struct wl_resource *resource) +{ + return !!wayland_tbm_server_get_surface(NULL, resource); +} + +static struct ds_buffer * +tbm_buffer_resource_iface_from_resource(struct wl_resource *resource) +{ + struct ds_tbm_client_buffer *buffer; + + buffer = tbm_client_buffer_get_or_create(resource); + if (!buffer) { + ds_err("Could not get or create ds_tbm_client_buffer"); + return NULL; + } + + return &buffer->base; +} + +static const struct ds_buffer_resource_interface tbm_buffer_resource_iface = { + .name = "tbm", + .is_instance = tbm_buffer_resource_iface_is_instance, + .from_resource = tbm_buffer_resource_iface_from_resource, +}; diff --git a/src/tbm_server/tbm_server.h b/src/tbm_server/tbm_server.h new file mode 100644 index 0000000..58e391b --- /dev/null +++ b/src/tbm_server/tbm_server.h @@ -0,0 +1,34 @@ +#ifndef DS_TIZEN_TBM_SERVER_H +#define DS_TIZEN_TBM_SERVER_H + +#include +#include +#include +#include + +struct ds_tbm_server +{ + struct wayland_tbm_server *wl_tbm; + + struct wl_listener display_destroy; + + struct { + struct wl_signal destroy; + } events; +}; + +struct ds_tbm_client_buffer +{ + struct ds_buffer base; + + tbm_surface_h surface; + struct wl_resource *resource; + + struct wl_listener buffer_release; + struct wl_listener resource_destroy; + + uint32_t format; + size_t stride; +}; + +#endif diff --git a/tests/test_backend.c b/tests/test_backend.c new file mode 100644 index 0000000..69417b6 --- /dev/null +++ b/tests/test_backend.c @@ -0,0 +1,80 @@ +#include +#include +#include + +#include +#include + +static struct ds_backend * +create_possible_wl_backend(struct wl_display *display) +{ + struct ds_backend *backend; + char wl_name[512]; + + for (int i = 0; i < 5; i++) { + snprintf(wl_name, sizeof wl_name, "wayland-%d", i); + backend = ds_wl_backend_create(display, wl_name); + if (backend) + break; + } + + return backend; +} + +static void +test_wl_backend_create(void) +{ + struct wl_display *display; + struct ds_backend *backend; + + display = wl_display_create(); + + backend = create_possible_wl_backend(display); + assert(backend); + + ds_backend_destroy(backend); +} + +struct test +{ + struct wl_listener backend_destroy; + bool cb_called; +}; + +static void +cb_backend_destroy(struct wl_listener *listener, void *data) +{ + struct test *test; + + test = wl_container_of(listener, test, backend_destroy); + test->cb_called = true; +} + +static void +test_wl_backend_destroy_signal(void) +{ + struct wl_display *display; + struct ds_backend *backend; + struct test test; + + display = wl_display_create(); + + backend = create_possible_wl_backend(display); + assert(backend); + + test.cb_called = false; + test.backend_destroy.notify = cb_backend_destroy; + ds_backend_add_destroy_listener(backend, &test.backend_destroy); + + wl_display_destroy(display); + assert(test.cb_called); +} + +int +main(void) +{ + test_wl_backend_create(); + test_wl_backend_destroy_signal(); + + return 0; +} diff --git a/tests/test_compositor.c b/tests/test_compositor.c new file mode 100644 index 0000000..b5883a5 --- /dev/null +++ b/tests/test_compositor.c @@ -0,0 +1,63 @@ +#include +#include + +#include +#include +#include + +static void +test_compositor_create(void) +{ + struct wl_display *display; + struct ds_compositor *compositor; + + display = wl_display_create(); + compositor = ds_compositor_create(display); + assert(compositor); + + wl_display_destroy(display); +} + +struct test +{ + struct wl_listener compositor_destroy; + bool destroyed; +}; + +static void +cb_compositor_destroy(struct wl_listener *listener, void *data) +{ + struct test *test; + + test = wl_container_of(listener, test, compositor_destroy); + test->destroyed = true; +} + +static void +test_compositor_destroy_signal(void) +{ + struct wl_display *display; + struct ds_compositor *compositor; + struct test test; + + display = wl_display_create(); + compositor = ds_compositor_create(display); + assert(compositor); + + test.destroyed = false; + test.compositor_destroy.notify = cb_compositor_destroy; + ds_compositor_add_destroy_listener(compositor, + &test.compositor_destroy); + + wl_display_destroy(display); + assert(test.destroyed == true); +} + +int +main(void) +{ + test_compositor_create(); + test_compositor_destroy_signal(); + + return 0; +} diff --git a/tests/test_subsurface.c b/tests/test_subsurface.c new file mode 100644 index 0000000..98e830d --- /dev/null +++ b/tests/test_subsurface.c @@ -0,0 +1,211 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +struct server_base +{ + struct wl_display *display; + struct ds_compositor *compositor; + const char *socket; +}; + +const char * +test_server_init(struct server_base *server) +{ + server->display = wl_display_create(); + assert(server->display); + server->compositor = ds_compositor_create(server->display); + assert(server->compositor); + server->socket = wl_display_add_socket_auto(server->display); + assert(server->socket); + + return server->socket; +} + +void +test_server_finish(struct server_base *server) +{ + wl_display_destroy(server->display); +} + +struct client +{ + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct wl_subcompositor *subcompositor; + struct wl_surface *surface; + struct wl_subsurface *subsurface; +}; + +static void +handle_global(void *data, struct wl_registry *registry, uint32_t id, + const char *interface, uint32_t version) +{ + struct client *client = data; + + if (strcmp(interface, "wl_compositor") == 0) { + client->compositor = + wl_registry_bind(registry, id, &wl_compositor_interface, version); + } + else if (strcmp(interface, "wl_subcompositor") == 0) { + client->subcompositor = + wl_registry_bind(registry, id, &wl_subcompositor_interface, version); + } +} + +static const struct wl_registry_listener registry_listener = { + .global = handle_global, +}; + +void +test_client_init(struct client *client, const char *name) +{ + client->display = wl_display_connect(name); + assert(client->display); + client->registry = wl_display_get_registry(client->display); + assert(client->registry); + + wl_registry_add_listener(client->registry, ®istry_listener, client); + + wl_display_roundtrip(client->display); + + assert(client->compositor); + assert(client->subcompositor); +} + +void +test_client_finish(struct client *client) +{ + wl_subcompositor_destroy(client->subcompositor); + wl_compositor_destroy(client->compositor); + wl_registry_destroy(client->registry); + wl_display_disconnect(client->display); +} + +struct test_server +{ + struct server_base base; + bool cb_called; + + struct wl_listener new_surface; + struct wl_listener new_subsurface1; + struct wl_listener new_subsurface2; +}; + +static void +cb_new_subsurface1(struct wl_listener *listener, void *data) +{ + struct ds_subsurface *subsurface = data; + struct test_server *server; + + assert(subsurface); + + server = wl_container_of(listener, server, new_subsurface1); + server->cb_called = true; + wl_display_terminate(server->base.display); +} + +static void +cb_new_subsurface2(struct wl_listener *listener, void *data) +{ + struct ds_subsurface *subsurface = data; + struct test_server *server; + + assert(subsurface); + + server = wl_container_of(listener, server, new_subsurface2); + server->cb_called = true; + wl_display_terminate(server->base.display); +} + +static void +cb_new_surface(struct wl_listener *listener, void *data) +{ + struct ds_surface *surface = data; + struct test_server *server; + + server = wl_container_of(listener, server, new_surface); + if (!server->new_subsurface1.notify) { + server->new_subsurface1.notify = cb_new_subsurface1; + ds_surface_add_new_subsurface_listener(surface, + &server->new_subsurface1); + } + else { + server->new_subsurface2.notify = cb_new_subsurface2; + ds_surface_add_new_subsurface_listener(surface, + &server->new_subsurface2); + } +} + +static void +run_client(const char *name) +{ + struct client client; + + test_client_init(&client, name); + + struct wl_surface *surface = + wl_compositor_create_surface(client.compositor); + + struct wl_surface *child_surface = + wl_compositor_create_surface(client.compositor); + + struct wl_subsurface *subsurface = + wl_subcompositor_get_subsurface(client.subcompositor, + child_surface, surface); + + wl_display_roundtrip(client.display); + + wl_subsurface_destroy(subsurface); + wl_surface_destroy(child_surface); + wl_surface_destroy(surface); + + test_client_finish(&client); +} + +static void +test_subsurface_create(void) +{ + struct test_server server = { + .new_subsurface1 = { .notify = NULL }, + .cb_called = false + }; + pid_t pid; + + const char *socket_name = test_server_init(&server.base); + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + run_client(socket_name); + exit(0); + } + + server.new_surface.notify = cb_new_surface; + ds_compositor_add_new_surface_listener(server.base.compositor, + &server.new_surface); + + wl_display_run(server.base.display); + + assert(server.cb_called); + + test_server_finish(&server.base); +} + +int +main(void) +{ + test_subsurface_create(); + return 0; +} diff --git a/tests/test_surface.c b/tests/test_surface.c new file mode 100644 index 0000000..eab2b77 --- /dev/null +++ b/tests/test_surface.c @@ -0,0 +1,162 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +struct server_base +{ + struct wl_display *display; + struct ds_compositor *compositor; + const char *socket; +}; + +void +test_server_init(struct server_base *server) +{ + server->display = wl_display_create(); + assert(server->display); + server->compositor = ds_compositor_create(server->display); + assert(server->compositor); + server->socket = wl_display_add_socket_auto(server->display); + assert(server->socket); +} + +void +test_server_finish(struct server_base *server) +{ + wl_display_destroy(server->display); +} + +struct client +{ + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct wl_surface *surface; +}; + +static void +handle_global(void *data, struct wl_registry *registry, uint32_t id, + const char *interface, uint32_t version) +{ + struct client *client = data; + + if (strcmp(interface, "wl_compositor") == 0) { + client->compositor = + wl_registry_bind(registry, id, &wl_compositor_interface, version); + } +} + +static const struct wl_registry_listener registry_listener = { + .global = handle_global, +}; + +void +test_client_init(struct client *client, const char *name) +{ + client->display = wl_display_connect(name); + assert(client->display); + client->registry = wl_display_get_registry(client->display); + assert(client->registry); + + wl_registry_add_listener(client->registry, ®istry_listener, client); + + wl_display_roundtrip(client->display); + + assert(client->compositor); +} + +void +test_client_finish(struct client *client) +{ + wl_compositor_destroy(client->compositor); + wl_registry_destroy(client->registry); + wl_display_disconnect(client->display); +} + +struct test_server { + struct server_base base; + bool cb_called; + + struct wl_listener new_surface; + struct wl_listener surface_destroy; +}; + +static void +cb_surface_destroy(struct wl_listener *listener, void *data) +{ + struct test_server *server; + + server = wl_container_of(listener, server, surface_destroy); + server->cb_called = true; + wl_display_terminate(server->base.display); +} + +static void +cb_new_surface(struct wl_listener *listener, void *data) +{ + struct ds_surface *surface = data; + struct test_server *server; + + server = wl_container_of(listener, server, new_surface); + server->surface_destroy.notify = cb_surface_destroy; + ds_surface_add_destroy_listener(surface, &server->surface_destroy); +} + +static void +run_client(const char *name) +{ + struct client client; + + test_client_init(&client, name); + + struct wl_surface *surface = + wl_compositor_create_surface(client.compositor); + + wl_display_roundtrip(client.display); + + wl_surface_destroy(surface); + + test_client_finish(&client); +} + +static void +test_surface_create_and_destroy(void) +{ + struct test_server server = { .cb_called = false }; + pid_t pid; + + test_server_init(&server.base); + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + run_client(server.base.socket); + exit(0); + } + + server.new_surface.notify = cb_new_surface; + ds_compositor_add_new_surface_listener(server.base.compositor, + &server.new_surface); + + wl_display_run(server.base.display); + + assert(server.cb_called); + + test_server_finish(&server.base); +} + +int +main(void) +{ + test_surface_create_and_destroy(); + return 0; +} -- 2.7.4 From f29f6dcedae721cec462af6f1bd8add294a0e585 Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Wed, 6 Jul 2022 10:54:44 +0900 Subject: [PATCH 03/16] keyrouter: Replace get_keyroutes() with notify_key() Instead of returning key routes list to the compositor, it will deal with keyboard key events on the behalf of the compositor according to keyrouter extension protocol. Change-Id: I6b7085402c6bb5cad0160f356c327af1467f9726 --- include/libds-tizen/keyrouter.h | 60 +++++++++------------------------ src/keyrouter/keyrouter.c | 74 +++++++++++++++++++++++++++++++++++++---- src/keyrouter/keyrouter.h | 27 ++++++++++++++- src/keyrouter/keyrouter_grab.c | 25 +++++++------- 4 files changed, 122 insertions(+), 64 deletions(-) diff --git a/include/libds-tizen/keyrouter.h b/include/libds-tizen/keyrouter.h index bd47b54..1be7944 100644 --- a/include/libds-tizen/keyrouter.h +++ b/include/libds-tizen/keyrouter.h @@ -2,6 +2,8 @@ #define LIBDS_TIZEN_KEYROUTER_H #include +#include +#include #ifdef __cplusplus extern "C" { @@ -13,39 +15,6 @@ extern "C" { struct ds_tizen_keyrouter; /** - * The information of keyroute - */ -struct ds_tizen_keyroute_info -{ - struct wl_client *wl_client; -}; - -/** - * Keyroute mode of keyroutes returned by ds_tizen_keyrouter_get_keyroutes() - * for a given keycode. - */ -enum ds_tizen_keyroute_mode { - /** - * The keycode is not under any keyroute mode. - * The compositor may send a keycode to a focused wayland cliet as usual. - */ - DS_TIZEN_KEYROUTE_MODE_NONE, - /** - * Exclusive mode means that a given keycode is required to be sent - * exclusively to wayland clients of returned keyroutes. So the compositor - * must not send this keycode to any other wayland clients except for - * returned wayland clients. - */ - DS_TIZEN_KEYROUTE_MODE_EXCLUSIVE, - /** - * The keycode is required to be shared with returned wayland clients. - * The compositor must send the keycode not only to returned wayland - * clients, but also to a focused wayland client. - */ - DS_TIZEN_KEYROUTE_MODE_SHARED, -}; - -/** * Creates a ds_tizen_keyrouter. */ struct ds_tizen_keyrouter * @@ -59,18 +28,21 @@ ds_tizen_keyrouter_add_destroy_listener(struct ds_tizen_keyrouter *keyrouter, struct wl_listener *listener); /** - * Gets keyroutes and mode for a given keycode. - * The compositor must send a keycode to returned wayland clients according to - * the mode. The parameter, topmost_client, should be given to determine whether - * the given topmost_client require a keycode exclusively when one of its - * surfaces is on very top of stack. See tizen_keyrouter protocol in - * tizen-extension.xml for more detail. + * Notify the keyrouter that a key has been pressed on the keyboard. + * This will eventually notify a given seat of key events, and if there is + * no destination to be routed, then it sends key events to the focused one + * automatically. So, if you call this, you don't need to call + * ds_seat_keyboard_notify_key() manually unless you want to do something + * differently. + * The parameter, topmost_surface, should be given for the keyrouter to + * determine whether the given topmost_surface require a keycode exclusively + * when the surface is on very top of stack. + * See tizen_keyrouter protocol in tizen-extension.xml for more detail. */ -enum ds_tizen_keyroute_mode -ds_tizen_keyrouter_get_keyroutes(struct ds_tizen_keyrouter *keyrouter, - int keycode, struct wl_client *topmost_client, - struct wl_array *keyroutes); - +void +ds_tizen_keyrouter_notify_key(struct ds_tizen_keyrouter *keyrouter, + struct ds_seat *seat, struct ds_surface *topmost_surface, + uint32_t time_msec, uint32_t key, uint32_t state); #ifdef __cplusplus } #endif diff --git a/src/keyrouter/keyrouter.c b/src/keyrouter/keyrouter.c index 2e825c6..097f4df 100644 --- a/src/keyrouter/keyrouter.c +++ b/src/keyrouter/keyrouter.c @@ -29,6 +29,9 @@ static int keyrouter_client_grab_key(struct keyrouter_client *client, int mode, int keycode); static int keyrouter_client_ungrab_key(struct keyrouter_client *client, int keycode); +static void +keyrouter_seat_broadcast_key(struct ds_seat *seat, struct wl_array *keyroutes, + bool with_focus, uint32_t time_msec, uint32_t keycode, uint32_t state); WL_EXPORT struct ds_tizen_keyrouter * ds_tizen_keyrouter_create(struct wl_display *display) @@ -76,13 +79,45 @@ ds_tizen_keyrouter_add_destroy_listener(struct ds_tizen_keyrouter *keyrouter, wl_signal_add(&keyrouter->events.destroy, listener); } -WL_EXPORT enum ds_tizen_keyroute_mode -ds_tizen_keyrouter_get_keyroutes(struct ds_tizen_keyrouter *keyrouter, - int keycode, struct wl_client *topmost_client, - struct wl_array *keyroutes) +WL_EXPORT void +ds_tizen_keyrouter_notify_key(struct ds_tizen_keyrouter *keyrouter, + struct ds_seat *seat, struct ds_surface *topmost_surface, + uint32_t time_msec, uint32_t keycode, uint32_t state) { - return keyrouter_grab_get_keyroutes(&keyrouter->keygrab, - keycode, topmost_client, keyroutes); + struct wl_client *topmost_wl_client = NULL; + struct wl_resource *surface_resource; + struct wl_array keyroutes; + enum keyroute_mode mode; + + wl_array_init(&keyroutes); + + if (topmost_surface) { + surface_resource = ds_surface_get_wl_resource(topmost_surface); + if (surface_resource) { + topmost_wl_client = wl_resource_get_client( + ds_surface_get_wl_resource(topmost_surface)); + } + } + + mode = keyrouter_grab_get_keyroutes(&keyrouter->keygrab, keycode + 8, + topmost_wl_client, &keyroutes); + + if (mode == KEYROUTE_MODE_EXCLUSIVE) { + keyrouter_seat_broadcast_key(seat, &keyroutes, true, + time_msec, keycode, state); + + goto end; + } + + if (mode == KEYROUTE_MODE_SHARED) { + keyrouter_seat_broadcast_key(seat, &keyroutes, false, + time_msec, keycode, state); + } + + ds_seat_keyboard_notify_key(seat, time_msec, keycode, state); + +end: + wl_array_release(&keyroutes); } static void @@ -424,3 +459,30 @@ keyrouter_client_ungrab_key(struct keyrouter_client *client, int keycode) return TIZEN_KEYROUTER_ERROR_NONE; } + +static void +keyrouter_seat_broadcast_key(struct ds_seat *seat, struct wl_array *keyroutes, + bool with_focus, uint32_t time_msec, uint32_t keycode, uint32_t state) +{ + struct ds_seat_client *seat_client, *focused_client; + struct wl_client **wl_client_ptr; + + focused_client = ds_seat_keyboard_get_focused_client(seat); + + wl_array_for_each(wl_client_ptr, keyroutes) { + seat_client = ds_seat_client_for_wl_client(seat, *wl_client_ptr); + if (!seat_client) { + ds_inf("Could not find ds_seat_client for given wl_client(%p)", + *wl_client_ptr); + continue; + } + + if (seat_client == focused_client) { + if (with_focus) + ds_seat_keyboard_notify_key(seat, time_msec, keycode, state); + } + else { + ds_seat_client_send_key(seat_client, time_msec, keycode, state); + } + } +} diff --git a/src/keyrouter/keyrouter.h b/src/keyrouter/keyrouter.h index bea9ab1..bd3faa9 100644 --- a/src/keyrouter/keyrouter.h +++ b/src/keyrouter/keyrouter.h @@ -9,6 +9,31 @@ #define KEYROUTER_MAX_KEYS 512 +/** + * Keyroute mode of keyroutes returned by keyrouter_grab_get_keyroutes() + * for a given keycode. + */ +enum keyroute_mode { + /** + * The keycode is not under any keyroute mode. + * The compositor may send a keycode to a focused wayland cliet as usual. + */ + KEYROUTE_MODE_NONE, + /** + * Exclusive mode means that a given keycode is required to be sent + * exclusively to wayland clients of returned keyroutes. So the compositor + * must not send this keycode to any other wayland clients except for + * returned wayland clients. + */ + KEYROUTE_MODE_EXCLUSIVE, + /** + * The keycode is required to be shared with returned wayland clients. + * The compositor must send the keycode not only to returned wayland + * clients, but also to a focused wayland client. + */ + KEYROUTE_MODE_SHARED, +}; + struct keyroute_info { struct wl_client *wl_client; @@ -92,7 +117,7 @@ int keyrouter_grab_grab_key(struct keyrouter_grab *keygrab, int mode, void keyrouter_grab_ungrab_key(struct keyrouter_grab *keygrab, int mode, int keycode, struct wl_client *wl_client); -enum ds_tizen_keyroute_mode +enum keyroute_mode keyrouter_grab_get_keyroutes(struct keyrouter_grab *keygrab, int keycode, struct wl_client *topmost_client, struct wl_array *keyroutes); diff --git a/src/keyrouter/keyrouter_grab.c b/src/keyrouter/keyrouter_grab.c index fed301b..4f27ea1 100644 --- a/src/keyrouter/keyrouter_grab.c +++ b/src/keyrouter/keyrouter_grab.c @@ -31,7 +31,7 @@ keyrouter_grab_topmost_find(struct keyrouter_grab *keygrab, int keycode, struct wl_client *topmost_client); static bool keyrouter_grab_get_shared_keyroutes(struct keyrouter_grab *keygrab, int keycode, struct wl_array *keyroutes); -static bool keyroutes_add_info(struct wl_array *keyroutes, +static bool keyroutes_add_wl_client(struct wl_array *keyroutes, struct wl_client *wl_client); void @@ -58,7 +58,7 @@ keyrouter_grab_finish(struct keyrouter_grab *keygrab) } } -enum ds_tizen_keyroute_mode +enum keyroute_mode keyrouter_grab_get_keyroutes(struct keyrouter_grab *keygrab, int keycode, struct wl_client *topmost_client, struct wl_array *keyroutes) { @@ -82,16 +82,16 @@ keyrouter_grab_get_keyroutes(struct keyrouter_grab *keygrab, int keycode, goto exclusive_out; if (keyrouter_grab_get_shared_keyroutes(keygrab, keycode, keyroutes)) - return DS_TIZEN_KEYROUTE_MODE_SHARED; + return KEYROUTE_MODE_SHARED; none_out: - return DS_TIZEN_KEYROUTE_MODE_NONE; + return KEYROUTE_MODE_NONE; exclusive_out: - if (!keyroutes_add_info(keyroutes, info->wl_client)) + if (!keyroutes_add_wl_client(keyroutes, info->wl_client)) ds_err("Could not add wl_client to keyroutes"); - return DS_TIZEN_KEYROUTE_MODE_EXCLUSIVE; + return KEYROUTE_MODE_EXCLUSIVE; } int @@ -299,7 +299,7 @@ keyrouter_grab_get_shared_keyroutes(struct keyrouter_grab *keygrab, int count = 0; wl_list_for_each(info, list, link) { - if (!keyroutes_add_info(keyroutes, info->wl_client)) { + if (!keyroutes_add_wl_client(keyroutes, info->wl_client)) { ds_err("Could not prepend wl_client to delivery_list"); continue; } @@ -314,16 +314,15 @@ keyrouter_grab_get_shared_keyroutes(struct keyrouter_grab *keygrab, } static bool -keyroutes_add_info(struct wl_array *keyroutes, - struct wl_client *wl_client) +keyroutes_add_wl_client(struct wl_array *keyroutes, struct wl_client *wl_client) { - struct ds_tizen_keyroute_info *info; + struct wl_client **p; - info = wl_array_add(keyroutes, sizeof *info); - if (!info) + p = wl_array_add(keyroutes, sizeof(struct wl_client *)); + if (!p) return false; - info->wl_client = wl_client; + *p = wl_client; return true; } -- 2.7.4 From c1d56f7d059dc0744fc95f35bec1e60ed46cd332 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Thu, 7 Jul 2022 19:57:35 +0900 Subject: [PATCH 04/16] remove the test files. This test files are useless. Change-Id: I0528e80430c947e1dfcf92f8b71860188d9d0ec5 --- tests/meson.build | 25 ------ tests/test_backend.c | 80 ------------------ tests/test_compositor.c | 63 --------------- tests/test_subsurface.c | 211 ------------------------------------------------ tests/test_surface.c | 162 ------------------------------------- 5 files changed, 541 deletions(-) delete mode 100644 tests/meson.build delete mode 100644 tests/test_backend.c delete mode 100644 tests/test_compositor.c delete mode 100644 tests/test_subsurface.c delete mode 100644 tests/test_surface.c diff --git a/tests/meson.build b/tests/meson.build deleted file mode 100644 index 324c760..0000000 --- a/tests/meson.build +++ /dev/null @@ -1,25 +0,0 @@ -tests = [ - { 'name': 'test_compositor' }, - { 'name': 'test_backend' }, - { - 'name': 'test_surface', - 'deps': [ dependency('wayland-client') ], - }, - { - 'name': 'test_subsurface', - 'deps': [ dependency('wayland-client') ], - }, -] - -foreach t : tests - t_deps = [ deps_libds_tizen ] - t_deps += t.get('deps', []) - - test('libds-tizen-' + t.get('name'), - executable('libds-tizen-' + t.get('name'), t.get('name') + '.c', - dependencies: t_deps , - include_directories: common_inc, - install: false - ) - ) -endforeach diff --git a/tests/test_backend.c b/tests/test_backend.c deleted file mode 100644 index 69417b6..0000000 --- a/tests/test_backend.c +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include -#include - -#include -#include - -static struct ds_backend * -create_possible_wl_backend(struct wl_display *display) -{ - struct ds_backend *backend; - char wl_name[512]; - - for (int i = 0; i < 5; i++) { - snprintf(wl_name, sizeof wl_name, "wayland-%d", i); - backend = ds_wl_backend_create(display, wl_name); - if (backend) - break; - } - - return backend; -} - -static void -test_wl_backend_create(void) -{ - struct wl_display *display; - struct ds_backend *backend; - - display = wl_display_create(); - - backend = create_possible_wl_backend(display); - assert(backend); - - ds_backend_destroy(backend); -} - -struct test -{ - struct wl_listener backend_destroy; - bool cb_called; -}; - -static void -cb_backend_destroy(struct wl_listener *listener, void *data) -{ - struct test *test; - - test = wl_container_of(listener, test, backend_destroy); - test->cb_called = true; -} - -static void -test_wl_backend_destroy_signal(void) -{ - struct wl_display *display; - struct ds_backend *backend; - struct test test; - - display = wl_display_create(); - - backend = create_possible_wl_backend(display); - assert(backend); - - test.cb_called = false; - test.backend_destroy.notify = cb_backend_destroy; - ds_backend_add_destroy_listener(backend, &test.backend_destroy); - - wl_display_destroy(display); - assert(test.cb_called); -} - -int -main(void) -{ - test_wl_backend_create(); - test_wl_backend_destroy_signal(); - - return 0; -} diff --git a/tests/test_compositor.c b/tests/test_compositor.c deleted file mode 100644 index b5883a5..0000000 --- a/tests/test_compositor.c +++ /dev/null @@ -1,63 +0,0 @@ -#include -#include - -#include -#include -#include - -static void -test_compositor_create(void) -{ - struct wl_display *display; - struct ds_compositor *compositor; - - display = wl_display_create(); - compositor = ds_compositor_create(display); - assert(compositor); - - wl_display_destroy(display); -} - -struct test -{ - struct wl_listener compositor_destroy; - bool destroyed; -}; - -static void -cb_compositor_destroy(struct wl_listener *listener, void *data) -{ - struct test *test; - - test = wl_container_of(listener, test, compositor_destroy); - test->destroyed = true; -} - -static void -test_compositor_destroy_signal(void) -{ - struct wl_display *display; - struct ds_compositor *compositor; - struct test test; - - display = wl_display_create(); - compositor = ds_compositor_create(display); - assert(compositor); - - test.destroyed = false; - test.compositor_destroy.notify = cb_compositor_destroy; - ds_compositor_add_destroy_listener(compositor, - &test.compositor_destroy); - - wl_display_destroy(display); - assert(test.destroyed == true); -} - -int -main(void) -{ - test_compositor_create(); - test_compositor_destroy_signal(); - - return 0; -} diff --git a/tests/test_subsurface.c b/tests/test_subsurface.c deleted file mode 100644 index 98e830d..0000000 --- a/tests/test_subsurface.c +++ /dev/null @@ -1,211 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -struct server_base -{ - struct wl_display *display; - struct ds_compositor *compositor; - const char *socket; -}; - -const char * -test_server_init(struct server_base *server) -{ - server->display = wl_display_create(); - assert(server->display); - server->compositor = ds_compositor_create(server->display); - assert(server->compositor); - server->socket = wl_display_add_socket_auto(server->display); - assert(server->socket); - - return server->socket; -} - -void -test_server_finish(struct server_base *server) -{ - wl_display_destroy(server->display); -} - -struct client -{ - struct wl_display *display; - struct wl_registry *registry; - struct wl_compositor *compositor; - struct wl_subcompositor *subcompositor; - struct wl_surface *surface; - struct wl_subsurface *subsurface; -}; - -static void -handle_global(void *data, struct wl_registry *registry, uint32_t id, - const char *interface, uint32_t version) -{ - struct client *client = data; - - if (strcmp(interface, "wl_compositor") == 0) { - client->compositor = - wl_registry_bind(registry, id, &wl_compositor_interface, version); - } - else if (strcmp(interface, "wl_subcompositor") == 0) { - client->subcompositor = - wl_registry_bind(registry, id, &wl_subcompositor_interface, version); - } -} - -static const struct wl_registry_listener registry_listener = { - .global = handle_global, -}; - -void -test_client_init(struct client *client, const char *name) -{ - client->display = wl_display_connect(name); - assert(client->display); - client->registry = wl_display_get_registry(client->display); - assert(client->registry); - - wl_registry_add_listener(client->registry, ®istry_listener, client); - - wl_display_roundtrip(client->display); - - assert(client->compositor); - assert(client->subcompositor); -} - -void -test_client_finish(struct client *client) -{ - wl_subcompositor_destroy(client->subcompositor); - wl_compositor_destroy(client->compositor); - wl_registry_destroy(client->registry); - wl_display_disconnect(client->display); -} - -struct test_server -{ - struct server_base base; - bool cb_called; - - struct wl_listener new_surface; - struct wl_listener new_subsurface1; - struct wl_listener new_subsurface2; -}; - -static void -cb_new_subsurface1(struct wl_listener *listener, void *data) -{ - struct ds_subsurface *subsurface = data; - struct test_server *server; - - assert(subsurface); - - server = wl_container_of(listener, server, new_subsurface1); - server->cb_called = true; - wl_display_terminate(server->base.display); -} - -static void -cb_new_subsurface2(struct wl_listener *listener, void *data) -{ - struct ds_subsurface *subsurface = data; - struct test_server *server; - - assert(subsurface); - - server = wl_container_of(listener, server, new_subsurface2); - server->cb_called = true; - wl_display_terminate(server->base.display); -} - -static void -cb_new_surface(struct wl_listener *listener, void *data) -{ - struct ds_surface *surface = data; - struct test_server *server; - - server = wl_container_of(listener, server, new_surface); - if (!server->new_subsurface1.notify) { - server->new_subsurface1.notify = cb_new_subsurface1; - ds_surface_add_new_subsurface_listener(surface, - &server->new_subsurface1); - } - else { - server->new_subsurface2.notify = cb_new_subsurface2; - ds_surface_add_new_subsurface_listener(surface, - &server->new_subsurface2); - } -} - -static void -run_client(const char *name) -{ - struct client client; - - test_client_init(&client, name); - - struct wl_surface *surface = - wl_compositor_create_surface(client.compositor); - - struct wl_surface *child_surface = - wl_compositor_create_surface(client.compositor); - - struct wl_subsurface *subsurface = - wl_subcompositor_get_subsurface(client.subcompositor, - child_surface, surface); - - wl_display_roundtrip(client.display); - - wl_subsurface_destroy(subsurface); - wl_surface_destroy(child_surface); - wl_surface_destroy(surface); - - test_client_finish(&client); -} - -static void -test_subsurface_create(void) -{ - struct test_server server = { - .new_subsurface1 = { .notify = NULL }, - .cb_called = false - }; - pid_t pid; - - const char *socket_name = test_server_init(&server.base); - - pid = fork(); - assert(pid != -1); - - if (pid == 0) { - run_client(socket_name); - exit(0); - } - - server.new_surface.notify = cb_new_surface; - ds_compositor_add_new_surface_listener(server.base.compositor, - &server.new_surface); - - wl_display_run(server.base.display); - - assert(server.cb_called); - - test_server_finish(&server.base); -} - -int -main(void) -{ - test_subsurface_create(); - return 0; -} diff --git a/tests/test_surface.c b/tests/test_surface.c deleted file mode 100644 index eab2b77..0000000 --- a/tests/test_surface.c +++ /dev/null @@ -1,162 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -struct server_base -{ - struct wl_display *display; - struct ds_compositor *compositor; - const char *socket; -}; - -void -test_server_init(struct server_base *server) -{ - server->display = wl_display_create(); - assert(server->display); - server->compositor = ds_compositor_create(server->display); - assert(server->compositor); - server->socket = wl_display_add_socket_auto(server->display); - assert(server->socket); -} - -void -test_server_finish(struct server_base *server) -{ - wl_display_destroy(server->display); -} - -struct client -{ - struct wl_display *display; - struct wl_registry *registry; - struct wl_compositor *compositor; - struct wl_surface *surface; -}; - -static void -handle_global(void *data, struct wl_registry *registry, uint32_t id, - const char *interface, uint32_t version) -{ - struct client *client = data; - - if (strcmp(interface, "wl_compositor") == 0) { - client->compositor = - wl_registry_bind(registry, id, &wl_compositor_interface, version); - } -} - -static const struct wl_registry_listener registry_listener = { - .global = handle_global, -}; - -void -test_client_init(struct client *client, const char *name) -{ - client->display = wl_display_connect(name); - assert(client->display); - client->registry = wl_display_get_registry(client->display); - assert(client->registry); - - wl_registry_add_listener(client->registry, ®istry_listener, client); - - wl_display_roundtrip(client->display); - - assert(client->compositor); -} - -void -test_client_finish(struct client *client) -{ - wl_compositor_destroy(client->compositor); - wl_registry_destroy(client->registry); - wl_display_disconnect(client->display); -} - -struct test_server { - struct server_base base; - bool cb_called; - - struct wl_listener new_surface; - struct wl_listener surface_destroy; -}; - -static void -cb_surface_destroy(struct wl_listener *listener, void *data) -{ - struct test_server *server; - - server = wl_container_of(listener, server, surface_destroy); - server->cb_called = true; - wl_display_terminate(server->base.display); -} - -static void -cb_new_surface(struct wl_listener *listener, void *data) -{ - struct ds_surface *surface = data; - struct test_server *server; - - server = wl_container_of(listener, server, new_surface); - server->surface_destroy.notify = cb_surface_destroy; - ds_surface_add_destroy_listener(surface, &server->surface_destroy); -} - -static void -run_client(const char *name) -{ - struct client client; - - test_client_init(&client, name); - - struct wl_surface *surface = - wl_compositor_create_surface(client.compositor); - - wl_display_roundtrip(client.display); - - wl_surface_destroy(surface); - - test_client_finish(&client); -} - -static void -test_surface_create_and_destroy(void) -{ - struct test_server server = { .cb_called = false }; - pid_t pid; - - test_server_init(&server.base); - - pid = fork(); - assert(pid != -1); - - if (pid == 0) { - run_client(server.base.socket); - exit(0); - } - - server.new_surface.notify = cb_new_surface; - ds_compositor_add_new_surface_listener(server.base.compositor, - &server.new_surface); - - wl_display_run(server.base.display); - - assert(server.cb_called); - - test_server_finish(&server.base); -} - -int -main(void) -{ - test_surface_create_and_destroy(); - return 0; -} -- 2.7.4 From f7790557494db83dc62a50b285bed2bce9775a3e Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Fri, 8 Jul 2022 08:08:36 +0900 Subject: [PATCH 05/16] add gtest files Change-Id: I2d2c87aee0a1c842467d04d907f249ce4bd6b57b --- packaging/libds-tizen.spec | 1 + tests/tc_main.cpp | 26 ++++++++++++++++++++++++++ tests/tc_main.h | 12 ++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 tests/tc_main.cpp create mode 100644 tests/tc_main.h diff --git a/packaging/libds-tizen.spec b/packaging/libds-tizen.spec index fe34941..0ac145b 100644 --- a/packaging/libds-tizen.spec +++ b/packaging/libds-tizen.spec @@ -27,6 +27,7 @@ BuildRequires: pkgconfig(tizen-dpms-client) BuildRequires: pkgconfig(cynara-client) BuildRequires: pkgconfig(cynara-session) BuildRequires: pkgconfig(libsmack) +BuildRequires: pkgconfig(gmock) %description Wayland Compositor Library for Tizen diff --git a/tests/tc_main.cpp b/tests/tc_main.cpp new file mode 100644 index 0000000..5e99a34 --- /dev/null +++ b/tests/tc_main.cpp @@ -0,0 +1,26 @@ +#include "gmock/gmock.h" + +int +main(int argc, char **argv) +{ + auto AllTestSuccess = false; + + try { + ::testing::InitGoogleMock(&argc, argv); + ::testing::FLAGS_gtest_death_test_style = "fast"; + } catch (...) { + std::cout << "error while trying to init google tests.\n"; + exit(EXIT_FAILURE); + } + + try { + AllTestSuccess = RUN_ALL_TESTS() == 0 ? true : false; + } catch (const ::testing::internal::GoogleTestFailureException &e) { + AllTestSuccess = false; + std::cout << "GoogleTestFailureException was thrown:" << e.what() + << std::endl; + std::cout << "\n"; + } + + return AllTestSuccess; +} diff --git a/tests/tc_main.h b/tests/tc_main.h new file mode 100644 index 0000000..41e630a --- /dev/null +++ b/tests/tc_main.h @@ -0,0 +1,12 @@ +#ifndef TC_MAIN_H +#define TC_MAIN_H + +#include +#include + +using ::testing::Bool; +using ::testing::Combine; +using ::testing::TestWithParam; +using ::testing::Values; + +#endif -- 2.7.4 From 1508e2530138dda950de00d07692e9dfe607c457 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Fri, 8 Jul 2022 08:09:51 +0900 Subject: [PATCH 06/16] add .clang-format file Change-Id: I988a24843b381135d65b53cb78bd8dfc4d11ce0a --- .clang-format | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..fc9ad87 --- /dev/null +++ b/.clang-format @@ -0,0 +1,102 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: DontAlign +AlignConsecutiveMacros: false +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +# AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: TopLevelDefinitions +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +BinPackArguments: true +BinPackParameters: true +BreakBeforeBraces: Linux +BreakBeforeBinaryOperators: None +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: false +BreakStringLiterals: false +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + - Regex: '.*' + Priority: 1 + SortPriority: 0 +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentCaseLabels: true +IndentGotoLabels: true +IndentPPDirectives: AfterHash +IndentWidth: 4 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +PointerAlignment: Right +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +Standard: Latest +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +UseCRLF: false +UseTab: Never +... -- 2.7.4 From 81cb74f424ed1877183b71640bd65e2ce30bd10b Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Fri, 8 Jul 2022 08:11:23 +0900 Subject: [PATCH 07/16] add libds-tizen-allocator-tbm tests Change-Id: I305f0b527ff2b2408f7ce5017cfa8f5107d057fb --- meson.build | 5 +++-- packaging/libds-tizen.spec | 1 + tests/meson.build | 21 ++++++++++++++++++++ tests/tc_allocator_tbm.cpp | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 tests/meson.build create mode 100644 tests/tc_allocator_tbm.cpp diff --git a/meson.build b/meson.build index dfe8129..d6804fe 100644 --- a/meson.build +++ b/meson.build @@ -1,9 +1,10 @@ -project('libds_tizen', 'c', +project('libds_tizen', + ['c', 'cpp'], license: 'MIT', version: '0.1.2', default_options: [ 'warning_level=1', - 'c_std=gnu99', + 'c_std=gnu99', 'cpp_std=c++20', 'buildtype=debug' ] ) diff --git a/packaging/libds-tizen.spec b/packaging/libds-tizen.spec index 0ac145b..c5e0623 100644 --- a/packaging/libds-tizen.spec +++ b/packaging/libds-tizen.spec @@ -152,6 +152,7 @@ ninja -C builddir install %{_includedir}/libds-tizen/allocator/tbm.h %{_libdir}/pkgconfig/libds-tizen-allocator-tbm.pc %{_libdir}/libds-tizen-allocator-tbm.so +%{_bindir}/libds-tizen-allocator-tbm-tests %files backend-tdm %manifest %{name}.manifest diff --git a/tests/meson.build b/tests/meson.build new file mode 100644 index 0000000..1d8158c --- /dev/null +++ b/tests/meson.build @@ -0,0 +1,21 @@ +deps_test_common = [ + deps_libds_tizen, + dependency('gmock', required: true), + dependency('wayland-client', required: true), +] + +tc_allocator_tbm_files = [ + 'tc_main.cpp', + 'tc_allocator_tbm.cpp', +] + +executable('libds-tizen-allocator-tbm-tests', + tc_allocator_tbm_files, + dependencies: [ + deps_test_common, + deps_libds_tizen_allocator_tbm, + dependency('libdrm', required: true), + ], + install_dir: libds_tizen_bindir, + install : true +) diff --git a/tests/tc_allocator_tbm.cpp b/tests/tc_allocator_tbm.cpp new file mode 100644 index 0000000..678ad88 --- /dev/null +++ b/tests/tc_allocator_tbm.cpp @@ -0,0 +1,49 @@ +#include "tc_main.h" +#include +#include + +class AllocatorTbmTest : public ::testing::Test +{ + public: + void SetUp(void) override; + void TearDown(void) override; +}; + +void +AllocatorTbmTest::SetUp(void) +{ +} + +void +AllocatorTbmTest::TearDown(void) +{ +} + +TEST_F(AllocatorTbmTest, Create_P) +{ + struct ds_allocator *allocator; + + allocator = ds_tbm_allocator_create(); + EXPECT_TRUE(allocator != NULL); + + ds_allocator_destroy(allocator); +} + +TEST_F(AllocatorTbmTest, GetSurface_P) +{ + struct ds_allocator *allocator; + struct ds_buffer *buffer; + void *tbm_buffer; + + allocator = ds_tbm_allocator_create(); + EXPECT_TRUE(allocator != NULL); + + buffer = + ds_allocator_create_buffer(allocator, 100, 100, DRM_FORMAT_XRGB8888); + EXPECT_TRUE(buffer != NULL); + + tbm_buffer = ds_tbm_buffer_get_surface(buffer); + EXPECT_TRUE(tbm_buffer != NULL); + + ds_allocator_destroy(allocator); +} -- 2.7.4 From fc9c4e058e23ef37d0caa155016a3d6f792c2938 Mon Sep 17 00:00:00 2001 From: "duna.oh" Date: Tue, 12 Jul 2022 01:26:50 +0900 Subject: [PATCH 08/16] devicemgr: implement pointer_warp Change-Id: Ia3054efdedc15b04b93f4b3e089fa1491c87f981 --- clients/simple-tbm.c | 19 ++++++- examples/tinyds-tdm-libinput.c | 101 ++++++++++++++++++++++++++++++---- include/libds-tizen/input_devicemgr.h | 13 +++++ src/input_devicemgr/input_devicemgr.c | 61 +++++++++++++++++++- src/input_devicemgr/input_devicemgr.h | 1 + 5 files changed, 182 insertions(+), 13 deletions(-) diff --git a/clients/simple-tbm.c b/clients/simple-tbm.c index c82349f..f68fedb 100644 --- a/clients/simple-tbm.c +++ b/clients/simple-tbm.c @@ -56,6 +56,7 @@ struct display { struct tizen_input_device_manager *devicemgr; int notified; bool blocked; + struct wl_surface *entered_surface; }; struct window { @@ -338,8 +339,21 @@ static const struct xdg_wm_base_listener xdg_wm_base_listener = { static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { + struct display *d = data; + static int warp_x = 0, warp_y = 0; + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { fprintf(stderr, "pointer_handle_button: PRESSED\n"); + + warp_x += 10; + warp_y += 10; + tizen_input_device_manager_pointer_warp(d->devicemgr, + d->entered_surface, + wl_fixed_from_int(warp_x), + wl_fixed_from_int(warp_y)); + fprintf(stderr, "requesting pointer_warp: surface:%p sx: %d sy: %d\n", + d->entered_surface, + warp_x, warp_y); } else { fprintf(stderr, "pointer_handle_button: RELEASED\n"); @@ -350,8 +364,11 @@ static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { + struct display *d = data; + fprintf(stderr, "pointer_handle_enter surface_x:%d, surface_y:%d\n", wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y)); + d->entered_surface = surface; } static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer, @@ -508,7 +525,7 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, } if ((caps & WL_SEAT_CAPABILITY_POINTER)) { struct wl_pointer *pointer = wl_seat_get_pointer(wl_seat); - wl_pointer_add_listener(pointer, &pointer_listener, NULL); + wl_pointer_add_listener(pointer, &pointer_listener, d); fprintf(stderr, "seat_handle_capabilities: pointer\n"); } if ((caps & WL_SEAT_CAPABILITY_TOUCH)) { diff --git a/examples/tinyds-tdm-libinput.c b/examples/tinyds-tdm-libinput.c index b1974f1..10c02bb 100644 --- a/examples/tinyds-tdm-libinput.c +++ b/examples/tinyds-tdm-libinput.c @@ -40,6 +40,7 @@ #define TINYDS_UNUSED __attribute__((unused)) struct tinyds_keyboard; +struct tinyds_pointer; struct tinyds_output { @@ -87,8 +88,11 @@ struct tinyds_server struct wl_listener new_output; struct wl_listener new_input; struct wl_listener new_xdg_surface; + struct wl_listener devicemgr_destroy; + struct wl_listener pointer_warp; - struct tinyds_keyboard *keyboard; + struct wl_list keyboards; + struct wl_list pointers; }; struct tinyds_view @@ -119,6 +123,7 @@ struct tinyds_pointer struct wl_listener motion; //relative struct wl_listener button; struct wl_listener frame; + struct wl_list link; //tinyds_server::pointers }; struct tinyds_keyboard @@ -128,6 +133,7 @@ struct tinyds_keyboard struct wl_listener destroy; struct wl_listener key; + struct wl_list link; //tinyds_server::keyboards }; struct tinyds_touch @@ -169,6 +175,10 @@ static void server_add_pointer(struct tinyds_server *server, static void server_add_touch(struct tinyds_server *server, struct ds_input_device *dev); +static struct tinyds_view * +server_view_at(struct tinyds_server *server, double lx, double ly, + double *sx, double *sy); + int main(void) { @@ -214,16 +224,19 @@ view_handle_xdg_surface_map(struct wl_listener *listener, { struct tinyds_view *view; struct ds_keyboard *keyboard; + struct tinyds_keyboard *kbd; view = wl_container_of(listener, view, xdg_surface_map); view->mapped = true; - if (!view->server->keyboard) return; - keyboard = ds_input_device_get_keyboard(view->server->keyboard->dev); - if (keyboard != NULL) { - ds_seat_keyboard_notify_enter(view->server->seat, - ds_xdg_surface_get_surface(view->xdg_surface), - keyboard->keycodes, keyboard->num_keycodes, - &keyboard->modifiers); + wl_list_for_each(kbd, &view->server->keyboards, link) { + keyboard = ds_input_device_get_keyboard(kbd->dev); + if (keyboard != NULL) { + ds_seat_keyboard_notify_enter(view->server->seat, + ds_xdg_surface_get_surface(view->xdg_surface), + keyboard->keycodes, keyboard->num_keycodes, + &keyboard->modifiers); + return; + } } } @@ -306,6 +319,8 @@ server_new_xdg_surface(struct wl_listener *listener, void *data) view->x = rand() % 1000; view->y = rand() % 500; + + ds_inf("view at (%d, %d)", view->x, view->y); } static void @@ -444,6 +459,55 @@ devicemgr_set_keymap(struct ds_tizen_input_devicemgr *devicemgr) devicemgr_remove_keymap_data(&keymap_list, 458); } +static void +devicemgr_handle_pointer_warp(struct wl_listener *listener, void *data) +{ + struct tinyds_server *server; + struct tinyds_pointer *pointer; + struct ds_tizen_input_devicemgr_event_pointer_warp *event = data; + double sx = 0.f, sy = 0.f; + struct tinyds_view *view = NULL; + + server = wl_container_of(listener, server, pointer_warp); + + ds_inf("Pointer warp: surface(%p) x(%.2f) y(%.2f)", event->surface, + event->x, event->y); + + wl_list_for_each(pointer, &server->pointers, link){ + if (!pointer->focused_view) continue; + view = pointer->focused_view; + } + if (!view) return; + + if (event->surface != ds_xdg_surface_get_surface(view->xdg_surface)) { + ds_inf("Pointer is not on the requested surface"); + return; + } + + server->output_x = view->x + (event->x * server->output->width); + server->output_y = view->y + (event->y * server->output->height); + + server_view_at(server, server->output_x, server->output_y, &sx, &sy); + + ds_inf("notify motion: sx:%.2f sy:%.2f, output_x:%.1f, output_y:%.1f", + sx, sy, server->output_x, server->output_y); + + ds_seat_pointer_notify_motion(server->seat, + event->time_msec, sx, sy); +} + +static void +devicemgr_handle_destroy(struct wl_listener *listener, void *data TINYDS_UNUSED) +{ + struct tinyds_server *server = + wl_container_of(listener, server, devicemgr_destroy); + + wl_list_remove(&server->devicemgr_destroy.link); + wl_list_remove(&server->pointer_warp.link); + + server->devicemgr = NULL; +} + static bool init_server(struct tinyds_server *server, struct wl_display *display) { @@ -468,6 +532,8 @@ init_server(struct tinyds_server *server, struct wl_display *display) ds_backend_add_new_output_listener(server->backend, &server->new_output); + wl_list_init(&server->keyboards); + wl_list_init(&server->pointers); server->new_input.notify = backend_handle_new_input; ds_backend_add_new_input_listener(server->input_backend, &server->new_input); @@ -499,6 +565,15 @@ init_server(struct tinyds_server *server, struct wl_display *display) } devicemgr_set_keymap(server->devicemgr); + + server->devicemgr_destroy.notify = devicemgr_handle_destroy; + ds_tizen_input_devicemgr_add_destroy_listener(server->devicemgr, + &server->devicemgr_destroy); + + server->pointer_warp.notify = devicemgr_handle_pointer_warp; + ds_tizen_input_devicemgr_add_pointer_warp_listener(server->devicemgr, + &server->pointer_warp); + return true; err: @@ -762,8 +837,7 @@ keyboard_handle_device_destroy(struct wl_listener *listener, void *data) wl_list_remove(&kbd->destroy.link); wl_list_remove(&kbd->key.link); - - kbd->server->keyboard = NULL; + wl_list_remove(&kbd->link); free(kbd); } @@ -852,7 +926,7 @@ server_add_keyboard(struct tinyds_server *server, struct ds_input_device *dev) kbd->key.notify = keyboard_handle_key; ds_keyboard_add_key_listener(ds_input_device_get_keyboard(dev), &kbd->key); - server->keyboard = kbd; + wl_list_insert(&server->keyboards, &kbd->link); ds_inf("Keyboard(%p) added", kbd); } @@ -1007,6 +1081,7 @@ pointer_handle_device_destroy(struct wl_listener *listener, void *data) wl_list_remove(&pointer->motion.link); wl_list_remove(&pointer->button.link); wl_list_remove(&pointer->frame.link); + wl_list_remove(&pointer->link); free(pointer); } @@ -1123,5 +1198,9 @@ server_add_pointer(struct tinyds_server *server, struct ds_input_device *dev) ds_pointer_add_frame_listener(ds_input_device_get_pointer(dev), &pointer->frame); + pointer->focused_view = NULL; + + wl_list_insert(&server->pointers, &pointer->link); + ds_inf("Pointer(%p) added", pointer); } diff --git a/include/libds-tizen/input_devicemgr.h b/include/libds-tizen/input_devicemgr.h index fc1060e..458d581 100644 --- a/include/libds-tizen/input_devicemgr.h +++ b/include/libds-tizen/input_devicemgr.h @@ -2,6 +2,7 @@ #define LIBDS_TIZEN_INPUT_DEVICEMGR_H #include +#include #ifdef __cplusplus extern "C" { @@ -19,6 +20,13 @@ struct ds_tizen_input_devicemgr_keymap_data struct wl_list link; }; +struct ds_tizen_input_devicemgr_event_pointer_warp +{ + uint32_t time_msec; + struct ds_surface *surface; + double x, y; +}; + struct ds_tizen_input_devicemgr * ds_tizen_input_devicemgr_create(struct ds_backend *backend, struct ds_seat *seat); @@ -28,6 +36,11 @@ ds_tizen_input_devicemgr_add_destroy_listener( struct ds_tizen_input_devicemgr *devicemgr, struct wl_listener *listener); +void +ds_tizen_input_devicemgr_add_pointer_warp_listener( + struct ds_tizen_input_devicemgr *devicemgr, + struct wl_listener *listener); + bool ds_tizen_input_devicemgr_set_keymap_list( struct ds_tizen_input_devicemgr *devicemgr, diff --git a/src/input_devicemgr/input_devicemgr.c b/src/input_devicemgr/input_devicemgr.c index f690fe7..00c99b0 100644 --- a/src/input_devicemgr/input_devicemgr.c +++ b/src/input_devicemgr/input_devicemgr.c @@ -64,6 +64,10 @@ device_manager_handle_generate_pointer(struct wl_client *client, struct wl_resource *resource, uint32_t type, uint32_t x, uint32_t y, uint32_t button); static void +device_manager_handle_pointer_warp(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *surface, + wl_fixed_t x, wl_fixed_t y); +static void device_manager_handle_destroy(struct wl_client *client, struct wl_resource *resource); @@ -169,6 +173,7 @@ ds_tizen_input_devicemgr_create(struct ds_backend *backend, } wl_signal_init(&tz_devicemgr->events.destroy); + wl_signal_init(&tz_devicemgr->events.pointer_warp); wl_list_init(&tz_devicemgr->clients); wl_list_init(&tz_devicemgr->devices.kbd->key.pressed); wl_list_init(&tz_devicemgr->keymap_list); @@ -205,6 +210,14 @@ ds_tizen_input_devicemgr_add_destroy_listener( wl_signal_add(&tz_devicemgr->events.destroy, listener); } +WL_EXPORT void +ds_tizen_input_devicemgr_add_pointer_warp_listener( + struct ds_tizen_input_devicemgr *tz_devicemgr, + struct wl_listener *listener) +{ + wl_signal_add(&tz_devicemgr->events.pointer_warp, listener); +} + WL_EXPORT bool ds_tizen_input_devicemgr_set_keymap_list( struct ds_tizen_input_devicemgr *tz_devicemgr, struct wl_list *list) @@ -347,7 +360,7 @@ static const struct tizen_input_device_manager_interface _devicemgr_impl = { .generate_key = device_manager_handle_generate_key, .generate_pointer = device_manager_handle_generate_pointer, .generate_touch = device_manager_handle_generate_touch, - .pointer_warp = NULL, + .pointer_warp = device_manager_handle_pointer_warp, .init_generator_with_name = device_manager_handle_init_generator_with_name, // v2 .destroy = device_manager_handle_destroy, // v3 @@ -767,6 +780,52 @@ finish: } static void +device_manager_handle_pointer_warp(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *surface, + wl_fixed_t x, wl_fixed_t y) +{ + struct ds_tizen_input_devicemgr *tz_devicemgr; + int ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NO_SYSTEM_RESOURCES; + int32_t new_x, new_y; + double transformed_x = .0, transformed_y = .0; + struct ds_tizen_input_devicemgr_event_pointer_warp ds_event; + struct timeval time; + unsigned int timestamp; + + tz_devicemgr = wl_resource_get_user_data(resource); + + if (!tz_devicemgr->devices.ptr || + !tz_devicemgr->devices.ptr->input_device) { + ds_err("Pointer device is not initialized\n"); + goto finish; + } + + new_x = wl_fixed_to_int(x); + new_y = wl_fixed_to_int(y); + + if (tz_devicemgr->output.width != 0 && tz_devicemgr->output.height != 0) { + transformed_x = new_x / (double)tz_devicemgr->output.width; + transformed_y = new_y / (double)tz_devicemgr->output.height; + } + + gettimeofday(&time, NULL); + timestamp = time.tv_sec * 1000 + time.tv_usec / 1000; + + ds_event.time_msec = timestamp; + ds_event.surface = ds_surface_from_resource(surface); + ds_event.x = transformed_x; + ds_event.y = transformed_y; + ds_inf("Pointer warp. surface:%p, x:%.2f, y:%.2f", ds_event.surface, + ds_event.x, ds_event.y); + + wl_signal_emit(&tz_devicemgr->events.pointer_warp, &ds_event); + ret = TIZEN_INPUT_DEVICE_MANAGER_ERROR_NONE; + +finish: + tizen_input_device_manager_send_error(resource, ret); +} + +static void device_manager_handle_destroy(struct wl_client *client, struct wl_resource *resource) { diff --git a/src/input_devicemgr/input_devicemgr.h b/src/input_devicemgr/input_devicemgr.h index a4428ee..f773fc1 100644 --- a/src/input_devicemgr/input_devicemgr.h +++ b/src/input_devicemgr/input_devicemgr.h @@ -36,6 +36,7 @@ struct ds_tizen_input_devicemgr { struct { struct wl_signal destroy; + struct wl_signal pointer_warp; //ds_tizen_input_devicemgr_event_pointer_warp } events; struct wl_listener new_input; -- 2.7.4 From 776c8e08e66e1f5a354f811462ca074359c76bce Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Thu, 7 Jul 2022 19:28:26 +0900 Subject: [PATCH 09/16] impelement libds-tizen-indicator This is the server implementation for tizen_indicator protocol. Change-Id: I97efef30d5f9e5c540d51b92bcac43808b53951a --- include/libds-tizen/indicator.h | 75 ++++++ packaging/libds-tizen.spec | 29 +++ src/indicator/indicator.c | 499 ++++++++++++++++++++++++++++++++++++++++ src/indicator/meson.build | 29 +++ src/meson.build | 1 + 5 files changed, 633 insertions(+) create mode 100644 include/libds-tizen/indicator.h create mode 100644 src/indicator/indicator.c create mode 100644 src/indicator/meson.build diff --git a/include/libds-tizen/indicator.h b/include/libds-tizen/indicator.h new file mode 100644 index 0000000..69fc82e --- /dev/null +++ b/include/libds-tizen/indicator.h @@ -0,0 +1,75 @@ +#ifndef LIBDS_TIZEN_INDICATOR_H +#define LIBDS_TIZEN_INDICATOR_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct ds_tizen_indicator; + +enum ds_tizen_indicator_state +{ + DS_TIZEN_INDICATOR_STATE_UNKNOWN, + DS_TIZEN_INDICATOR_STATE_OFF, + DS_TIZEN_INDICATOR_STATE_ON, +}; + +enum ds_tizen_indicator_opacity_mode +{ + DS_TIZEN_INDICATOR_OPACITY_MODE_UNKNOWN, + DS_TIZEN_INDICATOR_OPACITY_MODE_OPAQUE, + DS_TIZEN_INDICATOR_OPACITY_MODE_TRANSLUCENT, + DS_TIZEN_INDICATOR_OPACITY_MODE_TRANSPARENT, + DS_TIZEN_INDICATOR_OPACITY_MODE_BG_TRANSPARENT, +}; + +enum ds_tizen_indicator_visible_type +{ + DS_TIZEN_INDICATOR_VISIBLE_TYPE_HIDDEN, + DS_TIZEN_INDICATOR_VISIBLE_TYPE_SHOWN, +}; + +struct ds_tizen_indicator * +ds_tizen_indicator_create(struct wl_display *display); + +void +ds_tizen_indicator_add_destroy_listener(struct ds_tizen_indicator *indicator, + struct wl_listener *listener); + +void +ds_tizen_indicator_add_change_state_listener( + struct ds_tizen_indicator *indicator, struct wl_listener *listener); + +void +ds_tizen_indicator_add_change_opacity_mode_listener( + struct ds_tizen_indicator *indicator, struct wl_listener *listener); + +void +ds_tizen_indicator_add_change_visible_type_listener( + struct ds_tizen_indicator *indicator, struct wl_listener *listener); + +enum ds_tizen_indicator_state +ds_tizen_indicator_get_state(struct ds_tizen_indicator *indicator, + struct ds_surface *surface); + +enum ds_tizen_indicator_opacity_mode +ds_tizen_indicator_get_opacity_mode(struct ds_tizen_indicator *indicator, + struct ds_surface *surface); + +enum ds_tizen_indicator_visible_type +ds_tizen_indicator_get_visible_type(struct ds_tizen_indicator *indicator, + struct ds_surface *surface); + +void +ds_tizen_indicator_send_flick(struct ds_tizen_indicator *indicator, + struct ds_surface *surface); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/packaging/libds-tizen.spec b/packaging/libds-tizen.spec index c5e0623..34b918c 100644 --- a/packaging/libds-tizen.spec +++ b/packaging/libds-tizen.spec @@ -122,6 +122,21 @@ Group: Development/Libraries %description dpms-devel Development package for tizen dpms +## libds-tizen-indicator +%package indicator +Summary: Library for tizen indicator +Group: Development/Libraries + +%description indicator +Library for tizen indicator + +%package indicator-devel +Summary: Development package for tizen indicator +Group: Development/Libraries + +%description indicator-devel +Development package for tizen indicator + %prep %setup -q cp %{SOURCE1001} . @@ -232,3 +247,17 @@ ninja -C builddir install %{_libdir}/libds-tizen-dpms.so %{_bindir}/tinyds-tdm-dpms %{_bindir}/ds-simple-dpms + +%files indicator +%manifest %{name}.manifest +%defattr(-,root,root,-) +%license LICENSE +%{_libdir}/libds-tizen-indicator.so.* + +%files indicator-devel +%manifest %{name}.manifest +%defattr(-,root,root,-) +%license LICENSE +%{_includedir}/libds-tizen/indicator.h +%{_libdir}/pkgconfig/libds-tizen-indicator.pc +%{_libdir}/libds-tizen-indicator.so diff --git a/src/indicator/indicator.c b/src/indicator/indicator.c new file mode 100644 index 0000000..23f0e9f --- /dev/null +++ b/src/indicator/indicator.c @@ -0,0 +1,499 @@ +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "libds-tizen/indicator.h" + +#define TIZEN_INDICATOR_VERSION 1 + +struct ds_tizen_indicator +{ + struct wl_global *global; + + struct wl_list clients; + + struct wl_listener destroy; + + struct { + struct wl_signal destroy; + struct wl_signal change_state; + struct wl_signal change_opacity_mode; + struct wl_signal change_visible_type; + } events; +}; + +struct ds_tizen_indicator_client +{ + struct ds_tizen_indicator *indicator; + + struct wl_resource *resource; + struct wl_client *wl_client; + + struct wl_list infos; + + struct wl_list link; // ds_tizen_indicator::clients +}; + +struct ds_tizen_indicator_info +{ + struct ds_surface *surface; + + int32_t state; + int32_t opacity_mode; + int32_t visible_type; + + struct wl_list link; // ds_tizen_indicator_client::infos +}; + +static void indicator_handle_display_destroy(struct wl_listener *listener, + void *data); + +static void indicator_bind(struct wl_client *wl_client, void *data, + uint32_t version, uint32_t id); + +static struct ds_tizen_indicator_client *tizen_indicator_find_client( + struct ds_tizen_indicator *indicator, + struct ds_surface *surface); + +static struct ds_tizen_indicator_info *tizen_indicator_find_info( + struct ds_tizen_indicator *indicator, + struct ds_surface *surface); + +static struct ds_tizen_indicator_info *tizen_indicator_client_find_info( + struct ds_tizen_indicator_client *client, + struct ds_surface *surface); + +static struct ds_tizen_indicator_info *tizen_indicator_client_get_info( + struct ds_tizen_indicator_client *client, + struct ds_surface *surface); + +WL_EXPORT struct ds_tizen_indicator * +ds_tizen_indicator_create(struct wl_display *display) +{ + struct ds_tizen_indicator *indicator; + + indicator = calloc(1, sizeof *indicator); + if (!indicator) { + ds_err("calloc() failed."); + return NULL; + } + + indicator->global = wl_global_create(display, &tizen_indicator_interface, + 1, indicator, indicator_bind); + if (!indicator->global) { + ds_err("wl_global_create() failed. tizen_indicator_interface"); + free(indicator); + return NULL; + } + + wl_list_init(&indicator->clients); + + indicator->destroy.notify = indicator_handle_display_destroy; + wl_display_add_destroy_listener(display, &indicator->destroy); + + wl_signal_init(&indicator->events.destroy); + wl_signal_init(&indicator->events.change_state); + wl_signal_init(&indicator->events.change_opacity_mode); + wl_signal_init(&indicator->events.change_visible_type); + + ds_inf("Global created: tizen_indicator(%p)", indicator); + + return indicator; +} + +WL_EXPORT void +ds_tizen_indicator_add_destroy_listener(struct ds_tizen_indicator *indicator, + struct wl_listener *listener) +{ + wl_signal_add(&indicator->events.destroy, listener); +} + +WL_EXPORT void +ds_tizen_indicator_add_change_state_listener( + struct ds_tizen_indicator *indicator, struct wl_listener *listener) +{ + wl_signal_add(&indicator->events.change_state, listener); +} + +WL_EXPORT void +ds_tizen_indicator_add_change_opacity_mode_listener( + struct ds_tizen_indicator *indicator, struct wl_listener *listener) +{ + wl_signal_add(&indicator->events.change_opacity_mode, listener); +} + +WL_EXPORT void +ds_tizen_indicator_add_change_visible_type_listener( + struct ds_tizen_indicator *indicator, struct wl_listener *listener) +{ + wl_signal_add(&indicator->events.change_visible_type, listener); +} + +WL_EXPORT enum ds_tizen_indicator_state +ds_tizen_indicator_get_state(struct ds_tizen_indicator *indicator, + struct ds_surface *surface) +{ + struct ds_tizen_indicator_info *info; + + info = tizen_indicator_find_info(indicator, surface); + if (info == NULL) { + ds_err("tizen_indicator: tizen_indicator_find_info() failed."); + return DS_TIZEN_INDICATOR_STATE_UNKNOWN; + } + + return info->state; +} + +WL_EXPORT enum ds_tizen_indicator_opacity_mode +ds_tizen_indicator_get_opacity_mode(struct ds_tizen_indicator *indicator, + struct ds_surface *surface) +{ + struct ds_tizen_indicator_info *info; + + info = tizen_indicator_find_info(indicator, surface); + if (info == NULL) { + ds_err("tizen_indicator: tizen_indicator_find_info() failed."); + return DS_TIZEN_INDICATOR_OPACITY_MODE_UNKNOWN; + } + + return info->opacity_mode; +} + +WL_EXPORT enum ds_tizen_indicator_visible_type +ds_tizen_indicator_get_visible_type(struct ds_tizen_indicator *indicator, + struct ds_surface *surface) +{ + struct ds_tizen_indicator_info *info; + + info = tizen_indicator_find_info(indicator, surface); + if (info == NULL) { + ds_err("tizen_indicator: tizen_indicator_find_info() failed."); + return DS_TIZEN_INDICATOR_VISIBLE_TYPE_HIDDEN; + } + + return info->visible_type; +} + +WL_EXPORT void +ds_tizen_indicator_send_flick(struct ds_tizen_indicator *indicator, + struct ds_surface *surface) +{ + struct ds_tizen_indicator_client *client; + + client = tizen_indicator_find_client(indicator, surface); + if (client == NULL) { + ds_err("tizen_indicator: tizen_indicator_find_client() failed."); + return; + } + + tizen_indicator_send_flick(client->resource, + ds_surface_get_wl_resource(surface), 0); +} + +static struct ds_tizen_indicator_client * +tizen_indicator_find_client(struct ds_tizen_indicator *indicator, + struct ds_surface *surface) +{ + struct ds_tizen_indicator_info *info; + struct ds_tizen_indicator_client *client; + + wl_list_for_each(client, &indicator->clients, link) { + info = tizen_indicator_client_find_info(client, surface); + if (info != NULL) + return client; + } + + return NULL; +} + +static struct ds_tizen_indicator_info * +tizen_indicator_find_info(struct ds_tizen_indicator *indicator, + struct ds_surface *surface) +{ + struct ds_tizen_indicator_info *info; + struct ds_tizen_indicator_client *client; + + wl_list_for_each(client, &indicator->clients, link) { + info = tizen_indicator_client_find_info(client, surface); + if (info != NULL) + return info; + } + + return NULL; +} + +static struct ds_tizen_indicator_info * +tizen_indicator_client_find_info(struct ds_tizen_indicator_client *client, + struct ds_surface *surface) +{ + struct ds_tizen_indicator_info *info; + + wl_list_for_each(info, &client->infos, link) { + if (surface == info->surface) + return info; + } + + return NULL; +} + +static struct ds_tizen_indicator_info * +tizen_indicator_client_get_info(struct ds_tizen_indicator_client *client, + struct ds_surface *surface) +{ + struct ds_tizen_indicator_info *info; + + info = tizen_indicator_client_find_info(client, surface); + if (info) + return info; + + info = calloc(1, sizeof *info); + if (info == NULL) { + ds_err("calloc() failed. tizen_indicator"); + return NULL; + } + + info->surface = surface; + + // The initial values of state, opacity_mode and visible_type are not + // each value of enum ds_tizen_indicator_state, + // enum ds_tizen_indicator_opacity_mode and + // enum ds_tizen_indicator_opacity_mode + // because of the first signal emit for change_state signal. + info->state = -1; + info->opacity_mode = -1; + info->visible_type = -1; + + wl_list_insert(&client->infos, &info->link); + + return info; +} + +static void +indicator_handle_display_destroy(struct wl_listener *listener, void *data) +{ + struct ds_tizen_indicator *indicator; + + indicator = wl_container_of(listener, indicator, destroy); + + ds_inf("Global destroy: indicator(%p)", indicator); + + wl_signal_emit(&indicator->events.destroy, indicator); + wl_list_remove(&indicator->destroy.link); + wl_global_destroy(indicator->global); + free(indicator); +} + +static void +indicator_handle_destroy(struct wl_client *wl_client, + struct wl_resource *resource) +{ + struct ds_tizen_indicator_client *client; + + client = wl_resource_get_user_data(resource); + + if (!wl_list_empty(&client->infos)) { + ds_err("tizen_indicator was destroyed before children"); + return; + } + + wl_resource_destroy(resource); +} + +static void +indicator_handle_set_state(struct wl_client *wl_client, + struct wl_resource *resource, struct wl_resource *surface_resource, + int32_t state) +{ + struct ds_tizen_indicator_client *client; + struct ds_tizen_indicator_info *info; + struct ds_surface *surface; + enum ds_tizen_indicator_state indicator_state; + + ds_inf("tizen_indicator: set_state : state %d", state); + + client = wl_resource_get_user_data(resource); + surface = ds_surface_from_resource(surface_resource); + + info = tizen_indicator_client_get_info(client, surface); + if (info == NULL) { + ds_err("tizen_indicator_client_get_info() failed. tizen_indicator"); + wl_client_post_no_memory(wl_client); + return; + } + + switch (state) { + case TIZEN_INDICATOR_STATE_OFF: + indicator_state = DS_TIZEN_INDICATOR_STATE_OFF; + break; + case TIZEN_INDICATOR_STATE_ON: + indicator_state = DS_TIZEN_INDICATOR_STATE_ON; + break; + default: + indicator_state = DS_TIZEN_INDICATOR_STATE_UNKNOWN; + break; + } + + if (info->state != indicator_state) { + ds_inf("tizen_indicator: surface : %p, change_state : %d -> %d", + surface, info->state, indicator_state); + info->state = indicator_state; + + wl_signal_emit(&client->indicator->events.change_state, surface); + } +} + +static void +indicator_handle_set_opacity_mode(struct wl_client *wl_client, + struct wl_resource *resource, struct wl_resource *surface_resource, + int32_t mode) +{ + struct ds_tizen_indicator_client *client; + struct ds_tizen_indicator_info *info; + struct ds_surface *surface; + enum ds_tizen_indicator_opacity_mode opacity_mode; + + ds_inf("tizen_indicator: set_opacity_mode : opacity_mode %d", mode); + + client = wl_resource_get_user_data(resource); + surface = ds_surface_from_resource(surface_resource); + + info = tizen_indicator_client_get_info(client, surface); + if (info == NULL) { + ds_err("tizen_indicator_client_get_info() failed. tizen_indicator"); + wl_client_post_no_memory(wl_client); + return; + } + + switch (mode) { + case TIZEN_INDICATOR_OPACITY_MODE_OPAQUE: + opacity_mode = DS_TIZEN_INDICATOR_OPACITY_MODE_OPAQUE; + break; + case TIZEN_INDICATOR_OPACITY_MODE_TRANSLUCENT: + opacity_mode = DS_TIZEN_INDICATOR_OPACITY_MODE_TRANSLUCENT; + break; + case TIZEN_INDICATOR_OPACITY_MODE_TRANSPARENT: + opacity_mode = DS_TIZEN_INDICATOR_OPACITY_MODE_TRANSPARENT; + break; + case TIZEN_INDICATOR_OPACITY_MODE_BG_TRANSPARENT: + opacity_mode = DS_TIZEN_INDICATOR_OPACITY_MODE_BG_TRANSPARENT; + break; + default: + opacity_mode = DS_TIZEN_INDICATOR_OPACITY_MODE_UNKNOWN; + break; + } + + if (info->opacity_mode != opacity_mode) { + ds_inf("tizen_indicator: surface : %p, change_opacity_mode : %d -> %d", + surface, info->opacity_mode, opacity_mode); + info->opacity_mode = opacity_mode; + + wl_signal_emit(&client->indicator->events.change_opacity_mode, surface); + } +} + +static void +indicator_handle_set_visible_type(struct wl_client *wl_client, + struct wl_resource *resource, struct wl_resource *surface_resource, + int32_t type) +{ + struct ds_tizen_indicator_client *client; + struct ds_tizen_indicator_info *info; + struct ds_surface *surface; + enum ds_tizen_indicator_visible_type visible_type; + + ds_inf("tizen_indicator: set_visible_type : visible_type %d", type); + + client = wl_resource_get_user_data(resource); + surface = ds_surface_from_resource(surface_resource); + + info = tizen_indicator_client_get_info(client, surface); + if (info == NULL) { + ds_err("tizen_indicator_client_get_info() failed. tizen_indicator"); + wl_client_post_no_memory(wl_client); + return; + } + + switch (type) { + case TIZEN_INDICATOR_VISIBLE_TYPE_SHOWN: + visible_type = DS_TIZEN_INDICATOR_VISIBLE_TYPE_SHOWN; + break; + default: + visible_type = DS_TIZEN_INDICATOR_VISIBLE_TYPE_HIDDEN; + break; + } + + if (info->visible_type != visible_type) { + ds_inf("tizen_indicator: surface : %p, change_visible_type : %d -> %d", + surface, info->visible_type, visible_type); + info->visible_type = visible_type; + + wl_signal_emit(&client->indicator->events.change_visible_type, surface); + } +} + +static const struct tizen_indicator_interface indicator_impl = +{ + indicator_handle_destroy, + indicator_handle_set_state, + indicator_handle_set_opacity_mode, + indicator_handle_set_visible_type, +}; + +static void +_tizen_indicator_client_handle_destroy(struct wl_resource *resource) +{ + struct ds_tizen_indicator_client *client; + struct ds_tizen_indicator_info *info, *tmp; + + client = wl_resource_get_user_data(resource); + + ds_inf("_tizen_indicator_client_handle_destroy (client:%p)", client); + + wl_list_for_each_safe(info, tmp, &client->infos, link) { + wl_list_remove(&info->link); + free(info); + } + + wl_list_remove(&client->link); + free(client); +} + +static void +indicator_bind(struct wl_client *wl_client, void *data, uint32_t version, + uint32_t id) +{ + struct ds_tizen_indicator *indicator = data; + struct ds_tizen_indicator_client *client; + + client = calloc(1, sizeof *client); + if (client == NULL) { + ds_err("calloc() failed. tizen_indicator"); + wl_client_post_no_memory(wl_client); + return; + } + + ds_inf("tizen_indicator_client binds. (client:%p)", client); + + client->indicator = indicator; + client->wl_client = wl_client; + + wl_list_init(&client->infos); + + client->resource = wl_resource_create(wl_client, &tizen_indicator_interface, + MIN(version, TIZEN_INDICATOR_VERSION), id); + if (client->resource == NULL) { + ds_err("tizen_indicator : wl_resource_create() failed."); + free(client); + wl_client_post_no_memory(wl_client); + return; + } + + wl_resource_set_implementation(client->resource, &indicator_impl, client, + _tizen_indicator_client_handle_destroy); + + wl_list_insert(&indicator->clients, &client->link); +} diff --git a/src/indicator/meson.build b/src/indicator/meson.build new file mode 100644 index 0000000..a87c690 --- /dev/null +++ b/src/indicator/meson.build @@ -0,0 +1,29 @@ +libds_tizen_indicator_files = [ + 'indicator.c', +] + +libds_tizen_indicator_deps = [ + deps_libds_tizen, + dependency('tizen-extension-server', required: true), +] + +lib_libds_tizen_indicator = shared_library('ds-tizen-indicator', libds_tizen_indicator_files, + dependencies: libds_tizen_indicator_deps, + include_directories: [ common_inc, include_directories('.'), include_directories('..') ], + version: meson.project_version(), + install: true +) + +deps_libds_tizen_indicator = declare_dependency( + link_with: lib_libds_tizen_indicator, + dependencies: libds_tizen_indicator_deps, + include_directories: [ common_inc, include_directories('.') ], +) + +pkgconfig = import('pkgconfig') +pkgconfig.generate(lib_libds_tizen_indicator, + version: meson.project_version(), + filebase: 'libds-tizen-indicator', + name: 'libds-tizen-indicator', + description: 'tizen indicator extension of libds-tizen for tizen platform', +) diff --git a/src/meson.build b/src/meson.build index 46d807d..09a7f1a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -33,3 +33,4 @@ subdir('backend') subdir('keyrouter') subdir('input_devicemgr') subdir('dpms') +subdir('indicator') -- 2.7.4 From 477ee0aa51b96b54ad4e63e994c281b36e999c22 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Sun, 10 Jul 2022 13:46:59 +0900 Subject: [PATCH 10/16] add mock client/compositor This is the mock classes for testing request/event interaction between client and server. Change-Id: Ide1366d47f3cae83944520c8459a62bf4d737a1f --- tests/mockclient.cpp | 70 ++++++++++++++++++++++ tests/mockclient.h | 48 +++++++++++++++ tests/mockcompositor.cpp | 149 +++++++++++++++++++++++++++++++++++++++++++++++ tests/mockcompositor.h | 80 +++++++++++++++++++++++++ 4 files changed, 347 insertions(+) create mode 100644 tests/mockclient.cpp create mode 100644 tests/mockclient.h create mode 100644 tests/mockcompositor.cpp create mode 100644 tests/mockcompositor.h diff --git a/tests/mockclient.cpp b/tests/mockclient.cpp new file mode 100644 index 0000000..f9f9581 --- /dev/null +++ b/tests/mockclient.cpp @@ -0,0 +1,70 @@ +/************************************************************************** + * + * Copyright 2022 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Contact: SooChan Lim + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * +**************************************************************************/ + +#include "mockclient.h" +#include + +MockClient::MockClient() +{ + display = wl_display_connect("test-ds-tizen"); + if (!display) + ds_err("wl_display_connect() filed."); + + registry = wl_display_get_registry(display); + if (!registry) + ds_err("wl_display_get_registry() filed."); +} + +MockClient::MockClient(const struct wl_registry_listener *registry_listener, void *data) +{ + int ret; + display = wl_display_connect("test-ds-tizen"); + if (!display) + ds_err("wl_display_connect() filed."); + + registry = wl_display_get_registry(display); + if (!registry) + ds_err("wl_display_get_registry() filed."); + + ret = wl_registry_add_listener(registry, registry_listener, data); + if (ret) + ds_err("wl_registry_add_listener() filed."); + + wl_display_roundtrip(display); +} + +MockClient::~MockClient() +{ + wl_registry_destroy(registry); + wl_display_disconnect(display); +} + +void MockClient::RoundTrip() +{ + wl_display_roundtrip(display); +} \ No newline at end of file diff --git a/tests/mockclient.h b/tests/mockclient.h new file mode 100644 index 0000000..178b9bc --- /dev/null +++ b/tests/mockclient.h @@ -0,0 +1,48 @@ +/************************************************************************** + * + * Copyright 2022 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Contact: SooChan Lim + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * +**************************************************************************/ + +#ifndef _MOCKCLIENT_H_ +#define _MOCKCLIENT_H_ + +#include +#include + +class MockClient +{ +public: + MockClient(); + MockClient(const struct wl_registry_listener *registry_listener, void *data); + virtual ~MockClient(); + void RoundTrip(); + +private: + struct wl_display *display; + struct wl_registry *registry; +}; + +#endif diff --git a/tests/mockcompositor.cpp b/tests/mockcompositor.cpp new file mode 100644 index 0000000..62fc779 --- /dev/null +++ b/tests/mockcompositor.cpp @@ -0,0 +1,149 @@ +/************************************************************************** + * + * Copyright 2020 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Contact: SooChan Lim + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * +**************************************************************************/ + +#include "mockcompositor.h" +#include + +static void +logger_func(void *user_data, enum wl_protocol_logger_type type, + const struct wl_protocol_logger_message *message) +{ + //Compositor *c = static_cast(user_data); +#if 0 + std::cout << "res:" << wl_resource_get_class(message->resource) << " " + << "op:" << message->message_opcode << " " + << "name:" << message->message->name << "\n"; +#endif +} + +Compositor::Compositor() + : display(wl_display_create()), + loop(wl_display_get_event_loop(display)), + logger(wl_display_add_protocol_logger(display, logger_func, this)) +{ + int ret; + + ret = wl_display_add_socket(display, "test-ds-tizen"); + if (ret != 0) + ds_err("wl_display_add_socket() filed."); + + ret = wl_display_init_shm(display); + if (ret != 0) + ds_err("wl_display_init_shm() filed."); + + compositor = ds_compositor_create(display); + if (compositor == nullptr) + ds_err("ds_compositor_create() filed."); +} + +Compositor::~Compositor () +{ + wl_list *clients = wl_display_get_client_list(display); + wl_client *client = nullptr; + + wl_client_for_each(client, clients) { + wl_client_destroy(client); + } + + wl_protocol_logger_destroy(logger); + wl_display_destroy(display); +} + +void Compositor::DispatchEvents() +{ + /* flush any pending client events */ + wl_display_flush_clients(display); + /* dispatch any pending main loop events */ + wl_event_loop_dispatch(loop, 0); +} + +MockCompositor::MockCompositor() + : compositor(nullptr) +{ + th = std::thread([this](){ + Compositor c; + compositor = &c; + + // mockcompositor is ready + ready = true; + cv.notify_one(); + + while (alive) { + // mockcompositor process the events in every 40 milliseconds + std::this_thread::sleep_for(std::chrono::milliseconds(40)); + Process(); + } + }); + + // wait until the child thread(mockcompositor) is ready + { + std::unique_lock lock(m); + cv.wait(lock, [this]{return ready;}); + } +} + +MockCompositor::MockCompositor(ds_test_setup_func_t setup_func, void *data) + : compositor(nullptr) +{ + th = std::thread([this, setup_func, data](){ + Compositor c; + compositor = &c; + + // call test setup_function + setup_func(data); + + // mockcompositor is ready + ready = true; + cv.notify_one(); + + while (alive) { + // mockcompositor process the events in every 40 milliseconds + std::this_thread::sleep_for(std::chrono::milliseconds(40)); + + Process(); + } + }); + + // wait until the child thread(mockcompositor) is ready + { + std::unique_lock lock(m); + cv.wait(lock, [this]{return ready;}); + } +} + +MockCompositor::~MockCompositor() { + // finish the child thread(mockcompositor). + alive = false; + th.join(); +} + +void MockCompositor::Process() +{ + std::lock_guard lock(m); + compositor->DispatchEvents(); +} diff --git a/tests/mockcompositor.h b/tests/mockcompositor.h new file mode 100644 index 0000000..2b03f46 --- /dev/null +++ b/tests/mockcompositor.h @@ -0,0 +1,80 @@ +/************************************************************************** + * + * Copyright 2020 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Contact: SooChan Lim + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * +**************************************************************************/ + +#ifndef _MOCKCOMPOSITOR_H_ +#define _MOCKCOMPOSITOR_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ds_listener test function call +typedef void (*ds_test_setup_func_t)(void *data); + +class Compositor +{ +public: + Compositor(); + ~Compositor(); + void DispatchEvents(); + +public: + struct wl_display *display; + struct wl_event_loop *loop; + struct wl_protocol_logger *logger; + struct ds_compositor *compositor; +}; + +class MockCompositor +{ +public: + MockCompositor(); + MockCompositor(ds_test_setup_func_t setup_func, void *data); + virtual ~MockCompositor(); + + void Process(); + +protected: + Compositor *compositor; + +private: + std::thread th; + std::mutex m; + std::condition_variable cv; + + bool alive = true; + bool ready = false; +}; + +#endif -- 2.7.4 From 5ccdb3a9e3de42970111f84d166e21c1dc55727c Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Sun, 10 Jul 2022 13:48:27 +0900 Subject: [PATCH 11/16] add indicator test cases Change-Id: I6c755aff6ca1764b73289b64991544b26bed08fb --- packaging/libds-tizen.spec | 1 + tests/meson.build | 26 +++ tests/tc_indicator.cpp | 505 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 532 insertions(+) create mode 100644 tests/tc_indicator.cpp diff --git a/packaging/libds-tizen.spec b/packaging/libds-tizen.spec index 34b918c..8935d5d 100644 --- a/packaging/libds-tizen.spec +++ b/packaging/libds-tizen.spec @@ -261,3 +261,4 @@ ninja -C builddir install %{_includedir}/libds-tizen/indicator.h %{_libdir}/pkgconfig/libds-tizen-indicator.pc %{_libdir}/libds-tizen-indicator.so +%{_bindir}/libds-tizen-indicator-tests diff --git a/tests/meson.build b/tests/meson.build index 1d8158c..8cff406 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -4,6 +4,12 @@ deps_test_common = [ dependency('wayland-client', required: true), ] +tc_mock_files = [ + 'mockclient.cpp', + 'mockcompositor.cpp' +] + +## allcoator_tbm tests tc_allocator_tbm_files = [ 'tc_main.cpp', 'tc_allocator_tbm.cpp', @@ -19,3 +25,23 @@ executable('libds-tizen-allocator-tbm-tests', install_dir: libds_tizen_bindir, install : true ) + +## indicator tests +tc_indicator_files = [ + 'tc_main.cpp', + 'tc_indicator.cpp', +] + +executable('libds-tizen-indicator-tests', + [ + tc_mock_files, + tc_indicator_files + ], + dependencies: [ + deps_test_common, + deps_libds_tizen_indicator, + dependency('libdrm', required: true), + ], + install_dir: libds_tizen_bindir, + install : true +) diff --git a/tests/tc_indicator.cpp b/tests/tc_indicator.cpp new file mode 100644 index 0000000..c589ef3 --- /dev/null +++ b/tests/tc_indicator.cpp @@ -0,0 +1,505 @@ +#include "tc_main.h" +#include "mockclient.h" +#include "mockcompositor.h" +#include +#include + +class MockIndicatorCompositor : public MockCompositor +{ +public: + MockIndicatorCompositor() + : MockCompositor(&MockIndicatorCompositor::TestSetup, this) + { + ds_inf("%s : this(%p)", __func__, this); + + // initialize the flags to check + bSurfaceDestroyed = false; + + bDestroyed = false; + + bStateOn = false; + bStateOff = false; + + bOpacityModeOpque = false; + bOpacityModeTranslucent = false; + bOpacityModeTransparent = false; + bOpacityModeBgTransparent = false; + + bVisibleTypeHidden = false; + bVisibleTypeShown = false; + } + + ~MockIndicatorCompositor() + { + ds_inf("%s : this(%p)", __func__, this); + } + + static void TestSetup(void *data) + { + MockIndicatorCompositor *mockComp = + static_cast(data); + Compositor *comp = mockComp->compositor; + + ds_inf("%s: mockComp(%p)", __func__, mockComp); + + // new surface listener + mockComp->mNewSurfaceListener.notify = + MockIndicatorCompositor::NewSurfaceCallback; + mockComp->mNewSurfaceListener.parent = mockComp; + ds_compositor_add_new_surface_listener(comp->compositor, + &mockComp->mNewSurfaceListener); + + mockComp->mIndicator = ds_tizen_indicator_create(comp->display); + + // destroy listener + mockComp->mDestroyListener.notify = + MockIndicatorCompositor::DestroyCallback; + mockComp->mDestroyListener.parent = mockComp; + ds_tizen_indicator_add_destroy_listener(mockComp->mIndicator, + &mockComp->mDestroyListener); + + // change_state listener + mockComp->mChangeStateListener.notify = + MockIndicatorCompositor::ChangeStateCallback; + mockComp->mChangeStateListener.parent = mockComp; + ds_tizen_indicator_add_change_state_listener(mockComp->mIndicator, + &mockComp->mChangeStateListener); + + // change_opacity_mode listener + mockComp->mChangeOpacityModeListener.notify = + MockIndicatorCompositor::ChangeOpacityModeCallback; + mockComp->mChangeOpacityModeListener.parent = mockComp; + ds_tizen_indicator_add_change_opacity_mode_listener( + mockComp->mIndicator, + &mockComp->mChangeOpacityModeListener); + + // change_visible_type listener + mockComp->mChangeVisibleTypeListener.notify = + MockIndicatorCompositor::ChangeVisibleTypeCallback; + mockComp->mChangeVisibleTypeListener.parent = mockComp; + ds_tizen_indicator_add_change_visible_type_listener( + mockComp->mIndicator, + &mockComp->mChangeVisibleTypeListener); + } + + static void NewSurfaceCallback(struct wl_listener *listener, void *data) + { + MockIndicatorCompositor *mockComp = + reinterpret_cast(listener)->parent; + struct ds_surface *surface = static_cast(data); + + ds_inf("%s: mockComp(%p), surface(%p)", __func__, mockComp, surface); + + mockComp->mSurface = surface; + + // del surface listener + mockComp->mDelSurfaceListener.notify = + MockIndicatorCompositor::DelSurfaceCallback; + mockComp->mDelSurfaceListener.parent = mockComp; + ds_surface_add_destroy_listener(surface, + &mockComp->mDelSurfaceListener); + } + + static void DelSurfaceCallback(struct wl_listener *listener, void *data) + { + MockIndicatorCompositor *mockComp = + reinterpret_cast(listener)->parent; + struct ds_surface *surface = static_cast(data); + + ds_inf("%s: mockComp(%p), surface(%p)", __func__, mockComp, surface); + + if (ds_surface_get_wl_resource(mockComp->mSurface) == + ds_surface_get_wl_resource(surface)) { + ds_inf("%s: surface is deleted.", __func__); + mockComp->bSurfaceDestroyed = true; + } + } + + static void DestroyCallback(struct wl_listener *listener, void *data) + { + ds_inf("%s", __func__); + + MockIndicatorCompositor *mockComp = + reinterpret_cast(listener)->parent; + + mockComp->bDestroyed = true; + } + + static void ChangeStateCallback(struct wl_listener *listener, void *data) + { + ds_inf("%s", __func__); + + MockIndicatorCompositor *mockComp = + reinterpret_cast(listener)->parent; + struct ds_surface *surface = static_cast(data); + enum ds_tizen_indicator_state state; + + ds_inf("%s: mockComp(%p), surface(%p)", __func__, mockComp, surface); + + state = ds_tizen_indicator_get_state(mockComp->mIndicator, + mockComp->mSurface); + + if (state == DS_TIZEN_INDICATOR_STATE_ON) + mockComp->bStateOn = true; + if (state == DS_TIZEN_INDICATOR_STATE_OFF) + mockComp->bStateOff = true; + } + + static void ChangeOpacityModeCallback(struct wl_listener *listener, + void *data) + { + ds_inf("%s", __func__); + + MockIndicatorCompositor *mockComp = + reinterpret_cast(listener)->parent; + struct ds_surface *surface = static_cast(data); + enum ds_tizen_indicator_opacity_mode opacity_mode; + + ds_inf("%s: mockComp(%p), surface(%p)", __func__, mockComp, surface); + + opacity_mode = ds_tizen_indicator_get_opacity_mode(mockComp->mIndicator, + mockComp->mSurface); + + if (opacity_mode == DS_TIZEN_INDICATOR_OPACITY_MODE_OPAQUE) + mockComp->bOpacityModeOpque = true; + if (opacity_mode == DS_TIZEN_INDICATOR_OPACITY_MODE_TRANSLUCENT) + mockComp->bOpacityModeTranslucent = true; + if (opacity_mode == DS_TIZEN_INDICATOR_OPACITY_MODE_TRANSPARENT) + mockComp->bOpacityModeTransparent = true; + if (opacity_mode == DS_TIZEN_INDICATOR_OPACITY_MODE_BG_TRANSPARENT) + mockComp->bOpacityModeBgTransparent = true; + } + + static void ChangeVisibleTypeCallback(struct wl_listener *listener, + void *data) + { + ds_inf("%s", __func__); + + MockIndicatorCompositor *mockComp = + reinterpret_cast(listener)->parent; + struct ds_surface *surface = static_cast(data); + enum ds_tizen_indicator_visible_type visible_type; + + ds_inf("%s: mockComp(%p), surface(%p)", __func__, mockComp, surface); + + visible_type = ds_tizen_indicator_get_visible_type(mockComp->mIndicator, + mockComp->mSurface); + + if (visible_type == DS_TIZEN_INDICATOR_VISIBLE_TYPE_HIDDEN) + mockComp->bVisibleTypeHidden = true; + if (visible_type == DS_TIZEN_INDICATOR_VISIBLE_TYPE_SHOWN) + mockComp->bVisibleTypeShown = true; + } + + void SendFlick() + { + ds_inf("%s", __func__); + + ds_tizen_indicator_send_flick(mIndicator, mSurface); + } + +public: + bool bSurfaceDestroyed; + bool bDestroyed; + bool bStateOn; + bool bStateOff; + bool bOpacityModeOpque; + bool bOpacityModeTranslucent; + bool bOpacityModeTransparent; + bool bOpacityModeBgTransparent; + bool bVisibleTypeHidden; + bool bVisibleTypeShown; + +private: + struct ds_surface *mSurface; + struct NewSurfaceListener : ::wl_listener { + MockIndicatorCompositor *parent; + }; + NewSurfaceListener mNewSurfaceListener; + struct DelSurfaceListener : ::wl_listener { + MockIndicatorCompositor *parent; + }; + NewSurfaceListener mDelSurfaceListener; + + struct ds_tizen_indicator *mIndicator; + struct DestroyListener : ::wl_listener { + MockIndicatorCompositor *parent; + }; + NewSurfaceListener mDestroyListener; + struct ChangeStateListener : ::wl_listener { + MockIndicatorCompositor *parent; + }; + NewSurfaceListener mChangeStateListener; + struct ChangeOpacityModeListener : ::wl_listener { + MockIndicatorCompositor *parent; + }; + NewSurfaceListener mChangeOpacityModeListener; + struct ChangeVisibleTypeListener : ::wl_listener { + MockIndicatorCompositor *parent; + }; + NewSurfaceListener mChangeVisibleTypeListener; +}; + +class MockIndicatorClient : public MockClient +{ +public: + MockIndicatorClient() + : bFlickEvent(false), + compositor_res(nullptr), + tizen_indicator(nullptr) + {} + MockIndicatorClient(const struct wl_registry_listener *listener) + : MockClient(listener, this) + { + ds_inf("%s", __func__); + + bFlickEvent = false; + } + ~MockIndicatorClient() + { + ds_inf("%s", __func__); + } + + void SetWlCompositor(struct wl_compositor *global_res) + { + ds_inf("%s", __func__); + + compositor_res = global_res; + } + + struct wl_compositor *GetWlCompositor() + { + ds_inf("%s", __func__); + + return compositor_res; + } + + void SetTizenIndicator(struct tizen_indicator *global_res) + { + ds_inf("%s", __func__); + + tizen_indicator = global_res; + } + + struct tizen_indicator *GetTizenIndicator() + { + ds_inf("%s", __func__); + + return tizen_indicator; + } + +public: + bool bFlickEvent; + +private: + struct wl_compositor *compositor_res; + struct tizen_indicator *tizen_indicator; +}; + +static void +client_tizen_indicator_cb_flick(void *data, struct tizen_indicator *indicator, + struct wl_surface *surface, int32_t type) +{ + ds_inf("%s", __func__); + + MockIndicatorClient *client = static_cast(data); + + client->bFlickEvent = true; +} + +static const struct tizen_indicator_listener indicator_cb_listener = { + .flick = client_tizen_indicator_cb_flick +}; + +static void +client_registry_cb_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) +{ + ds_inf("%s", __func__); + + MockIndicatorClient *client = static_cast(data); + struct wl_compositor *compositor_res; + struct tizen_indicator *tizen_indicator; + + if (!strcmp(interface, "wl_compositor")) { + compositor_res = (struct wl_compositor *)wl_registry_bind(registry, + name, &wl_compositor_interface, 1); + if (compositor_res == nullptr) { + ds_err("wl_registry_bind() failed. wl_compositor resource."); + return; + } + client->SetWlCompositor(compositor_res); + } else if (!strcmp(interface, "tizen_indicator")) { + tizen_indicator = (struct tizen_indicator *)wl_registry_bind(registry, + name, &tizen_indicator_interface, 1); + if (tizen_indicator == nullptr) { + ds_err("wl_registry_bind() failed. tizen_indicator resource."); + return; + } + client->SetTizenIndicator(tizen_indicator); + + tizen_indicator_add_listener(tizen_indicator, &indicator_cb_listener, + client); + } +} + +static void +client_registry_cb_global_remove(void *data, struct wl_registry *registry, + uint32_t name) +{ + ds_inf("%s", __func__); + + MockIndicatorClient *client = static_cast(data); + struct wl_compositor *compositor_res = client->GetWlCompositor(); + struct tizen_indicator *indicator_res = client->GetTizenIndicator(); + + tizen_indicator_destroy(indicator_res); + wl_compositor_destroy(compositor_res); +} + +static const struct wl_registry_listener registry_listener = { + .global = client_registry_cb_global, + .global_remove = client_registry_cb_global_remove +}; + +class IndicatorTest : public ::testing::Test +{ +public: + void SetUp(void) override; + void TearDown(void) override; + + MockIndicatorCompositor *comp; + MockIndicatorClient *client; + struct wl_compositor *compositor_res; + struct tizen_indicator *indicator_res; + struct wl_surface *surface_res; +}; + +void +IndicatorTest::SetUp(void) +{ + //ds_log_init(DS_DBG, NULL); + + ds_inf("%s", __func__); + + comp = new MockIndicatorCompositor(); + client = new MockIndicatorClient(®istry_listener); + compositor_res = client->GetWlCompositor(); + indicator_res = client->GetTizenIndicator(); + surface_res = wl_compositor_create_surface(compositor_res); + + client->RoundTrip(); +} + +void +IndicatorTest::TearDown(void) +{ + ds_inf("%s", __func__); + + wl_surface_destroy(surface_res); + client->RoundTrip(); + EXPECT_TRUE(comp->bSurfaceDestroyed); + + delete client; + delete comp; +} + + +TEST_F(IndicatorTest, Create_P) +{ + EXPECT_TRUE(true); +} + +TEST_F(IndicatorTest, Req_TizenIndicatorStateOn) +{ + tizen_indicator_set_state(indicator_res, surface_res, + TIZEN_INDICATOR_STATE_ON); + client->RoundTrip(); + EXPECT_TRUE(comp->bStateOn); + EXPECT_FALSE(comp->bStateOff); +} + +TEST_F(IndicatorTest, Req_TizenIndicatorStateOff) +{ + tizen_indicator_set_state(indicator_res, surface_res, + TIZEN_INDICATOR_STATE_OFF); + client->RoundTrip(); + EXPECT_FALSE(comp->bStateOn); + EXPECT_TRUE(comp->bStateOff); +} + +TEST_F(IndicatorTest, Req_TizenIndicatorOpacityModeOpaque) +{ + tizen_indicator_set_opacity_mode(indicator_res, surface_res, + TIZEN_INDICATOR_OPACITY_MODE_OPAQUE); + client->RoundTrip(); + EXPECT_TRUE(comp->bOpacityModeOpque); + EXPECT_FALSE(comp->bOpacityModeTranslucent); + EXPECT_FALSE(comp->bOpacityModeTransparent); + EXPECT_FALSE(comp->bOpacityModeBgTransparent); +} + +TEST_F(IndicatorTest, Req_TizenIndicatorOpacityModeTranslucent) +{ + tizen_indicator_set_opacity_mode(indicator_res, surface_res, + TIZEN_INDICATOR_OPACITY_MODE_TRANSLUCENT); + client->RoundTrip(); + EXPECT_FALSE(comp->bOpacityModeOpque); + EXPECT_TRUE(comp->bOpacityModeTranslucent); + EXPECT_FALSE(comp->bOpacityModeTransparent); + EXPECT_FALSE(comp->bOpacityModeBgTransparent); +} + +TEST_F(IndicatorTest, Req_TizenIndicatorOpacityModeTransparent) +{ + tizen_indicator_set_opacity_mode(indicator_res, surface_res, + TIZEN_INDICATOR_OPACITY_MODE_TRANSPARENT); + client->RoundTrip(); + EXPECT_FALSE(comp->bOpacityModeOpque); + EXPECT_FALSE(comp->bOpacityModeTranslucent); + EXPECT_TRUE(comp->bOpacityModeTransparent); + EXPECT_FALSE(comp->bOpacityModeBgTransparent); +} + +TEST_F(IndicatorTest, Req_TizenIndicatorOpacityModeBgTransparent) +{ + tizen_indicator_set_opacity_mode(indicator_res, surface_res, + TIZEN_INDICATOR_OPACITY_MODE_BG_TRANSPARENT); + client->RoundTrip(); + EXPECT_FALSE(comp->bOpacityModeOpque); + EXPECT_FALSE(comp->bOpacityModeTranslucent); + EXPECT_FALSE(comp->bOpacityModeTransparent); + EXPECT_TRUE(comp->bOpacityModeBgTransparent); +} + +TEST_F(IndicatorTest, Req_TizenIndicatorVisibleTypeHidden) +{ + tizen_indicator_set_visible_type(indicator_res, surface_res, + TIZEN_INDICATOR_VISIBLE_TYPE_HIDDEN); + client->RoundTrip(); + EXPECT_TRUE(comp->bVisibleTypeHidden); + EXPECT_FALSE(comp->bVisibleTypeShown); +} + +TEST_F(IndicatorTest, Req_TizenIndicatorVisibleTypeShown) +{ + tizen_indicator_set_visible_type(indicator_res, surface_res, + TIZEN_INDICATOR_VISIBLE_TYPE_SHOWN); + client->RoundTrip(); + EXPECT_FALSE(comp->bVisibleTypeHidden); + EXPECT_TRUE(comp->bVisibleTypeShown); +} + +TEST_F(IndicatorTest, Ev_TizenIndicatorFlick) +{ + tizen_indicator_set_state(indicator_res, surface_res, + TIZEN_INDICATOR_STATE_ON); + client->RoundTrip(); + EXPECT_TRUE(comp->bStateOn); + + comp->SendFlick(); + comp->Process(); + + client->RoundTrip(); + EXPECT_TRUE(client->bFlickEvent); +} -- 2.7.4 From d63e154e2c3427e6d0f1f4509eaddd99638bff38 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Wed, 20 Jul 2022 16:11:22 +0900 Subject: [PATCH 12/16] impelement libds-tizen-clipboard This is the server implementation for tizen_clipboard protocol. Change-Id: Idb03da717fd867bb9fd31e8bf9878e1006553ada --- include/libds-tizen/clipboard.h | 50 ++++++ packaging/libds-tizen.spec | 29 ++++ src/clipboard/clipboard.c | 366 ++++++++++++++++++++++++++++++++++++++++ src/clipboard/meson.build | 29 ++++ src/meson.build | 1 + 5 files changed, 475 insertions(+) create mode 100644 include/libds-tizen/clipboard.h create mode 100644 src/clipboard/clipboard.c create mode 100644 src/clipboard/meson.build diff --git a/include/libds-tizen/clipboard.h b/include/libds-tizen/clipboard.h new file mode 100644 index 0000000..697c9e7 --- /dev/null +++ b/include/libds-tizen/clipboard.h @@ -0,0 +1,50 @@ +#ifndef LIBDS_TIZEN_CLIPBOARD_H +#define LIBDS_TIZEN_CLIPBOARD_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct ds_tizen_clipboard; +struct ds_tizen_clipboard_client; + +struct ds_tizen_clipboard * +ds_tizen_clipboard_create(struct wl_display *display); + +void +ds_tizen_clipboard_add_destroy_listener(struct ds_tizen_clipboard *clipboard, + struct wl_listener *listener); + +void +ds_tizen_clipboard_add_show_listener( + struct ds_tizen_clipboard *clipboard, struct wl_listener *listener); + +void +ds_tizen_clipboard_add_hide_listener( + struct ds_tizen_clipboard *clipboard, struct wl_listener *listener); + +void +ds_tizen_clipboard_add_set_data_only_listener( + struct ds_tizen_clipboard *clipboard, struct wl_listener *listener); + +uint32_t +ds_tizen_clipboard_client_get_data_only( + struct ds_tizen_clipboard_client *client); + +void +ds_tizen_clipboard_send_data_selected(struct ds_tizen_clipboard *clipboard, + struct ds_surface *surface); + +void +ds_tizen_clipboard_client_send_allowed_data_only( + struct ds_tizen_clipboard_client *client, uint32_t allowed); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/packaging/libds-tizen.spec b/packaging/libds-tizen.spec index 8935d5d..bb5c681 100644 --- a/packaging/libds-tizen.spec +++ b/packaging/libds-tizen.spec @@ -137,6 +137,21 @@ Group: Development/Libraries %description indicator-devel Development package for tizen indicator +## libds-tizen-clipboard +%package clipboard +Summary: Library for tizen clipboard +Group: Development/Libraries + +%description clipboard +Library for tizen clipboard + +%package clipboard-devel +Summary: Development package for tizen clipboard +Group: Development/Libraries + +%description clipboard-devel +Development package for tizen clipboard + %prep %setup -q cp %{SOURCE1001} . @@ -262,3 +277,17 @@ ninja -C builddir install %{_libdir}/pkgconfig/libds-tizen-indicator.pc %{_libdir}/libds-tizen-indicator.so %{_bindir}/libds-tizen-indicator-tests + +%files clipboard +%manifest %{name}.manifest +%defattr(-,root,root,-) +%license LICENSE +%{_libdir}/libds-tizen-clipboard.so.* + +%files clipboard-devel +%manifest %{name}.manifest +%defattr(-,root,root,-) +%license LICENSE +%{_includedir}/libds-tizen/clipboard.h +%{_libdir}/pkgconfig/libds-tizen-clipboard.pc +%{_libdir}/libds-tizen-clipboard.so diff --git a/src/clipboard/clipboard.c b/src/clipboard/clipboard.c new file mode 100644 index 0000000..bfa9595 --- /dev/null +++ b/src/clipboard/clipboard.c @@ -0,0 +1,366 @@ +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "libds-tizen/clipboard.h" + +#define TIZEN_CLIPBOARD_VERSION 2 + +struct ds_tizen_clipboard +{ + struct wl_global *global; + + struct wl_list clients; + + struct wl_listener destroy; + + struct { + struct wl_signal destroy; + struct wl_signal show; + struct wl_signal hide; + struct wl_signal set_data_only; + } events; +}; + +struct ds_tizen_clipboard_client +{ + struct ds_tizen_clipboard *clipboard; + + struct wl_resource *resource; + struct wl_client *wl_client; + + struct wl_list infos; + + uint32_t data_only; + + struct wl_list link; // ds_tizen_clipboard::clients +}; + +struct ds_tizen_clipboard_info +{ + struct ds_surface *surface; + + struct wl_list link; // ds_tizen_clipboard_client::infos +}; + +static void clipboard_handle_display_destroy(struct wl_listener *listener, + void *data); + +static void clipboard_bind(struct wl_client *wl_client, void *data, + uint32_t version, uint32_t id); + +static struct ds_tizen_clipboard_client *tizen_clipboard_find_client( + struct ds_tizen_clipboard *clipboard, + struct ds_surface *surface); + +static struct ds_tizen_clipboard_info *tizen_clipboard_client_find_info( + struct ds_tizen_clipboard_client *client, + struct ds_surface *surface); + +static struct ds_tizen_clipboard_info *tizen_clipboard_client_get_info( + struct ds_tizen_clipboard_client *client, + struct ds_surface *surface); + +WL_EXPORT struct ds_tizen_clipboard * +ds_tizen_clipboard_create(struct wl_display *display) +{ + struct ds_tizen_clipboard *clipboard; + + clipboard = calloc(1, sizeof *clipboard); + if (!clipboard) { + ds_err("calloc() failed."); + return NULL; + } + + clipboard->global = wl_global_create(display, &tizen_clipboard_interface, + TIZEN_CLIPBOARD_VERSION, clipboard, clipboard_bind); + if (!clipboard->global) { + ds_err("wl_global_create() failed. tizen_clipboard_interface"); + free(clipboard); + return NULL; + } + + wl_list_init(&clipboard->clients); + + clipboard->destroy.notify = clipboard_handle_display_destroy; + wl_display_add_destroy_listener(display, &clipboard->destroy); + + wl_signal_init(&clipboard->events.destroy); + wl_signal_init(&clipboard->events.show); + wl_signal_init(&clipboard->events.hide); + wl_signal_init(&clipboard->events.set_data_only); + + ds_inf("Global created: tizen_clipboard(%p)", clipboard); + + return clipboard; +} + +WL_EXPORT void +ds_tizen_clipboard_add_destroy_listener(struct ds_tizen_clipboard *clipboard, + struct wl_listener *listener) +{ + wl_signal_add(&clipboard->events.destroy, listener); +} + +WL_EXPORT void +ds_tizen_clipboard_add_show_listener( + struct ds_tizen_clipboard *clipboard, struct wl_listener *listener) +{ + wl_signal_add(&clipboard->events.show, listener); +} + +WL_EXPORT void +ds_tizen_clipboard_add_hide_listener( + struct ds_tizen_clipboard *clipboard, struct wl_listener *listener) +{ + wl_signal_add(&clipboard->events.hide, listener); +} + +WL_EXPORT void +ds_tizen_clipboard_add_set_data_only_listener( + struct ds_tizen_clipboard *clipboard, struct wl_listener *listener) +{ + wl_signal_add(&clipboard->events.set_data_only, listener); +} + +WL_EXPORT uint32_t +ds_tizen_clipboard_client_get_data_only( + struct ds_tizen_clipboard_client *client) +{ + return client->data_only; +} + +WL_EXPORT void +ds_tizen_clipboard_send_data_selected(struct ds_tizen_clipboard *clipboard, + struct ds_surface *surface) +{ + struct ds_tizen_clipboard_client *client; + + client = tizen_clipboard_find_client(clipboard, surface); + if (client == NULL) { + ds_err("tizen_clipboard: tizen_clipboard_find_client() failed."); + return; + } + + tizen_clipboard_send_data_selected(client->resource, + ds_surface_get_wl_resource(surface)); +} + +WL_EXPORT void +ds_tizen_clipboard_client_send_allowed_data_only( + struct ds_tizen_clipboard_client *client, uint32_t allowed) +{ + tizen_clipboard_send_allowed_data_only(client->resource, allowed); +} + +static struct ds_tizen_clipboard_client * +tizen_clipboard_find_client(struct ds_tizen_clipboard *clipboard, + struct ds_surface *surface) +{ + struct ds_tizen_clipboard_info *info; + struct ds_tizen_clipboard_client *client; + + wl_list_for_each(client, &clipboard->clients, link) { + info = tizen_clipboard_client_find_info(client, surface); + if (info != NULL) + return client; + } + + return NULL; +} + +static struct ds_tizen_clipboard_info * +tizen_clipboard_client_find_info(struct ds_tizen_clipboard_client *client, + struct ds_surface *surface) +{ + struct ds_tizen_clipboard_info *info; + + wl_list_for_each(info, &client->infos, link) { + if (surface == info->surface) + return info; + } + + return NULL; +} + +static struct ds_tizen_clipboard_info * +tizen_clipboard_client_get_info(struct ds_tizen_clipboard_client *client, + struct ds_surface *surface) +{ + struct ds_tizen_clipboard_info *info; + + info = tizen_clipboard_client_find_info(client, surface); + if (info) + return info; + + info = calloc(1, sizeof *info); + if (info == NULL) { + ds_err("calloc() failed. tizen_clipboard"); + return NULL; + } + + info->surface = surface; + + wl_list_insert(&client->infos, &info->link); + + return info; +} + +static void +clipboard_handle_display_destroy(struct wl_listener *listener, void *data) +{ + struct ds_tizen_clipboard *clipboard; + + clipboard = wl_container_of(listener, clipboard, destroy); + + ds_inf("Global destroy: clipboard(%p)", clipboard); + + wl_signal_emit(&clipboard->events.destroy, clipboard); + wl_list_remove(&clipboard->destroy.link); + wl_global_destroy(clipboard->global); + free(clipboard); +} + +static void +clipboard_handle_destroy(struct wl_client *wl_client, + struct wl_resource *resource) +{ + struct ds_tizen_clipboard_client *client; + + client = wl_resource_get_user_data(resource); + + if (!wl_list_empty(&client->infos)) { + ds_err("tizen_clipboard was destroyed before children"); + return; + } + + wl_resource_destroy(resource); +} + +static void +clipboard_handle_show(struct wl_client *wl_client, + struct wl_resource *resource, struct wl_resource *surface_resource) +{ + struct ds_tizen_clipboard_client *client; + struct ds_tizen_clipboard_info *info; + struct ds_surface *surface; + + ds_inf("tizen_clipboard: show"); + + client = wl_resource_get_user_data(resource); + surface = ds_surface_from_resource(surface_resource); + + info = tizen_clipboard_client_get_info(client, surface); + if (info == NULL) { + ds_err("tizen_clipboard_client_get_info() failed. tizen_clipboard"); + wl_client_post_no_memory(wl_client); + return; + } + + wl_signal_emit(&client->clipboard->events.show, surface); +} + +static void +clipboard_handle_hide(struct wl_client *wl_client, + struct wl_resource *resource, struct wl_resource *surface_resource) +{ + struct ds_tizen_clipboard_client *client; + struct ds_tizen_clipboard_info *info; + struct ds_surface *surface; + + ds_inf("tizen_clipboard: hide"); + + client = wl_resource_get_user_data(resource); + surface = ds_surface_from_resource(surface_resource); + + info = tizen_clipboard_client_get_info(client, surface); + if (info == NULL) { + ds_err("tizen_clipboard_client_get_info() failed. tizen_clipboard"); + wl_client_post_no_memory(wl_client); + return; + } + + wl_signal_emit(&client->clipboard->events.hide, surface); +} + +static void +clipboard_handle_set_data_only(struct wl_client *wl_client, + struct wl_resource *resource, uint32_t set) +{ + struct ds_tizen_clipboard_client *client; + + ds_inf("tizen_clipboard: set_data_only. set(%d)", set); + + client = wl_resource_get_user_data(resource); + client->data_only = set; + + wl_signal_emit(&client->clipboard->events.set_data_only, client); +} + +static const struct tizen_clipboard_interface clipboard_impl = +{ + clipboard_handle_destroy, + clipboard_handle_show, + clipboard_handle_hide, + clipboard_handle_set_data_only, +}; + +static void +_tizen_clipboard_client_handle_destroy(struct wl_resource *resource) +{ + struct ds_tizen_clipboard_client *client; + struct ds_tizen_clipboard_info *info, *tmp; + + client = wl_resource_get_user_data(resource); + + ds_inf("_tizen_clipboard_client_handle_destroy (client:%p)", client); + + wl_list_for_each_safe(info, tmp, &client->infos, link) { + wl_list_remove(&info->link); + free(info); + } + + wl_list_remove(&client->link); + free(client); +} + +static void +clipboard_bind(struct wl_client *wl_client, void *data, uint32_t version, + uint32_t id) +{ + struct ds_tizen_clipboard *clipboard = data; + struct ds_tizen_clipboard_client *client; + + client = calloc(1, sizeof *client); + if (client == NULL) { + ds_err("calloc() failed. tizen_clipboard"); + wl_client_post_no_memory(wl_client); + return; + } + + ds_inf("tizen_clipboard_client binds. (client:%p)", client); + + client->clipboard = clipboard; + client->wl_client = wl_client; + + wl_list_init(&client->infos); + + client->resource = wl_resource_create(wl_client, &tizen_clipboard_interface, + MIN(version, TIZEN_CLIPBOARD_VERSION), id); + + if (client->resource == NULL) { + ds_err("tizen_clipboard : wl_resource_create() failed."); + free(client); + wl_client_post_no_memory(wl_client); + return; + } + + wl_resource_set_implementation(client->resource, &clipboard_impl, client, + _tizen_clipboard_client_handle_destroy); + + wl_list_insert(&clipboard->clients, &client->link); +} diff --git a/src/clipboard/meson.build b/src/clipboard/meson.build new file mode 100644 index 0000000..2889c22 --- /dev/null +++ b/src/clipboard/meson.build @@ -0,0 +1,29 @@ +libds_tizen_clipboard_files = [ + 'clipboard.c', +] + +libds_tizen_clipboard_deps = [ + deps_libds_tizen, + dependency('tizen-extension-server', required: true), +] + +lib_libds_tizen_clipboard = shared_library('ds-tizen-clipboard', libds_tizen_clipboard_files, + dependencies: libds_tizen_clipboard_deps, + include_directories: [ common_inc, include_directories('.'), include_directories('..') ], + version: meson.project_version(), + install: true +) + +deps_libds_tizen_clipboard = declare_dependency( + link_with: lib_libds_tizen_clipboard, + dependencies: libds_tizen_clipboard_deps, + include_directories: [ common_inc, include_directories('.') ], +) + +pkgconfig = import('pkgconfig') +pkgconfig.generate(lib_libds_tizen_clipboard, + version: meson.project_version(), + filebase: 'libds-tizen-clipboard', + name: 'libds-tizen-clipboard', + description: 'tizen clipboard extension of libds-tizen for tizen platform', +) diff --git a/src/meson.build b/src/meson.build index 09a7f1a..73c169c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -34,3 +34,4 @@ subdir('keyrouter') subdir('input_devicemgr') subdir('dpms') subdir('indicator') +subdir('clipboard') -- 2.7.4 From 80dd61f50c67535a2093118e8c9102063204ea7f Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Wed, 20 Jul 2022 17:13:47 +0900 Subject: [PATCH 13/16] add test cases for ds_tizen_clipboard These are unit testcases. Change-Id: I17d0d392c3c8af7041fe93752b5facb6be7e41b5 --- packaging/libds-tizen.spec | 1 + tests/meson.build | 20 ++ tests/tc_clipboard.cpp | 457 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 478 insertions(+) create mode 100644 tests/tc_clipboard.cpp diff --git a/packaging/libds-tizen.spec b/packaging/libds-tizen.spec index bb5c681..16e6ce7 100644 --- a/packaging/libds-tizen.spec +++ b/packaging/libds-tizen.spec @@ -291,3 +291,4 @@ ninja -C builddir install %{_includedir}/libds-tizen/clipboard.h %{_libdir}/pkgconfig/libds-tizen-clipboard.pc %{_libdir}/libds-tizen-clipboard.so +%{_bindir}/libds-tizen-clipboard-tests diff --git a/tests/meson.build b/tests/meson.build index 8cff406..eb8d221 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -45,3 +45,23 @@ executable('libds-tizen-indicator-tests', install_dir: libds_tizen_bindir, install : true ) + +## clipboard tests +tc_clipboard_files = [ + 'tc_main.cpp', + 'tc_clipboard.cpp', +] + +executable('libds-tizen-clipboard-tests', + [ + tc_mock_files, + tc_clipboard_files + ], + dependencies: [ + deps_test_common, + deps_libds_tizen_clipboard, + dependency('libdrm', required: true), + ], + install_dir: libds_tizen_bindir, + install : true +) diff --git a/tests/tc_clipboard.cpp b/tests/tc_clipboard.cpp new file mode 100644 index 0000000..710495a --- /dev/null +++ b/tests/tc_clipboard.cpp @@ -0,0 +1,457 @@ +#include "tc_main.h" +#include "mockclient.h" +#include "mockcompositor.h" +#include +#include + +#define TIZEN_CLIPBOARD_VERSION 2 + +class MockClipboardCompositor : public MockCompositor +{ +public: + MockClipboardCompositor() + : MockCompositor(&MockClipboardCompositor::TestSetup, this) + { + ds_inf("%s : this(%p)", __func__, this); + + // initialize the flags to check + bSurfaceDestroyed = false; + + bDestroyed = false; + bShow = false; + bHide = false; + mSetDataOnly = 0; + + mAllowed = 0; + } + + ~MockClipboardCompositor() + { + ds_inf("%s : this(%p)", __func__, this); + } + + static void TestSetup(void *data) + { + MockClipboardCompositor *mockComp = + static_cast(data); + Compositor *comp = mockComp->compositor; + + ds_inf("%s: mockComp(%p)", __func__, mockComp); + + // new surface listener + mockComp->mNewSurfaceListener.notify = + MockClipboardCompositor::NewSurfaceCallback; + mockComp->mNewSurfaceListener.parent = mockComp; + ds_compositor_add_new_surface_listener(comp->compositor, + &mockComp->mNewSurfaceListener); + + mockComp->mClipboard = ds_tizen_clipboard_create(comp->display); + + // destroy listener + mockComp->mDestroyListener.notify = + MockClipboardCompositor::DestroyCallback; + mockComp->mDestroyListener.parent = mockComp; + ds_tizen_clipboard_add_destroy_listener(mockComp->mClipboard, + &mockComp->mDestroyListener); + + // show listener + mockComp->mShowListener.notify = + MockClipboardCompositor::ShowCallback; + mockComp->mShowListener.parent = mockComp; + ds_tizen_clipboard_add_show_listener(mockComp->mClipboard, + &mockComp->mShowListener); + + // hide listener + mockComp->mHideListener.notify = + MockClipboardCompositor::HideCallback; + mockComp->mHideListener.parent = mockComp; + ds_tizen_clipboard_add_hide_listener( + mockComp->mClipboard, + &mockComp->mHideListener); + + // set_data_only listener + mockComp->mSetDataOnlyListener.notify = + MockClipboardCompositor::SetDataOnlyCallback; + mockComp->mSetDataOnlyListener.parent = mockComp; + ds_tizen_clipboard_add_set_data_only_listener( + mockComp->mClipboard, + &mockComp->mSetDataOnlyListener); + } + + static void NewSurfaceCallback(struct wl_listener *listener, void *data) + { + MockClipboardCompositor *mockComp = + reinterpret_cast(listener)->parent; + struct ds_surface *surface = static_cast(data); + + ds_inf("%s: mockComp(%p), surface(%p)", __func__, mockComp, surface); + + mockComp->mSurface = surface; + + // del surface listener + mockComp->mDelSurfaceListener.notify = + MockClipboardCompositor::DelSurfaceCallback; + mockComp->mDelSurfaceListener.parent = mockComp; + ds_surface_add_destroy_listener(surface, + &mockComp->mDelSurfaceListener); + } + + static void DelSurfaceCallback(struct wl_listener *listener, void *data) + { + MockClipboardCompositor *mockComp = + reinterpret_cast(listener)->parent; + struct ds_surface *surface = static_cast(data); + + ds_inf("%s: mockComp(%p), surface(%p)", __func__, mockComp, surface); + + if (ds_surface_get_wl_resource(mockComp->mSurface) == + ds_surface_get_wl_resource(surface)) { + ds_inf("%s: surface is deleted.", __func__); + mockComp->bSurfaceDestroyed = true; + } + } + + static void DestroyCallback(struct wl_listener *listener, void *data) + { + ds_inf("%s", __func__); + + MockClipboardCompositor *mockComp = + reinterpret_cast(listener)->parent; + + mockComp->bDestroyed = true; + } + + static void ShowCallback(struct wl_listener *listener, void *data) + { + ds_inf("%s", __func__); + + MockClipboardCompositor *mockComp = + reinterpret_cast(listener)->parent; + struct ds_surface *surface = static_cast(data); + + ds_inf("%s: mockComp(%p), surface(%p)", __func__, mockComp, surface); + + mockComp->bShow = true; + } + + static void HideCallback(struct wl_listener *listener, + void *data) + { + ds_inf("%s", __func__); + + MockClipboardCompositor *mockComp = + reinterpret_cast(listener)->parent; + struct ds_surface *surface = static_cast(data); + + ds_inf("%s: mockComp(%p), surface(%p)", __func__, mockComp, surface); + + mockComp->bHide = true; + } + + static void SetDataOnlyCallback(struct wl_listener *listener, + void *data) + { + ds_inf("%s", __func__); + + MockClipboardCompositor *mockComp = + reinterpret_cast(listener)->parent; + struct ds_tizen_clipboard_client *client = + static_cast(data); + + ds_inf("%s: mockComp(%p), client(%p)", __func__, mockComp, client); + + mockComp->mSetDataOnly = + ds_tizen_clipboard_client_get_data_only(client); + + ds_tizen_clipboard_client_send_allowed_data_only(client, + mockComp->mAllowed); + } + + void SendDataSelected() + { + ds_inf("%s", __func__); + + ds_tizen_clipboard_send_data_selected(mClipboard, mSurface); + } + + void SetAllowedDataOnly(uint32_t allowed) + { + ds_inf("%s", __func__); + + mAllowed = allowed; + } + +public: + bool bSurfaceDestroyed; + bool bDestroyed; + bool bShow; + bool bHide; + uint32_t mSetDataOnly; + + uint32_t mAllowed; + +private: + struct ds_surface *mSurface; + struct NewSurfaceListener : ::wl_listener { + MockClipboardCompositor *parent; + }; + NewSurfaceListener mNewSurfaceListener; + struct DelSurfaceListener : ::wl_listener { + MockClipboardCompositor *parent; + }; + DelSurfaceListener mDelSurfaceListener; + + struct ds_tizen_clipboard *mClipboard; + struct DestroyListener : ::wl_listener { + MockClipboardCompositor *parent; + }; + DestroyListener mDestroyListener; + struct ShowListener : ::wl_listener { + MockClipboardCompositor *parent; + }; + ShowListener mShowListener; + struct HideListener : ::wl_listener { + MockClipboardCompositor *parent; + }; + HideListener mHideListener; + struct SetDataOnlyListener : ::wl_listener { + MockClipboardCompositor *parent; + }; + SetDataOnlyListener mSetDataOnlyListener; +}; + +class MockClipboardClient : public MockClient +{ +public: + MockClipboardClient() + : bDataSelectedEvent(false), + mAllowedDataOnly(0), + compositor_res(nullptr), + tizen_clipboard(nullptr) + {} + MockClipboardClient(const struct wl_registry_listener *listener) + : MockClient(listener, this) + { + ds_inf("%s", __func__); + + bDataSelectedEvent = false; + mAllowedDataOnly = 0; + } + ~MockClipboardClient() + { + ds_inf("%s", __func__); + } + + void SetWlCompositor(struct wl_compositor *global_res) + { + ds_inf("%s", __func__); + + compositor_res = global_res; + } + + struct wl_compositor *GetWlCompositor() + { + ds_inf("%s", __func__); + + return compositor_res; + } + + void SetTizenClipboard(struct tizen_clipboard *global_res) + { + ds_inf("%s", __func__); + + tizen_clipboard = global_res; + } + + struct tizen_clipboard *GetTizenClipboard() + { + ds_inf("%s", __func__); + + return tizen_clipboard; + } + +public: + bool bDataSelectedEvent; + uint32_t mAllowedDataOnly; + +private: + struct wl_compositor *compositor_res; + struct tizen_clipboard *tizen_clipboard; +}; + +static void +client_tizen_clipboard_cb_data_selected(void *data, + struct tizen_clipboard *clipboard, struct wl_surface *surface) +{ + ds_inf("%s", __func__); + + MockClipboardClient *client = static_cast(data); + + client->bDataSelectedEvent = true; +} + +static void +client_tizen_clipboard_cb_allowed_data_only(void *data, + struct tizen_clipboard *clipboard, uint32_t allowed) +{ + ds_inf("%s", __func__); + + MockClipboardClient *client = static_cast(data); + + client->mAllowedDataOnly = allowed; +} + +static const struct tizen_clipboard_listener clipboard_cb_listener = { + .data_selected = client_tizen_clipboard_cb_data_selected, + .allowed_data_only = client_tizen_clipboard_cb_allowed_data_only +}; + +static void +client_registry_cb_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) +{ + ds_inf("%s", __func__); + + MockClipboardClient *client = static_cast(data); + struct wl_compositor *compositor_res; + struct tizen_clipboard *tizen_clipboard; + + if (!strcmp(interface, "wl_compositor")) { + compositor_res = (struct wl_compositor *)wl_registry_bind(registry, + name, &wl_compositor_interface, 1); + if (compositor_res == nullptr) { + ds_err("wl_registry_bind() failed. wl_compositor resource."); + return; + } + client->SetWlCompositor(compositor_res); + } else if (!strcmp(interface, "tizen_clipboard")) { + tizen_clipboard = (struct tizen_clipboard *)wl_registry_bind(registry, + name, &tizen_clipboard_interface, TIZEN_CLIPBOARD_VERSION); + if (tizen_clipboard == nullptr) { + ds_err("wl_registry_bind() failed. tizen_clipboard resource."); + return; + } + client->SetTizenClipboard(tizen_clipboard); + + tizen_clipboard_add_listener(tizen_clipboard, &clipboard_cb_listener, + client); + } +} + +static void +client_registry_cb_global_remove(void *data, struct wl_registry *registry, + uint32_t name) +{ + ds_inf("%s", __func__); + + MockClipboardClient *client = static_cast(data); + struct wl_compositor *compositor_res = client->GetWlCompositor(); + struct tizen_clipboard *clipboard_res = client->GetTizenClipboard(); + + tizen_clipboard_destroy(clipboard_res); + wl_compositor_destroy(compositor_res); +} + +static const struct wl_registry_listener registry_listener = { + .global = client_registry_cb_global, + .global_remove = client_registry_cb_global_remove +}; + +class ClipboardTest : public ::testing::Test +{ +public: + void SetUp(void) override; + void TearDown(void) override; + + MockClipboardCompositor *comp; + MockClipboardClient *client; + struct wl_compositor *compositor_res; + struct tizen_clipboard *clipboard_res; + struct wl_surface *surface_res; +}; + +void +ClipboardTest::SetUp(void) +{ + //ds_log_init(DS_DBG, NULL); + + ds_inf("%s", __func__); + + comp = new MockClipboardCompositor(); + client = new MockClipboardClient(®istry_listener); + compositor_res = client->GetWlCompositor(); + clipboard_res = client->GetTizenClipboard(); + surface_res = wl_compositor_create_surface(compositor_res); + + client->RoundTrip(); +} + +void +ClipboardTest::TearDown(void) +{ + ds_inf("%s", __func__); + + wl_surface_destroy(surface_res); + client->RoundTrip(); + EXPECT_TRUE(comp->bSurfaceDestroyed); + + delete client; + delete comp; +} + +TEST_F(ClipboardTest, Create_P) +{ + EXPECT_TRUE(true); +} + +TEST_F(ClipboardTest, Req_TizenClipboardShow) +{ + tizen_clipboard_show(clipboard_res, surface_res); + client->RoundTrip(); + EXPECT_TRUE(comp->bShow); + EXPECT_FALSE(comp->bHide); +} + +TEST_F(ClipboardTest, Req_TizenClipboardHide) +{ + tizen_clipboard_hide(clipboard_res, surface_res); + client->RoundTrip(); + EXPECT_FALSE(comp->bShow); + EXPECT_TRUE(comp->bHide); +} + +TEST_F(ClipboardTest, Req_TizenClipboardSetDataOnly) +{ + tizen_clipboard_set_data_only(clipboard_res, 1); + client->RoundTrip(); + EXPECT_TRUE(comp->mSetDataOnly == 1); +} + +TEST_F(ClipboardTest, Ev_TizenClipboardDataSelected) +{ + tizen_clipboard_show(clipboard_res, surface_res); + client->RoundTrip(); + EXPECT_TRUE(comp->bShow); + + comp->SendDataSelected(); + comp->Process(); + + client->RoundTrip(); + EXPECT_TRUE(client->bDataSelectedEvent); +} + +TEST_F(ClipboardTest, Ev_TizenClipboardAllowedDataOnly) +{ + // set the value of allowed to be 1 at compositor + comp->SetAllowedDataOnly(1); + + // send a client's request + tizen_clipboard_set_data_only(clipboard_res, 1); + client->RoundTrip(); + EXPECT_TRUE(comp->mSetDataOnly); + + comp->Process(); + + client->RoundTrip(); + EXPECT_TRUE(client->mAllowedDataOnly == 1); +} -- 2.7.4 From e9c16b49a848b2f7ee81c38907d85da975916a3e Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Thu, 21 Jul 2022 08:27:35 +0900 Subject: [PATCH 14/16] fix the typo Change-Id: I8a9325b59e7f6cf4e372554bde71386ca9d15514 --- tests/tc_indicator.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/tc_indicator.cpp b/tests/tc_indicator.cpp index c589ef3..7585496 100644 --- a/tests/tc_indicator.cpp +++ b/tests/tc_indicator.cpp @@ -219,25 +219,25 @@ private: struct DelSurfaceListener : ::wl_listener { MockIndicatorCompositor *parent; }; - NewSurfaceListener mDelSurfaceListener; + DelSurfaceListener mDelSurfaceListener; struct ds_tizen_indicator *mIndicator; struct DestroyListener : ::wl_listener { MockIndicatorCompositor *parent; }; - NewSurfaceListener mDestroyListener; + DestroyListener mDestroyListener; struct ChangeStateListener : ::wl_listener { MockIndicatorCompositor *parent; }; - NewSurfaceListener mChangeStateListener; + ChangeStateListener mChangeStateListener; struct ChangeOpacityModeListener : ::wl_listener { MockIndicatorCompositor *parent; }; - NewSurfaceListener mChangeOpacityModeListener; + ChangeOpacityModeListener mChangeOpacityModeListener; struct ChangeVisibleTypeListener : ::wl_listener { MockIndicatorCompositor *parent; }; - NewSurfaceListener mChangeVisibleTypeListener; + ChangeVisibleTypeListener mChangeVisibleTypeListener; }; class MockIndicatorClient : public MockClient -- 2.7.4 From 6413a0a4cdd781569f759eaf5f3c8ab6b586d81c Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Thu, 21 Jul 2022 08:30:20 +0900 Subject: [PATCH 15/16] indicator: create and bind global resource with version info Change-Id: Iaa20162c9a2d6a918e72e5071f7f7d809902f33e --- src/indicator/indicator.c | 2 +- tests/tc_indicator.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/indicator/indicator.c b/src/indicator/indicator.c index 23f0e9f..b08dae5 100644 --- a/src/indicator/indicator.c +++ b/src/indicator/indicator.c @@ -83,7 +83,7 @@ ds_tizen_indicator_create(struct wl_display *display) } indicator->global = wl_global_create(display, &tizen_indicator_interface, - 1, indicator, indicator_bind); + TIZEN_INDICATOR_VERSION, indicator, indicator_bind); if (!indicator->global) { ds_err("wl_global_create() failed. tizen_indicator_interface"); free(indicator); diff --git a/tests/tc_indicator.cpp b/tests/tc_indicator.cpp index 7585496..837aa3a 100644 --- a/tests/tc_indicator.cpp +++ b/tests/tc_indicator.cpp @@ -4,6 +4,7 @@ #include #include +#define TIZEN_INDICATOR_VERSION 1 class MockIndicatorCompositor : public MockCompositor { public: @@ -331,7 +332,7 @@ client_registry_cb_global(void *data, struct wl_registry *registry, client->SetWlCompositor(compositor_res); } else if (!strcmp(interface, "tizen_indicator")) { tizen_indicator = (struct tizen_indicator *)wl_registry_bind(registry, - name, &tizen_indicator_interface, 1); + name, &tizen_indicator_interface, TIZEN_INDICATOR_VERSION); if (tizen_indicator == nullptr) { ds_err("wl_registry_bind() failed. tizen_indicator resource."); return; -- 2.7.4 From 8c8a08f2a79bbd614f47da139fcd2e31637248e2 Mon Sep 17 00:00:00 2001 From: "duna.oh" Date: Fri, 22 Jul 2022 02:41:29 +0900 Subject: [PATCH 16/16] launch: implement appinfo interface Change-Id: I6afd36726e16ed967bffc6f9b24c772c1740bab8 --- include/libds-tizen/launch/appinfo.h | 42 +++ packaging/libds-tizen.spec | 32 ++ src/launch/appinfo.c | 579 +++++++++++++++++++++++++++++++++++ src/launch/meson.build | 29 ++ src/meson.build | 1 + tests/meson.build | 19 ++ tests/tc_launch_appinfo.cpp | 421 +++++++++++++++++++++++++ 7 files changed, 1123 insertions(+) create mode 100644 include/libds-tizen/launch/appinfo.h create mode 100644 src/launch/appinfo.c create mode 100644 src/launch/meson.build create mode 100644 tests/tc_launch_appinfo.cpp diff --git a/include/libds-tizen/launch/appinfo.h b/include/libds-tizen/launch/appinfo.h new file mode 100644 index 0000000..bd62e9b --- /dev/null +++ b/include/libds-tizen/launch/appinfo.h @@ -0,0 +1,42 @@ +#ifndef LIBDS_TIZEN_APPINFO_H +#define LIBDS_TIZEN_APPINFO_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct ds_tizen_appinfo_mgr; +struct ds_tizen_appinfo; + +struct ds_tizen_appinfo_mgr * +ds_tizen_appinfo_mgr_create(struct wl_display *display); + +void +ds_tizen_appinfo_mgr_add_destroy_listener( + struct ds_tizen_appinfo_mgr *appinfo_mgr, struct wl_listener *listener); + +void +ds_tizen_appinfo_mgr_add_set_pid_listener( + struct ds_tizen_appinfo_mgr *appinfo_mgr, struct wl_listener *listener); + +void +ds_tizen_appinfo_mgr_add_set_appid_listener( + struct ds_tizen_appinfo_mgr *appinfo_mgr, struct wl_listener *listener); + +void +ds_tizen_appinfo_mgr_add_metadata_ready_listener( + struct ds_tizen_appinfo_mgr *appinfo_mgr, struct wl_listener *listener); + +//for gtest +struct wl_resource * +ds_tizen_appinfo_mgr_get_appinfo_resource( + struct ds_tizen_appinfo_mgr *appinfo_mgr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/packaging/libds-tizen.spec b/packaging/libds-tizen.spec index 16e6ce7..27af91c 100644 --- a/packaging/libds-tizen.spec +++ b/packaging/libds-tizen.spec @@ -14,6 +14,8 @@ BuildRequires: pkgconfig(wayland-client) BuildRequires: pkgconfig(wayland-protocols) BuildRequires: pkgconfig(tizen-extension-server) BuildRequires: pkgconfig(tizen-extension-client) +BuildRequires: pkgconfig(tizen-launch-server) +BuildRequires: pkgconfig(tizen-launch-client) BuildRequires: pkgconfig(pixman-1) BuildRequires: pkgconfig(libdrm) BuildRequires: pkgconfig(xkbcommon) @@ -152,6 +154,21 @@ Group: Development/Libraries %description clipboard-devel Development package for tizen clipboard +## libds-tizen-launch +%package launch +Summary: Library for tizen launch +Group: Development/Libraries + +%description launch +Library for tizen launch + +%package launch-devel +Summary: Development package for tizen launch +Group: Development/Libraries + +%description launch-devel +Development package for tizen launch + %prep %setup -q cp %{SOURCE1001} . @@ -292,3 +309,18 @@ ninja -C builddir install %{_libdir}/pkgconfig/libds-tizen-clipboard.pc %{_libdir}/libds-tizen-clipboard.so %{_bindir}/libds-tizen-clipboard-tests + +%files launch +%manifest %{name}.manifest +%defattr(-,root,root,-) +%license LICENSE +%{_libdir}/libds-tizen-launch.so.* + +%files launch-devel +%manifest %{name}.manifest +%defattr(-,root,root,-) +%license LICENSE +%{_includedir}/libds-tizen/launch/appinfo.h +%{_libdir}/pkgconfig/libds-tizen-launch.pc +%{_libdir}/libds-tizen-launch.so +%{_bindir}/libds-tizen-launch-appinfo-tests diff --git a/src/launch/appinfo.c b/src/launch/appinfo.c new file mode 100644 index 0000000..0b6c8c2 --- /dev/null +++ b/src/launch/appinfo.c @@ -0,0 +1,579 @@ +#include +#include +#include +#include + +#include "util.h" +#include + +#define TIZEN_APPINFO_VERSION 1 + +struct ds_tizen_appinfo_mgr +{ + struct wl_global *global; + + struct wl_list clients; + struct wl_list infos; + + struct wl_listener destroy; + + struct { + struct wl_signal destroy; + struct wl_signal set_pid; + struct wl_signal set_appid; + struct wl_signal metadata_ready; + } events; + + struct wl_resource *resource; //for gtest +}; + +struct ds_tizen_appinfo_client +{ + struct ds_tizen_appinfo_mgr *appinfo_mgr; + + struct wl_resource *resource; + struct wl_client *wl_client; + + struct wl_list link; // ds_tizen_appinfo_mgr::clients +}; + +enum ds_tizen_appinfo_owner +{ + DS_TIZEN_APPINFO_OWNER_SERVER, + DS_TIZEN_APPINFO_OWNER_CLIENT, +}; + +struct ds_tizen_appinfo +{ + struct ds_tizen_appinfo_mgr *appinfo_mgr; + + pid_t pid; + char *appid; + bool base_output_available; + int base_output_width; + int base_output_height; + enum ds_tizen_appinfo_owner owner; + + struct wl_list link; //ds_tizen_appinfo_mgr::infos; +}; + +static void appinfo_mgr_handle_display_destroy(struct wl_listener *listener, + void *data); + +static void appinfo_mgr_bind(struct wl_client *wl_client, void *data, + uint32_t version, uint32_t id); + +WL_EXPORT struct ds_tizen_appinfo_mgr * +ds_tizen_appinfo_mgr_create(struct wl_display *display) +{ + struct ds_tizen_appinfo_mgr *appinfo_mgr; + + appinfo_mgr = calloc(1, sizeof *appinfo_mgr); + if (!appinfo_mgr) { + ds_err("calloc() failed."); + return NULL; + } + + appinfo_mgr->global = wl_global_create(display, &tizen_launch_appinfo_interface, + TIZEN_APPINFO_VERSION, appinfo_mgr, appinfo_mgr_bind); + if (!appinfo_mgr->global) { + ds_err("wl_global_create() failed. tizen_launch_appinfo_interface"); + free(appinfo_mgr); + return NULL; + } + + wl_list_init(&appinfo_mgr->clients); + wl_list_init(&appinfo_mgr->infos); + + appinfo_mgr->destroy.notify = appinfo_mgr_handle_display_destroy; + wl_display_add_destroy_listener(display, &appinfo_mgr->destroy); + + wl_signal_init(&appinfo_mgr->events.destroy); + wl_signal_init(&appinfo_mgr->events.set_pid); + wl_signal_init(&appinfo_mgr->events.set_appid); + wl_signal_init(&appinfo_mgr->events.metadata_ready); + + ds_inf("Global create: tizen_launch_appinfo. appinfo_mgr(%p)", appinfo_mgr); + + return appinfo_mgr; +} + +WL_EXPORT void +ds_tizen_appinfo_mgr_add_destroy_listener( + struct ds_tizen_appinfo_mgr *appinfo_mgr, struct wl_listener *listener) +{ + wl_signal_add(&appinfo_mgr->events.destroy, listener); +} + +WL_EXPORT void +ds_tizen_appinfo_mgr_add_set_pid_listener( + struct ds_tizen_appinfo_mgr *appinfo_mgr, struct wl_listener *listener) +{ + wl_signal_add(&appinfo_mgr->events.set_pid, listener); +} + +WL_EXPORT void +ds_tizen_appinfo_mgr_add_set_appid_listener( + struct ds_tizen_appinfo_mgr *appinfo_mgr, struct wl_listener *listener) +{ + wl_signal_add(&appinfo_mgr->events.set_appid, listener); +} + +WL_EXPORT void +ds_tizen_appinfo_mgr_add_metadata_ready_listener( + struct ds_tizen_appinfo_mgr *appinfo_mgr, struct wl_listener *listener) +{ + wl_signal_add(&appinfo_mgr->events.metadata_ready, listener); +} + +static void +appinfo_mgr_handle_display_destroy(struct wl_listener *listener, void *data) +{ + struct ds_tizen_appinfo_mgr *appinfo_mgr; + struct ds_tizen_appinfo_client *client, *tmp_client; + struct ds_tizen_appinfo *info, *tmp_info; + + appinfo_mgr = wl_container_of(listener, appinfo_mgr, destroy); + + ds_inf("Global destroy: appinfo_mgr(%p)", appinfo_mgr); + + wl_signal_emit(&appinfo_mgr->events.destroy, appinfo_mgr); + wl_list_remove(&appinfo_mgr->destroy.link); + + wl_list_for_each_safe(client, tmp_client, &appinfo_mgr->clients, link) { + wl_list_remove(&client->link); + free(client); + } + + wl_list_for_each_safe(info, tmp_info, &appinfo_mgr->infos, link) { + wl_list_remove(&info->link); + free(info->appid); + free(info); + } + + wl_global_destroy(appinfo_mgr->global); + free(appinfo_mgr); +} + +static void +appinfo_handle_destroy(struct wl_client *wl_client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static struct ds_tizen_appinfo * +appinfo_mgr_find_with_pid(struct ds_tizen_appinfo_mgr *appinfo_mgr, + pid_t pid) +{ + struct ds_tizen_appinfo *info; + + wl_list_for_each(info, &appinfo_mgr->infos, link) { + if (pid == info->pid) + return info; + } + + return NULL; +} + +static struct ds_tizen_appinfo * +appinfo_mgr_find_with_appid(struct ds_tizen_appinfo_mgr *appinfo_mgr, + const char *appid) +{ + struct ds_tizen_appinfo *info; + + wl_list_for_each(info, &appinfo_mgr->infos, link) { + if (appid && !strcmp(appid, info->appid)) + return info; + } + + return NULL; +} +static struct ds_tizen_appinfo * +appinfo_mgr_get_info(struct ds_tizen_appinfo_mgr *appinfo_mgr, + pid_t pid, const char *appid) +{ + struct ds_tizen_appinfo *info = NULL; + + if (pid > 0) + info = appinfo_mgr_find_with_pid(appinfo_mgr, pid); + else if (appid) + info = appinfo_mgr_find_with_appid(appinfo_mgr, appid); + + if (info) { + return info; + } + + info = calloc(1, sizeof *info); + if (info == NULL) { + ds_err("calloc() failed. tizen_appinfo"); + return NULL; + } + + if (pid > 0) + info->pid = pid; + else if (appid) + info->appid = strdup(appid); + + info->appinfo_mgr = appinfo_mgr; + + wl_list_insert(&appinfo_mgr->infos, &info->link); + + return info; +} + +static void +appinfo_destroy(struct ds_tizen_appinfo *info) +{ + wl_list_remove(&info->link); + free(info->appid); + free(info); +} + +static bool +appinfo_set_pid(struct ds_tizen_appinfo *info, pid_t pid) +{ + struct ds_tizen_appinfo_mgr *appinfo_mgr; + struct ds_tizen_appinfo *info2, *tmp; + + if (!info) return false; + if (pid < 0) return false; + + appinfo_mgr = info->appinfo_mgr; + + wl_list_for_each_safe(info2, tmp, &appinfo_mgr->infos, link) { + if ((info2->pid == pid) && (info2 != info)) { + ds_inf("removed duplicated appinfo"); + + if (!info->appid) info->appid = strdup(info2->appid); + if (!info->base_output_available && info2->base_output_available) { + ds_inf("copy base_output variable into appinfo."); + info->base_output_available = true; + info->base_output_width = info2->base_output_width; + info->base_output_height = info2->base_output_height; + } + appinfo_destroy(info2); + } + } + + info->pid = pid; + ds_inf("appinfo(%p) set pid(%u)", info, pid); + + wl_signal_emit(&appinfo_mgr->events.set_pid, info); + + return true; +} + +static bool +appinfo_set_appid(struct ds_tizen_appinfo *info, const char *appid) +{ + struct ds_tizen_appinfo_mgr *appinfo_mgr; + struct ds_tizen_appinfo *info2, *tmp; + + if (!info) return false; + if (!appid) return false; + + appinfo_mgr = info->appinfo_mgr; + + wl_list_for_each_safe(info2, tmp, &appinfo_mgr->infos, link) { + if ((info2->appid && !strcmp(info2->appid, appid)) && + (info2 != info)) { + ds_inf("removed duplicated appinfo"); + + if (info->pid < 0) info->pid = info2->pid; + if (!info->base_output_available && info2->base_output_available) + { + ds_inf("copy base_output variable into appinfo."); + info->base_output_available = true; + info->base_output_width = info2->base_output_width; + info->base_output_height = info2->base_output_height; + } + appinfo_destroy(info2); + } + } + + free(info->appid); + info->appid = strdup(appid); + ds_inf("appinfo(%p) set appid(%u)", info, appid); + + wl_signal_emit(&appinfo_mgr->events.set_appid, info); + + return true; +} + +static bool +appinfo_get_base_output_resolution(struct ds_tizen_appinfo *info, int *width, int *height) +{ + if (!info) return false; + if (!width) return false; + if (!height) return false; + + if (!info->base_output_available) { + *width = 0; + *height = 0; + return false; + } + *width = info->base_output_width; + *height = info->base_output_height; + + return true; +} + +static void +appinfo_set_owner(struct ds_tizen_appinfo *info, enum ds_tizen_appinfo_owner owner) +{ + if (!info) return; + + info->owner = owner; +} + +static void +appinfo_handle_register_pid(struct wl_client *wl_client, + struct wl_resource *resource, uint32_t pid) +{ + struct ds_tizen_appinfo_client *client; + struct ds_tizen_appinfo *info; + + client = wl_resource_get_user_data(resource); + + if (pid <= 0) { + ds_err("tizen_appinfo: pid is invalid. pid:%u", pid); + return; + } + + info = appinfo_mgr_get_info(client->appinfo_mgr, pid, NULL); + if (info == NULL) { + ds_err("appinfo_mgr_get_info() failed. tizen_appinfo"); + wl_client_post_no_memory(wl_client); + return; + } + + appinfo_set_pid(info, pid); + appinfo_set_owner(info, DS_TIZEN_APPINFO_OWNER_CLIENT); +} + + +static void +appinfo_handle_deregister_pid(struct wl_client *wl_client, + struct wl_resource *resource, uint32_t pid) +{ + struct ds_tizen_appinfo_client *client; + struct ds_tizen_appinfo *info; + + client = wl_resource_get_user_data(resource); + + info = appinfo_mgr_find_with_pid(client->appinfo_mgr, pid); + if (info == NULL) { + ds_err("tizen_appinfo: no appinfo found by pid(%u)", pid); + return; + } + + appinfo_destroy(info); +} + +static void +appinfo_handle_set_appid(struct wl_client *wl_client, + struct wl_resource *resource, uint32_t pid, const char *appid) +{ + struct ds_tizen_appinfo_client *client; + struct ds_tizen_appinfo *info; + + client = wl_resource_get_user_data(resource); + + if (pid <= 0) { + ds_err("tizen_appinfo: pid is invalid. pid:%u", pid); + return; + } + + info = appinfo_mgr_find_with_pid(client->appinfo_mgr, pid); + if (info == NULL) { + ds_err("tizen_appinfo: no appinfo found by pid(%u)", pid); + return; + } + + appinfo_set_appid(info, appid); + + /* TODO: base output resolution */ +} + +//for gtest +WL_EXPORT struct wl_resource * +ds_tizen_appinfo_mgr_get_appinfo_resource( + struct ds_tizen_appinfo_mgr *appinfo_mgr) +{ + return appinfo_mgr->resource; +} + +static void +appinfo_handle_get_base_output_resolution(struct wl_client *wl_client, + struct wl_resource *resource, uint32_t pid) +{ + struct ds_tizen_appinfo_client *client; + struct ds_tizen_appinfo *info; + int width = 0, height = 0; + + client = wl_resource_get_user_data(resource); + + if (pid <= 0) { + ds_err("tizen_appinfo: pid is invalid. pid:%u", pid); + goto finish; + } + + info = appinfo_mgr_find_with_pid(client->appinfo_mgr, pid); + if (info == NULL) { + ds_err("tizen_appinfo: no appinfo found by pid(%u)", pid); + goto finish; + } + + appinfo_get_base_output_resolution(info, &width, &height); + + client->appinfo_mgr->resource = resource; + +finish: + if (width == 0 && height == 0) { + width = 1080; //e_config->configured_output_resolution.w; + height = 1920; //e_config->configured_output_resolution.h; + } + + ds_inf("send base_output_resolution. size(%d x %d). pid(%u)", width, height, pid); + tizen_launch_appinfo_send_base_output_resolution_done(resource, pid, width, height); +} + +static void +appinfo_handle_register_appid(struct wl_client *wl_client, struct wl_resource *resource, + const char *appid) +{ + struct ds_tizen_appinfo_client *client; + struct ds_tizen_appinfo *info; + + client = wl_resource_get_user_data(resource); + + info = appinfo_mgr_get_info(client->appinfo_mgr, -1, appid); + if (info == NULL) { + ds_err("appinfo_mgr_get_info() failed. tizen_appinfo"); + wl_client_post_no_memory(wl_client); + return; + } + + appinfo_set_appid(info, appid); + appinfo_set_owner(info, DS_TIZEN_APPINFO_OWNER_CLIENT); +} + +static void +appinfo_handle_deregister_appid(struct wl_client *wl_client, + struct wl_resource *resource, const char *appid) +{ + struct ds_tizen_appinfo_client *client; + struct ds_tizen_appinfo *info; + + client = wl_resource_get_user_data(resource); + + info = appinfo_mgr_find_with_appid(client->appinfo_mgr, appid); + if (info == NULL) { + ds_err("tizen_appinfo: no appinfo found by appid(%u)", appid); + return; + } + + appinfo_destroy(info); +} + +static void +appinfo_handle_set_pid(struct wl_client *wl_client, + struct wl_resource *resource, const char *appid, uint32_t pid) +{ + struct ds_tizen_appinfo_client *client; + struct ds_tizen_appinfo *info; + + client = wl_resource_get_user_data(resource); + + info = appinfo_mgr_find_with_appid(client->appinfo_mgr, appid); + if (info == NULL) { + ds_err("tizen_appinfo: no appinfo found by appid(%u)", appid); + return; + } + + appinfo_set_pid(info, pid); +} + +static void +appinfo_handle_ready_metadata(struct wl_client *wl_client, + struct wl_resource *resource, const char *appid, uint32_t pid) +{ + struct ds_tizen_appinfo_client *client; + struct ds_tizen_appinfo *info; + + client = wl_resource_get_user_data(resource); + + info = appinfo_mgr_find_with_appid(client->appinfo_mgr, appid); + if (info == NULL) { + ds_err("tizen_appinfo: no appinfo found by appid(%u)", appid); + return; + } + + if (info->pid != pid) + appinfo_set_pid(info, pid); + + wl_signal_emit(&client->appinfo_mgr->events.metadata_ready, info); + + /* TODO: base output resolution */ +} + +static const struct tizen_launch_appinfo_interface appinfo_impl = +{ + appinfo_handle_destroy, + appinfo_handle_register_pid, + appinfo_handle_deregister_pid, + appinfo_handle_set_appid, + appinfo_handle_get_base_output_resolution, + appinfo_handle_register_appid, + appinfo_handle_deregister_appid, + appinfo_handle_set_pid, + appinfo_handle_ready_metadata, +}; + +static void +_tizen_appinfo_client_handle_destroy(struct wl_resource *resource) +{ + struct ds_tizen_appinfo_client *client; + + client = wl_resource_get_user_data(resource); + + ds_inf("_tizen_appinfo_client_handle_destroy (client:%p)", client); + + wl_list_remove(&client->link); + free(client); +} + +static void appinfo_mgr_bind(struct wl_client *wl_client, void *data, + uint32_t version, uint32_t id) +{ + struct ds_tizen_appinfo_mgr *appinfo_mgr = data; + struct ds_tizen_appinfo_client *client; + + client = calloc(1, sizeof *client); + if (client == NULL) { + ds_err("calloc() failed. tizen_appinfo"); + wl_client_post_no_memory(wl_client); + return; + } + + ds_inf("tizen_appinfo_client_binds. (client:%p)", client); + + client->appinfo_mgr = appinfo_mgr; + client->wl_client = wl_client; + + client->resource = wl_resource_create(wl_client, + &tizen_launch_appinfo_interface, + MIN(version, TIZEN_APPINFO_VERSION), id); + + if (client->resource == NULL) { + ds_err("tizen_appinfo : wl_resource_create() failed."); + free(client); + wl_client_post_no_memory(wl_client); + return; + } + + wl_resource_set_implementation(client->resource, &appinfo_impl, client, + _tizen_appinfo_client_handle_destroy); + + wl_list_insert(&appinfo_mgr->clients, &client->link); +} diff --git a/src/launch/meson.build b/src/launch/meson.build new file mode 100644 index 0000000..b3e2efd --- /dev/null +++ b/src/launch/meson.build @@ -0,0 +1,29 @@ +libds_tizen_launch_files = [ + 'appinfo.c', +] + +libds_tizen_launch_deps = [ + deps_libds_tizen, + dependency('tizen-launch-server', required: true), +] + +lib_libds_tizen_launch = shared_library('ds-tizen-launch', libds_tizen_launch_files, + dependencies: libds_tizen_launch_deps, + include_directories: [ common_inc, include_directories('.'), include_directories('..') ], + version: meson.project_version(), + install: true +) + +deps_libds_tizen_launch = declare_dependency( + link_with: lib_libds_tizen_launch, + dependencies: libds_tizen_launch_deps, + include_directories: [ common_inc, include_directories('.') ], +) + +pkgconfig = import('pkgconfig') +pkgconfig.generate(lib_libds_tizen_launch, + version: meson.project_version(), + filebase: 'libds-tizen-launch', + name: 'libds-tizen-launch', + description: 'tizen launch extension of libds-tizen for tizen platform', +) diff --git a/src/meson.build b/src/meson.build index 73c169c..3ea00d7 100644 --- a/src/meson.build +++ b/src/meson.build @@ -35,3 +35,4 @@ subdir('input_devicemgr') subdir('dpms') subdir('indicator') subdir('clipboard') +subdir('launch') diff --git a/tests/meson.build b/tests/meson.build index eb8d221..4b4686d 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -65,3 +65,22 @@ executable('libds-tizen-clipboard-tests', install_dir: libds_tizen_bindir, install : true ) + +## appinfo tests +tc_launch_appinfo_files = [ + 'tc_main.cpp', + 'tc_launch_appinfo.cpp', +] + +executable('libds-tizen-launch-appinfo-tests', + [ + tc_mock_files, + tc_launch_appinfo_files + ], + dependencies: [ + deps_test_common, + deps_libds_tizen_launch, + ], + install_dir: libds_tizen_bindir, + install : true +) diff --git a/tests/tc_launch_appinfo.cpp b/tests/tc_launch_appinfo.cpp new file mode 100644 index 0000000..9e7bab1 --- /dev/null +++ b/tests/tc_launch_appinfo.cpp @@ -0,0 +1,421 @@ +#include "tc_main.h" +#include "mockclient.h" +#include "mockcompositor.h" +#include +#include +#include + +#define TIZEN_APPINFO_VERSION 1 + +class MockAppinfoCompositor : public MockCompositor +{ +public: + MockAppinfoCompositor() + : MockCompositor(&MockAppinfoCompositor::TestSetup, this) + { + ds_inf("%s : this(%p)", __func__, this); + + // initialize the flags to check + bDestroyed = false; + + bSetPid = false; + bSetAppid = false; + bMetadataReady = false; + } + + ~MockAppinfoCompositor() + { + ds_inf("%s : this(%p)", __func__, this); + } + + static void TestSetup(void *data) + { + MockAppinfoCompositor *mockComp = + static_cast(data); + Compositor *comp = mockComp->compositor; + + ds_inf("%s: mockComp(%p)", __func__, mockComp); + + mockComp->mAppinfoMgr = ds_tizen_appinfo_mgr_create(comp->display); + + // destroy listener + mockComp->mDestroyListener.notify = + MockAppinfoCompositor::DestroyCallback; + mockComp->mDestroyListener.parent = mockComp; + ds_tizen_appinfo_mgr_add_destroy_listener(mockComp->mAppinfoMgr, + &mockComp->mDestroyListener); + + // set_pid listener + mockComp->mSetPidListener.notify = + MockAppinfoCompositor::SetPidCallback; + mockComp->mSetPidListener.parent = mockComp; + ds_tizen_appinfo_mgr_add_set_pid_listener(mockComp->mAppinfoMgr, + &mockComp->mSetPidListener); + + // set_appid listener + mockComp->mSetAppidListener.notify = + MockAppinfoCompositor::SetAppidCallback; + mockComp->mSetAppidListener.parent = mockComp; + ds_tizen_appinfo_mgr_add_set_appid_listener(mockComp->mAppinfoMgr, + &mockComp->mSetAppidListener); + + // metadata_ready listener + mockComp->mMetadataReadyListener.notify = + MockAppinfoCompositor::MetadataReadyCallback; + mockComp->mMetadataReadyListener.parent = mockComp; + ds_tizen_appinfo_mgr_add_metadata_ready_listener( + mockComp->mAppinfoMgr, + &mockComp->mMetadataReadyListener); + } + + static void DestroyCallback(struct wl_listener *listener, void *data) + { + ds_inf("%s", __func__); + + MockAppinfoCompositor *mockComp = + reinterpret_cast(listener)->parent; + + mockComp->bDestroyed = true; + } + + static void SetPidCallback(struct wl_listener *listener, void *data) + { + ds_inf("%s", __func__); + + MockAppinfoCompositor *mockComp = + reinterpret_cast(listener)->parent; + struct ds_tizen_appinfo *info = static_cast(data); + + ds_inf("%s: mockComp(%p), appinfo(%p)", __func__, mockComp, info); + + mockComp->bSetPid = true; + } + + static void SetAppidCallback(struct wl_listener *listener, void *data) + { + ds_inf("%s", __func__); + + MockAppinfoCompositor *mockComp = + reinterpret_cast(listener)->parent; + struct ds_tizen_appinfo *info = static_cast(data); + + ds_inf("%s: mockComp(%p), appinfo(%p)", __func__, mockComp, info); + + mockComp->bSetAppid = true; + } + + + static void MetadataReadyCallback(struct wl_listener *listener, void *data) + { + ds_inf("%s", __func__); + + MockAppinfoCompositor *mockComp = + reinterpret_cast(listener)->parent; + struct ds_tizen_appinfo *info = static_cast(data); + + ds_inf("%s: mockComp(%p), appinfo(%p)", __func__, mockComp, info); + + mockComp->bMetadataReady = true; + } + + void SendBaseOutputResolutionDone(uint32_t pid, uint32_t width, uint32_t height) + { + ds_inf("%s", __func__); + + wl_resource *res_appinfo = ds_tizen_appinfo_mgr_get_appinfo_resource(mAppinfoMgr); + + tizen_launch_appinfo_send_base_output_resolution_done(res_appinfo, pid, width, height); + } + +public: + bool bDestroyed; + bool bSetPid; + bool bSetAppid; + bool bMetadataReady; + +private: + struct ds_tizen_appinfo_mgr *mAppinfoMgr; + struct DestroyListener: ::wl_listener { + MockAppinfoCompositor *parent; + }; + DestroyListener mDestroyListener; + struct SetPidListener: ::wl_listener { + MockAppinfoCompositor *parent; + }; + SetPidListener mSetPidListener; + struct SetAppidListener: ::wl_listener { + MockAppinfoCompositor *parent; + }; + SetAppidListener mSetAppidListener; + struct MetadataReadyListener: ::wl_listener { + MockAppinfoCompositor *parent; + }; + MetadataReadyListener mMetadataReadyListener; +}; + +class MockAppinfoClient : public MockClient +{ +public: + MockAppinfoClient() + : bBaseOutputResolutionDone(false), + mBaseOutputResolutionWidth(0), + mBaseOutputResolutionHeight(0), + compositor_res(nullptr), + tizen_launch_appinfo(nullptr) + {} + MockAppinfoClient(const struct wl_registry_listener *listener) + : MockClient(listener, this) + { + ds_inf("%s", __func__); + + bBaseOutputResolutionDone = false; + mBaseOutputResolutionWidth = 0; + mBaseOutputResolutionHeight = 0; + } + ~MockAppinfoClient() + { + ds_inf("%s", __func__); + } + + void SetWlCompositor(struct wl_compositor *global_res) + { + ds_inf("%s", __func__); + + compositor_res = global_res; + } + + struct wl_compositor *GetWlCompositor() + { + ds_inf("%s", __func__); + + return compositor_res; + } + + void SetTizenAppinfo(struct tizen_launch_appinfo *global_res) + { + ds_inf("%s", __func__); + tizen_launch_appinfo = global_res; + } + + struct tizen_launch_appinfo *GetTizenAppinfo() + { + ds_inf("%s", __func__); + + return tizen_launch_appinfo; + } + +public: + bool bBaseOutputResolutionDone; + uint32_t mBaseOutputResolutionWidth; + uint32_t mBaseOutputResolutionHeight; + +private: + struct wl_compositor *compositor_res; + struct tizen_launch_appinfo *tizen_launch_appinfo; +}; + +static void +client_tizen_appinfo_cb_base_output_resolution_done(void *data, + struct tizen_launch_appinfo *appinfo, uint32_t pid, + uint32_t width, uint32_t height) +{ + ds_inf("%s", __func__); + + MockAppinfoClient *client = static_cast(data); + + client->bBaseOutputResolutionDone = true; + client->mBaseOutputResolutionWidth = width; + client->mBaseOutputResolutionHeight = height; +} + +static const struct tizen_launch_appinfo_listener appinfo_cb_listener = { + .base_output_resolution_done = client_tizen_appinfo_cb_base_output_resolution_done, +}; + +static void +client_registry_cb_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) +{ + ds_inf("%s", __func__); + + MockAppinfoClient *client = static_cast(data); + struct wl_compositor *compositor_res; + struct tizen_launch_appinfo *tizen_launch_appinfo; + + if (!strcmp(interface, "wl_compositor")) { + compositor_res = (struct wl_compositor *)wl_registry_bind(registry, + name, &wl_compositor_interface, 1); + if (compositor_res == nullptr) { + ds_err("wl_registry_bind() failed. wl_compositor resource."); + return; + } + client->SetWlCompositor(compositor_res); + } else if (!strcmp(interface, "tizen_launch_appinfo")) { + tizen_launch_appinfo = (struct tizen_launch_appinfo *)wl_registry_bind(registry, + name, &tizen_launch_appinfo_interface, TIZEN_APPINFO_VERSION); + if (tizen_launch_appinfo == nullptr) { + ds_err("wl_registry_bind() failed. tizen_launch_appinfo resource."); + return; + } + client->SetTizenAppinfo(tizen_launch_appinfo); + + tizen_launch_appinfo_add_listener(tizen_launch_appinfo, &appinfo_cb_listener, + client); + } +} + +static void +client_registry_cb_global_remove(void *data, struct wl_registry *registry, + uint32_t name) +{ + ds_inf("%s", __func__); + + MockAppinfoClient *client = static_cast(data); + struct wl_compositor *compositor_res = client->GetWlCompositor(); + struct tizen_launch_appinfo *appinfo_res = client->GetTizenAppinfo(); + + tizen_launch_appinfo_destroy(appinfo_res); + wl_compositor_destroy(compositor_res); +} + +static const struct wl_registry_listener registry_listener = { + .global = client_registry_cb_global, + .global_remove = client_registry_cb_global_remove +}; + +class AppinfoTest : public ::testing::Test +{ +public: + void SetUp(void) override; + void TearDown(void) override; + + MockAppinfoCompositor *comp; + MockAppinfoClient *client; + struct wl_compositor *compositor_res; + struct tizen_launch_appinfo *appinfo_res; +}; + +void +AppinfoTest::SetUp(void) +{ + ds_inf("%s", __func__); + + comp = new MockAppinfoCompositor(); + client = new MockAppinfoClient(®istry_listener); + compositor_res = client->GetWlCompositor(); + appinfo_res = client->GetTizenAppinfo(); + + client->RoundTrip(); +} + +void +AppinfoTest::TearDown(void) +{ + ds_inf("%s", __func__); + + client->RoundTrip(); + + delete client; + delete comp; +} + +TEST_F(AppinfoTest, Create_P) +{ + EXPECT_TRUE(true); +} + +TEST_F(AppinfoTest, Req_TizenAppinfoRegisterPid) +{ + uint32_t pid = 1234; + + tizen_launch_appinfo_register_pid(appinfo_res, pid); + client->RoundTrip(); + EXPECT_TRUE(comp->bSetPid); +} + +TEST_F(AppinfoTest, Req_TizenAppinfoSetAppid) +{ + uint32_t pid = 1234; + const char *appid = "org.tizen.libds-tizen-appinfo-test"; + + tizen_launch_appinfo_register_pid(appinfo_res, pid); + client->RoundTrip(); + EXPECT_TRUE(comp->bSetPid); + + tizen_launch_appinfo_set_appid(appinfo_res, pid, appid); + client->RoundTrip(); + EXPECT_TRUE(comp->bSetAppid); +} + +TEST_F(AppinfoTest, Req_TizenAppinfoRegisterAppid) +{ + const char *appid = "org.tizen.libds-tizen-appinfo-test"; + + tizen_launch_appinfo_register_appid(appinfo_res, appid); + client->RoundTrip(); + EXPECT_TRUE(comp->bSetAppid); +} + +TEST_F(AppinfoTest, Req_TizenAppinfoSetPid) +{ + uint32_t pid = 1234; + const char *appid = "org.tizen.libds-tizen-appinfo-test"; + + tizen_launch_appinfo_register_appid(appinfo_res, appid); + client->RoundTrip(); + EXPECT_TRUE(comp->bSetAppid); + + tizen_launch_appinfo_set_pid(appinfo_res, appid, pid); + client->RoundTrip(); + EXPECT_TRUE(comp->bSetPid); +} + +TEST_F(AppinfoTest, Req_TizenAppinfoMetadataReady) +{ + const char *appid = "org.tizen.libds-tizen-appinfo-test"; + uint32_t pid = 1234; + + tizen_launch_appinfo_register_appid(appinfo_res, appid); + client->RoundTrip(); + EXPECT_TRUE(comp->bSetAppid); + + tizen_launch_appinfo_ready_metadata(appinfo_res, appid, pid); + client->RoundTrip(); + EXPECT_TRUE(comp->bMetadataReady); +} + +TEST_F(AppinfoTest, Req_TizenAppinfoGetBaseOutputResolution) +{ + uint32_t pid = 1234; + + tizen_launch_appinfo_register_pid(appinfo_res, pid); + client->RoundTrip(); + EXPECT_TRUE(comp->bSetPid); + + tizen_launch_appinfo_get_base_output_resolution(appinfo_res, pid); + client->RoundTrip(); + + EXPECT_TRUE(client->bBaseOutputResolutionDone); + EXPECT_TRUE(client->mBaseOutputResolutionWidth == 1080); //default value + EXPECT_TRUE(client->mBaseOutputResolutionHeight == 1920); // default value +} + +TEST_F(AppinfoTest, Ev_TizenAppinfoBaseOutputResolutionDone) +{ + uint32_t pid = 1234, width = 1920, height = 1080; + + tizen_launch_appinfo_register_pid(appinfo_res, pid); + client->RoundTrip(); + EXPECT_TRUE(comp->bSetPid); + + tizen_launch_appinfo_get_base_output_resolution(appinfo_res, pid); + client->RoundTrip(); + + comp->SendBaseOutputResolutionDone(pid, width, height); + comp->Process(); + + client->RoundTrip(); + EXPECT_TRUE(client->bBaseOutputResolutionDone); + EXPECT_TRUE(client->mBaseOutputResolutionWidth == 1920); + EXPECT_TRUE(client->mBaseOutputResolutionHeight == 1080); +} -- 2.7.4