From fc468ef9c026c6fc4910de018f81d90cb905d617 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Wed, 11 May 2022 16:45:58 +0900 Subject: [PATCH 01/16] add security api which uses cynara The keyrouter and devicemgr has to check the privilege when display server gets the requests from these extensions. For this, security api support the previlege check with cynara. Change-Id: I6086e60e9d611de219b05856da6668bd7510cdc4 --- src/libds-tizen/meson.build | 1 + src/libds-tizen/util.h | 17 ++++ src/libds-tizen/util/meson.build | 1 + src/libds-tizen/util/security.c | 162 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 181 insertions(+) create mode 100644 src/libds-tizen/util.h create mode 100644 src/libds-tizen/util/meson.build create mode 100644 src/libds-tizen/util/security.c diff --git a/src/libds-tizen/meson.build b/src/libds-tizen/meson.build index 4f76b68..81370e6 100644 --- a/src/libds-tizen/meson.build +++ b/src/libds-tizen/meson.build @@ -12,6 +12,7 @@ libds_tizen_deps = [ subdir('allocator') subdir('backend') +subdir('util') lib_libds_tizen = shared_library('ds-tizen', libds_tizen_files, dependencies: libds_tizen_deps, diff --git a/src/libds-tizen/util.h b/src/libds-tizen/util.h new file mode 100644 index 0000000..18ad811 --- /dev/null +++ b/src/libds-tizen/util.h @@ -0,0 +1,17 @@ +#ifndef DS_UTIL_H +#define DS_UTIL_H + +#include + +#define MIN(a,b) ((a)<(b)?(a):(b)) + +int +tizen_security_init(void); + +void +tizen_security_finish(void); + +bool +tizen_security_check_privilege(pid_t pid, uid_t uid, const char *privilege); + +#endif diff --git a/src/libds-tizen/util/meson.build b/src/libds-tizen/util/meson.build new file mode 100644 index 0000000..3d34ab8 --- /dev/null +++ b/src/libds-tizen/util/meson.build @@ -0,0 +1 @@ +libds_tizen_files += files('security.c') diff --git a/src/libds-tizen/util/security.c b/src/libds-tizen/util/security.c new file mode 100644 index 0000000..735caf5 --- /dev/null +++ b/src/libds-tizen/util/security.c @@ -0,0 +1,162 @@ + +#include +#include +#include + +#include "libds/log.h" + +#include "util.h" + +#ifdef HAVE_CYNARA +#include +#include +#include +#include +#include +#include + +#define CYNARA_BUFSIZE 128 + +static cynara *g_cynara = NULL; +static int g_cynara_refcount = 0; + +static void +__security_log_print(int err, const char *fmt, ...) +{ + int ret; + va_list args; + char buf[CYNARA_BUFSIZE] = "\0"; + char tmp[CYNARA_BUFSIZE + CYNARA_BUFSIZE] = "\0"; + + if (fmt) { + va_start(args, fmt); + vsnprintf(tmp, CYNARA_BUFSIZE + CYNARA_BUFSIZE, fmt, args); + va_end(args); + } + + ret = cynara_strerror(err, buf, CYNARA_BUFSIZE); + if (ret != CYNARA_API_SUCCESS) { + ds_err("Failed to get cynara_strerror. error : %d (error log about %s: %d)\n", ret, tmp, err); + return; + } + + ds_err("%s is failed. (%s)\n", tmp, buf); +} +#endif + +bool +tizen_security_check_privilege(pid_t pid, uid_t uid, const char *privilege) +{ +#ifdef HAVE_CYNARA + bool res = false; + char *client_smack = NULL; + char *client_session = NULL; + char uid_str[16] = { 0, }; + int len = -1; + int ret = -1; + + /* If cynara_initialize() has been (retried) and failed, we suppose that cynara is not available. */ + /* Then we return true as if there is no security check available. */ + if (!g_cynara) + return true; + + if (!g_cynara) { + ds_err("security has not been initialized.\n"); + return false; + } + + ret = smack_new_label_from_process((int)pid, &client_smack); + if (ret <= 0) + goto finish; + + snprintf(uid_str, 15, "%d", (int)uid); + + client_session = cynara_session_from_pid(pid); + if (!client_session) + goto finish; + + ret = cynara_check(g_cynara, client_smack, client_session, + uid_str, privilege); + + if (ret == CYNARA_API_ACCESS_ALLOWED) + res = true; + else + __security_log_print(ret, "privilege: %s, client_smack: %s, pid: %d", privilege, client_smack, pid); + +finish: + ds_dbg("Privilege Check For '%s' %s pid:%u uid:%u client_smack:%s(len:%d) client_session:%s ret:%d", + privilege, res ? "SUCCESS" : "FAIL", pid, uid, + client_smack ? client_smack : "N/A", len, + client_session ? client_session: "N/A", ret); + + if (client_session) + free(client_session); + if (client_smack) + free(client_smack); + + return res; +#else + return true; +#endif +} + +int +tizen_security_init(void) +{ +#ifdef HAVE_CYNARA + int ret = CYNARA_API_SUCCESS; + int retry_cnt = 0; + static bool retried = false; + + if (++g_cynara_refcount != 1) + return g_cynara_refcount; + + if (!g_cynara && false == retried) { + retried = true; + + for (retry_cnt = 0; retry_cnt < 5; retry_cnt++) { + ds_dbg("Retry cynara initialize: %d\n", retry_cnt + 1); + + ret = cynara_initialize(&g_cynara, NULL); + + if (CYNARA_API_SUCCESS == ret) { + ds_dbg("Succeed to initialize cynara !\n"); + return 1; + } + + __security_log_print(ret, "cynara_initialize"); + g_cynara = NULL; + } + } + + ds_err("Failed to initialize _security ! (error:%d, retry_cnt=%d)\n", + ret, retry_cnt); + --g_cynara_refcount; + + return 0; +#else + return 1; +#endif +} + +void +tizen_security_finish(void) +{ +#ifdef HAVE_CYNARA + if (g_cynara_refcount < 1) { + ds_err("%s called without tizen_security_init\n", __FUNCTION__); + return 0; + } + + if (--g_cynara_refcount != 0) + return 1; + + if (g_cynara) { + cynara_finish(g_cynara); + g_cynara = NULL; + } +#endif + + return 1; +} + -- 2.7.4 From d73020d0efe3530b94fde46f289867d180fecf1e Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Wed, 11 May 2022 16:48:55 +0900 Subject: [PATCH 02/16] implement the libds-tizen-keyrouter libds-tizen-keyrouter supports the tizen_keyrouter extension. This is the first impelemtaion for it. It can be added the additional apis onward. Change-Id: I9c819b03e38c5a1d7f8abb0257040502608e9131 --- include/libds-tizen/keyrouter.h | 23 ++ meson_options.txt | 1 + packaging/libds.spec | 35 ++- src/libds-tizen/keyrouter/keyrouter.c | 430 +++++++++++++++++++++++++++++ src/libds-tizen/keyrouter/keyrouter.h | 108 ++++++++ src/libds-tizen/keyrouter/keyrouter_grab.c | 351 +++++++++++++++++++++++ src/libds-tizen/keyrouter/meson.build | 34 +++ src/libds-tizen/meson.build | 3 + 8 files changed, 983 insertions(+), 2 deletions(-) create mode 100644 include/libds-tizen/keyrouter.h create mode 100644 src/libds-tizen/keyrouter/keyrouter.c create mode 100644 src/libds-tizen/keyrouter/keyrouter.h create mode 100644 src/libds-tizen/keyrouter/keyrouter_grab.c create mode 100644 src/libds-tizen/keyrouter/meson.build diff --git a/include/libds-tizen/keyrouter.h b/include/libds-tizen/keyrouter.h new file mode 100644 index 0000000..a870003 --- /dev/null +++ b/include/libds-tizen/keyrouter.h @@ -0,0 +1,23 @@ +#ifndef LIBDS_TIZEN_KEYROUTER_H +#define LIBDS_TIZEN_KEYROUTER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct ds_tizen_keyrouter; + +struct ds_tizen_keyrouter * +ds_tizen_keyrouter_create(struct wl_display *display); + +void +ds_tizen_keyrouter_add_destroy_listener(struct ds_tizen_keyrouter *keyrouter, + struct wl_listener *listener); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/meson_options.txt b/meson_options.txt index 6ef6c8e..1d6ff21 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1 +1,2 @@ option('tizen', type: 'boolean', value: false, description: 'Build Tizen features') +option('keylayout_dir', type: 'string', value: '', description: 'Directory where tizen key layout file is') \ No newline at end of file diff --git a/packaging/libds.spec b/packaging/libds.spec index efba8cb..bee3972 100644 --- a/packaging/libds.spec +++ b/packaging/libds.spec @@ -11,6 +11,7 @@ BuildRequires: meson BuildRequires: pkgconfig(wayland-server) BuildRequires: pkgconfig(wayland-client) BuildRequires: pkgconfig(wayland-protocols) +BuildRequires: pkgconfig(tizen-extension-server) BuildRequires: pkgconfig(pixman-1) BuildRequires: pkgconfig(libdrm) BuildRequires: pkgconfig(xkbcommon) @@ -37,6 +38,20 @@ Summary: Wayland Compositor development package on Tizen %description tizen-devel Wayland Compositor development library for Tizen platform +%package tizen-keyrouter +Summary: Wayland Compositor Library for keyrouter +Group: Development/Libraries + +%description tizen-keyrouter +Wayland Compositor Library for tizen keyrouter + +%package tizen-keyrouter-devel +Summary: Keyrouter Development package for Wayland Compositor Library +Group: Development/Libraries + +%description tizen-keyrouter-devel +Keyrouter Development package for Wayland Compositor Library + %prep %setup -q cp %{SOURCE1001} . @@ -47,7 +62,8 @@ meson setup \ --libdir %{_libdir} \ --bindir %{_bindir} \ builddir \ - -Dtizen=true + -Dtizen=true \ + -Dkeylayout_dir="%{TZ_SYS_RO_SHARE}/X11/xkb/tizen_key_layout.txt" ninja -C builddir all %install @@ -58,7 +74,8 @@ ninja -C builddir install %manifest %{name}.manifest %defattr(-,root,root,-) %license LICENSE -%{_libdir}/*.so.* +%{_libdir}/libds.so.* +%{_libdir}/libds-tizen.so.* %files devel %manifest %{name}.manifest @@ -81,3 +98,17 @@ ninja -C builddir install %{_bindir}/tdm-backend %{_bindir}/tinyds-tdm %{_bindir}/ds-simple-tbm + +%files tizen-keyrouter +%manifest %{name}.manifest +%defattr(-,root,root,-) +%license LICENSE +%{_libdir}/libds-tizen-keyrouter.so.* + +%files tizen-keyrouter-devel +%manifest %{name}.manifest +%defattr(-,root,root,-) +%license LICENSE +%{_includedir}/libds-tizen/keyrouter.h +%{_libdir}/pkgconfig/libds-tizen-keyrouter.pc +%{_libdir}/libds-tizen-keyrouter.so diff --git a/src/libds-tizen/keyrouter/keyrouter.c b/src/libds-tizen/keyrouter/keyrouter.c new file mode 100644 index 0000000..eaa831a --- /dev/null +++ b/src/libds-tizen/keyrouter/keyrouter.c @@ -0,0 +1,430 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "libds/log.h" +#include "libds-tizen/keyrouter.h" + +#include "util.h" +#include "keyrouter.h" + +static void +tizen_keyrouter_bind(struct wl_client *client, void *data, uint32_t version, + uint32_t id); +static bool +keyrouter_check_privilege(struct ds_tizen_keyrouter_client *keyrouter_client, + struct wl_client *client, uint32_t mode, uint32_t keycode); +static void +tizen_keyrouter_options_set(struct ds_tizen_keyrouter *keyrouter); + +static void +keyrouter_handle_display_destroy(struct wl_listener *listener, void *data) +{ + struct ds_tizen_keyrouter *keyrouter; + + keyrouter = wl_container_of(listener, keyrouter, display_destroy); + + ds_inf("Global destroy: ds_tizen_keyrouter(%p)", keyrouter); + + wl_signal_emit(&keyrouter->events.destroy, keyrouter); + + tizen_security_finish(); + + free(keyrouter->opts); + + wl_list_remove(&keyrouter->display_destroy.link); + + wl_global_destroy(keyrouter->global); + + tizen_keyrouter_grab_destroy(keyrouter->keyrouter_grab); + + free(keyrouter); +} + +WL_EXPORT struct ds_tizen_keyrouter * +ds_tizen_keyrouter_create(struct wl_display *display) +{ + struct ds_tizen_keyrouter *keyrouter; + + keyrouter = calloc(1, sizeof *keyrouter); + if (!keyrouter) { + return NULL; + } + + keyrouter->keyrouter_grab = tizen_keyrouter_grab_create(); + if (keyrouter->keyrouter_grab == NULL) { + ds_err("Failed to create keyrouter."); + free(keyrouter); + return NULL; + } + + keyrouter->global = wl_global_create(display, &tizen_keyrouter_interface, + 2, keyrouter, tizen_keyrouter_bind); + if (!keyrouter->global) { + tizen_keyrouter_grab_destroy(keyrouter->keyrouter_grab); + free(keyrouter); + return NULL; + } + + wl_list_init(&keyrouter->clients); + + wl_signal_init(&keyrouter->events.destroy); + + keyrouter->display_destroy.notify = keyrouter_handle_display_destroy; + wl_display_add_destroy_listener(display, &keyrouter->display_destroy); + + tizen_keyrouter_options_set(keyrouter); + + if (!tizen_security_init()) { + ds_inf("tizen_security_init() is not sucessful. keyrouter works without security."); + } + + ds_inf("Global created: ds_tizen_keyrouter(%p)", keyrouter); + + return keyrouter; +} + +WL_EXPORT void +ds_tizen_keyrouter_add_destroy_listener(struct ds_tizen_keyrouter *keyrouter, + struct wl_listener *listener) +{ + wl_signal_add(&keyrouter->events.destroy, listener); +} + +static void +keyrouter_handle_keygrab_set(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *surface, + uint32_t key, uint32_t mode) +{ + struct ds_tizen_keyrouter_client *keyrouter_client; + struct ds_tizen_keyrouter *keyrouter; + int res = TIZEN_KEYROUTER_ERROR_NONE; + bool ret; + + keyrouter_client = wl_resource_get_user_data(resource); + keyrouter = keyrouter_client->keyrouter; + + ret = keyrouter_check_privilege(keyrouter_client, client, mode, key); + if (ret == false) { + tizen_keyrouter_send_keygrab_notify(resource, surface, + key, mode, TIZEN_KEYROUTER_ERROR_NO_PERMISSION); + return; + } + + res = tizen_keyrouter_grab_grab_key(keyrouter->keyrouter_grab, + mode, key, (void *)client); + if (res == TIZEN_KEYROUTER_ERROR_NONE && keyrouter_client->grabbed != true) + keyrouter_client->grabbed = true; + + tizen_keyrouter_send_keygrab_notify(resource, surface, key, mode, res); +} + +static void +keyrouter_handle_keygrab_unset(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *surface, uint32_t key) +{ + struct ds_tizen_keyrouter_client *keyrouter_client; + struct ds_tizen_keyrouter *keyrouter; + int res = TIZEN_KEYROUTER_ERROR_NONE; + bool ret; + + keyrouter_client = wl_resource_get_user_data(resource); + keyrouter = keyrouter_client->keyrouter; + + /* ungrab TOP POSITION grab first, this grab mode is not check privilege */ + tizen_keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, + TIZEN_KEYROUTER_MODE_TOPMOST, key, (void *)client); + + ret = keyrouter_check_privilege(keyrouter_client, + client, TIZEN_KEYROUTER_MODE_NONE, key); + if (ret == false) { + tizen_keyrouter_send_keygrab_notify(resource, surface, key, + TIZEN_KEYROUTER_MODE_NONE, TIZEN_KEYROUTER_ERROR_NO_PERMISSION); + return; + } + + tizen_keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, + TIZEN_KEYROUTER_MODE_EXCLUSIVE, key, (void *)client); + tizen_keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, + TIZEN_KEYROUTER_MODE_OVERRIDABLE_EXCLUSIVE, key, (void *)client); + tizen_keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, + TIZEN_KEYROUTER_MODE_TOPMOST, key, (void *)client); + tizen_keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, + TIZEN_KEYROUTER_MODE_SHARED, key, (void *)client); + + tizen_keyrouter_send_keygrab_notify(resource, surface, key, TIZEN_KEYROUTER_MODE_NONE, res); +} + +static void +keyrouter_handle_get_keygrab_status(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *surface, uint32_t key) +{ + tizen_keyrouter_send_keygrab_notify(resource, surface, key, + TIZEN_KEYROUTER_MODE_NONE, TIZEN_KEYROUTER_ERROR_NO_PERMISSION); +} + +static int +keyrouter_get_array_length(const struct wl_array *array) +{ + int *data = NULL; + int count = 0; + + wl_array_for_each(data, array) { + count++; + } + + return count; +} + +static void +keyrouter_handle_keygrab_set_list(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *surface, struct wl_array *grab_list) +{ + struct ds_tizen_keyrouter_client *keyrouter_client; + struct ds_tizen_keyrouter *keyrouter; + struct wl_array *return_list; + struct ds_tizen_grab_data *grab_data = NULL; + int res = TIZEN_KEYROUTER_ERROR_NONE; + bool ret; + + keyrouter_client = wl_resource_get_user_data(resource); + keyrouter = keyrouter_client->keyrouter; + + if ((keyrouter_get_array_length(grab_list) % 3) != 0) { + ds_err("Invalid keycode and grab mode pair. Check arguments in a list."); + tizen_keyrouter_send_keygrab_notify_list(resource, surface, NULL); + return; + } + + wl_array_for_each(grab_data, grab_list) { + ret = keyrouter_check_privilege(keyrouter_client, client, grab_data->mode, grab_data->key); + if (ret == false) { + grab_data->err = TIZEN_KEYROUTER_ERROR_NO_PERMISSION; + } else { + res = tizen_keyrouter_grab_grab_key(keyrouter->keyrouter_grab, + grab_data->mode, grab_data->key, (void *)client); + if (res == TIZEN_KEYROUTER_ERROR_NONE && keyrouter_client->grabbed != true) + keyrouter_client->grabbed = true; + + grab_data->err = res; + } + } + + return_list = grab_list; + + tizen_keyrouter_send_keygrab_notify_list(resource, surface, return_list); +} + +static void +keyrouter_handle_keygrab_unset_list(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *surface, + struct wl_array *ungrab_list) +{ + struct ds_tizen_keyrouter_client *keyrouter_client; + struct ds_tizen_keyrouter *keyrouter; + struct wl_array *return_list = NULL; + struct ds_tizen_ungrab_data *ungrab_data = NULL; + bool ret; + + keyrouter_client = wl_resource_get_user_data(resource); + keyrouter = keyrouter_client->keyrouter; + + if ((keyrouter_get_array_length(ungrab_list) % 3) != 0) { + ds_err("Invalid keycode and grab mode pair. Check arguments in a list."); + tizen_keyrouter_send_keygrab_notify_list(resource, surface, NULL); + return; + } + + wl_array_for_each(ungrab_data, ungrab_list) { + tizen_keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, + TIZEN_KEYROUTER_MODE_TOPMOST, ungrab_data->key, (void *)client); + + ret = keyrouter_check_privilege(keyrouter_client, client, + TIZEN_KEYROUTER_MODE_TOPMOST, ungrab_data->key); + if (!ret) { + ungrab_data->err = TIZEN_KEYROUTER_ERROR_NO_PERMISSION; + } else { + tizen_keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, + TIZEN_KEYROUTER_MODE_EXCLUSIVE, ungrab_data->key, (void *)client); + + tizen_keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, + TIZEN_KEYROUTER_MODE_OVERRIDABLE_EXCLUSIVE, ungrab_data->key, (void *)client); + + tizen_keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, + TIZEN_KEYROUTER_MODE_TOPMOST, ungrab_data->key, (void *)client); + + tizen_keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, + TIZEN_KEYROUTER_MODE_SHARED, ungrab_data->key, (void *)client); + + ungrab_data->err = TIZEN_KEYROUTER_ERROR_NONE; + } + } + + return_list = ungrab_list; + + tizen_keyrouter_send_keygrab_notify_list(resource, surface, return_list); +} + +static void +keyrouter_handle_get_keygrab_list(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *surface) +{ + tizen_keyrouter_send_getgrab_notify_list(resource, surface, NULL); +} + +static void +keyrouter_handle_set_register_none_key(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *surface, + uint32_t data) +{ + tizen_keyrouter_send_set_register_none_key_notify(resource, NULL, 0); +} + +static void +keyrouter_handle_get_keyregister_status(struct wl_client *client, + struct wl_resource *resource, uint32_t key) +{ + tizen_keyrouter_send_keyregister_notify(resource, (int)false); +} + +static void +keyrouter_handle_set_input_config(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *surface, + uint32_t config_mode, uint32_t value) +{ + tizen_keyrouter_send_set_input_config_notify(resource, (int)false); +} + +static void +keyrouter_handle_destory(struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct tizen_keyrouter_interface tizen_keyrouter_impl = { + keyrouter_handle_keygrab_set, + keyrouter_handle_keygrab_unset, + keyrouter_handle_get_keygrab_status, + keyrouter_handle_keygrab_set_list, + keyrouter_handle_keygrab_unset_list, + keyrouter_handle_get_keygrab_list, + keyrouter_handle_set_register_none_key, + keyrouter_handle_get_keyregister_status, + keyrouter_handle_set_input_config, + keyrouter_handle_destory +}; + +static void +keyrouter_handle_resource_destory(struct wl_resource *resource) +{ + struct ds_tizen_keyrouter_client *keyrouter_client = wl_resource_get_user_data(resource); + + wl_list_remove(&keyrouter_client->link); + free(keyrouter_client); +} + +static void +tizen_keyrouter_bind(struct wl_client *client, void *data, uint32_t version, + uint32_t id) +{ + struct ds_tizen_keyrouter *keyrouter = data; + struct ds_tizen_keyrouter_client *keyrouter_client; + + keyrouter_client = calloc(1, sizeof *keyrouter_client); + if (keyrouter_client == NULL) { + wl_client_post_no_memory(client); + return; + } + + keyrouter_client->resource = + wl_resource_create(client, &tizen_keyrouter_interface, MIN(version, 2), id); + if (keyrouter_client->resource == NULL) { + ds_err("wl_resource_create() failed.(version :%d, id:%d)", version, id); + free(keyrouter_client); + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(keyrouter_client->resource, &tizen_keyrouter_impl, + keyrouter_client, keyrouter_handle_resource_destory); + + wl_list_insert(&keyrouter->clients, &keyrouter_client->link); +} + +static bool +keyrouter_check_privilege(struct ds_tizen_keyrouter_client *keyrouter_client, + struct wl_client *client, uint32_t mode, uint32_t keycode) +{ + struct ds_tizen_keyrouter *keyrouter = keyrouter_client->keyrouter; + + pid_t pid = 0; + uid_t uid = 0; + gid_t gid = 0; + + /* Top position grab is always allowed. This mode do not need privilege.*/ + if (mode == TIZEN_KEYROUTER_MODE_TOPMOST) + return true; + + // check no privilege option on the keycode + if (keyrouter->opts) { + if (keyrouter->opts[keycode].no_privilege) + return true; + } + + // grabbed client is already checked the privilege before. + if (keyrouter_client->grabbed) + return true; + + wl_client_get_credentials(client, &pid, &uid, &gid); + + return tizen_security_check_privilege(pid, uid, "http://tizen.org/privilege/keygrab"); +} + +static void +tizen_keyrouter_options_set(struct ds_tizen_keyrouter *keyrouter) +{ + FILE *file; + int keycode; + char *ret, *tmp, *buf_ptr, buf[1024] = {0,}; + + keyrouter->opts = calloc(KEYROUTER_MAX_KEYS, + sizeof(struct ds_tizen_keyrouter_key_options)); + if (!keyrouter->opts) { + return; + } + + file = fopen(KEYLAYOUT_DIR, "r"); + if (!file) { + ds_err("Failed to open key layout file(%s): (err msg: %m)\n", KEYLAYOUT_DIR); + free(keyrouter->opts); + keyrouter->opts = NULL; + return; + } + + while (!feof(file)) { + ret = fgets(buf, 1024, file); + if (!ret) continue; + + tmp = strtok_r(buf, " ", &buf_ptr); + tmp = strtok_r(NULL, " ", &buf_ptr); + if (!tmp) continue; + keycode = atoi(tmp); + if ((0 >= keycode) || (keycode >= KEYROUTER_MAX_KEYS)) { + ds_err("Currently %d key is invalid to support\n", keycode); + continue; + } + + keyrouter->opts[keycode].enabled = true; + + if (strstr(buf_ptr, "no_priv") != NULL) { + keyrouter->opts[keycode].no_privilege = true; + } + } + + fclose(file); +} diff --git a/src/libds-tizen/keyrouter/keyrouter.h b/src/libds-tizen/keyrouter/keyrouter.h new file mode 100644 index 0000000..c8dfa0f --- /dev/null +++ b/src/libds-tizen/keyrouter/keyrouter.h @@ -0,0 +1,108 @@ +#ifndef DS_TIZEN_KEYROUTER_H +#define DS_TIZEN_KEYROUTER_H + +#include +#include +#include + +#define KEYROUTER_MAX_KEYS 512 + +struct ds_tizen_keyrouter_key_info +{ + void *data; + struct wl_list link; +}; + +struct ds_tizen_keyrouter_grabbed +{ + int keycode; + + struct { + struct wl_list excl; + struct wl_list or_excl; + struct wl_list top; + struct wl_list shared; + } grab; + + struct wl_list pressed; +}; + +struct ds_tizen_keyrouter_grab +{ + struct ds_tizen_keyrouter_grabbed *hard_keys; + + void *focus_client; + void *top_client; +}; + +struct ds_tizen_keyrouter_key_options +{ + bool enabled; + bool no_privilege; +}; + +struct ds_tizen_keyrouter +{ + struct wl_global *global; + + struct wl_list clients; + + struct wl_listener display_destroy; + + struct { + struct wl_signal destroy; + } events; + + struct ds_tizen_keyrouter_key_options *opts; + + struct ds_tizen_keyrouter_grab *keyrouter_grab; +}; + +struct ds_tizen_keyrouter_client +{ + struct ds_tizen_keyrouter *keyrouter; + + struct wl_resource *resource; + + bool grabbed; + + struct wl_list link; // ds_tizen_keyrouter::clients +}; + +struct ds_tizen_grab_data +{ + int key; + int mode; + int err; +}; + +struct ds_tizen_ungrab_data +{ + int key; + int err; +}; + +struct ds_tizen_keyrouter_grab * +tizen_keyrouter_grab_create(void); +void +tizen_keyrouter_grab_destroy(struct ds_tizen_keyrouter_grab *keyrouter_grab); +int +tizen_keyrouter_grab_grab_key(struct ds_tizen_keyrouter_grab *keyrouter_grab, + int type, int keycode, void *data); +void +tizen_keyrouter_grab_ungrab_key(struct ds_tizen_keyrouter_grab *keyrouter_grab, + int type, int keycode, void *data); +int +tizen_keyrouter_grab_key_process(struct ds_tizen_keyrouter_grab *keyrouter_grab, + int keycode, int pressed, struct wl_list *delivery_list); +void +tizen_keyrouter_grab_set_focus_client(struct ds_tizen_keyrouter_grab *keyrouter_grab, + void *focus_client); +void +tizen_keyrouter_grab_set_top_client(struct ds_tizen_keyrouter_grab *keyrouter_grab, + void *top_client); +bool +tizen_keyrouter_grab_check_grabbed_key(struct ds_tizen_keyrouter_grab *keyrouter_grab, + int keycode); + +#endif diff --git a/src/libds-tizen/keyrouter/keyrouter_grab.c b/src/libds-tizen/keyrouter/keyrouter_grab.c new file mode 100644 index 0000000..873f4b1 --- /dev/null +++ b/src/libds-tizen/keyrouter/keyrouter_grab.c @@ -0,0 +1,351 @@ +#include +#include + +#include "libds/log.h" + +#include "keyrouter.h" + +static struct wl_list * +keyrouter_grab_get_grabbed_list(struct ds_tizen_keyrouter_grab *keyrouter_grab, + int type, int keycode) +{ + switch(type) { + case TIZEN_KEYROUTER_MODE_EXCLUSIVE: + return &keyrouter_grab->hard_keys[keycode].grab.excl; + case TIZEN_KEYROUTER_MODE_OVERRIDABLE_EXCLUSIVE: + return &keyrouter_grab->hard_keys[keycode].grab.or_excl; + case TIZEN_KEYROUTER_MODE_TOPMOST: + return &keyrouter_grab->hard_keys[keycode].grab.top; + case TIZEN_KEYROUTER_MODE_SHARED: + return &keyrouter_grab->hard_keys[keycode].grab.shared; + default: + return NULL; + } +} + +bool +tizen_keyrouter_grab_check_grabbed_key(struct ds_tizen_keyrouter_grab *keyrouter_grab, int keycode) +{ + struct wl_list *list; + + list = keyrouter_grab_get_grabbed_list(keyrouter_grab, TIZEN_KEYROUTER_MODE_EXCLUSIVE, keycode); + if (list && !wl_list_empty(list)) + return true; + + list = keyrouter_grab_get_grabbed_list(keyrouter_grab, TIZEN_KEYROUTER_MODE_OVERRIDABLE_EXCLUSIVE, keycode); + if (list && !wl_list_empty(list)) + return true; + + list = keyrouter_grab_get_grabbed_list(keyrouter_grab, TIZEN_KEYROUTER_MODE_TOPMOST, keycode); + if (list && !wl_list_empty(list)) + return true; + + list = keyrouter_grab_get_grabbed_list(keyrouter_grab, TIZEN_KEYROUTER_MODE_SHARED, keycode); + if (list && !wl_list_empty(list)) + return true; + + return false; +} + +static bool +keyrouter_grab_check_duplicated_data(struct wl_list *list, void *data) +{ + struct ds_tizen_keyrouter_key_info *info; + + if (wl_list_empty(list)) + return false; + + wl_list_for_each(info, list, link) { + if (info->data == data) + return true; + } + + return false; +} + +static bool +keyrouter_grab_check_grabbed(struct ds_tizen_keyrouter_grab *keyrouter_grab, + int type, int keycode, void *data) +{ + struct wl_list *list; + bool ret; + + list = keyrouter_grab_get_grabbed_list(keyrouter_grab, type, keycode); + if (list == NULL) { + ds_err("keycode(%d) had no list for type(%d).", keycode, type); + return false; + } + + switch(type) { + case TIZEN_KEYROUTER_MODE_EXCLUSIVE: + if (wl_list_empty(list) == false) + ret = false; + else + ret = true; + break; + case TIZEN_KEYROUTER_MODE_OVERRIDABLE_EXCLUSIVE: + ret = keyrouter_grab_check_duplicated_data(list, data); + break; + case TIZEN_KEYROUTER_MODE_TOPMOST: + ret = keyrouter_grab_check_duplicated_data(list, data); + break; + case TIZEN_KEYROUTER_MODE_SHARED: + ret = keyrouter_grab_check_duplicated_data(list, data); + break; + default: + ret = TIZEN_KEYROUTER_ERROR_INVALID_MODE; + break; + } + + return ret; +} + +void +tizen_keyrouter_grab_set_focus_client(struct ds_tizen_keyrouter_grab *keyrouter_grab, + void *focus_client) +{ + keyrouter_grab->focus_client = focus_client; + + if (focus_client) + ds_dbg("[%s] focus client has been set. (focus_client=0x%p)", __FUNCTION__, focus_client); + else + ds_dbg("[%s] focus client has been set to NULL.", __FUNCTION__); +} + +void +tizen_keyrouter_grab_set_top_client(struct ds_tizen_keyrouter_grab *keyrouter_grab, void *top_client) +{ + keyrouter_grab->top_client = top_client; + + if (top_client) + ds_dbg("[%s] top client has been set. (top_client=0x%p)", __FUNCTION__, top_client); + else + ds_dbg("[%s] top client has been set to NULL.", __FUNCTION__); +} + +int +tizen_keyrouter_grab_key_process(struct ds_tizen_keyrouter_grab *keyrouter_grab, + int keycode, int pressed, struct wl_list *delivery_list) +{ + struct ds_tizen_keyrouter_key_info *info, *delivery; + int count = 0; + + if (keycode <= 0 && keycode >= KEYROUTER_MAX_KEYS) { + ds_err("Invalid keycode(%d)", keycode); + return 0; + } + + if (!wl_list_empty(&keyrouter_grab->hard_keys[keycode].grab.excl)) { + info = wl_container_of(keyrouter_grab->hard_keys[keycode].grab.excl.next, info, link); + if (info) { + delivery = calloc(1, sizeof(struct ds_tizen_keyrouter_key_info)); + if (delivery == NULL) { + ds_err("Failed to allocate memory."); + return 0; + } + delivery->data = info->data; + wl_list_insert(delivery_list, &delivery->link); + ds_dbg("Exclusive Mode: keycode: %d to data: %p", keycode, info->data); + return 1; + } + } else if (!wl_list_empty(&keyrouter_grab->hard_keys[keycode].grab.or_excl)) { + info = wl_container_of(keyrouter_grab->hard_keys[keycode].grab.or_excl.next, info, link); + if (info) { + delivery = calloc(1, sizeof(struct ds_tizen_keyrouter_key_info)); + if (delivery == NULL) { + ds_err("Failed to allocate memory."); + return 0; + } + delivery->data = info->data; + wl_list_insert(delivery_list, &delivery->link); + ds_dbg("OR-Excl Mode: keycode: %d to data: %p", keycode, info->data); + return 1; + } + } else if (!wl_list_empty(&keyrouter_grab->hard_keys[keycode].grab.top)) { + wl_list_for_each(info, &keyrouter_grab->hard_keys[keycode].grab.top, link) { + if (keyrouter_grab->top_client && keyrouter_grab->top_client == info->data) { + delivery = calloc(1, sizeof(struct ds_tizen_keyrouter_key_info)); + if (delivery == NULL) { + ds_err("Failed to allocate memory."); + return 0; + } + delivery->data = info->data; + wl_list_insert(delivery_list, &delivery->link); + ds_dbg("Topmost Mode: keycode: %d to data: %p", keycode, info->data); + return 1; + } + } + } + + if (keyrouter_grab->focus_client) { + delivery = calloc(1, sizeof(struct ds_tizen_keyrouter_key_info)); + if (delivery == NULL) { + ds_err("Failed to allocate memory."); + return 0; + } + delivery->data = keyrouter_grab->focus_client; + wl_list_insert(delivery_list, &delivery->link); + count++; + ds_dbg("Focus: keycode: %d to data: %p, count: %d", keycode, delivery->data, count); + } + + if (!wl_list_empty(&keyrouter_grab->hard_keys[keycode].grab.shared)) { + wl_list_for_each(info, &keyrouter_grab->hard_keys[keycode].grab.shared, link) { + if (keyrouter_grab->focus_client && keyrouter_grab->focus_client == info->data) + continue; + delivery = calloc(1, sizeof(struct ds_tizen_keyrouter_key_info)); + if (delivery == NULL) { + ds_err("Failed to allocate memory."); + return 0; + } + delivery->data = info->data; + wl_list_insert(delivery_list, &delivery->link); + count++; + ds_dbg("Shared: keycode: %d to data: %p, count: %d", keycode, info->data, count); + } + } + + return count; +} + +int +tizen_keyrouter_grab_grab_key(struct ds_tizen_keyrouter_grab *keyrouter_grab, + int type, int keycode, void *data) +{ + struct ds_tizen_keyrouter_key_info *info = NULL; + struct wl_list *list = NULL; + + if (keycode <= 0 && keycode >= KEYROUTER_MAX_KEYS) { + ds_err("Invalid keycode(%d)", keycode); + return TIZEN_KEYROUTER_ERROR_INVALID_KEY; + } + + if (keyrouter_grab_check_grabbed(keyrouter_grab, type, keycode, data)) + return TIZEN_KEYROUTER_ERROR_GRABBED_ALREADY; + + info = calloc(1, sizeof(struct ds_tizen_keyrouter_key_info)); + if (info == NULL) { + ds_err("Failed to allocate memory."); + return TIZEN_KEYROUTER_ERROR_NO_SYSTEM_RESOURCES; + } + info->data = data; + + wl_list_init(&info->link); + + list = keyrouter_grab_get_grabbed_list(keyrouter_grab, type, keycode); + if (!list) { + ds_err("keycode(%d) had no list for type(%d)", keycode, type); + free(info); + return TIZEN_KEYROUTER_ERROR_INVALID_MODE; + } + + if (!keyrouter_grab->hard_keys[keycode].keycode) + keyrouter_grab->hard_keys[keycode].keycode = keycode; + + wl_list_insert(list, &info->link); + + return TIZEN_KEYROUTER_ERROR_NONE; +} + +static void +keyrouter_list_remove_data(struct wl_list *list, void *data) +{ + struct ds_tizen_keyrouter_key_info *info, *tmp; + + if (wl_list_empty(list)) + return; + + wl_list_for_each_safe(info ,tmp, list, link) { + if (info->data == data) { + wl_list_remove(&info->link); + free(info); + } + } +} + +void +tizen_keyrouter_grab_ungrab_key(struct ds_tizen_keyrouter_grab *keyrouter_grab, + int type, int keycode, void *data) +{ + struct wl_list *list; + + if (keycode <= 0 && keycode >= KEYROUTER_MAX_KEYS) { + ds_err("Invalid keycode(%d)", keycode); + return; + } + + if (keyrouter_grab->hard_keys[keycode].keycode == 0) + return; + + list = keyrouter_grab_get_grabbed_list(keyrouter_grab, type, keycode); + if (list == NULL) { + ds_err("keycode(%d) had no list for type(%d)", keycode, type); + return; + } + + keyrouter_list_remove_data(list, data); +} + +struct ds_tizen_keyrouter_grab * +tizen_keyrouter_grab_create(void) +{ + struct ds_tizen_keyrouter_grab *keyrouter_grab = NULL; + int i; + + keyrouter_grab = calloc(1, sizeof(struct ds_tizen_keyrouter_grab)); + if (keyrouter_grab == NULL) { + ds_err("Failed to allocate memory."); + return NULL; + } + + /* FIXME: Who defined max keycode? */ + keyrouter_grab->hard_keys = calloc(KEYROUTER_MAX_KEYS, sizeof(struct ds_tizen_keyrouter_grabbed)); + if (keyrouter_grab == NULL) { + ds_err("Failed to allocate memory."); + free(keyrouter_grab); + return NULL; + } + + for (i = 0; i < KEYROUTER_MAX_KEYS; i++) { + /* Enable all of keys to grab */ + //keyrouter_grab->hard_keys[i].keycode = i; + wl_list_init(&keyrouter_grab->hard_keys[i].grab.excl); + wl_list_init(&keyrouter_grab->hard_keys[i].grab.or_excl); + wl_list_init(&keyrouter_grab->hard_keys[i].grab.top); + wl_list_init(&keyrouter_grab->hard_keys[i].grab.shared); + wl_list_init(&keyrouter_grab->hard_keys[i].pressed); + } + + return keyrouter_grab; +} + +static void +keyrouter_grab_delete_list(struct wl_list *list) +{ + struct ds_tizen_keyrouter_key_info *info, *tmp; + + if (wl_list_empty(list)) + return; + + wl_list_for_each_safe(info, tmp, list, link) { + wl_list_remove(&info->link); + free(info); + } +} + +void +tizen_keyrouter_grab_destroy(struct ds_tizen_keyrouter_grab *keyrouter_grab) +{ + int i; + + for (i = 0; i < KEYROUTER_MAX_KEYS; i++) { + keyrouter_grab_delete_list(&keyrouter_grab->hard_keys[i].grab.excl); + keyrouter_grab_delete_list(&keyrouter_grab->hard_keys[i].grab.or_excl); + keyrouter_grab_delete_list(&keyrouter_grab->hard_keys[i].grab.top); + keyrouter_grab_delete_list(&keyrouter_grab->hard_keys[i].grab.shared); + keyrouter_grab_delete_list(&keyrouter_grab->hard_keys[i].pressed); + } + + free(keyrouter_grab->hard_keys); + free(keyrouter_grab); +} diff --git a/src/libds-tizen/keyrouter/meson.build b/src/libds-tizen/keyrouter/meson.build new file mode 100644 index 0000000..7b27d3b --- /dev/null +++ b/src/libds-tizen/keyrouter/meson.build @@ -0,0 +1,34 @@ +libds_tizen_keyrouter_files = [ + 'keyrouter_grab.c', + 'keyrouter.c', +] + +libds_tizen_keyrouter_deps = [ + dep_libds, + dep_libds_tizen, + dependency('tizen-extension-server', required: true), +] + +keylayout_dir = get_option('keylayout_dir') + +lib_libds_tizen_keyrouter = shared_library('ds-tizen-keyrouter', libds_tizen_keyrouter_files, + dependencies: libds_tizen_keyrouter_deps, + include_directories: [ common_inc, include_directories('.'), include_directories('..') ], + version: meson.project_version(), + c_args: [ '-DKEYLAYOUT_DIR="@0@"'.format(keylayout_dir) ], + install: true +) + +dep_libds_tizen_keyrouter = declare_dependency( + link_with: lib_libds_tizen_keyrouter, + dependencies: libds_tizen_keyrouter_deps, + include_directories: [ common_inc, include_directories('.') ], +) + +pkgconfig = import('pkgconfig') +pkgconfig.generate(lib_libds_tizen_keyrouter, + version: meson.project_version(), + filebase: 'libds-tizen-keyrouter', + name: 'libds-tizen-keyrouter', + description: 'tizen keyrouter extension of libds-tizen for tizen platform', +) \ No newline at end of file diff --git a/src/libds-tizen/meson.build b/src/libds-tizen/meson.build index 81370e6..0d780a3 100644 --- a/src/libds-tizen/meson.build +++ b/src/libds-tizen/meson.build @@ -34,3 +34,6 @@ pkgconfig.generate(lib_libds_tizen, name: 'libds-tizen', description: 'extension of libds for tizen platform', ) + +subdir('keyrouter') + -- 2.7.4 From 93bf9b102f97446344c3748816cd06ce0328ef17 Mon Sep 17 00:00:00 2001 From: "duna.oh" Date: Thu, 12 May 2022 15:52:31 +0900 Subject: [PATCH 03/16] backend/libinput: Support libinput backend add struct ds_libinput_backend, ds_libinput_input_device, etc. add an executable 'libinput-backend' to test libinput events Change-Id: I7134f91f14f7a1bbd65f1216c63a4c3b59cd15ee --- include/libds/backend/libinput.h | 17 ++ include/libds/pointer.h | 7 + packaging/libds.spec | 3 + src/examples/libinput-backend.c | 340 +++++++++++++++++++++++++++++++++ src/examples/meson.build | 7 + src/libds/backend/libinput/backend.c | 227 ++++++++++++++++++++++ src/libds/backend/libinput/backend.h | 71 +++++++ src/libds/backend/libinput/input.c | 210 ++++++++++++++++++++ src/libds/backend/libinput/keyboard.c | 35 ++++ src/libds/backend/libinput/meson.build | 15 ++ src/libds/backend/libinput/pointer.c | 91 +++++++++ src/libds/backend/libinput/touch.c | 98 ++++++++++ src/libds/backend/meson.build | 1 + 13 files changed, 1122 insertions(+) create mode 100644 include/libds/backend/libinput.h create mode 100644 src/examples/libinput-backend.c create mode 100644 src/libds/backend/libinput/backend.c create mode 100644 src/libds/backend/libinput/backend.h create mode 100644 src/libds/backend/libinput/input.c create mode 100644 src/libds/backend/libinput/keyboard.c create mode 100644 src/libds/backend/libinput/meson.build create mode 100644 src/libds/backend/libinput/pointer.c create mode 100644 src/libds/backend/libinput/touch.c diff --git a/include/libds/backend/libinput.h b/include/libds/backend/libinput.h new file mode 100644 index 0000000..29efb0c --- /dev/null +++ b/include/libds/backend/libinput.h @@ -0,0 +1,17 @@ +#ifndef LIBDS_BACKEND_LIBINPUT_H +#define LIBDS_BACKEND_LIBINPUT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct ds_backend * +ds_libinput_backend_create(struct wl_display *display); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libds/pointer.h b/include/libds/pointer.h index 30bfaca..99545df 100644 --- a/include/libds/pointer.h +++ b/include/libds/pointer.h @@ -15,6 +15,13 @@ struct ds_event_pointer_motion_absolute double x, y; }; +struct ds_event_pointer_motion +{ + struct ds_input_device *device; + uint32_t time_msec; + double delta_x, delta_y; +}; + struct ds_event_pointer_button { struct ds_input_device *device; diff --git a/packaging/libds.spec b/packaging/libds.spec index bee3972..5656382 100644 --- a/packaging/libds.spec +++ b/packaging/libds.spec @@ -15,6 +15,8 @@ BuildRequires: pkgconfig(tizen-extension-server) BuildRequires: pkgconfig(pixman-1) BuildRequires: pkgconfig(libdrm) BuildRequires: pkgconfig(xkbcommon) +BuildRequires: pkgconfig(libinput) +BuildRequires: pkgconfig(libudev) BuildRequires: pkgconfig(libtdm) BuildRequires: pkgconfig(libtbm) @@ -87,6 +89,7 @@ ninja -C builddir install %{_bindir}/wl-backend %{_bindir}/tinyds %{_bindir}/input-device-test +%{_bindir}/libinput-backend %files tizen-devel %manifest %{name}.manifest diff --git a/src/examples/libinput-backend.c b/src/examples/libinput-backend.c new file mode 100644 index 0000000..592fbc3 --- /dev/null +++ b/src/examples/libinput-backend.c @@ -0,0 +1,340 @@ +#include + #include + +#include +#include +#include +#include +#include +#include +#include + +#define UNUSED __attribute__((unused)) + +struct keyboard_device +{ + struct server *server; + struct ds_keyboard *ds_keyboard; + + struct wl_listener destroy; + struct wl_listener key; +}; + +struct pointer_device +{ + struct ds_pointer *ds_pointer; + + struct wl_listener destroy; + struct wl_listener motion; + struct wl_listener button; +}; + +struct touch_device +{ + struct ds_touch *ds_touch; + + struct wl_listener destroy; + struct wl_listener down; + struct wl_listener up; + struct wl_listener motion; +}; + +struct server +{ + struct wl_display *display; + + struct ds_backend *backend; + + struct wl_listener backend_destroy; + struct wl_listener new_input; +}; + +struct server _server; + +static struct ds_backend *create_backend_auto(struct wl_display *display); +static void handle_backend_destroy(struct wl_listener *listener, void *data); +static void handle_new_input(struct wl_listener *listener, void *data); + +int +main(void) +{ + struct server *server = &_server; + + ds_log_init(DS_DBG, NULL); + + server->display = wl_display_create(); + assert(server->display); + + server->backend = create_backend_auto(server->display); + assert(server->backend); + + server->backend_destroy.notify = handle_backend_destroy; + ds_backend_add_destroy_listener(server->backend, &server->backend_destroy); + + server->new_input.notify = handle_new_input; + ds_backend_add_new_input_listener(server->backend, &server->new_input); + + ds_backend_start(server->backend); + + wl_display_run(server->display); + + wl_display_destroy(server->display); + + return 0; +} + +static struct ds_backend * +create_backend_auto(struct wl_display *display) +{ + struct ds_backend *backend = NULL; + backend = ds_libinput_backend_create(display); + if (!backend) + ds_err("Failed to create libinput backend"); + + return backend; +} + +static void +handle_backend_destroy(struct wl_listener *listener, void *data) +{ + struct server *server; + + server = wl_container_of(listener, server, backend_destroy); + + wl_list_remove(&server->backend_destroy.link); + wl_list_remove(&server->new_input.link); +} + +static char * +device_type_to_string(enum ds_input_device_type type) +{ + switch (type) { + case DS_INPUT_DEVICE_POINTER: + return "pointer"; + break; + case DS_INPUT_DEVICE_KEYBOARD: + return "keyboard"; + break; + case DS_INPUT_DEVICE_TOUCH: + return "touch"; + break; + default: + return "Unknown"; + } +} + +static void +keyboard_handle_device_destroy(struct wl_listener *listener, void *data) +{ + struct keyboard_device *keyboard; + + keyboard = wl_container_of(listener, keyboard, destroy); + + wl_list_remove(&keyboard->destroy.link); + wl_list_remove(&keyboard->key.link); + + free(keyboard); +} + +static void +keyboard_handle_key(struct wl_listener *listener, void *data) +{ + struct ds_event_keyboard_key *event = data; + struct keyboard_device *keyboard; + + keyboard = wl_container_of(listener, keyboard, key); + + ds_inf("Keyboard(%p) event key: keycode(%d), state(%d), time_msec(%d), " + "update_state(%d)", keyboard->ds_keyboard, + event->keycode, event->state, event->time_msec, + event->update_state); +} + +static void +add_keyboard(struct server *server, struct ds_input_device *dev) +{ + struct keyboard_device *keyboard; + + keyboard = calloc(1, sizeof *keyboard); + if (!keyboard) + return; + + keyboard->server = server; + keyboard->ds_keyboard = ds_input_device_get_keyboard(dev); + + ds_inf("Keyboard(%p) added", keyboard->ds_keyboard); + + keyboard->destroy.notify = keyboard_handle_device_destroy; + ds_input_device_add_destroy_listener(dev, &keyboard->destroy); + + keyboard->key.notify = keyboard_handle_key; + ds_keyboard_add_key_listener(keyboard->ds_keyboard, + &keyboard->key); +} + +static void +pointer_handle_device_destroy(struct wl_listener *listener, void *data) +{ + struct pointer_device *pointer; + + pointer = wl_container_of(listener, pointer, destroy); + + wl_list_remove(&pointer->destroy.link); + wl_list_remove(&pointer->button.link); + wl_list_remove(&pointer->motion.link); + + free(pointer); +} + +static void +pointer_handle_motion(struct wl_listener *listener, void *data) +{ + struct ds_event_pointer_motion *event = data; + struct pointer_device *pointer; + + pointer = wl_container_of(listener, pointer, motion); + + ds_inf("Pointer Device(%p): motion delta_x,y(%f, %f) time(%d ms)", + pointer->ds_pointer, event->delta_x, event->delta_y, + event->time_msec); +} + +static void +pointer_handle_button(struct wl_listener *listener, void *data) +{ + struct ds_event_pointer_button *event = data; + struct pointer_device *pointer; + + pointer = wl_container_of(listener, pointer, button); + ds_inf("Pointer Device(%p): button(%d) state(%d) time(%d ms)", + pointer->ds_pointer, event->button, event->state, + event->time_msec); +} + +static void +add_pointer(struct ds_input_device *dev) +{ + struct pointer_device *pointer; + + pointer = calloc(1, sizeof *pointer); + if (!pointer) + return; + + pointer->ds_pointer = ds_input_device_get_pointer(dev); + + ds_inf("Pointer(%p) added", pointer->ds_pointer); + + pointer->destroy.notify = pointer_handle_device_destroy; + ds_input_device_add_destroy_listener(dev, &pointer->destroy); + + pointer->motion.notify = pointer_handle_motion; + ds_pointer_add_motion_listener(pointer->ds_pointer, + &pointer->motion); + + pointer->button.notify = pointer_handle_button; + ds_pointer_add_button_listener(pointer->ds_pointer, + &pointer->button); +} + +static void +touch_handle_device_destroy(struct wl_listener *listener, void *data) +{ + struct touch_device *touch; + + touch = wl_container_of(listener, touch, destroy); + + wl_list_remove(&touch->destroy.link); + wl_list_remove(&touch->down.link); + wl_list_remove(&touch->up.link); + wl_list_remove(&touch->motion.link); + + free(touch); +} + +static void +touch_handle_down(struct wl_listener *listener, void *data) +{ + struct ds_event_touch_down *event = data; + struct touch_device *touch; + + touch = wl_container_of(listener, touch, down); + + ds_inf("Touch(%p) event down: id(%d) x(%f) y(%f) time_msec(%d)", + touch->ds_touch, event->id, event->x, event->y, event->time_msec); +} + +static void +touch_handle_up(struct wl_listener *listener, void *data) +{ + struct ds_event_touch_up *event = data; + struct touch_device *touch; + + touch = wl_container_of(listener, touch, up); + + ds_inf("Touch(%p) event up: id(%d) time_msec(%d)", + touch->ds_touch, event->id, event->time_msec); +} + +static void +touch_handle_motion(struct wl_listener *listener, void *data) +{ + struct ds_event_touch_motion *event = data; + struct touch_device *touch; + + touch = wl_container_of(listener, touch, motion); + + ds_inf("Touch(%p) event motion: id(%d) x(%f) y(%f) time_msec(%d)", + touch->ds_touch, event->id, event->x, event->y, event->time_msec); +} + +static void +add_touch(struct server *server, struct ds_input_device *dev) +{ + struct touch_device *touch; + + touch = calloc(1, sizeof *touch); + if (!touch) + return; + + touch->ds_touch = ds_input_device_get_touch(dev); + + ds_inf("Touch(%p) added", touch->ds_touch); + + touch->destroy.notify = touch_handle_device_destroy; + ds_input_device_add_destroy_listener(dev, &touch->destroy); + + touch->down.notify = touch_handle_down; + ds_touch_add_down_listener(touch->ds_touch, + &touch->down); + + touch->up.notify = touch_handle_up; + ds_touch_add_up_listener(touch->ds_touch, + &touch->up); + + touch->motion.notify = touch_handle_motion; + ds_touch_add_motion_listener(touch->ds_touch, + &touch->motion); +} + +static void +handle_new_input(struct wl_listener *listener, void *data) +{ + struct server *server; + struct ds_input_device *dev = data; + enum ds_input_device_type type; + + type = ds_input_device_get_type(dev); + + ds_inf("New device(%p) type(%s)", dev, device_type_to_string(type)); + + if (type == DS_INPUT_DEVICE_POINTER) + add_pointer(dev); + else if (type == DS_INPUT_DEVICE_KEYBOARD) { + server = wl_container_of(listener, server, new_input); + add_keyboard(server, dev); + } + else if (type == DS_INPUT_DEVICE_TOUCH) { + server = wl_container_of(listener, server, new_input); + add_touch(server, dev); + } +} diff --git a/src/examples/meson.build b/src/examples/meson.build index c568eb6..9f2d976 100644 --- a/src/examples/meson.build +++ b/src/examples/meson.build @@ -58,4 +58,11 @@ if get_option('tizen') install_dir: libds_bindir, install : true ) + + executable('libinput-backend', + 'libinput-backend.c', + dependencies: common_deps, + install_dir: libds_bindir, + install : true + ) endif diff --git a/src/libds/backend/libinput/backend.c b/src/libds/backend/libinput/backend.c new file mode 100644 index 0000000..f3ee044 --- /dev/null +++ b/src/libds/backend/libinput/backend.c @@ -0,0 +1,227 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "libds/log.h" + +#include "backend.h" + +static const struct ds_backend_interface libinput_backend_interface; +static void libinput_backend_handle_display_destroy( + struct wl_listener *listener, void *data); + +WL_EXPORT struct ds_backend * +ds_libinput_backend_create(struct wl_display *display) +{ + struct ds_libinput_backend *libinput_backend; + + libinput_backend = calloc(1, sizeof *libinput_backend); + if (!libinput_backend) { + ds_log_errno(DS_ERR, "Could not allocate memory"); + return NULL; + } + + ds_backend_init(&libinput_backend->base, &libinput_backend_interface); + + libinput_backend->display = display; + wl_list_init(&libinput_backend->devices); + + libinput_backend->display_destroy.notify = + libinput_backend_handle_display_destroy; + wl_display_add_destroy_listener(display, + &libinput_backend->display_destroy); + + ds_inf("Libinput backend(%p) created", + libinput_backend); + + return &libinput_backend->base; +} + +WL_EXPORT struct ds_libinput_backend * +libinput_backend_from_backend(struct ds_backend *backend) +{ + assert(backend->iface == &libinput_backend_interface); + return (struct ds_libinput_backend *)backend; +} + +void +destroy_libinput_input_device(struct ds_libinput_input_device *dev) +{ + ds_input_device_destroy(&dev->base); + libinput_device_unref(dev->handle); + wl_list_remove(&dev->link); + free(dev); +} + +static void +libinput_backend_destroy(struct ds_libinput_backend *backend) +{ + struct ds_libinput_input_device *dev, *tmp_dev; + + wl_list_for_each_safe(dev, tmp_dev, &backend->devices, link) + destroy_libinput_input_device(dev); + + ds_backend_finish(&backend->base); + wl_list_remove(&backend->display_destroy.link); + wl_event_source_remove(backend->server_event_source); + udev_unref(backend->udev); + libinput_unref(backend->libinput_context); + + free(backend); +} + +static int +libinput_open_restricted(const char *path, int flags, void *_backend) +{ + int fd = -1; + struct stat s; + + fd = open(path, flags | O_CLOEXEC); + + if (fd < 0 || (fstat(fd, &s) == -1)) { + ds_log(DS_ERR, "Could not open device"); + close(fd); + return -1; + } + return fd; +} + +static void +libinput_close_restricted(int fd, void *_backend) +{ + if (fd >= 0) close(fd); +} + +static const struct libinput_interface libinput_impl = { + .open_restricted = libinput_open_restricted, + .close_restricted = libinput_close_restricted +}; + +static int +handle_libinput_readable(int fd, uint32_t mask, void *_backend) +{ + struct ds_libinput_backend *backend = _backend; + struct libinput_event *event; + int ret; + + ret = libinput_dispatch(backend->libinput_context); + if (ret != 0) { + ds_log(DS_ERR, "Failed to dispatch libinput: %s", strerror(-ret)); + wl_display_terminate(backend->display); + return 0; + } + + while ((event = libinput_get_event(backend->libinput_context))) { + handle_libinput_event(backend, event); + libinput_event_destroy(event); + } + return 0; +} + +static void +log_libinput(struct libinput *libinput_context, + enum libinput_log_priority priority, const char *fmt, va_list args) +{ + char buf[1024] = {0,}; + + vsnprintf(buf, 1024, fmt, args); + switch (priority) { + case LIBINPUT_LOG_PRIORITY_DEBUG: + ds_log(DS_DBG,"%s", buf); + break; + case LIBINPUT_LOG_PRIORITY_INFO: + ds_log(DS_INF, "%s", buf); + break; + case LIBINPUT_LOG_PRIORITY_ERROR: + ds_log(DS_ERR, "%s", buf); + break; + default: + break; + } +} + +static bool +libinput_backend_iface_start(struct ds_backend *ds_backend) +{ + struct ds_libinput_backend *backend; + struct wl_event_loop *event_loop; + int libinput_fd; + + backend = libinput_backend_from_backend(ds_backend); + ds_log(DS_DBG, "Starting libinput backend"); + + backend->udev = udev_new(); + backend->libinput_context = libinput_udev_create_context(&libinput_impl, + backend, backend->udev); + if (!backend->libinput_context) { + ds_log(DS_ERR, "Failed to create libinput context"); + return false; + } + + if (libinput_udev_assign_seat(backend->libinput_context, "seat0") != 0) { + ds_log(DS_ERR, "Failed to assign libinput seat"); + return false; + } + + libinput_log_set_handler(backend->libinput_context, log_libinput); + libinput_log_set_priority(backend->libinput_context, + LIBINPUT_LOG_PRIORITY_DEBUG); + + libinput_fd = libinput_get_fd(backend->libinput_context); + if (wl_list_empty(&backend->devices)) { + handle_libinput_readable(libinput_fd, WL_EVENT_READABLE, backend); + if (wl_list_empty(&backend->devices)) { + ds_log(DS_ERR, "libinput initialization failed, no input devices"); + return false; + } + } + + event_loop = wl_display_get_event_loop(backend->display); + if (backend->server_event_source) { + wl_event_source_remove(backend->server_event_source); + } + backend->server_event_source = + wl_event_loop_add_fd(event_loop, libinput_fd, WL_EVENT_READABLE, + handle_libinput_readable, backend); + if (!backend->server_event_source) { + ds_log(DS_ERR, "Failed to create input event on event loop"); + return false; + } + ds_log(DS_DBG, "libinput successfully initialized"); + + return true; +} + +static void +libinput_backend_iface_destroy(struct ds_backend *backend) +{ + struct ds_libinput_backend *libinput_backend; + + if (!backend) + return; + + libinput_backend = libinput_backend_from_backend(backend); + libinput_backend_destroy(libinput_backend); +} + +static const struct ds_backend_interface libinput_backend_interface = +{ + .start = libinput_backend_iface_start, + .destroy = libinput_backend_iface_destroy, + .get_drm_fd = NULL, +}; + +static void +libinput_backend_handle_display_destroy(struct wl_listener *listener, + void *data) +{ + struct ds_libinput_backend *libinput_backend; + + libinput_backend = + wl_container_of(listener, libinput_backend, display_destroy); + libinput_backend_destroy(libinput_backend); +} diff --git a/src/libds/backend/libinput/backend.h b/src/libds/backend/libinput/backend.h new file mode 100644 index 0000000..6fa2122 --- /dev/null +++ b/src/libds/backend/libinput/backend.h @@ -0,0 +1,71 @@ +#ifndef DS_BACKEND_LIBINPUT_H +#define DS_BACKEND_LIBINPUT_H + +#include +#include + +#include "libds/interfaces/backend.h" +#include "libds/interfaces/input_device.h" +#include "libds/interfaces/pointer.h" +#include "libds/interfaces/keyboard.h" +#include "libds/interfaces/touch.h" + +struct ds_libinput_backend +{ + struct ds_backend base; + + struct wl_display *display; + struct wl_listener display_destroy; + + struct udev *udev; + struct libinput *libinput_context; + struct wl_list devices; // ds_libinput_input_device::link + + struct wl_event_source *server_event_source; +}; + +struct ds_libinput_input_device +{ + struct ds_input_device base; + + struct ds_libinput_backend *backend; + struct libinput_device *handle; + + struct wl_list link; //ds_libinput_backend.devices +}; + +struct ds_libinput_backend * + libinput_backend_from_backend(struct ds_backend *backend); + +void handle_libinput_event(struct ds_libinput_backend *state, + struct libinput_event *event); + +void destroy_libinput_input_device(struct ds_libinput_input_device *dev); + +//keyboard +void handle_keyboard_key(struct libinput_event *event, + struct ds_keyboard *kbd); + +//pointer +void handle_pointer_motion(struct libinput_event *event, + struct ds_pointer *pointer); +void handle_pointer_motion_abs(struct libinput_event *event, + struct ds_pointer *pointer); +void handle_pointer_button(struct libinput_event *event, + struct ds_pointer *pointer); +void handle_pointer_axis(struct libinput_event *event, + struct ds_pointer *pointer); + +//touch +void handle_touch_down(struct libinput_event *event, + struct ds_touch *touch); +void handle_touch_up(struct libinput_event *event, + struct ds_touch *touch); +void handle_touch_motion(struct libinput_event *event, + struct ds_touch *touch); +void handle_touch_cancel(struct libinput_event *event, + struct ds_touch *touch); +void handle_touch_frame(struct libinput_event *event, + struct ds_touch *touch); + +#endif diff --git a/src/libds/backend/libinput/input.c b/src/libds/backend/libinput/input.c new file mode 100644 index 0000000..2457a06 --- /dev/null +++ b/src/libds/backend/libinput/input.c @@ -0,0 +1,210 @@ +#include + +#include "libds/log.h" +#include "backend.h" + +static const struct ds_input_device_interface input_device_iface; + +static bool +ds_input_device_is_libinput(struct ds_input_device *ds_dev) +{ + return ds_dev->iface == &input_device_iface; +} + +static struct ds_libinput_input_device * +get_libinput_input_device_from_input_device(struct ds_input_device *ds_dev) +{ + assert(ds_input_device_is_libinput(ds_dev)); + return (struct ds_libinput_input_device *)ds_dev; +} + +static void +input_device_iface_destroy(struct ds_input_device *ds_dev) +{ + struct ds_libinput_input_device *dev; + + dev = get_libinput_input_device_from_input_device(ds_dev); + + free(dev); +} + +static const struct ds_input_device_interface input_device_iface = +{ + .destroy = input_device_iface_destroy, +}; + +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 void +handle_device_added(struct ds_libinput_backend *backend, + struct libinput_device *libinput_dev) +{ + int vendor, product; + const char *name; + struct ds_libinput_input_device *dev; + + vendor = libinput_device_get_id_vendor(libinput_dev); + product = libinput_device_get_id_product(libinput_dev); + name = libinput_device_get_name(libinput_dev); + ds_log(DS_DBG, "Adding %s [%d:%d]", name, vendor, product); + + dev = calloc(1, sizeof(struct ds_libinput_input_device)); + if (dev == NULL) { + ds_log(DS_ERR, "failed to allocate ds_libinput_input_device"); + return; + } + + dev->handle = libinput_dev; + dev->backend = backend; + libinput_device_ref(libinput_dev); + libinput_device_set_user_data(libinput_dev, dev); + + wl_list_insert(&backend->devices, &dev->link); + + if (libinput_device_has_capability( + libinput_dev, LIBINPUT_DEVICE_CAP_KEYBOARD)) { + ds_input_device_init(&dev->base, DS_INPUT_DEVICE_KEYBOARD, + &input_device_iface, name, vendor, product); + dev->base.keyboard = create_ds_keyboard(); + + wl_signal_emit(&backend->base.events.new_input, + &dev->base); + } + + if (libinput_device_has_capability( + libinput_dev, LIBINPUT_DEVICE_CAP_POINTER)) { + ds_input_device_init(&dev->base, DS_INPUT_DEVICE_POINTER, + &input_device_iface, name, vendor, product); + dev->base.pointer = create_ds_pointer(); + + wl_signal_emit(&backend->base.events.new_input, + &dev->base); + } + + if (libinput_device_has_capability( + libinput_dev, LIBINPUT_DEVICE_CAP_TOUCH)) { + ds_input_device_init(&dev->base, DS_INPUT_DEVICE_TOUCH, + &input_device_iface, name, vendor, product); + dev->base.touch = create_ds_touch(); + + wl_signal_emit(&backend->base.events.new_input, + &dev->base); + } +} + +static void +handle_device_removed(struct ds_libinput_backend *backend, + struct libinput_device *libinput_dev) +{ + int vendor, product; + const char *name; + struct ds_libinput_input_device *dev; + + vendor = libinput_device_get_id_vendor(libinput_dev); + product = libinput_device_get_id_product(libinput_dev); + name = libinput_device_get_name(libinput_dev); + ds_log(DS_DBG, "Removing %s [%d:%d]", name, vendor, product); + + dev = libinput_device_get_user_data(libinput_dev); + if (dev == NULL) { + ds_log(DS_ERR, "libinput_device has no ds_libinput_input_device"); + return; + } + + destroy_libinput_input_device(dev); +} + +void +handle_libinput_event(struct ds_libinput_backend *backend, + struct libinput_event *event) +{ + struct libinput_device *libinput_dev; + struct ds_libinput_input_device *dev; + enum libinput_event_type event_type; + + libinput_dev = libinput_event_get_device(event); + event_type = libinput_event_get_type(event); + dev = libinput_device_get_user_data(libinput_dev); + + switch (event_type) { + case LIBINPUT_EVENT_DEVICE_ADDED: + handle_device_added(backend, libinput_dev); + break; + case LIBINPUT_EVENT_DEVICE_REMOVED: + handle_device_removed(backend, libinput_dev); + break; + case LIBINPUT_EVENT_KEYBOARD_KEY: + handle_keyboard_key(event, dev->base.keyboard); + break; + case LIBINPUT_EVENT_POINTER_MOTION: + handle_pointer_motion(event, dev->base.pointer); + break; + case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: + handle_pointer_motion_abs(event, dev->base.pointer); + break; + case LIBINPUT_EVENT_POINTER_BUTTON: + handle_pointer_button(event, dev->base.pointer); + break; + case LIBINPUT_EVENT_POINTER_AXIS: + handle_pointer_axis(event, dev->base.pointer); + break; + case LIBINPUT_EVENT_TOUCH_DOWN: + handle_touch_down(event, dev->base.touch); + break; + case LIBINPUT_EVENT_TOUCH_UP: + handle_touch_up(event, dev->base.touch); + break; + case LIBINPUT_EVENT_TOUCH_MOTION: + handle_touch_motion(event, dev->base.touch); + break; + case LIBINPUT_EVENT_TOUCH_CANCEL: + handle_touch_cancel(event, dev->base.touch); + break; + case LIBINPUT_EVENT_TOUCH_FRAME: + handle_touch_frame(event, dev->base.touch); + break; + default: + ds_log(DS_DBG, "Unknown libinput event %d", event_type); + break; + } +} diff --git a/src/libds/backend/libinput/keyboard.c b/src/libds/backend/libinput/keyboard.c new file mode 100644 index 0000000..53db0c9 --- /dev/null +++ b/src/libds/backend/libinput/keyboard.c @@ -0,0 +1,35 @@ +#include "libds/log.h" +#include "backend.h" + +void +handle_keyboard_key(struct libinput_event *event, + struct ds_keyboard *kbd) +{ + struct libinput_event_keyboard *kbevent; + uint32_t key; + enum libinput_key_state li_state; + enum wl_keyboard_key_state state = WL_KEYBOARD_KEY_STATE_PRESSED; + + kbevent = libinput_event_get_keyboard_event(event); + + key = libinput_event_keyboard_get_key(kbevent); + li_state = libinput_event_keyboard_get_key_state(kbevent); + if (li_state == LIBINPUT_KEY_STATE_PRESSED) { + state = WL_KEYBOARD_KEY_STATE_PRESSED; + } + else { + state = WL_KEYBOARD_KEY_STATE_RELEASED; + } + + ds_log(DS_DBG, "Keyboard(%p) key event key:%d, state:%s", kbd, key, + (state == WL_KEYBOARD_KEY_STATE_PRESSED) ? "PRESSED":"RELEASED"); + + struct ds_event_keyboard_key ds_event = { + .keycode = key, + .state = state, + .time_msec = -1, + .update_state = false, + }; + + ds_keyboard_notify_key(kbd, &ds_event); +} diff --git a/src/libds/backend/libinput/meson.build b/src/libds/backend/libinput/meson.build new file mode 100644 index 0000000..be85808 --- /dev/null +++ b/src/libds/backend/libinput/meson.build @@ -0,0 +1,15 @@ +libds_files += files( + 'backend.c', + 'input.c', + 'keyboard.c', + 'pointer.c', + 'touch.c', +) + +libinput = dependency('libinput', required: true) +libudev = dependency('libudev', required: true) + +libds_deps += [ + libinput, + libudev, +] diff --git a/src/libds/backend/libinput/pointer.c b/src/libds/backend/libinput/pointer.c new file mode 100644 index 0000000..c118d06 --- /dev/null +++ b/src/libds/backend/libinput/pointer.c @@ -0,0 +1,91 @@ +#include "libds/log.h" +#include "backend.h" + +void +handle_pointer_motion(struct libinput_event *event, + struct ds_pointer *pointer) +{ + struct libinput_event_pointer *pevent; + double delta_x, delta_y; + struct ds_event_pointer_motion ds_event = { 0 }; + + pevent = libinput_event_get_pointer_event(event); + + delta_x = libinput_event_pointer_get_dx(pevent); + delta_y = libinput_event_pointer_get_dy(pevent); + + ds_log(DS_DBG, "pointer motion event delta_x:%f, delta_y:%f", delta_x, delta_y); + + ds_event.device = NULL; + ds_event.time_msec = -1; + ds_event.delta_x = delta_x; + ds_event.delta_y = delta_y; + + wl_signal_emit(&pointer->events.motion, &ds_event); +} + +void +handle_pointer_motion_abs(struct libinput_event *event, + struct ds_pointer *pointer) +{ + struct libinput_event_pointer *pevent; + double x, y; + struct ds_event_pointer_motion_absolute ds_event = { 0 }; + + pevent = libinput_event_get_pointer_event(event); + + x = libinput_event_pointer_get_absolute_x_transformed(pevent, 1); + y = libinput_event_pointer_get_absolute_y_transformed(pevent, 1); + + ds_log(DS_DBG, "Pointer(%p) motion abs event x:%f, y:%f", pointer, x, y); + + ds_event.device = NULL; + ds_event.time_msec = -1; + ds_event.x = x; + ds_event.y = y; + + wl_signal_emit(&pointer->events.motion_absolute, &ds_event); +} +void +handle_pointer_button(struct libinput_event *event, + struct ds_pointer *pointer) +{ + struct libinput_event_pointer *pevent; + uint32_t button; + enum libinput_button_state li_state = LIBINPUT_BUTTON_STATE_PRESSED; + enum ds_button_state state = DS_BUTTON_PRESSED; + struct ds_event_pointer_button ds_event = { 0 }; + + pevent = libinput_event_get_pointer_event(event); + + button = libinput_event_pointer_get_button(pevent); + button = ((button & 0x00F) + 1); + if (button == 3) button = 2; + else if (button == 2) button = 3; + + li_state = libinput_event_pointer_get_button_state(pevent); + if (li_state == LIBINPUT_BUTTON_STATE_PRESSED) { + state = DS_BUTTON_PRESSED; + } + else { + state = DS_BUTTON_RELEASED; + } + ds_log(DS_DBG, "Pointer(%p) button event button:%d state:%s", + pointer, button, + (state == DS_BUTTON_PRESSED) ? "PRESSED" : "RELEASED"); + + ds_event.device = NULL; + ds_event.time_msec = -1; + ds_event.button = button; + ds_event.state = state; + + wl_signal_emit(&pointer->events.button, &ds_event); +} + +void +handle_pointer_axis(struct libinput_event *event, + struct ds_pointer *pointer) +{ + ds_log(DS_DBG, "pointer(%p) axis event", pointer); + /* TODO: wl_signal_emit() */ +} diff --git a/src/libds/backend/libinput/touch.c b/src/libds/backend/libinput/touch.c new file mode 100644 index 0000000..08b7b80 --- /dev/null +++ b/src/libds/backend/libinput/touch.c @@ -0,0 +1,98 @@ +#include "libds/log.h" +#include "backend.h" + +void +handle_touch_down(struct libinput_event *event, + struct ds_touch *touch) +{ + struct libinput_event_touch *tevent; + uint32_t touch_id; + double x, y; + struct ds_event_touch_down ds_event = { 0 }; + + tevent = libinput_event_get_touch_event(event); + + touch_id = libinput_event_touch_get_seat_slot(tevent); + x = libinput_event_touch_get_x_transformed(tevent, 1); + y = libinput_event_touch_get_y_transformed(tevent, 1); + + ds_log(DS_DBG, "touch down event id:%d, x:%f, y:%f", touch_id, x, y); + + ds_event.device = NULL; + ds_event.time_msec = -1; + ds_event.id = touch_id; + ds_event.x = x; + ds_event.y = y; + + wl_signal_emit(&touch->events.down, &ds_event); +} + +void +handle_touch_up(struct libinput_event *event, + struct ds_touch *touch) +{ + struct libinput_event_touch *tevent; + uint32_t touch_id; + struct ds_event_touch_up ds_event = { 0 }; + + tevent = libinput_event_get_touch_event(event); + + touch_id = libinput_event_touch_get_seat_slot(tevent); + + ds_log(DS_DBG, "touch up event id:%d", touch_id); + + ds_event.device = NULL; + ds_event.time_msec = -1; + ds_event.id = touch_id; + + wl_signal_emit(&touch->events.up, &ds_event); +} + +void +handle_touch_motion(struct libinput_event *event, + struct ds_touch *touch) +{ + struct libinput_event_touch *tevent; + uint32_t touch_id; + double x, y; + struct ds_event_touch_motion ds_event = { 0 }; + + tevent = libinput_event_get_touch_event(event); + + touch_id = libinput_event_touch_get_seat_slot(tevent); + x = libinput_event_touch_get_x_transformed(tevent, 1); + y = libinput_event_touch_get_y_transformed(tevent, 1); + + ds_log(DS_DBG, "touch motion event id:%d, x:%f, y:%f", touch_id, x, y); + + ds_event.device = NULL; + ds_event.time_msec = -1; + ds_event.id = touch_id; + ds_event.x = x; + ds_event.y = y; + + wl_signal_emit(&touch->events.motion, &ds_event); +} + +void +handle_touch_cancel(struct libinput_event *event, + struct ds_touch *touch) +{ + struct libinput_event_touch *tevent; + uint32_t touch_id; + + tevent = libinput_event_get_touch_event(event); + + touch_id = libinput_event_touch_get_seat_slot(tevent); + + ds_log(DS_DBG, "touch cancel event id:%d", touch_id); + /* TODO: wl_signal_emit() */ +} + +void +handle_touch_frame(struct libinput_event *event, + struct ds_touch *touch) +{ + ds_log(DS_DBG, "touch frame event"); + /* TODO: wl_signal_emit() */ +} diff --git a/src/libds/backend/meson.build b/src/libds/backend/meson.build index 895ffca..97e8087 100644 --- a/src/libds/backend/meson.build +++ b/src/libds/backend/meson.build @@ -1 +1,2 @@ subdir('wayland') +subdir('libinput') -- 2.7.4 From 3cebcc6d2ee3536c4c076959a7703e9866f891ac Mon Sep 17 00:00:00 2001 From: Junkyeong Kim Date: Wed, 11 May 2022 16:30:21 +0900 Subject: [PATCH 04/16] ds_dpms: Init ds_dpms Ds_dpms is wayland-server implemantation of tizen_dpms protocol. Change-Id: I774a743de26bd8c516ae0991daf28c3e20f50ec5 Signed-off-by: Junkyeong Kim --- include/libds-tizen/dpms.h | 63 ++++++++++++++ packaging/libds.spec | 1 + src/libds-tizen/dpms.c | 203 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 267 insertions(+) create mode 100644 include/libds-tizen/dpms.h create mode 100644 src/libds-tizen/dpms.c diff --git a/include/libds-tizen/dpms.h b/include/libds-tizen/dpms.h new file mode 100644 index 0000000..3293ae8 --- /dev/null +++ b/include/libds-tizen/dpms.h @@ -0,0 +1,63 @@ +#ifndef LIBDS_DPMS_H +#define LIBDS_DPMS_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct ds_tizen_dpms; + +enum ds_tizen_dpms_mode +{ + DS_TIZEN_DPMS_MODE_ON = 0, + DS_TIZEN_DPMS_MODE_STANDBY = 1, + DS_TIZEN_DPMS_MODE_SUSPEND = 2, + DS_TIZEN_DPMS_MODE_OFF = 3, +}; + +enum ds_tizen_dpms_error +{ + DS_TIZEN_DPMS_ERROR_NONE = 0, + DS_TIZEN_DPMS_ERROR_INVALID_PERMISSION = 1, + DS_TIZEN_DPMS_ERROR_INVALID_PARAMETER = 2, + DS_TIZEN_DPMS_ERROR_NOT_SUPPORTED = 3, + DS_TIZEN_DPMS_ERROR_ALREADY_DONE = 4, +}; + +struct ds_tizen_dpms_event +{ + //struct ds_output *output; + ds_tizen_dpms_mode mode; +}; + +struct ds_tizen_dpms * +ds_tizen_dpms_create(struct wl_display *display); + +void +ds_tizen_dpms_add_destroy_listener(struct ds_tizen_dpms *dpms, + struct wl_listener *listener); + +void +ds_tizen_dpms_add_set_dpms_listener(struct ds_tizen_dpms *dpms, + struct wl_listener *listener); + +void +ds_tizen_dpms_add_get_dpms_listener(struct ds_tizen_dpms *dpms, + struct wl_listener *listener); + +void +ds_tizen_dpms_send_set_result(struct ds_tizen_dpms *dpms, + enum ds_tizen_dpms_mode mode, enum ds_tizen_dpms_error error); + +void +ds_tizen_dpms_send_get_result(struct ds_tizen_dpms *dpms, + enum ds_tizen_dpms_mode mode, enum ds_tizen_dpms_error error); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/packaging/libds.spec b/packaging/libds.spec index 5656382..45329fe 100644 --- a/packaging/libds.spec +++ b/packaging/libds.spec @@ -22,6 +22,7 @@ BuildRequires: pkgconfig(libtdm) BuildRequires: pkgconfig(libtbm) BuildRequires: pkgconfig(wayland-tbm-server) BuildRequires: pkgconfig(wayland-tbm-client) +BuildRequires: pkgconfig(tizen-dpms-server) %description Wayland Compositor Library diff --git a/src/libds-tizen/dpms.c b/src/libds-tizen/dpms.c new file mode 100644 index 0000000..c5bcabb --- /dev/null +++ b/src/libds-tizen/dpms.c @@ -0,0 +1,203 @@ +#include +#include +#include +#include +#include "libds/log.h" +#include "libds-tizen/dpms.h" +#include "libds/output.h" +#include "tizen-dpms-server-protocol.h" + +struct ds_tizen_dpms +{ + struct wl_global *global; + struct wl_resource *res; + + struct wl_listener destroy; + + struct { + struct wl_signal destroy; + struct wl_signal set_dpms; + struct wl_signal get_dpms; + } events; + + bool binded; +}; + +#define TIZEN_DPMS_VERSION 1 + +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); + +WL_EXPORT struct ds_tizen_dpms * +ds_tizen_dpms_create(struct wl_display *display) +{ + struct ds_tizen_dpms *dpms; + + dpms = calloc(1, sizeof *dpms); + if (!dpms) { + ds_err("dpms create fail : memory alloc failed"); + return NULL; + } + + dpms->global = wl_global_create(display, &tizen_dpms_manager_interface, + 1, dpms, dpms_bind); + if (!dpms->global) { + ds_err("global create fail : tizen_dpms_manager_interface failed"); + free(dpms); + return NULL; + } + + wl_signal_init(&dpms->events.destroy); + wl_signal_init(&dpms->events.set_dpms); + wl_signal_init(&dpms->events.get_dpms); + + dpms->display_destroy.notify = dpms_handle_display_destroy; + wl_display_add_destroy_listener(display, &dpms->display_destroy); + + ds_inf("global create : tizen_dpms_manager(%p)", dpms); + + return dpms; +} + +WL_EXPORT void +ds_tizen_dpms_add_destroy_listener(struct ds_tizen_dpms *dpms, + struct wl_listener *listener) +{ + wl_signal_add(&dpms->events.destroy, listener); +} + +WL_EXPORT void +ds_tizen_dpms_add_set_dpms_listener(struct ds_tizen_dpms *dpms, + struct wl_listener *listener) +{ + wl_signal_add(&dpms->events.set_dpms, listener); +} + +WL_EXPORT void +ds_tizen_dpms_add_get_dpms_listener(struct ds_tizen_dpms *dpms, + struct wl_listener *listener) +{ + wl_signal_add(&dpms->events.get_dpms, listener); +} + +WL_EXPORT void +ds_tizen_dpms_send_set_result(struct ds_tizen_dpms *dpms, + enum ds_tizen_dpms_mode mode, enum ds_tizen_dpms_error error) +{ + ds_dbg("dpms send set result : mode(%d), error(%d)", mode, error); + tizen_dpms_manager_send_set_state(dpms->res, mode, error); +} + +WL_EXPORT void +ds_tizen_dpms_send_get_result(struct ds_tizen_dpms *dpms, + enum ds_tizen_dpms_mode mode, enum ds_tizen_dpms_error error) +{ + ds_dbg("dpms send get result : mode(%d), error(%d)", mode, error); + tizen_dpms_manager_send_get_state(dpms->res, mode, error); +} + +static void +dpms_handle_display_destroy(struct wl_listener *listener, void *data) +{ + struct ds_tizen_dpms *dpms; + + dpms = wl_container_of(listener, dpms, display_destroy); + + ds_inf("global destroy : tizen_dpms_manager(%p)", dpms); + + wl_signal_emit(&dpms->events.destroy, dpms); + wl_list_remove(&dpms->display_destroy.link); + wl_resource_set_user_data(dpms->res, NULL); + wl_global_destroy(dpms->global); + free(dpms); +} + +static void +_tizen_dpms_manager_handle_destroy(struct wl_client *client, + struct wl_resource *resource) +{ + ds_inf("tizen_dpms_manager cb_destroy (res:%p)", resource); + wl_resource_destroy(resource); +} + +static void +_tizen_dpms_manager_handle_set_dpms(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *output, uint32_t mode) +{ + struct ds_tizen_dpms *dpms; + + dpms = wl_resource_get_user_data(resource); + + if (mode > DS_TIZEN_DPMS_MODE_OFF) { + ds_err("set dpms error : not supported mode(%d)", mode); + tizen_dpms_manager_send_set_state(resource, E_DPMS_MODE_OFF, + E_DPMS_MANAGER_ERROR_INVALID_PARAMETER); + return; + } + + struct ds_tizen_dpms_event event = { + .mode = mode, + }; + + wl_signal_emit(&dpms->events.set_dpms, &event); +} + +static void +_tizen_dpms_manager_handle_get_dpms(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *output) +{ + struct ds_tizen_dpms *dpms; + + dpms = wl_resource_get_user_data(resource); + + wl_signal_emit(&dpms->events.get_dpms, NULL); +} + +static const struct tizen_dpms_manager_interface dpms_impl = +{ + _tizen_dpms_manager_handle_destroy, + _tizen_dpms_manager_handle_set_dpms, + _tizen_dpms_manager_handle_get_dpms, +}; + +static void +_tizen_dpms_client_cb_destroy(struct wl_resource *resource) +{ + struct ds_tizen_dpms *dpms; + + ds_inf("tizen_dpms_client_cb_destroy (res:%p)", resource); + + dpms = wl_resource_get_user_data(resource); + if (dpms) { + dpms->binded = false; + dpms->res = NULL; + } +} + +static void +dpms_bind(struct wl_client *client, void *data, uint32_t version, + uint32_t id) +{ + struct ds_tizen_dpms *dpms = data; + + if (dpms->binded == true) { + //support only one client. + ds_err("dpms bind error : already binded."); + return; + } + + dpms->res = wl_resource_create(client, &tizen_dpms_manager_interface, + MIN(version, TIZEN_DPMS_VERSION), id); + if (dpms->res == NULL) { + ds_err("dpms bind error : wl_resource_create failed."); + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(dpms->res, &dpms_impl, dpms, + _tizen_dpms_client_cb_destroy); + + dpms->binded = true; +} -- 2.7.4 From 7a6443887bc2bc35bc7f0e7187101a209e79c553 Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Wed, 20 Apr 2022 10:11:12 +0900 Subject: [PATCH 05/16] Add ds_seat ds_seat abstracts wl_seat protocol of server implementation. Change-Id: Ic3dcf2c9c374af762c2fbeccbe0f2174b9b1be3d --- include/libds/interfaces/keyboard.h | 8 - include/libds/keyboard.h | 8 + include/libds/seat.h | 117 ++++++++ src/libds/meson.build | 8 + src/libds/seat.h | 135 ++++++++++ src/libds/seat/seat.c | 390 +++++++++++++++++++++++++++ src/libds/seat/seat_keyboard.c | 383 +++++++++++++++++++++++++++ src/libds/seat/seat_pointer.c | 512 ++++++++++++++++++++++++++++++++++++ src/libds/seat/seat_private.h | 172 ++++++++++++ src/libds/seat/seat_touch.c | 432 ++++++++++++++++++++++++++++++ src/libds/surface.h | 3 + src/libds/surface/surface.c | 6 + 12 files changed, 2166 insertions(+), 8 deletions(-) create mode 100644 include/libds/seat.h create mode 100644 src/libds/seat.h create mode 100644 src/libds/seat/seat.c create mode 100644 src/libds/seat/seat_keyboard.c create mode 100644 src/libds/seat/seat_pointer.c create mode 100644 src/libds/seat/seat_private.h create mode 100644 src/libds/seat/seat_touch.c diff --git a/include/libds/interfaces/keyboard.h b/include/libds/interfaces/keyboard.h index 3602fe5..85a20a5 100644 --- a/include/libds/interfaces/keyboard.h +++ b/include/libds/interfaces/keyboard.h @@ -23,14 +23,6 @@ struct ds_keyboard_interface void (*destroy)(struct ds_keyboard *keyboard); }; -struct ds_keyboard_modifiers -{ - xkb_mod_mask_t depressed; - xkb_mod_mask_t latched; - xkb_mod_mask_t locked; - xkb_mod_mask_t group; -}; - struct ds_keyboard { const struct ds_keyboard_interface *iface; diff --git a/include/libds/keyboard.h b/include/libds/keyboard.h index 2a515e3..80165e6 100644 --- a/include/libds/keyboard.h +++ b/include/libds/keyboard.h @@ -22,6 +22,14 @@ enum ds_keyboard_modifier { DS_MODIFIER_MOD5 = 1 << 7, }; +struct ds_keyboard_modifiers +{ + xkb_mod_mask_t depressed; + xkb_mod_mask_t latched; + xkb_mod_mask_t locked; + xkb_mod_mask_t group; +}; + struct ds_event_keyboard_key { uint32_t time_msec; diff --git a/include/libds/seat.h b/include/libds/seat.h new file mode 100644 index 0000000..dbae0ea --- /dev/null +++ b/include/libds/seat.h @@ -0,0 +1,117 @@ +#ifndef LIBDS_SEAT_H +#define LIBDS_SEAT_H + +#include +#include + +#include +#include +#include + +struct ds_seat; + +enum ds_axis_orientation +{ + DS_AXIS_ORIENTATION_VERTICAL, + DS_AXIS_ORIENTATION_HORIZONTAL, +}; + +enum ds_axis_source +{ + DS_AXIS_SOURCE_WHEEL, + DS_AXIS_SOURCE_FINGER, + DS_AXIS_SOURCE_CONTINUOUS, + DS_AXIS_SOURCE_WHEEL_TILT, +}; + +struct ds_event_seat_pointer_focus_change +{ + struct ds_seat *seat; + struct ds_surface *old_surface, *new_surface; + double sx, sy; +}; + +struct ds_event_seat_keyboard_focus_change +{ + struct ds_seat *seat; + struct ds_surface *old_surface, *new_surface; +}; + +struct ds_seat *ds_seat_create(struct wl_display *display, const char *name); + +void ds_seat_destroy(struct ds_seat *seat); + +void ds_seat_set_capabilities(struct ds_seat *seat, + enum wl_seat_capability capabilities); + +void ds_seat_set_name(struct ds_seat *seat, const char *name); + +void ds_seat_add_destroy_listener(struct ds_seat *seat, + struct wl_listener *listener); + +void ds_seat_pointer_notify_enter(struct ds_seat *seat, + struct ds_surface *surface, double sx, double sy); + +void ds_seat_pointer_notify_clear_focus(struct ds_seat *seat); + +void ds_seat_pointer_notify_motion(struct ds_seat *seat, uint32_t time_msec, + double sx, double sy); + +uint32_t ds_seat_pointer_notify_button(struct ds_seat *seat, + uint32_t time_msec, uint32_t button, enum ds_button_state state); + +void ds_seat_pointer_notify_axis(struct ds_seat *seat, uint32_t time_msec, + enum ds_axis_orientation orientation, double value, + int32_t value_discrete, enum ds_axis_source source); + +void ds_seat_pointer_notify_frame(struct ds_seat *seat); + +void ds_seat_pointer_add_grab_begin_listener(struct ds_seat *seat, + struct wl_listener *listener); + +void ds_seat_pointer_add_grab_end_listener(struct ds_seat *seat, + struct wl_listener *listener); + +void ds_seat_pointer_add_focus_change_listener(struct ds_seat *seat, + struct wl_listener *listener); + +void ds_seat_keyboard_notify_enter(struct ds_seat *seat, + struct ds_surface *surface, uint32_t keycodes[], size_t num_keycodes, + struct ds_keyboard_modifiers *modifiers); + +void ds_seat_keyboard_notify_clear_focus(struct ds_seat *seat); + +void ds_seat_keyboard_notify_modifiers(struct ds_seat *seat, + struct ds_keyboard_modifiers *modifiers); + +void ds_seat_keyboard_notify_key(struct ds_seat *seat, uint32_t time_msec, + uint32_t key, uint32_t state); + +void ds_seat_keyboard_add_grab_begin_listener(struct ds_seat *seat, + struct wl_listener *listener); + +void ds_seat_keyboard_add_grab_end_listener(struct ds_seat *seat, + struct wl_listener *listener); + +void ds_seat_keyboard_add_focus_change_listener(struct ds_seat *seat, + struct wl_listener *listener); + +void ds_seat_touch_end_grab_start_listener(struct ds_seat *seat, + struct wl_listener *listener); + +uint32_t ds_seat_touch_notify_down(struct ds_seat *seat, + struct ds_surface *surface, uint32_t time_msec, int32_t touch_id, + double sx, double sy); + +void ds_seat_touch_notify_up(struct ds_seat *seat, uint32_t time_msec, + int32_t touch_id); + +void ds_seat_touch_notify_motion(struct ds_seat *seat, uint32_t time_msec, + int32_t touch_id, double sx, double sy); + +void ds_seat_touch_notify_frame(struct ds_seat *seat); + +void ds_seat_touch_add_grab_start_listener(struct ds_seat *seat, + struct wl_listener *listener); + +#endif diff --git a/src/libds/meson.build b/src/libds/meson.build index be07132..43bdeae 100644 --- a/src/libds/meson.build +++ b/src/libds/meson.build @@ -22,6 +22,10 @@ libds_files = [ 'pointer.c', 'keyboard.c', 'touch.c', + 'seat/seat.c', + 'seat/seat_pointer.c', + 'seat/seat_keyboard.c', + 'seat/seat_touch.c', ] protocols = { @@ -57,6 +61,10 @@ libdrm = dependency('libdrm', required: true) xkbcommon = dependency('xkbcommon', required: true) rt = meson.get_compiler('c').find_library('rt') +if wayland_server.version().version_compare('>= 1.19') + cdata.set('HAVE_WL_SEAT_ERROR_MISSING_CAPABILITY', '1') +endif + libds_deps = [ math, wayland_server, diff --git a/src/libds/seat.h b/src/libds/seat.h new file mode 100644 index 0000000..6295647 --- /dev/null +++ b/src/libds/seat.h @@ -0,0 +1,135 @@ +#ifndef DS_SEAT_H +#define DS_SEAT_H + +#include "libds/keyboard.h" +#include "libds/seat.h" + +struct ds_seat_pointer_grab; + +struct ds_pointer_grab_interface +{ + void (*enter)(struct ds_seat_pointer_grab *grab, + struct ds_surface *surface, double sx, double sy); + void (*clear_focus)(struct ds_seat_pointer_grab *grab); + void (*motion)(struct ds_seat_pointer_grab *grab, uint32_t time_msec, + double sx, double sy); + uint32_t (*button)(struct ds_seat_pointer_grab *grab, uint32_t time_msec, + uint32_t button, enum ds_button_state state); + void (*axis)(struct ds_seat_pointer_grab *grab, uint32_t time_msec, + enum ds_axis_orientation orientation, double value, + int32_t value_discrete, enum ds_axis_source source); + void (*frame)(struct ds_seat_pointer_grab *grab); + void (*cancel)(struct ds_seat_pointer_grab *grab); +}; + +struct ds_seat_keyboard_grab; + +struct ds_keyboard_grab_interface +{ + void (*enter)(struct ds_seat_keyboard_grab *grab, + struct ds_surface *surface, uint32_t keycodes[], + size_t num_keycodes, struct ds_keyboard_modifiers *modifiers); + void (*clear_focus)(struct ds_seat_keyboard_grab *grab); + void (*key)(struct ds_seat_keyboard_grab *grab, uint32_t time_msec, + uint32_t key, uint32_t state); + void (*modifiers)(struct ds_seat_keyboard_grab *grab, + struct ds_keyboard_modifiers *modifiers); + void (*cancel)(struct ds_seat_keyboard_grab *grab); +}; + +struct ds_touch_point; + +struct ds_seat_touch_grab; + +struct ds_touch_grab_interface +{ + uint32_t (*down)(struct ds_seat_touch_grab *grab, uint32_t time_msec, + struct ds_touch_point *point); + void (*up)(struct ds_seat_touch_grab *grab, uint32_t time_msec, + struct ds_touch_point *point); + void (*motion)(struct ds_seat_touch_grab *grab, uint32_t time_msec, + struct ds_touch_point *point); + void (*enter)(struct ds_seat_touch_grab *grab, uint32_t time_msec, + struct ds_touch_point *point); + void (*frame)(struct ds_seat_touch_grab *grab); + void (*cancel)(struct ds_seat_touch_grab *grab); +}; + +struct ds_seat_pointer_grab +{ + const struct ds_pointer_grab_interface *iface; + struct ds_seat *seat; + void *data; +}; + +struct ds_seat_keyboard_grab +{ + const struct ds_keyboard_grab_interface *iface; + struct ds_seat *seat; + void *data; +}; + +struct ds_seat_touch_grab +{ + const struct ds_touch_grab_interface *iface; + struct ds_seat *seat; + void *data; +}; + +void ds_seat_pointer_start_grab(struct ds_seat *seat, + struct ds_seat_pointer_grab *grab); + +void ds_seat_pointer_end_grab(struct ds_seat *seat); + +void ds_seat_pointer_enter(struct ds_seat *seat, struct ds_surface *surface, + double sx, double sy); + +void ds_seat_pointer_clear_focus(struct ds_seat *seat); + +void ds_seat_pointer_send_motion(struct ds_seat *seat, uint32_t time_msec, + double sx, double sy); + +uint32_t ds_seat_pointer_send_button(struct ds_seat *seat, uint32_t time_msec, + uint32_t button, enum ds_button_state state); + +void ds_seat_pointer_send_axis(struct ds_seat *seat, uint32_t time_msec, + enum ds_axis_orientation orientation, double value, + int32_t value_discrete, enum ds_axis_source source); + +void ds_seat_pointer_send_frame(struct ds_seat *seat); + +void ds_seat_keyboard_start_grab(struct ds_seat *seat, + struct ds_seat_keyboard_grab *grab); + +void ds_seat_keyboard_end_grab(struct ds_seat *seat); + +void ds_seat_keyboard_enter(struct ds_seat *seat, struct ds_surface *surface, + uint32_t keycodes[], size_t num_keycodes, + struct ds_keyboard_modifiers *modifiers); + +void ds_seat_keyboard_clear_focus(struct ds_seat *seat); + +void ds_seat_keyboard_send_key(struct ds_seat *seat, uint32_t time_msec, + uint32_t key, uint32_t state); + +void ds_seat_keyboard_send_modifiers(struct ds_seat *seat, + struct ds_keyboard_modifiers *modifiers); + +void ds_seat_touch_start_grab(struct ds_seat *seat, + struct ds_seat_touch_grab *grab); + +void ds_seat_touch_end_grab(struct ds_seat *seat); + +uint32_t ds_seat_touch_send_down(struct ds_seat *seat, + struct ds_surface *surface, uint32_t time_msec, int32_t touch_id, + double sx, double sy); + +void ds_seat_touch_send_up(struct ds_seat *seat, uint32_t time_msec, + int32_t touch_id); + +void ds_seat_touch_send_motion(struct ds_seat *seat, uint32_t time_msec, + int32_t touch_id, double sx, double sy); + +void ds_seat_touch_send_frame(struct ds_seat *seat); + +#endif diff --git a/src/libds/seat/seat.c b/src/libds/seat/seat.c new file mode 100644 index 0000000..542186e --- /dev/null +++ b/src/libds/seat/seat.c @@ -0,0 +1,390 @@ +#include "config.h" + +#define _POSIX_C_SOURCE 200809L +#include +#include +#include + +#include "libds/log.h" +#include "seat_private.h" + +#define SEAT_VERSION 7 + +static void seat_handle_bind(struct wl_client *wl_client, void *data, + uint32_t version, uint32_t id); +static void seat_handle_display_destroy(struct wl_listener *listener, + void *data); +static void seat_destroy(struct ds_seat *seat); +static struct ds_seat_client *seat_client_create(struct ds_seat *seat, + struct wl_client *wl_client); +static void seat_client_destroy(struct ds_seat_client *seat_client); +static void +seat_client_send_capabilities(struct ds_seat_client *seat_client); +static void seat_client_send_name(struct ds_seat_client *seat_client); + +WL_EXPORT struct ds_seat * +ds_seat_create(struct wl_display *display, const char *name) +{ + struct ds_seat *seat; + + seat = calloc(1, sizeof *seat); + if (!seat) + return NULL; + + if (!seat_pointer_init(seat)) { + ds_err("Failed to initialize pointer for seat(%s)", name); + goto err_ptr; + } + + if (!seat_keyboard_init(seat)) { + ds_err("Failed to initialize keyboard for seat(%s)", name); + goto err_kbd; + } + + if (!seat_touch_init(seat)) { + ds_err("Failed to initialize touch for seat(%s)", name); + goto err_touch; + } + + seat->global = wl_global_create(display, &wl_seat_interface, + SEAT_VERSION, seat, seat_handle_bind); + if (!seat->global) { + ds_err("Failed to create wl_global for seat(%s)", name); + goto err_global; + } + + seat->display = display; + seat->name = strdup(name); + + wl_list_init(&seat->clients); + + wl_signal_init(&seat->events.destroy); + wl_signal_init(&seat->events.pointer_grab_begin); + wl_signal_init(&seat->events.pointer_grab_end); + wl_signal_init(&seat->events.keyboard_grab_begin); + wl_signal_init(&seat->events.keyboard_grab_end); + wl_signal_init(&seat->events.touch_grab_begin); + wl_signal_init(&seat->events.touch_grab_end); + + seat->display_destroy.notify = seat_handle_display_destroy; + wl_display_add_destroy_listener(display, &seat->display_destroy); + + return seat; + +err_global: + seat_touch_finish(seat); +err_touch: + seat_keyboard_finish(seat); +err_kbd: + seat_pointer_finish(seat); +err_ptr: + free(seat); + + return NULL; +} + +WL_EXPORT void +ds_seat_destroy(struct ds_seat *seat) +{ + seat_destroy(seat); +} + +WL_EXPORT void +ds_seat_set_capabilities(struct ds_seat *seat, + enum wl_seat_capability capabilities) +{ + struct ds_seat_client *seat_client; + + if (capabilities == seat->capabilities) + return; + + seat->capabilities = capabilities; + seat->accumulated_capabilities |= capabilities; + + wl_list_for_each(seat_client, &seat->clients, link) { + if (!(capabilities & WL_SEAT_CAPABILITY_POINTER)) { + seat_client_remove_all_pointer_resources(seat_client); + } + if (!(capabilities & WL_SEAT_CAPABILITY_KEYBOARD)) { + seat_client_remove_all_keyboard_resources(seat_client); + } + if (!(capabilities & WL_SEAT_CAPABILITY_TOUCH)) { + seat_client_remove_all_touch_resources(seat_client); + } + + seat_client_send_capabilities(seat_client); + } +} + +WL_EXPORT void +ds_seat_set_name(struct ds_seat *seat, const char *name) +{ + struct ds_seat_client *seat_client; + + free(seat->name); + seat->name = strdup(name); + + wl_list_for_each(seat_client, &seat->clients, link) { + seat_client_send_name(seat_client); + } +} + +WL_EXPORT void +ds_seat_add_destroy_listener(struct ds_seat *seat, + struct wl_listener *listener) +{ + wl_signal_add(&seat->events.destroy, listener); +} + +struct ds_seat_client * +seat_client_for_wl_client(struct ds_seat *seat, struct wl_client *wl_client) +{ + struct ds_seat_client *seat_client; + + wl_list_for_each(seat_client, &seat->clients, link) { + if (seat_client->wl_client == wl_client) + return seat_client; + } + + return NULL; +} + +static struct ds_seat_client * +ds_seat_client_get_or_create(struct ds_seat *seat, struct wl_client *wl_client) +{ + struct ds_seat_client *seat_client; + + seat_client = seat_client_for_wl_client(seat, wl_client); + if (!seat_client) { + seat_client = seat_client_create(seat, wl_client); + if (!seat_client) + return NULL; + + wl_list_insert(&seat->clients, &seat_client->link); + } + + return seat_client; +} + +static void +seat_handle_get_pointer(struct wl_client *wl_client, + struct wl_resource *resource, uint32_t id) +{ + struct ds_seat_client *seat_client; + + seat_client = wl_resource_get_user_data(resource); + if (!seat_client) + return; + + if (!(seat_client->seat->accumulated_capabilities & + WL_SEAT_CAPABILITY_POINTER)) { +#ifdef HAVE_WL_SEAT_ERROR_MISSING_CAPABILITY + wl_resource_post_error(resource, WL_SEAT_ERROR_MISSING_CAPABILITY, + "wl_seat.get_pointer called when no " + "pointer capability has existed"); +#endif + return; + } + + seat_client_add_pointer_resource(seat_client, + wl_resource_get_version(resource), id); +} + +static void +seat_handle_get_keyboard(struct wl_client *wl_client, + struct wl_resource *resource, uint32_t id) +{ + struct ds_seat_client *seat_client; + + seat_client = wl_resource_get_user_data(resource); + if (!seat_client) + return; + + if (!(seat_client->seat->accumulated_capabilities & + WL_SEAT_CAPABILITY_POINTER)) { +#ifdef HAVE_WL_SEAT_ERROR_MISSING_CAPABILITY + wl_resource_post_error(resource, WL_SEAT_ERROR_MISSING_CAPABILITY, + "wl_seat.get_pointer called when no " + "keyboard capability has existed"); +#endif + return; + } + + seat_client_add_keyboard_resource(seat_client, + wl_resource_get_version(resource), id); +} + +static void +seat_handle_get_touch(struct wl_client *wl_client, + struct wl_resource *resource, uint32_t id) +{ + struct ds_seat_client *seat_client; + + seat_client = wl_resource_get_user_data(resource); + if (!seat_client) + return; + + if (!(seat_client->seat->accumulated_capabilities & + WL_SEAT_CAPABILITY_POINTER)) { +#ifdef HAVE_WL_SEAT_ERROR_MISSING_CAPABILITY + wl_resource_post_error(resource, WL_SEAT_ERROR_MISSING_CAPABILITY, + "wl_seat.get_pointer called when no " + "touch capability has existed"); +#endif + return; + } + + seat_client_add_touch_resource(seat_client, + wl_resource_get_version(resource), id); +} + +static void +seat_handle_release(struct wl_client *wl_client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct wl_seat_interface seat_impl = +{ + .get_pointer = seat_handle_get_pointer, + .get_keyboard = seat_handle_get_keyboard, + .get_touch = seat_handle_get_touch, + .release = seat_handle_release, +}; + +static void +seat_client_handle_resource_destroy(struct wl_resource *resource) +{ + struct ds_seat_client *seat_client; + + seat_client = wl_resource_get_user_data(resource); + if (!seat_client) + return; + + wl_list_remove(wl_resource_get_link(resource)); + if (!wl_list_empty(&seat_client->resources)) + return; + + seat_client_destroy(seat_client); +} + +static void +seat_handle_bind(struct wl_client *wl_client, void *data, uint32_t version, + uint32_t id) +{ + struct ds_seat *seat = data; + struct ds_seat_client *seat_client; + struct wl_resource *resource; + + resource = wl_resource_create(wl_client, &wl_seat_interface, version, id); + if (!resource) { + wl_client_post_no_memory(wl_client); + return; + } + + seat_client = ds_seat_client_get_or_create(seat, wl_client); + if (!seat_client) { + wl_resource_destroy(resource); + wl_client_post_no_memory(wl_client); + return; + } + + wl_resource_set_implementation(resource, &seat_impl, + seat_client, seat_client_handle_resource_destroy); + + wl_list_insert(&seat_client->resources, wl_resource_get_link(resource)); + + wl_seat_send_capabilities(resource, seat->capabilities); + + if (version >= WL_SEAT_NAME_SINCE_VERSION) + wl_seat_send_name(resource, seat->name); +} + +static void +seat_handle_display_destroy(struct wl_listener *listener, void *data) +{ + struct ds_seat *seat; + + seat = wl_container_of(listener, seat, display_destroy); + seat_destroy(seat); +} + +static void +seat_destroy(struct ds_seat *seat) +{ + struct ds_seat_client *seat_client, *tmp; + struct wl_resource *resource, *next; + + wl_signal_emit(&seat->events.destroy, seat); + + wl_list_remove(&seat->display_destroy.link); + + wl_list_for_each_safe(seat_client, tmp, &seat->clients, link) { + wl_resource_for_each_safe(resource, next, &seat_client->resources) { + wl_list_remove(wl_resource_get_link(resource)); + wl_resource_set_user_data(resource, NULL); + } + seat_client_destroy(seat_client); + } + + seat_pointer_finish(seat); + seat_keyboard_finish(seat); + seat_touch_finish(seat); + + wl_global_destroy(seat->global); + free(seat->name); + free(seat); +} + +static struct ds_seat_client * +seat_client_create(struct ds_seat *seat, struct wl_client *wl_client) +{ + struct ds_seat_client *seat_client; + + seat_client = calloc(1, sizeof *seat_client); + seat_client->seat = seat; + seat_client->wl_client = wl_client; + + wl_list_init(&seat_client->resources); + wl_list_init(&seat_client->pointers); + wl_list_init(&seat_client->keyboards); + wl_list_init(&seat_client->touches); + + wl_signal_init(&seat_client->events.destroy); + + return seat_client; +} + +static void +seat_client_destroy(struct ds_seat_client *seat_client) +{ + wl_signal_emit(&seat_client->events.destroy, seat_client); + + seat_client_remove_all_pointer_resources(seat_client); + seat_client_remove_all_keyboard_resources(seat_client); + seat_client_remove_all_touch_resources(seat_client); + + wl_list_remove(&seat_client->link); + + free(seat_client); +} + +static void +seat_client_send_capabilities(struct ds_seat_client *seat_client) +{ + struct wl_resource *resource; + + wl_resource_for_each(resource, &seat_client->resources) { + wl_seat_send_capabilities(resource, seat_client->seat->capabilities); + } +} + +static void +seat_client_send_name(struct ds_seat_client *seat_client) +{ + struct wl_resource *resource; + + wl_resource_for_each(resource, &seat_client->resources) { + wl_seat_send_name(resource, seat_client->seat->name); + } +} diff --git a/src/libds/seat/seat_keyboard.c b/src/libds/seat/seat_keyboard.c new file mode 100644 index 0000000..381e2d6 --- /dev/null +++ b/src/libds/seat/seat_keyboard.c @@ -0,0 +1,383 @@ +#include +#include +#include +#include + +#include "libds/log.h" +#include "seat_private.h" + +static const struct ds_keyboard_grab_interface default_keyboard_grab_iface; +static const struct wl_keyboard_interface keyboard_impl; + +static void +seat_client_send_keyboard_leave_raw(struct ds_seat_client *seat_client, + struct ds_surface *surface); +static void +seat_keyboard_handle_surface_destroy(struct wl_listener *listener, + void *data); +static void keyboard_handle_resource_destroy(struct wl_resource *resource); + +WL_EXPORT void +ds_seat_keyboard_notify_enter(struct ds_seat *seat, + struct ds_surface *surface, uint32_t keycodes[], size_t num_keycodes, + struct ds_keyboard_modifiers *modifiers) +{ + struct ds_seat_keyboard_grab *grab = seat->keyboard.grab; + + grab->iface->enter(grab, surface, keycodes, num_keycodes, modifiers); +} + +WL_EXPORT void +ds_seat_keyboard_notify_clear_focus(struct ds_seat *seat) +{ + struct ds_seat_keyboard_grab *grab = seat->keyboard.grab; + + grab->iface->clear_focus(grab); +} + +WL_EXPORT void +ds_seat_keyboard_notify_modifiers(struct ds_seat *seat, + struct ds_keyboard_modifiers *modifiers) +{ + struct ds_seat_keyboard_grab *grab = seat->keyboard.grab; + + clock_gettime(CLOCK_MONOTONIC, &seat->last_event); + grab->iface->modifiers(grab, modifiers); +} + +WL_EXPORT void +ds_seat_keyboard_notify_key(struct ds_seat *seat, uint32_t time_msec, + uint32_t key, uint32_t state) +{ + struct ds_seat_keyboard_grab *grab = seat->keyboard.grab; + + clock_gettime(CLOCK_MONOTONIC, &seat->last_event); + grab->iface->key(grab, time_msec, key, state); +} + +WL_EXPORT void +ds_seat_keyboard_add_grab_begin_listener(struct ds_seat *seat, + struct wl_listener *listener) +{ + wl_signal_add(&seat->events.keyboard_grab_begin, listener); +} + +WL_EXPORT void +ds_seat_keyboard_add_grab_end_listener(struct ds_seat *seat, + struct wl_listener *listener) +{ + wl_signal_add(&seat->events.keyboard_grab_end, listener); +} + +WL_EXPORT void +ds_seat_keyboard_add_focus_change_listener(struct ds_seat *seat, + struct wl_listener *listener) +{ + wl_signal_add(&seat->keyboard.events.focus_change, listener); +} + +void +ds_seat_keyboard_start_grab(struct ds_seat *seat, + struct ds_seat_keyboard_grab *grab) +{ + grab->seat = seat; + seat->keyboard.grab = grab; + + wl_signal_emit(&seat->events.keyboard_grab_begin, grab); +} + +void +ds_seat_keyboard_end_grab(struct ds_seat *seat) +{ + struct ds_seat_keyboard *keyboard = &seat->keyboard; + struct ds_seat_keyboard_grab *grab = keyboard->grab; + + if (grab != keyboard->default_grab) { + keyboard->grab = keyboard->default_grab; + wl_signal_emit(&seat->events.keyboard_grab_end, grab); + if (grab->iface->cancel) + grab->iface->cancel(grab); + } +} + +void +ds_seat_keyboard_enter(struct ds_seat *seat, struct ds_surface *surface, + uint32_t keycodes[], size_t num_keycodes, + struct ds_keyboard_modifiers *modifiers) +{ + struct ds_seat_keyboard *keyboard = &seat->keyboard; + struct ds_seat_client *seat_client = NULL, *focused_client; + struct ds_surface *focused_surface; + struct wl_client *wl_client; + struct wl_array keys; + struct wl_resource *resource; + uint32_t *p; + uint32_t serial; + + if (keyboard->focused_surface == surface) + return; + + if (surface) { + wl_client = + wl_resource_get_client(ds_surface_get_wl_resource(surface)); + seat_client = seat_client_for_wl_client(seat, wl_client); + } + + focused_client = keyboard->focused_client; + focused_surface = keyboard->focused_surface; + + if (focused_client != NULL && focused_surface != NULL) + seat_client_send_keyboard_leave_raw(focused_client, focused_surface); + + if (seat_client) { + wl_array_init(&keys); + + for (size_t i = 0; i < num_keycodes; i++) { + p = wl_array_add(&keys, sizeof(uint32_t)); + if (!p) { + ds_err("Cannot allocate memory, skipping keycode: %" PRIu32 + "\n", keycodes[i]); + continue; + } + *p = keycodes[i]; + } + + serial = wl_display_next_serial(seat->display); + + wl_resource_for_each(resource, &seat_client->keyboards) { + wl_keyboard_send_enter(resource, serial, + ds_surface_get_wl_resource(surface), &keys); + } + wl_array_release(&keys); + } + + wl_list_remove(&keyboard->surface_destroy.link); + wl_list_init(&keyboard->surface_destroy.link); + + if (surface) { + keyboard->surface_destroy.notify = + seat_keyboard_handle_surface_destroy; + ds_surface_add_destroy_listener(surface, &keyboard->surface_destroy); + } + + keyboard->focused_client = seat_client; + keyboard->focused_surface = surface; + + if (seat_client) { + ds_seat_keyboard_send_modifiers(seat, modifiers); + + // TODO handle selection + } + + struct ds_event_seat_keyboard_focus_change event = { + .seat = seat, + .old_surface = focused_surface, + .new_surface = surface, + }; + wl_signal_emit(&keyboard->events.focus_change, &event); +} + +void +ds_seat_keyboard_clear_focus(struct ds_seat *seat) +{ + ds_seat_keyboard_enter(seat, NULL, NULL, 0, NULL); +} + +void +ds_seat_keyboard_send_key(struct ds_seat *seat, uint32_t time_msec, + uint32_t key, uint32_t state) +{ + struct ds_seat_client *seat_client; + struct wl_resource *resource; + uint32_t serial; + + seat_client = seat->keyboard.focused_client; + if (!seat_client) + return; + + serial = wl_display_next_serial(seat->display); + wl_resource_for_each(resource, &seat_client->keyboards) + wl_keyboard_send_key(resource, serial, time_msec, key, state); +} + +void +ds_seat_keyboard_send_modifiers(struct ds_seat *seat, + struct ds_keyboard_modifiers *modifiers) +{ + struct ds_seat_keyboard *keyboard = &seat->keyboard; + struct ds_seat_client *seat_client = keyboard->focused_client; + struct wl_resource *resource; + uint32_t serial; + + if (!seat_client) + return; + + serial = wl_display_next_serial(seat->display); + wl_resource_for_each(resource, &seat_client->keyboards) { + if (!modifiers) { + wl_keyboard_send_modifiers(resource, serial, 0, 0, 0, 0); + } + else { + wl_keyboard_send_modifiers(resource, serial, + modifiers->depressed, modifiers->latched, + modifiers->locked, modifiers->group); + } + } +} + +bool +seat_keyboard_init(struct ds_seat *seat) +{ + struct ds_seat_keyboard *keyboard = &seat->keyboard; + struct ds_seat_keyboard_grab *grab; + + grab = calloc(1, sizeof *grab); + if (!grab) + return false; + + grab->iface = &default_keyboard_grab_iface; + grab->seat = seat; + + keyboard->default_grab = grab; + keyboard->grab = grab; + keyboard->seat = seat; + + wl_list_init(&keyboard->surface_destroy.link); + + wl_signal_init(&keyboard->events.focus_change); + + return true; +} + +void seat_keyboard_finish(struct ds_seat *seat) +{ + struct ds_seat_keyboard *keyboard = &seat->keyboard; + + wl_list_remove(&keyboard->surface_destroy.link); + free(keyboard->default_grab); +} + +void +seat_client_add_keyboard_resource(struct ds_seat_client *seat_client, + uint32_t version, uint32_t id) +{ + struct wl_resource *resource; + + resource = wl_resource_create(seat_client->wl_client, + &wl_keyboard_interface, version, id); + if (!resource) { + wl_client_post_no_memory(seat_client->wl_client); + return; + } + + wl_resource_set_implementation(resource, &keyboard_impl, seat_client, + keyboard_handle_resource_destroy); + + wl_list_insert(&seat_client->keyboards, wl_resource_get_link(resource)); + + if (!(seat_client->seat->capabilities & WL_SEAT_CAPABILITY_KEYBOARD)) { + wl_resource_set_user_data(resource, NULL); + return; + } +} + +void +seat_client_remove_all_keyboard_resources(struct ds_seat_client *seat_client) +{ + struct wl_resource *resource, *tmp; + + wl_resource_for_each_safe(resource, tmp, &seat_client->keyboards) { + wl_list_remove(wl_resource_get_link(resource)); + wl_resource_set_user_data(resource, NULL); + } +} + +static void +keyboard_handle_release(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct wl_keyboard_interface keyboard_impl = +{ + .release = keyboard_handle_release, +}; + +static void +keyboard_handle_resource_destroy(struct wl_resource *resource) +{ + if (!wl_resource_get_user_data(resource)) + return; + + wl_list_remove(wl_resource_get_link(resource)); +} + +static void +default_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_seat_keyboard_enter(grab->seat, surface, keycodes, num_keycodes, + modifiers); +} + +static void +default_keyboard_grab_iface_clear_focus(struct ds_seat_keyboard_grab *grab) +{ + ds_seat_keyboard_clear_focus(grab->seat); +} + +static void +default_keyboard_grab_iface_key(struct ds_seat_keyboard_grab *grab, + uint32_t time_msec, uint32_t key, uint32_t state) +{ + ds_seat_keyboard_send_key(grab->seat, time_msec, key, state); +} + +static void +default_modifiers_grab_iface_key(struct ds_seat_keyboard_grab *grab, + struct ds_keyboard_modifiers *modifiers) +{ + ds_seat_keyboard_send_modifiers(grab->seat, modifiers); +} + +static void +default_cancel_grab_iface_key(struct ds_seat_keyboard_grab *grab) +{ + // cannot be cancelled +} + +static const struct ds_keyboard_grab_interface default_keyboard_grab_iface = { + .enter = default_keyboard_grab_iface_enter, + .clear_focus = default_keyboard_grab_iface_clear_focus, + .key = default_keyboard_grab_iface_key, + .modifiers = default_modifiers_grab_iface_key, + .cancel = default_cancel_grab_iface_key, +}; + +static void +seat_client_send_keyboard_leave_raw(struct ds_seat_client *seat_client, + struct ds_surface *surface) +{ + struct wl_resource *resource; + uint32_t serial; + + serial = wl_display_next_serial(seat_client->seat->display); + wl_resource_for_each(resource, &seat_client->keyboards) { + wl_keyboard_send_leave(resource, serial, + ds_surface_get_wl_resource(surface)); + } +} + +static void +seat_keyboard_handle_surface_destroy(struct wl_listener *listener, + void *data) +{ + struct ds_seat_keyboard *keyboard; + + keyboard = wl_container_of(listener, keyboard, surface_destroy); + + wl_list_remove(&keyboard->surface_destroy.link); + wl_list_init(&keyboard->surface_destroy.link); + ds_seat_keyboard_clear_focus(keyboard->seat); +} diff --git a/src/libds/seat/seat_pointer.c b/src/libds/seat/seat_pointer.c new file mode 100644 index 0000000..2616756 --- /dev/null +++ b/src/libds/seat/seat_pointer.c @@ -0,0 +1,512 @@ +#include +#include +#include +#include + +#include "seat_private.h" + +static const struct ds_pointer_grab_interface default_pointer_grab_iface; +static const struct wl_pointer_interface pointer_impl; + +static void seat_pointer_warp(struct ds_seat *seat, double sx, double sy); +static void +seat_client_send_pointer_leave_raw(struct ds_seat_client *seat_client, + struct ds_surface *surface); +static void +seat_pointer_handle_surface_destroy(struct wl_listener *listener, + void *data); +static void pointer_handle_resource_destroy(struct wl_resource *resource); +static void pointer_send_frame(struct wl_resource *resource); + +WL_EXPORT void +ds_seat_pointer_notify_enter(struct ds_seat *seat, struct ds_surface *surface, + double sx, double sy) +{ + struct ds_seat_pointer_grab *grab = seat->pointer.grab; + + assert(surface); + grab->iface->enter(grab, surface, sx, sy); +} + +WL_EXPORT void +ds_seat_pointer_notify_clear_focus(struct ds_seat *seat) +{ + struct ds_seat_pointer_grab *grab = seat->pointer.grab; + + grab->iface->clear_focus(grab); +} + +WL_EXPORT void +ds_seat_pointer_notify_motion(struct ds_seat *seat, uint32_t time_msec, + double sx, double sy) +{ + struct ds_seat_pointer_grab *grab = seat->pointer.grab; + + grab->iface->motion(grab, time_msec, sx, sy); +} + +WL_EXPORT uint32_t +ds_seat_pointer_notify_button(struct ds_seat *seat, uint32_t time_msec, + uint32_t button, enum ds_button_state state) +{ + struct ds_seat_pointer *pointer = &seat->pointer; + struct ds_seat_pointer_grab *grab = pointer->grab; + uint32_t serial; + + if (state == DS_BUTTON_PRESSED) { + if (pointer->button_count == 0) { + pointer->grab_button = button; + pointer->grab_time = time_msec; + } + // TODO need a set struct for assigning pointer->buttons + } + else { + // TODO + } + + serial = grab->iface->button(grab, time_msec, button, state); + if (serial && pointer->button_count == 1 && + state == DS_BUTTON_PRESSED) + pointer->grab_serial = serial; + + return serial; +} + +WL_EXPORT void +ds_seat_pointer_notify_axis(struct ds_seat *seat, uint32_t time_msec, + enum ds_axis_orientation orientation, double value, + int32_t value_discrete, enum ds_axis_source source) +{ + struct ds_seat_pointer_grab *grab = seat->pointer.grab; + + clock_gettime(CLOCK_MONOTONIC, &seat->last_event); + grab->iface->axis(grab, time_msec, orientation, value, value_discrete, source); +} + +WL_EXPORT void +ds_seat_pointer_notify_frame(struct ds_seat *seat) +{ + struct ds_seat_pointer_grab *grab = seat->pointer.grab; + + clock_gettime(CLOCK_MONOTONIC, &seat->last_event); + if (grab->iface->frame) + grab->iface->frame(grab); +} + +WL_EXPORT void +ds_seat_pointer_add_grab_begin_listener(struct ds_seat *seat, + struct wl_listener *listener) +{ + wl_signal_add(&seat->events.pointer_grab_begin, listener); +} + +WL_EXPORT void +ds_seat_pointer_add_grab_end_listener(struct ds_seat *seat, + struct wl_listener *listener) +{ + wl_signal_add(&seat->events.pointer_grab_end, listener); +} + +WL_EXPORT void +ds_seat_pointer_add_focus_change_listener(struct ds_seat *seat, + struct wl_listener *listener) +{ + wl_signal_add(&seat->pointer.events.focus_change, listener); +} + +void +ds_seat_pointer_start_grab(struct ds_seat *seat, + struct ds_seat_pointer_grab *grab) +{ + grab->seat = seat; + seat->pointer.grab = grab; + wl_signal_emit(&seat->events.pointer_grab_begin, grab); +} + +void +ds_seat_pointer_end_grab(struct ds_seat *seat) +{ + struct ds_seat_pointer *pointer = &seat->pointer; + struct ds_seat_pointer_grab *grab = pointer->grab; + + if (grab != pointer->default_grab) { + pointer->grab = pointer->default_grab; + wl_signal_emit(&seat->events.pointer_grab_end, grab); + if (grab->iface->cancel) + grab->iface->cancel(grab); + } +} + +void +ds_seat_pointer_enter(struct ds_seat *seat, struct ds_surface *surface, + double sx, double sy) +{ + struct ds_seat_pointer *pointer = &seat->pointer; + struct ds_seat_client *seat_client = NULL, *focused_client; + struct ds_surface *focused_surface; + struct wl_client *wl_client; + struct wl_resource *resource; + uint32_t serial; + + if (pointer->focused_surface == surface) { + // this surface already got an enter notify + return; + } + + focused_client = pointer->focused_client; + focused_surface = pointer->focused_surface; + + if (focused_client != NULL && focused_surface != NULL) + seat_client_send_pointer_leave_raw(focused_client, focused_surface); + + if (surface) { + wl_client = + wl_resource_get_client(ds_surface_get_wl_resource(surface)); + seat_client = seat_client_for_wl_client(seat, wl_client); + } + + if (seat_client) { + serial = wl_display_next_serial(seat->display); + wl_resource_for_each(resource, &seat_client->pointers) { + wl_pointer_send_enter(resource, serial, + ds_surface_get_wl_resource(surface), + wl_fixed_from_double(sx), wl_fixed_from_double(sy)); + pointer_send_frame(resource); + } + } + + wl_list_remove(&pointer->surface_destroy.link); + wl_list_init(&pointer->surface_destroy.link); + + if (surface) { + pointer->surface_destroy.notify = + seat_pointer_handle_surface_destroy; + ds_surface_add_destroy_listener(surface, &pointer->surface_destroy); + } + + pointer->focused_client = seat_client; + pointer->focused_surface = surface; + if (surface) + seat_pointer_warp(seat, sx, sy); + else + seat_pointer_warp(seat, NAN, NAN); + + struct ds_event_seat_pointer_focus_change event = { + .seat = seat, + .new_surface = surface, + .old_surface = focused_surface, + .sx = sx, + .sy = sy, + }; + wl_signal_emit(&pointer->events.focus_change, &event); +} + +void +ds_seat_pointer_clear_focus(struct ds_seat *seat) +{ + ds_seat_pointer_enter(seat, NULL, 0, 0); +} + +void +ds_seat_pointer_send_motion(struct ds_seat *seat, uint32_t time_msec, + double sx, double sy) +{ + struct ds_seat_pointer *pointer = &seat->pointer; + struct ds_seat_client *seat_client = pointer->focused_client; + struct wl_resource *resource; + wl_fixed_t sx_fixed, sy_fixed; + + if (!seat_client) + return; + + sx_fixed = wl_fixed_from_double(sx); + sy_fixed = wl_fixed_from_double(sy); + if (wl_fixed_from_double(pointer->sx) != sx_fixed || + wl_fixed_from_double(pointer->sy) != sy_fixed) { + wl_resource_for_each(resource, &seat_client->pointers) + wl_pointer_send_motion(resource, time_msec, sx_fixed, sy_fixed); + } + + seat_pointer_warp(seat, sx, sy); +} + +uint32_t +ds_seat_pointer_send_button(struct ds_seat *seat, uint32_t time_msec, + uint32_t button, enum ds_button_state state) +{ + struct ds_seat_client *seat_client = seat->pointer.focused_client; + struct wl_resource *resource; + uint32_t serial; + + if (!seat_client) + return 0; + + serial = wl_display_next_serial(seat->display); + wl_resource_for_each(resource, &seat_client->pointers) + wl_pointer_send_button(resource, serial, time_msec, button, state); + + return serial; +} + +void +ds_seat_pointer_send_axis(struct ds_seat *seat, uint32_t time_msec, + enum ds_axis_orientation orientation, double value, + int32_t value_discrete, enum ds_axis_source source) +{ + struct ds_seat_pointer *pointer = &seat->pointer; + struct ds_seat_client *seat_client = pointer->focused_client; + struct wl_resource *resource; + uint32_t version; + bool send_source = false; + + if (!seat_client) + return; + + if (pointer->sent_axis_source) { + assert(pointer->cached_axis_source == source); + } + else { + pointer->sent_axis_source = true; + pointer->cached_axis_source = source; + send_source = true; + } + + wl_resource_for_each(resource, &seat_client->pointers) { + version = wl_resource_get_version(resource); + + if (send_source && version >= WL_POINTER_AXIS_SOURCE_SINCE_VERSION) + wl_pointer_send_axis_source(resource, source); + + if (value) { + if (value_discrete && + version >= WL_POINTER_AXIS_DISCRETE_SINCE_VERSION) { + wl_pointer_send_axis_discrete(resource, orientation, + value_discrete); + } + + wl_pointer_send_axis(resource, time_msec, orientation, + wl_fixed_from_double(value)); + } + else if (version >= WL_POINTER_AXIS_STOP_SINCE_VERSION) { + wl_pointer_send_axis_stop(resource, time_msec, orientation); + } + } +} + +void +ds_seat_pointer_send_frame(struct ds_seat *seat) +{ + struct ds_seat_pointer *pointer = &seat->pointer; + struct ds_seat_client *seat_client = pointer->focused_client; + struct wl_resource *resource; + + if (!seat_client) + return; + + pointer->sent_axis_source = false; + + wl_resource_for_each(resource, &seat_client->pointers) + pointer_send_frame(resource); +} + +bool +seat_pointer_init(struct ds_seat *seat) +{ + struct ds_seat_pointer *pointer = &seat->pointer; + struct ds_seat_pointer_grab *grab; + + grab = calloc(1, sizeof *grab); + if (!grab) + return false; + + grab->iface = &default_pointer_grab_iface; + grab->seat = seat; + + pointer->default_grab = grab; + pointer->grab = grab; + pointer->seat = seat; + + wl_list_init(&pointer->surface_destroy.link); + + wl_signal_init(&pointer->events.focus_change); + + return true; +} + +void +seat_pointer_finish(struct ds_seat *seat) +{ + struct ds_seat_pointer *pointer = &seat->pointer; + + wl_list_remove(&pointer->surface_destroy.link); + free(pointer->default_grab); +} + +void +seat_client_add_pointer_resource(struct ds_seat_client *seat_client, + uint32_t version, uint32_t id) +{ + struct wl_resource *resource; + + resource = wl_resource_create(seat_client->wl_client, + &wl_pointer_interface, version, id); + if (!resource) { + wl_client_post_no_memory(seat_client->wl_client); + return; + } + + wl_resource_set_implementation(resource, &pointer_impl, seat_client, + &pointer_handle_resource_destroy); + + if (!(seat_client->seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) { + wl_resource_set_user_data(resource, NULL); + return; + } + + wl_list_insert(&seat_client->pointers, wl_resource_get_link(resource)); +} + +void +seat_client_remove_all_pointer_resources(struct ds_seat_client *seat_client) +{ + struct wl_resource *resource, *tmp; + + wl_resource_for_each_safe(resource, tmp, &seat_client->pointers) { + wl_list_remove(wl_resource_get_link(resource)); + wl_resource_set_user_data(resource, NULL); + } +} + +static void +seat_pointer_warp(struct ds_seat *seat, double sx, double sy) +{ + seat->pointer.sx = sx; + seat->pointer.sy = sy; +} + +static void +seat_client_send_pointer_leave_raw(struct ds_seat_client *seat_client, + struct ds_surface *surface) +{ + struct wl_resource *resource; + uint32_t serial; + + serial = wl_display_next_serial(seat_client->seat->display); + wl_resource_for_each(resource, &seat_client->pointers) { + wl_pointer_send_leave(resource, serial, + ds_surface_get_wl_resource(surface)); + pointer_send_frame(resource); + } +} + +static void +seat_pointer_handle_surface_destroy(struct wl_listener *listener, void *data) +{ + struct ds_seat_pointer *pointer; + + pointer = wl_container_of(listener, pointer, surface_destroy); + wl_list_remove(&pointer->surface_destroy.link); + wl_list_init(&pointer->surface_destroy.link); + ds_seat_pointer_clear_focus(pointer->seat); +} + +static void +default_pointer_grab_iface_enter(struct ds_seat_pointer_grab *grab, + struct ds_surface *surface, double sx, double sy) +{ + ds_seat_pointer_enter(grab->seat, surface, sx, sy); +} + +static void +default_pointer_grab_iface_clear_focus(struct ds_seat_pointer_grab *grab) +{ + ds_seat_pointer_clear_focus(grab->seat); +} + +static void +default_pointer_grab_iface_motion(struct ds_seat_pointer_grab *grab, + uint32_t time_msec, double sx, double sy) +{ + ds_seat_pointer_send_motion(grab->seat, time_msec, sx, sy); +} + +static uint32_t +default_pointer_grab_iface_button(struct ds_seat_pointer_grab *grab, + uint32_t time_msec, uint32_t button, enum ds_button_state state) +{ + return ds_seat_pointer_send_button(grab->seat, time_msec, button, state); +} + +static void +default_pointer_grab_iface_axis(struct ds_seat_pointer_grab *grab, + uint32_t time_msec, enum ds_axis_orientation orientation, double value, + int32_t value_discrete, enum ds_axis_source source) +{ + ds_seat_pointer_send_axis(grab->seat, time_msec, orientation, value, + value_discrete, source); +} + +static void +default_pointer_grab_iface_frame(struct ds_seat_pointer_grab *grab) +{ + ds_seat_pointer_send_frame(grab->seat); +} + +static void +default_pointer_grab_iface_cancel(struct ds_seat_pointer_grab *grab) +{ + // cannot be cancelled +} + +static const struct ds_pointer_grab_interface default_pointer_grab_iface = { + .enter = default_pointer_grab_iface_enter, + .clear_focus = default_pointer_grab_iface_clear_focus, + .motion = default_pointer_grab_iface_motion, + .button = default_pointer_grab_iface_button, + .axis = default_pointer_grab_iface_axis, + .frame = default_pointer_grab_iface_frame, + .cancel = default_pointer_grab_iface_cancel, +}; + +static void +pointer_handle_set_cursor(struct wl_client *client, + struct wl_resource *pointer_resource, uint32_t serial, + struct wl_resource *surface_resource, + int32_t hotspot_x, int32_t hotspot_y) +{ + struct ds_seat_client *seat_client; + + seat_client = wl_resource_get_user_data(pointer_resource); + if (!seat_client) + return; + + // TODO +} + +static void +pointer_handle_release(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct wl_pointer_interface pointer_impl = +{ + .set_cursor = pointer_handle_set_cursor, + .release = pointer_handle_release, +}; + +static void pointer_handle_resource_destroy(struct wl_resource *resource) +{ + if (!wl_resource_get_user_data(resource)) + return; + + wl_list_remove(wl_resource_get_link(resource)); +} + +static void +pointer_send_frame(struct wl_resource *resource) +{ + if (wl_resource_get_version(resource) >= + WL_POINTER_FRAME_SINCE_VERSION) + wl_pointer_send_frame(resource); +} diff --git a/src/libds/seat/seat_private.h b/src/libds/seat/seat_private.h new file mode 100644 index 0000000..7a566bb --- /dev/null +++ b/src/libds/seat/seat_private.h @@ -0,0 +1,172 @@ +#ifndef DS_SEAT_PRIVATE_H +#define DS_SEAT_PRIVATE_H + +#include +#include + +#include "libds/seat.h" +#include "seat.h" +#include "surface.h" + +struct ds_seat_client +{ + struct ds_seat *seat; + struct wl_client *wl_client; + struct wl_list link; + + struct wl_list resources; // wl_seat + struct wl_list pointers; // wl_pointer + struct wl_list keyboards; // wl_keyboard + struct wl_list touches; // wl_touch + + struct { + struct wl_signal destroy; + } events; + + bool needs_touch_frame; +}; + +#define DS_POINTER_BUTTONS_CAP 16 + +struct ds_seat_pointer +{ + struct ds_seat *seat; + struct ds_seat_client *focused_client; + struct ds_surface *focused_surface; + double sx, sy; + + struct ds_seat_pointer_grab *grab; + struct ds_seat_pointer_grab *default_grab; + + bool sent_axis_source; + enum ds_axis_source cached_axis_source; + + uint32_t buttons[DS_POINTER_BUTTONS_CAP]; + size_t button_count; + uint32_t grab_button; + uint32_t grab_serial; + uint32_t grab_time; + + struct wl_listener surface_destroy; + + struct { + struct wl_signal focus_change; + } events; +}; + +struct ds_seat_keyboard +{ + struct ds_seat *seat; + + struct ds_seat_client *focused_client; + struct ds_surface *focused_surface; + + struct wl_listener surface_destroy; + + struct ds_seat_keyboard_grab *grab; + struct ds_seat_keyboard_grab *default_grab; + + struct { + struct wl_signal focus_change; + } events; +}; + +struct ds_touch_point +{ + int32_t touch_id; + struct ds_surface *surface; + struct ds_seat_client *seat_client; + + struct ds_seat_client *focused_client; + struct ds_surface *focused_surface; + double sx, sy; + + struct wl_listener surface_destroy; + struct wl_listener focused_surface_destroy; + struct wl_listener client_destroy; + + struct { + struct wl_signal destroy; + } events; + + struct wl_list link; +}; + +struct ds_seat_touch +{ + struct ds_seat *seat; + struct wl_list touch_points; // ds_touch_point::link + + uint32_t grab_serial; + uint32_t grab_id; + + struct ds_seat_touch_grab *grab; + struct ds_seat_touch_grab *default_grab; +}; + +struct ds_seat +{ + char *name; + enum wl_seat_capability capabilities; + enum wl_seat_capability accumulated_capabilities; + struct timespec last_event; + + struct wl_display *display; + struct wl_global *global; + + struct wl_list clients; // ds_seat_client::link + + struct ds_seat_pointer pointer; + struct ds_seat_keyboard keyboard; + struct ds_seat_touch touch; + + struct wl_listener display_destroy; + + struct { + struct wl_signal destroy; + + struct wl_signal pointer_grab_begin; + struct wl_signal pointer_grab_end; + + struct wl_signal keyboard_grab_begin; + struct wl_signal keyboard_grab_end; + + struct wl_signal touch_grab_begin; + struct wl_signal touch_grab_end; + } events; +}; + +struct ds_seat_client * +seat_client_for_wl_client(struct ds_seat *seat, struct wl_client *wl_client); + +bool seat_pointer_init(struct ds_seat *seat); + +void seat_pointer_finish(struct ds_seat *seat); + +void seat_client_add_pointer_resource(struct ds_seat_client *seat_client, + uint32_t version, uint32_t id); + +void +seat_client_remove_all_pointer_resources(struct ds_seat_client *seat_client); + +bool seat_keyboard_init(struct ds_seat *seat); + +void seat_keyboard_finish(struct ds_seat *seat); + +void seat_client_add_keyboard_resource(struct ds_seat_client *seat_client, + uint32_t version, uint32_t id); + +void +seat_client_remove_all_keyboard_resources(struct ds_seat_client *seat_client); + +bool seat_touch_init(struct ds_seat *seat); + +void seat_touch_finish(struct ds_seat *seat); + +void seat_client_add_touch_resource(struct ds_seat_client *seat_client, + uint32_t version, uint32_t id); + +void +seat_client_remove_all_touch_resources(struct ds_seat_client *seat_client); + +#endif diff --git a/src/libds/seat/seat_touch.c b/src/libds/seat/seat_touch.c new file mode 100644 index 0000000..8a02e94 --- /dev/null +++ b/src/libds/seat/seat_touch.c @@ -0,0 +1,432 @@ +#include +#include +#include + +#include "libds/log.h" +#include "seat_private.h" + +static const struct ds_touch_grab_interface default_touch_grab_iface; +static const struct wl_touch_interface touch_impl; + +static void touch_handle_resource_destroy(struct wl_resource *resource); +static struct ds_touch_point *touch_point_create(struct ds_seat *seat, + int32_t touch_id, struct ds_surface *surface, double sx, double sy); +static void touch_point_destroy(struct ds_touch_point *point); +static void touch_point_clear_focus(struct ds_touch_point *point); +static struct ds_touch_point *seat_find_touch_point(struct ds_seat *seat, + int32_t touch_id); +static int seat_touch_num_points(struct ds_seat *seat); + +WL_EXPORT uint32_t +ds_seat_touch_notify_down(struct ds_seat *seat, struct ds_surface *surface, + uint32_t time_msec, int32_t touch_id, double sx, double sy) +{ + struct ds_seat_touch_grab *grab = seat->touch.grab; + struct ds_touch_point *point; + uint32_t serial; + + // FIXME + // What if ds_touch_point is already exist associated with given touch_id? + point = touch_point_create(seat, touch_id, surface, sx, sy); + if (!point) { + ds_err("Could not create touch point"); + return 0; + } + + serial = grab->iface->down(grab, time_msec, point); + if (!serial) { + touch_point_destroy(point); + return 0; + } + + if (serial && seat_touch_num_points(seat) == 1) { + seat->touch.grab_serial = serial; + seat->touch.grab_id = touch_id; + } + + return serial; +} + +WL_EXPORT void +ds_seat_touch_notify_up(struct ds_seat *seat, uint32_t time_msec, + int32_t touch_id) +{ + struct ds_seat_touch_grab *grab = seat->touch.grab; + struct ds_touch_point *point; + + clock_gettime(CLOCK_MONOTONIC, &seat->last_event); + + point = seat_find_touch_point(seat, touch_id); + if (!point) + return; + + grab->iface->up(grab, time_msec, point); + + touch_point_destroy(point); +} + +WL_EXPORT void +ds_seat_touch_notify_motion(struct ds_seat *seat, uint32_t time_msec, + int32_t touch_id, double sx, double sy) +{ + struct ds_seat_touch_grab *grab = seat->touch.grab; + struct ds_touch_point *point; + + clock_gettime(CLOCK_MONOTONIC, &seat->last_event); + + point = seat_find_touch_point(seat, touch_id); + if (!point) + return; + + point->sx = sx; + point->sy = sy; + + grab->iface->motion(grab, time_msec, point); +} + +WL_EXPORT void +ds_seat_touch_notify_frame(struct ds_seat *seat) +{ + struct ds_seat_touch_grab *grab = seat->touch.grab; + + if (grab->iface->frame) + grab->iface->frame(grab); +} + +WL_EXPORT void +ds_seat_touch_add_grab_start_listener(struct ds_seat *seat, + struct wl_listener *listener) +{ + wl_signal_add(&seat->events.touch_grab_begin, listener); +} + +WL_EXPORT void +ds_seat_touch_end_grab_start_listener(struct ds_seat *seat, + struct wl_listener *listener) +{ + wl_signal_add(&seat->events.touch_grab_end, listener); +} + +uint32_t +ds_seat_touch_send_down(struct ds_seat *seat, struct ds_surface *surface, + uint32_t time_msec, int32_t touch_id, double sx, double sy) +{ + struct ds_touch_point *point; + struct wl_resource *resource; + uint32_t serial; + + point = seat_find_touch_point(seat, touch_id); + if (!point) { + ds_err("Got touch down for unknown touch point"); + return 0; + } + + serial = wl_display_next_serial(seat->display); + wl_resource_for_each(resource, &point->seat_client->touches) { + wl_touch_send_down(resource, serial, time_msec, + ds_surface_get_wl_resource(surface), touch_id, + wl_fixed_from_double(sx), wl_fixed_from_double(sy)); + } + + point->seat_client->needs_touch_frame = true; + + return serial; +} + +void +ds_seat_touch_send_up(struct ds_seat *seat, uint32_t time_msec, + int32_t touch_id) +{ + struct ds_touch_point *point; + struct wl_resource *resource; + uint32_t serial; + + point = seat_find_touch_point(seat, touch_id); + if (!point) { + ds_err("Got touch up for unknown touch point"); + return; + } + + serial = wl_display_next_serial(seat->display); + wl_resource_for_each(resource, &point->seat_client->touches) + wl_touch_send_up(resource, serial, time_msec, touch_id); + + point->seat_client->needs_touch_frame = true; +} + +void +ds_seat_touch_send_motion(struct ds_seat *seat, uint32_t time_msec, + int32_t touch_id, double sx, double sy) +{ + struct ds_touch_point *point; + struct wl_resource *resource; + + point = seat_find_touch_point(seat, touch_id); + if (!point) { + ds_err("Got touch motion for unknown touch point"); + return; + } + + wl_resource_for_each(resource, &point->seat_client->touches) { + wl_touch_send_motion(resource, time_msec, touch_id, + wl_fixed_from_double(sx), wl_fixed_from_double(sy)); + } + + point->seat_client->needs_touch_frame = true; +} + +void +ds_seat_touch_send_frame(struct ds_seat *seat) +{ + struct ds_seat_client *seat_client; + struct wl_resource *resource; + + wl_list_for_each(seat_client, &seat->clients, link) { + if (!seat_client->needs_touch_frame) + continue; + + wl_resource_for_each(resource, &seat_client->touches) + wl_touch_send_frame(resource); + + seat_client->needs_touch_frame = false; + } +} + +bool +seat_touch_init(struct ds_seat *seat) +{ + struct ds_seat_touch *touch = &seat->touch; + struct ds_seat_touch_grab *grab; + + grab = calloc(1, sizeof *grab); + if (!grab) + return false; + + grab->iface = &default_touch_grab_iface; + grab->seat = seat; + + touch->default_grab = grab; + touch->grab = grab; + touch->seat = seat; + + wl_list_init(&touch->touch_points); + + return true; +} + +void +seat_touch_finish(struct ds_seat *seat) +{ + struct ds_seat_touch *touch = &seat->touch; + struct ds_touch_point *point; + + wl_list_for_each(point, &touch->touch_points, link) + touch_point_clear_focus(point); + + free(touch->default_grab); +} + +void +seat_client_add_touch_resource(struct ds_seat_client *seat_client, + uint32_t version, uint32_t id) +{ + struct wl_resource *resource; + + resource = wl_resource_create(seat_client->wl_client, + &wl_keyboard_interface, version, id); + if (!resource) { + wl_client_post_no_memory(seat_client->wl_client); + return; + } + + wl_resource_set_implementation(resource, &touch_impl, seat_client, + touch_handle_resource_destroy); + + wl_list_insert(&seat_client->touches, wl_resource_get_link(resource)); + + if (!(seat_client->seat->capabilities & WL_SEAT_CAPABILITY_TOUCH)) + wl_resource_set_user_data(resource, NULL); +} + +void +seat_client_remove_all_touch_resources(struct ds_seat_client *seat_client) +{ + struct wl_resource *resource, *tmp; + + wl_resource_for_each_safe(resource, tmp, &seat_client->touches) { + wl_list_remove(wl_resource_get_link(resource)); + wl_resource_set_user_data(resource, NULL); + } +} + +static uint32_t +default_touch_grab_iface_down(struct ds_seat_touch_grab *grab, + uint32_t time_msec, struct ds_touch_point *point) +{ + return ds_seat_touch_send_down(grab->seat, point->surface, time_msec, + point->touch_id, point->sx, point->sy); +} + +static void +default_touch_grab_iface_up(struct ds_seat_touch_grab *grab, + uint32_t time_msec, struct ds_touch_point *point) +{ + ds_seat_touch_send_up(grab->seat, time_msec, point->touch_id); +} + +static void +default_touch_grab_iface_motion(struct ds_seat_touch_grab *grab, + uint32_t time_msec, struct ds_touch_point *point) +{ + if (!point->focused_surface || point->focused_surface == point->surface) { + ds_seat_touch_send_motion(grab->seat, time_msec, point->touch_id, + point->sx, point->sy); + } +} + +static void +default_touch_grab_iface_enter(struct ds_seat_touch_grab *grab, + uint32_t time_msec, struct ds_touch_point *point) +{ + // not handled by default +} + +static void +default_touch_grab_iface_frame(struct ds_seat_touch_grab *grab) +{ + ds_seat_touch_send_frame(grab->seat); +} + +static void +default_touch_grab_iface_cancel(struct ds_seat_touch_grab *grab) +{ + // cannot be cancelled +} + +static const struct ds_touch_grab_interface default_touch_grab_iface = { + .down = default_touch_grab_iface_down, + .up = default_touch_grab_iface_up, + .motion = default_touch_grab_iface_motion, + .enter = default_touch_grab_iface_enter, + .frame = default_touch_grab_iface_frame, + .cancel = default_touch_grab_iface_cancel, +}; + +static void +touch_handle_release(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct wl_touch_interface touch_impl = +{ + .release = touch_handle_release, +}; + +static void +touch_handle_resource_destroy(struct wl_resource *resource) +{ + if (!wl_resource_get_user_data(resource)) + return; + + wl_list_remove(wl_resource_get_link(resource)); +} + +static void +touch_point_handle_surface_destroy(struct wl_listener *listener, void *data) +{ + struct ds_touch_point *point; + + point = wl_container_of(listener, point, surface_destroy); + point->surface = NULL; + wl_list_remove(&point->surface_destroy.link); + wl_list_init(&point->surface_destroy.link); +} + +static void +touch_point_handle_client_destroy(struct wl_listener *listener, void *data) +{ + struct ds_touch_point *point; + + point = wl_container_of(listener, point, surface_destroy); + touch_point_destroy(point); +} + +static struct ds_touch_point * +touch_point_create(struct ds_seat *seat, int32_t touch_id, + struct ds_surface *surface, double sx, double sy) +{ + struct ds_touch_point *point; + struct ds_seat_client *seat_client; + struct wl_client *wl_client; + + wl_client = wl_resource_get_client(ds_surface_get_wl_resource(surface)); + seat_client = seat_client_for_wl_client(seat, wl_client); + if (!seat_client || wl_list_empty(&seat_client->touches)) + return NULL; + + point = calloc(1, sizeof *point); + if (!point) + return NULL; + + point->touch_id = touch_id; + point->surface = surface; + point->seat_client = seat_client; + point->sx = sx; + point->sy = sy; + + wl_signal_init(&point->events.destroy); + + point->surface_destroy.notify = touch_point_handle_surface_destroy; + ds_surface_add_destroy_listener(surface, &point->surface_destroy); + + point->client_destroy.notify = touch_point_handle_client_destroy; + wl_signal_add(&seat_client->events.destroy, &point->client_destroy); + + wl_list_insert(&seat->touch.touch_points, &point->link); + + return point; +} + +static void +touch_point_destroy(struct ds_touch_point *point) +{ + wl_signal_emit(&point->events.destroy, point); + + touch_point_clear_focus(point); + + wl_list_remove(&point->surface_destroy.link); + wl_list_remove(&point->client_destroy.link); + wl_list_remove(&point->link); + free(point); +} + +static void +touch_point_clear_focus(struct ds_touch_point *point) +{ + if (!point->focused_surface) + return; + + wl_list_remove(&point->focused_surface_destroy.link); + point->focused_client = NULL; + point->focused_surface = NULL; +} + +static struct ds_touch_point *seat_find_touch_point(struct ds_seat *seat, + int32_t touch_id) +{ + struct ds_touch_point *point; + + wl_list_for_each(point, &seat->touch.touch_points, link) { + if (point->touch_id == touch_id) + return point; + } + + return NULL; +} + +static int +seat_touch_num_points(struct ds_seat *seat) +{ + return wl_list_length(&seat->touch.touch_points); +} diff --git a/src/libds/surface.h b/src/libds/surface.h index 8f3ceac..008fb9c 100644 --- a/src/libds/surface.h +++ b/src/libds/surface.h @@ -46,4 +46,7 @@ ds_subsurface_create(struct wl_resource *subcomp_resource, struct ds_surface *surface, struct ds_surface *parent, uint32_t version, uint32_t id); +struct wl_resource * +ds_surface_get_wl_resource(struct ds_surface *surface); + #endif diff --git a/src/libds/surface/surface.c b/src/libds/surface/surface.c index 0333561..78dc41c 100644 --- a/src/libds/surface/surface.c +++ b/src/libds/surface/surface.c @@ -220,6 +220,12 @@ ds_surface_has_buffer(struct ds_surface *surface) return !!surface->buffer; } +struct wl_resource * +ds_surface_get_wl_resource(struct ds_surface *surface) +{ + return surface->resource; +} + static void surface_handle_destroy(struct wl_client *client, struct wl_resource *resource) { -- 2.7.4 From 0581f981272aad25c99fcb58208f4291bdec8457 Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Wed, 4 May 2022 15:34:19 +0900 Subject: [PATCH 06/16] tinyds: Handle ds_seat tinyds now handles ds_seat, but only for pointer. Change-Id: I2b48841e8dcbbe0845d5cb2facd46b78b48eafd7 --- src/examples/tinyds.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/examples/tinyds.c b/src/examples/tinyds.c index 64612a6..0e25462 100644 --- a/src/examples/tinyds.c +++ b/src/examples/tinyds.c @@ -21,6 +21,7 @@ #include #include #include +#include #define TINYDS_UNUSED __attribute__((unused)) @@ -34,6 +35,8 @@ struct tinyds_pointer struct ds_input_device *dev; struct tinyds_server *server; + struct tinyds_view *focused_view; + struct wl_listener destroy; struct wl_listener motion; struct wl_listener motion_absolute; @@ -84,6 +87,7 @@ struct tinyds_server struct ds_backend *backend; struct ds_compositor *compositor; struct ds_xdg_shell *xdg_shell; + struct ds_seat *seat; struct tinyds_output output; @@ -115,6 +119,8 @@ static bool server_init(struct tinyds_server *server, static void server_fini(struct tinyds_server *server); static void server_add_view(struct tinyds_server *server, struct ds_xdg_surface *xdg_surface); +static struct tinyds_view *server_view_at(struct tinyds_server *server, + double lx, double ly, double *sx, double *sy); static bool output_init(struct tinyds_output *output, struct tinyds_server *server, struct ds_output *ds_output, int width, int height); @@ -354,11 +360,37 @@ pointer_handle_motion_absolute(struct wl_listener *listener, void *data) { struct tinyds_pointer *pointer; struct ds_event_pointer_motion_absolute *event = data; + struct tinyds_view *view; + double ox, oy, sx, sy; pointer = wl_container_of(listener, pointer, motion_absolute); ds_inf("Pointer(%p) motion absolute: (x %f y %f) time(%d)", pointer, event->x, event->y, event->time_msec); + + ox = event->x * OUTPUT_WIDTH; + oy = event->y * OUTPUT_HEIGHT; + view = server_view_at(pointer->server, ox, oy, &sx, &sy); + + if (pointer->focused_view != view) { + if (pointer->focused_view) { + ds_inf("Clear pointer focus from view(%p)", pointer->focused_view); + ds_seat_pointer_notify_clear_focus(pointer->server->seat); + pointer->focused_view = NULL; + } + + if (view) { + ds_inf("Set pointer focus to view(%p)", view); + ds_seat_pointer_notify_enter(pointer->server->seat, + ds_xdg_surface_get_surface(view->xdg_surface), sx, sy); + pointer->focused_view = view; + } + } + + if (view) { + ds_seat_pointer_notify_motion(pointer->server->seat, + event->time_msec, sx, sy); + } } static void @@ -383,6 +415,7 @@ pointer_handle_frame(struct wl_listener *listener, void *data) pointer = wl_container_of(listener, pointer, frame); ds_inf("Pointer(%p) frame", pointer); + ds_seat_pointer_notify_frame(pointer->server->seat); } static void @@ -437,6 +470,7 @@ server_handle_new_input(struct wl_listener *listener, void *data) break; case DS_INPUT_DEVICE_POINTER: server_add_pointer(server, dev); + ds_seat_set_capabilities(server->seat, WL_SEAT_CAPABILITY_POINTER); break; default: ds_err("Unknown type(%d) of ds_input_device", dev_type); @@ -534,6 +568,10 @@ server_init(struct tinyds_server *server, struct wl_display *display) ds_xdg_shell_add_new_surface_listener(server->xdg_shell, &server->new_xdg_surface); + server->seat = ds_seat_create(display, "seat0" /* arbitrary name */); + if (!server->seat) + goto err; + return true; err: @@ -696,6 +734,34 @@ server_add_view(struct tinyds_server *server, struct ds_xdg_surface *xdg_surface ds_inf("View(%p) added", view); } +static struct tinyds_view * +server_view_at(struct tinyds_server *server, double lx, double ly, + double *sx, double *sy) +{ + struct tinyds_view *view; + struct ds_surface *surface; + struct ds_buffer *buffer; + int x, y, w = 0, h = 0; + + wl_list_for_each(view, &server->views, link) { + surface = ds_xdg_surface_get_surface(view->xdg_surface); + buffer = ds_surface_get_buffer(surface); + ds_buffer_get_size(buffer, &w, &h); + + x = view->x; + y = view->y; + + if (lx >= x && lx <= w && ly >= y && ly <= h) { + *sx = lx - x; + *sy = ly - y; + + return view; + } + } + + return NULL; +} + static void view_destroy(struct tinyds_view *view) { -- 2.7.4 From c563346a8a7117f4a4e4b1daf8c1d44fdf4103e2 Mon Sep 17 00:00:00 2001 From: "duna.oh" Date: Wed, 18 May 2022 18:37:26 +0900 Subject: [PATCH 07/16] seat: fix the typo. should handle a proper device type Change-Id: Ifd7196e8f2a803973e1083b3baba7cff07428b20 --- src/libds/seat/seat.c | 8 ++++---- src/libds/seat/seat_touch.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libds/seat/seat.c b/src/libds/seat/seat.c index 542186e..51d5c23 100644 --- a/src/libds/seat/seat.c +++ b/src/libds/seat/seat.c @@ -201,10 +201,10 @@ seat_handle_get_keyboard(struct wl_client *wl_client, return; if (!(seat_client->seat->accumulated_capabilities & - WL_SEAT_CAPABILITY_POINTER)) { + WL_SEAT_CAPABILITY_KEYBOARD)) { #ifdef HAVE_WL_SEAT_ERROR_MISSING_CAPABILITY wl_resource_post_error(resource, WL_SEAT_ERROR_MISSING_CAPABILITY, - "wl_seat.get_pointer called when no " + "wl_seat.get_keyboard called when no " "keyboard capability has existed"); #endif return; @@ -225,10 +225,10 @@ seat_handle_get_touch(struct wl_client *wl_client, return; if (!(seat_client->seat->accumulated_capabilities & - WL_SEAT_CAPABILITY_POINTER)) { + WL_SEAT_CAPABILITY_TOUCH)) { #ifdef HAVE_WL_SEAT_ERROR_MISSING_CAPABILITY wl_resource_post_error(resource, WL_SEAT_ERROR_MISSING_CAPABILITY, - "wl_seat.get_pointer called when no " + "wl_seat.get_touch called when no " "touch capability has existed"); #endif return; diff --git a/src/libds/seat/seat_touch.c b/src/libds/seat/seat_touch.c index 8a02e94..476c6a8 100644 --- a/src/libds/seat/seat_touch.c +++ b/src/libds/seat/seat_touch.c @@ -233,7 +233,7 @@ seat_client_add_touch_resource(struct ds_seat_client *seat_client, struct wl_resource *resource; resource = wl_resource_create(seat_client->wl_client, - &wl_keyboard_interface, version, id); + &wl_touch_interface, version, id); if (!resource) { wl_client_post_no_memory(seat_client->wl_client); return; -- 2.7.4 From 754af62ee9070b524facf7b758f9d76a4c04aa4a Mon Sep 17 00:00:00 2001 From: "duna.oh" Date: Wed, 18 May 2022 18:39:54 +0900 Subject: [PATCH 08/16] example: add an executable 'tinyds-tdm-libinput' and handle ds_seat Change-Id: I965563b0f783c2f0ef685d9db34236e985edf9eb --- packaging/libds.spec | 1 + src/clients/simple-tbm.c | 168 +++++++ src/examples/meson.build | 17 + src/examples/tinyds-tdm-libinput.c | 992 +++++++++++++++++++++++++++++++++++++ 4 files changed, 1178 insertions(+) create mode 100644 src/examples/tinyds-tdm-libinput.c diff --git a/packaging/libds.spec b/packaging/libds.spec index 45329fe..2ffdca0 100644 --- a/packaging/libds.spec +++ b/packaging/libds.spec @@ -102,6 +102,7 @@ ninja -C builddir install %{_bindir}/tdm-backend %{_bindir}/tinyds-tdm %{_bindir}/ds-simple-tbm +%{_bindir}/tinyds-tdm-libinput %files tizen-keyrouter %manifest %{name}.manifest diff --git a/src/clients/simple-tbm.c b/src/clients/simple-tbm.c index f572b60..4fd9847 100644 --- a/src/clients/simple-tbm.c +++ b/src/clients/simple-tbm.c @@ -48,6 +48,7 @@ struct display { struct wl_compositor *compositor; struct xdg_wm_base *wm_base; struct wl_shm *shm; + struct wl_seat *seat; struct wayland_tbm_client *wl_tbm; bool has_xrgb; }; @@ -329,6 +330,168 @@ static const struct xdg_wm_base_listener xdg_wm_base_listener = { xdg_wm_base_ping, }; +static void pointer_handle_button(void *data, struct wl_pointer *pointer, + uint32_t serial, uint32_t time, uint32_t button, uint32_t state) +{ + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + fprintf(stderr, "pointer_handle_button: PRESSED\n"); + } + else { + fprintf(stderr, "pointer_handle_button: RELEASED\n"); + } +} + +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) +{ + fprintf(stderr, "pointer_handle_enter surface_x:%d, surface_y:%d\n", + wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y)); +} + +static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface) +{ + fprintf(stderr, "pointer_handle_leave\n"); +} + +static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer, + uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) +{ + fprintf(stderr, "pointer_handle_motion surface_x:%d, surface_y:%d\n", + wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y)); +} + +static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) +{ + fprintf(stderr, "pointer_handle_frame\n"); +} + +static struct wl_pointer_listener pointer_listener = { + .enter = pointer_handle_enter, + .leave = pointer_handle_leave, + .motion = pointer_handle_motion, + .button = pointer_handle_button, + .axis = NULL, + .frame = pointer_handle_frame, + .axis_source = NULL, + .axis_stop = NULL, + .axis_discrete = NULL, +}; + +static void touch_handle_down(void *data, struct wl_touch *wl_touch, + uint32_t serial, uint32_t time, struct wl_surface *surface, + int32_t id, wl_fixed_t x, wl_fixed_t y) +{ + fprintf(stderr, "touch_handle_down id:%d, x:%d, y:%d\n", + id, wl_fixed_to_int(x), wl_fixed_to_int(y)); +} + +static void touch_handle_up(void *data, struct wl_touch *wl_touch, + uint32_t serial, uint32_t time, int32_t id) +{ + fprintf(stderr, "touch_handle_up id:%d\n", id); +} + +static void touch_handle_motion(void *data, struct wl_touch *wl_touch, + uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y) +{ + fprintf(stderr, "touch_handle_motion id:%d, x:%d, y:%d\n", + id, wl_fixed_to_int(x), wl_fixed_to_int(y)); +} + +static void touch_handle_frame(void *data, struct wl_touch *wl_touch) +{ + fprintf(stderr, "touch_handle_frame\n"); +} + +static struct wl_touch_listener touch_listener = { + .down = touch_handle_down, + .up = touch_handle_up, + .motion = touch_handle_motion, + .frame = touch_handle_frame, + .cancel = NULL, + .shape = NULL, + .orientation = NULL, +}; + +static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, + uint32_t format, int32_t fd, uint32_t size) +{ + fprintf(stderr, "keyboard_handle_keymap\n"); +} +static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, struct wl_surface *surface, struct wl_array *keys) +{ + fprintf(stderr, "keyboard_handle_enter\n"); +} +static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, struct wl_surface *surface) +{ + fprintf(stderr, "keyboard_handle_leave\n"); +} +static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) +{ + fprintf(stderr, "keyboard_handle_modifiers\n"); +} +static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard, + int32_t rate, int32_t delay) +{ + fprintf(stderr, "keyboard_handle_repeat_info\n"); +} + +static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, uint32_t time, uint32_t key, uint32_t state) +{ + if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { + fprintf(stderr, "keyboard_handle_key: PRESSED\n"); + } else { + fprintf(stderr, "keyboard_handle_key: RELEASED\n"); + } +} + +static struct wl_keyboard_listener keyboard_listener = { + .keymap = keyboard_handle_keymap, + .enter = keyboard_handle_enter, + .leave = keyboard_handle_leave, + .key = keyboard_handle_key, + .modifiers = keyboard_handle_modifiers, + .repeat_info = keyboard_handle_repeat_info, +}; + +static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, + enum wl_seat_capability caps) +{ + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD)) { + struct wl_keyboard *keyboard = wl_seat_get_keyboard(wl_seat); + wl_keyboard_add_listener(keyboard, &keyboard_listener, NULL); + fprintf(stderr, "seat_handle_capabilities: keyboard\n"); + } + if ((caps & WL_SEAT_CAPABILITY_POINTER)) { + struct wl_pointer *pointer = wl_seat_get_pointer(wl_seat); + wl_pointer_add_listener(pointer, &pointer_listener, NULL); + fprintf(stderr, "seat_handle_capabilities: pointer\n"); + } + if ((caps & WL_SEAT_CAPABILITY_TOUCH)) { + struct wl_touch *touch = wl_seat_get_touch(wl_seat); + wl_touch_add_listener(touch, &touch_listener, NULL); + fprintf(stderr, "seat_handle_capabilities: touch\n"); + } +} + +static void seat_handle_name(void *data, struct wl_seat *wl_seat, + const char *name) +{ + fprintf(stderr, "seat_handle_name name:%s\n", name); +} + +const struct wl_seat_listener seat_listener = { + .capabilities = seat_handle_capabilities, + .name = seat_handle_name, +}; + static void registry_handle_global(void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version) @@ -347,6 +510,11 @@ registry_handle_global(void *data, struct wl_registry *registry, d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); wl_shm_add_listener(d->shm, &shm_listener, d); + } else if (strcmp(interface, "wl_seat") == 0) { + d->seat = wl_registry_bind(registry, + id, &wl_seat_interface, 7); + wl_seat_add_listener(d->seat, &seat_listener, d); + fprintf(stderr, "wl_seat bound!\n"); } } diff --git a/src/examples/meson.build b/src/examples/meson.build index 9f2d976..5c5c437 100644 --- a/src/examples/meson.build +++ b/src/examples/meson.build @@ -65,4 +65,21 @@ if get_option('tizen') install_dir: libds_bindir, install : true ) + + tinyds_tdm_libinput_files = [ + 'tinyds-tdm-libinput.c', + 'pixman-helper.c', + 'pixman-tbm-helper.c', + 'tinyds-tdm-renderer.c', + ] + executable('tinyds-tdm-libinput', + tinyds_tdm_libinput_files, + dependencies: [ + common_deps, + dependency('pixman-1', required: true), + dependency('threads', required: true), + ], + install_dir: libds_bindir, + install : true + ) endif diff --git a/src/examples/tinyds-tdm-libinput.c b/src/examples/tinyds-tdm-libinput.c new file mode 100644 index 0000000..f4e9319 --- /dev/null +++ b/src/examples/tinyds-tdm-libinput.c @@ -0,0 +1,992 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define USE_TDM_BUFFER_QUEUE + +#ifdef USE_TDM_BUFFER_QUEUE +#include "pixman-tbm-helper.h" +#include "tinyds-tdm-renderer.h" +#else +#include +#endif + +#include "pixman-helper.h" + +#define TINYDS_UNUSED __attribute__((unused)) + +struct tinyds_output +{ + struct tinyds_server *server; + struct ds_output *ds_output; + struct ds_allocator *allocator; +#ifdef USE_TDM_BUFFER_QUEUE + struct tinyds_renderer renderer; + struct ds_tdm_buffer_queue *buffer_queue; + struct wl_listener buffer_queue_acquirable; +#else + struct ds_swapchain *swapchain; +#endif + struct ds_buffer *front_buffer; + + struct wl_listener output_destroy; + struct wl_listener output_frame; + + int width, height; + + bool drawable; + bool damaged; +}; + +struct tinyds_server +{ + struct ds_tbm_server *tbm_server; + + struct wl_display *display; + + struct ds_backend *backend; + struct ds_backend *input_backend; + struct ds_compositor *compositor; + struct ds_xdg_shell *xdg_shell; + struct ds_seat *seat; + uint32_t seat_caps; + double output_x, output_y; + + struct tinyds_output *output; + struct wl_event_source *stdin_source; + + struct wl_list views; + + struct wl_listener new_output; + struct wl_listener new_input; + struct wl_listener new_xdg_surface; +}; + +struct tinyds_view +{ + struct tinyds_server *server; + + struct tinyds_texture *texture; + struct ds_xdg_surface *xdg_surface; + + struct wl_listener xdg_surface_map; + struct wl_listener xdg_surface_unmap; + struct wl_listener xdg_surface_destroy; + struct wl_listener surface_commit; + struct wl_list link; // tinyds_server::views + + int x, y; + bool mapped; +}; + +struct tinyds_pointer +{ + struct ds_input_device *dev; + struct tinyds_server *server; + + struct tinyds_view *focused_view; + + struct wl_listener destroy; + struct wl_listener motion; //relative + struct wl_listener button; + struct wl_listener frame; +}; + +struct tinyds_keyboard +{ + struct ds_input_device *dev; + struct tinyds_server *server; + + struct wl_listener destroy; + struct wl_listener key; +}; + +struct tinyds_touch +{ + struct ds_input_device *dev; + struct tinyds_server *server; + + struct wl_listener destroy; + struct wl_listener down; + struct wl_listener up; + struct wl_listener motion; +}; + +struct tinyds_server tinyds; + +static bool init_server(struct tinyds_server *server, struct wl_display *display); +static int server_dispatch_stdin(int fd, uint32_t mask, void *data); +static void output_handle_destroy(struct wl_listener *listener, void *data); +static void output_handle_frame(struct wl_listener *listener, void *data); +static void draw_server_with_damage(struct tinyds_server *server); +static void draw_output(struct tinyds_output *output); +static void output_swap_buffer(struct tinyds_output *output, + struct ds_buffer *buffer); +static void view_send_frame_done(struct tinyds_view *view); +#ifdef USE_TDM_BUFFER_QUEUE +static void output_buffer_queue_init(struct tinyds_output *output); +static void output_renderer_init(struct tinyds_output *output); +static void output_draw_with_renderer(struct tinyds_output *output); +#else +static void output_swapchain_init(struct tinyds_output *output, + int width, int height, uint32_t format); +static void output_draw_with_swapchain(struct tinyds_output *output); +static void draw_view(struct tinyds_view *view, pixman_image_t *dst_image); +#endif +static void server_add_keyboard(struct tinyds_server *server, + struct ds_input_device *dev); +static void server_add_pointer(struct tinyds_server *server, + struct ds_input_device *dev); +static void server_add_touch(struct tinyds_server *server, + struct ds_input_device *dev); + +int +main(void) +{ + struct tinyds_server *server = &tinyds; + struct wl_display *display; + struct wl_event_loop *loop; + const char *socket; + bool res; + + ds_log_init(DS_INF, NULL); + + display = wl_display_create(); + assert(display); + + res = init_server(server, display); + assert(res); + + socket = wl_display_add_socket_auto(display); + assert(socket); + + ds_backend_start(server->backend); + ds_backend_start(server->input_backend); + + setenv("WAYLAND_DISPLAY", socket, true); + + ds_inf("Running Wayland compositor on WAYLAND_DISPLAY=%s", socket); + + loop = wl_display_get_event_loop(display); + server->stdin_source = wl_event_loop_add_fd(loop, STDIN_FILENO, + WL_EVENT_READABLE, server_dispatch_stdin, server); + + wl_display_run(display); + + wl_display_destroy_clients(display); + wl_display_destroy(display); + + return 0; +} + +static void +view_handle_xdg_surface_map(struct wl_listener *listener, + void *data TINYDS_UNUSED) +{ + struct tinyds_view *view; + + view = wl_container_of(listener, view, xdg_surface_map); + view->mapped = true; +} + +static void +view_handle_xdg_surface_unmap(struct wl_listener *listener, + void *data TINYDS_UNUSED) +{ + struct tinyds_view *view; + + view = wl_container_of(listener, view, xdg_surface_unmap); + view->mapped = false; +} + +static void +view_handle_xdg_surface_destroy(struct wl_listener *listener, + void *data TINYDS_UNUSED) +{ + struct tinyds_view *view; + struct tinyds_server *server; + + view = wl_container_of(listener, view, xdg_surface_destroy); + server = view->server; + + wl_list_remove(&view->xdg_surface_destroy.link); + wl_list_remove(&view->xdg_surface_map.link); + wl_list_remove(&view->xdg_surface_unmap.link); + wl_list_remove(&view->surface_commit.link); + wl_list_remove(&view->link); + free(view); + + draw_server_with_damage(server); +} + +static void +view_handle_surface_commit(struct wl_listener *listener, + void *data TINYDS_UNUSED) +{ + struct tinyds_view *view; + + view = wl_container_of(listener, view, surface_commit); + draw_server_with_damage(view->server); +} + +static void +server_new_xdg_surface(struct wl_listener *listener, void *data) +{ + struct tinyds_server *server; + struct tinyds_view *view; + struct ds_xdg_surface *xdg_surface; + + server = wl_container_of(listener, server, new_xdg_surface); + xdg_surface = data; + + ds_inf("New xdg_surface(%p)", (void *)xdg_surface); + + view = calloc(1, sizeof *view); + assert(view); + + view->server = server; + view->xdg_surface = xdg_surface; + + view->xdg_surface_map.notify = view_handle_xdg_surface_map; + ds_xdg_surface_add_map_listener(xdg_surface, + &view->xdg_surface_map); + + view->xdg_surface_unmap.notify = view_handle_xdg_surface_unmap; + ds_xdg_surface_add_unmap_listener(xdg_surface, + &view->xdg_surface_unmap); + + view->xdg_surface_destroy.notify = view_handle_xdg_surface_destroy; + ds_xdg_surface_add_destroy_listener(xdg_surface, + &view->xdg_surface_destroy); + + view->surface_commit.notify = view_handle_surface_commit; + ds_surface_add_commit_listener( + ds_xdg_surface_get_surface(xdg_surface), + &view->surface_commit); + + wl_list_insert(server->views.prev, &view->link); + + view->x = rand() % 1000; + view->y = rand() % 500; +} + +static void +backend_handle_new_output(struct wl_listener *listener, void *data) +{ + struct tinyds_server *server; + struct tinyds_output *output; + struct ds_output *ds_output; + const struct ds_output_mode *mode; + + server = wl_container_of(listener, server, new_output); + ds_output = data; + + ds_inf("New output(%p)", ds_output); + + if (server->output) + return; + + mode = ds_output_get_preferred_mode(ds_output); + ds_output_set_mode(ds_output, mode); + + output = calloc(1, sizeof *output); + if (!output) + return; + + output->server = server; + output->ds_output = ds_output; + output->width = mode->width; + output->height = mode->height; + output->drawable = true; + output->damaged = true; + +#ifdef USE_TDM_BUFFER_QUEUE + output_buffer_queue_init(output); + output_renderer_init(output); +#else + output_swapchain_init(output, mode->width, mode->height, + DRM_FORMAT_XRGB8888); +#endif + + output->output_destroy.notify = output_handle_destroy; + ds_output_add_destroy_listener(ds_output, &output->output_destroy); + + output->output_frame.notify = output_handle_frame; + ds_output_add_frame_listener(ds_output, &output->output_frame); + + server->output = output; + + draw_output(output); +} + +static void +backend_handle_new_input(struct wl_listener *listener, void *data) +{ + struct tinyds_server *server; + struct ds_input_device *dev = data; + enum ds_input_device_type dev_type; + + server = wl_container_of(listener, server, new_input); + + dev_type = ds_input_device_get_type(dev); + + switch (dev_type) { + case DS_INPUT_DEVICE_KEYBOARD: + server_add_keyboard(server, dev); + server->seat_caps |= WL_SEAT_CAPABILITY_KEYBOARD; + break; + case DS_INPUT_DEVICE_TOUCH: + server_add_touch(server, dev); + server->seat_caps |= WL_SEAT_CAPABILITY_TOUCH; + break; + case DS_INPUT_DEVICE_POINTER: + server_add_pointer(server, dev); + server->seat_caps |= WL_SEAT_CAPABILITY_POINTER; + break; + default: + ds_err("Unknown type(%d) of ds_input_device", dev_type); + break; + } + + ds_seat_set_capabilities(server->seat, server->seat_caps); +} + +static bool +init_server(struct tinyds_server *server, struct wl_display *display) +{ + server->display = display; + + wl_list_init(&server->views); + + if (wl_display_init_shm(display) != 0) + return false; + + server->backend = ds_tdm_backend_create(display); + if (!server->backend) + return false; + + server->input_backend = ds_libinput_backend_create(display); + if (!server->input_backend) + return false; + + server->new_output.notify = backend_handle_new_output; + ds_backend_add_new_output_listener(server->backend, + &server->new_output); + + server->new_input.notify = backend_handle_new_input; + ds_backend_add_new_input_listener(server->input_backend, &server->new_input); + + server->compositor = ds_compositor_create(display); + if (!server->compositor) + goto err; + + server->tbm_server = ds_tbm_server_create(display); + if (!server->tbm_server) + goto err; + + server->xdg_shell = ds_xdg_shell_create(display); + if (!server->xdg_shell) + goto err; + + server->new_xdg_surface.notify = server_new_xdg_surface; + ds_xdg_shell_add_new_surface_listener(server->xdg_shell, + &server->new_xdg_surface); + + server->seat = ds_seat_create(display, "seat0" /* arbitrary name */); + if (!server->seat) + goto err; + server->seat_caps = 0; + + return true; + +err: + ds_backend_destroy(server->backend); + ds_backend_destroy(server->input_backend); + + return false; +} + +static void +output_handle_destroy(struct wl_listener *listener, void *data TINYDS_UNUSED) +{ + struct tinyds_output *output = + wl_container_of(listener, output, output_destroy); + + wl_list_remove(&output->output_destroy.link); + wl_list_remove(&output->output_frame.link); + + if (output->front_buffer) + ds_buffer_unlock(output->front_buffer); + +#ifdef USE_TDM_BUFFER_QUEUE + fini_renderer(&output->renderer); +#else + if (output->swapchain) + ds_swapchain_destroy(output->swapchain); + + if (output->allocator) + ds_allocator_destroy(output->allocator); +#endif + + wl_display_terminate(output->server->display); + + output->server->output = NULL; + + free(output); +} + +static void +output_handle_frame(struct wl_listener *listener, void *data TINYDS_UNUSED) +{ + struct tinyds_output *output = + wl_container_of(listener, output, output_frame); + + output->drawable = true; + draw_output(output); +} + +static void +draw_server_with_damage(struct tinyds_server *server) +{ + server->output->damaged = true; + draw_output(server->output); +} + +#ifdef USE_TDM_BUFFER_QUEUE +static void +output_handle_buffer_queue_acquirable(struct wl_listener *listener, + void *data TINYDS_UNUSED) +{ + struct tinyds_output *output; + struct ds_buffer *buffer; + + output = wl_container_of(listener, output, buffer_queue_acquirable); + + buffer = ds_tdm_buffer_queue_acquire(output->buffer_queue); + assert(buffer); + + output_swap_buffer(output, buffer); +} + +static void +output_buffer_queue_init(struct tinyds_output *output) +{ + struct ds_tdm_output *tdm_output; + + tdm_output = ds_tdm_output_from_output(output->ds_output); + assert(tdm_output); + + output->buffer_queue = ds_tdm_output_get_buffer_queue(tdm_output); + assert(output->buffer_queue); + + output->buffer_queue_acquirable.notify = + output_handle_buffer_queue_acquirable; + ds_tdm_buffer_queue_add_acquirable_listener(output->buffer_queue, + &output->buffer_queue_acquirable); +} + +static void +output_renderer_init(struct tinyds_output *output) +{ + init_renderer(&output->renderer); + + renderer_set_surface_queue(&output->renderer, + ds_tdm_buffer_queue_get_native_queue(output->buffer_queue)); + + renderer_set_bg_color(&output->renderer, 80, 80, 80); +} + +static void +output_draw_with_renderer(struct tinyds_output *output) +{ + struct tinyds_view *view; + + ds_dbg(">> BEGIN UPDATE TEXTURES"); + + wl_list_for_each(view, &output->server->views, link) { + struct ds_buffer *ds_buffer; + struct ds_tbm_client_buffer *tbm_buffer; + tbm_surface_h surface; + + if (!view->mapped) + continue; + + ds_buffer = ds_surface_get_buffer( + ds_xdg_surface_get_surface(view->xdg_surface)); + assert(ds_buffer); + + tbm_buffer = ds_tbm_client_buffer_from_buffer(ds_buffer); + assert(tbm_buffer); + + surface = ds_tbm_client_buffer_get_tbm_surface(tbm_buffer); + + renderer_add_texture(&output->renderer, surface, view->x, view->y); + + view_send_frame_done(view); + } + + ds_dbg("<< END UPDATE TEXTURES"); + + renderer_draw(&output->renderer); + +} +#else +static void +output_swapchain_init(struct tinyds_output *output, + int width, int height, uint32_t format); + +{ + output->allocator = ds_tbm_allocator_create(); + assert(output->allocator); + + output->swapchain = ds_swapchain_create(output->allocator, + width, height, format); + assert(output->swapchain); +} + +static void +output_draw_with_swapchain(struct tinyds_output *output) +{ + struct tinyds_view *view; + struct ds_buffer *output_buffer; + pixman_image_t *output_image; + + output_buffer = ds_swapchain_acquire(output->swapchain, NULL); + if (!output_buffer) + return; + + output_image = pixman_image_from_buffer(output_buffer, + DS_BUFFER_DATA_PTR_ACCESS_WRITE); + if (!output_image) { + ds_buffer_unlock(output_buffer); + return; + } + + pixman_image_fill_color(output_image, 80, 80, 80); + + wl_list_for_each(view, &output->server->views, link) { + if (!view->mapped) + continue; + draw_view(view, output_image); + } + pixman_image_unref(output_image); + + output_swap_buffer(output, output_buffer); +} + +static void +draw_view(struct tinyds_view *view, pixman_image_t *dst_image) +{ + struct ds_buffer *buffer; + pixman_image_t *src_image; + + buffer = ds_surface_get_buffer( + ds_xdg_surface_get_surface(view->xdg_surface)); + if (!buffer) + return; + + src_image = pixman_image_from_buffer(buffer, + DS_BUFFER_DATA_PTR_ACCESS_READ); + pixman_image_composite32(PIXMAN_OP_OVER, + src_image, + NULL, + dst_image, + 0, 0, 0, 0, + view->x, view->y, + pixman_image_get_width(src_image), + pixman_image_get_height(src_image)); + pixman_image_unref(src_image); + + view_send_frame_done(view); +} +#endif + +static void +draw_output(struct tinyds_output *output) +{ + + if (!output->drawable || !output->damaged) + return; + +#ifdef USE_TDM_BUFFER_QUEUE + output_draw_with_renderer(output); +#else + output_draw_with_swapchain(output); +#endif + + output->drawable = false; + output->damaged = false; +} + +static void +output_swap_buffer(struct tinyds_output *output, struct ds_buffer *buffer) +{ + ds_output_attach_buffer(output->ds_output, buffer); + ds_output_commit(output->ds_output); + + if (output->front_buffer) + ds_buffer_unlock(output->front_buffer); + output->front_buffer = buffer; +} + +static void +view_send_frame_done(struct tinyds_view *view) +{ + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + ds_surface_send_frame_done(ds_xdg_surface_get_surface(view->xdg_surface), + &now); +} + +static int +server_dispatch_stdin(int fd, uint32_t mask, void *data) +{ + struct tinyds_server *server = data; + + wl_display_terminate(server->display); + + return 1; +} + + +static void +keyboard_handle_device_destroy(struct wl_listener *listener, void *data) +{ + struct tinyds_keyboard *kbd; + + kbd = wl_container_of(listener, kbd, destroy); + + ds_inf("Keyboard(%p) destroyed", kbd); + + wl_list_remove(&kbd->destroy.link); + wl_list_remove(&kbd->key.link); + + free(kbd); +} + +static void +keyboard_handle_key(struct wl_listener *listener, void *data) +{ + struct tinyds_keyboard *kbd; + struct ds_event_keyboard_key *event = data; + + kbd = wl_container_of(listener, kbd, key); + + ds_inf("Keyboard(%p) event key: keycode(%d), state(%d), time_msec(%d), " + "update_state(%d)", kbd->dev, + event->keycode, event->state, event->time_msec, + event->update_state); + //TODO: + //ds_seat_keyboard_notify_key() +} + +static void +server_add_keyboard(struct tinyds_server *server, struct ds_input_device *dev) +{ + struct tinyds_keyboard *kbd; + + kbd = calloc(1, sizeof *kbd); + assert(kbd); + + kbd->dev = dev; + kbd->server = server; + + kbd->destroy.notify = keyboard_handle_device_destroy; + ds_input_device_add_destroy_listener(dev, &kbd->destroy); + + kbd->key.notify = keyboard_handle_key; + ds_keyboard_add_key_listener(ds_input_device_get_keyboard(dev), &kbd->key); + + ds_inf("Keyboard(%p) added", kbd); +} + +static struct tinyds_view * +server_view_at(struct tinyds_server *server, double lx, double ly, + double *sx, double *sy) +{ + struct tinyds_view *view; + struct ds_surface *surface; + struct ds_buffer *buffer; + int x, y, w = 0, h = 0; + + wl_list_for_each(view, &server->views, link) { + surface = ds_xdg_surface_get_surface(view->xdg_surface); + buffer = ds_surface_get_buffer(surface); + ds_buffer_get_size(buffer, &w, &h); + + x = view->x; + y = view->y; + + if (lx >= x && lx <= x + w && ly >= y && ly <= y + h) { + *sx = lx - x; + *sy = ly - y; + + return view; + } + } + + return NULL; +} + +static void +touch_handle_device_destroy(struct wl_listener *listener, void *data) +{ + struct tinyds_touch *touch; + + touch = wl_container_of(listener, touch, destroy); + + ds_inf("Touch(%p) destroyed", touch); + + wl_list_remove(&touch->destroy.link); + wl_list_remove(&touch->down.link); + wl_list_remove(&touch->up.link); + wl_list_remove(&touch->motion.link); + + free(touch); +} + +static void +touch_handle_down(struct wl_listener *listener, void *data) +{ + struct ds_event_touch_down *event = data; + struct tinyds_touch *touch; + struct tinyds_view *view; + struct tinyds_server *server; + double sx = 0.f, sy = 0.f; + + touch = wl_container_of(listener, touch, down); + + server = touch->server; + server->output_x = event->x * server->output->width; + server->output_y = event->y * server->output->height; + + ds_inf("Touch(%p) event down: id(%d) x %.3f y %.3f output_x %.1f output_y %.1f", + touch->dev, event->id, event->x, event->y, server->output_x, server->output_y); + + view = server_view_at(server, server->output_x, server->output_y, &sx, &sy); + + if (view) { + ds_seat_touch_notify_down(touch->server->seat, ds_xdg_surface_get_surface(view->xdg_surface), + event->time_msec, event->id, sx, sy); + } +} + +static void +touch_handle_up(struct wl_listener *listener, void *data) +{ + struct ds_event_touch_up *event = data; + struct tinyds_touch *touch; + + touch = wl_container_of(listener, touch, up); + + ds_inf("Touch(%p) event up: id(%d) time_msec(%d)", + touch->dev, event->id, event->time_msec); + + ds_seat_touch_notify_up(touch->server->seat, event->time_msec, event->id); +} + +static void +touch_handle_motion(struct wl_listener *listener, void *data) +{ + struct ds_event_touch_motion *event = data; + struct tinyds_touch *touch; + struct tinyds_view *view; + struct tinyds_server *server; + double sx = 0.f, sy = 0.f; + + touch = wl_container_of(listener, touch, motion); + + server = touch->server; + server->output_x = event->x * server->output->width; + server->output_y = event->y * server->output->height; + + ds_inf("Touch(%p) event motion: id(%d) x %.3f y %.3f output_x %.1f output_y %.1f", + touch->dev, event->id, event->x, event->y, server->output_x, server->output_y); + + view = server_view_at(server, server->output_x, server->output_y, &sx, &sy); + + if (view) { + ds_seat_touch_notify_motion(server->seat, event->time_msec, + event->id, sx, sy); + } +} + +static void +server_add_touch(struct tinyds_server *server, struct ds_input_device *dev) +{ + struct tinyds_touch *touch; + + touch = calloc(1, sizeof *touch); + assert(touch); + + touch->dev = dev; + touch->server = server; + + touch->destroy.notify = touch_handle_device_destroy; + ds_input_device_add_destroy_listener(dev, &touch->destroy); + + touch->down.notify = touch_handle_down; + ds_touch_add_down_listener(ds_input_device_get_touch(dev), &touch->down); + + touch->up.notify = touch_handle_up; + ds_touch_add_up_listener(ds_input_device_get_touch(dev), &touch->up); + + touch->motion.notify = touch_handle_motion; + ds_touch_add_motion_listener(ds_input_device_get_touch(dev), &touch->motion); + + ds_inf("Touch(%p) added", touch); +} + +static void +pointer_handle_device_destroy(struct wl_listener *listener, void *data) +{ + struct tinyds_pointer *pointer; + + pointer = wl_container_of(listener, pointer, destroy); + + ds_inf("Pointer(%p) destroyed", pointer); + + wl_list_remove(&pointer->destroy.link); + wl_list_remove(&pointer->motion.link); + wl_list_remove(&pointer->button.link); + wl_list_remove(&pointer->frame.link); + + free(pointer); +} + +static void +pointer_handle_motion(struct wl_listener *listener, void *data) +{ + struct tinyds_pointer *pointer; + struct ds_event_pointer_motion *event = data; + struct tinyds_view *view; + struct tinyds_server *server; + int ow = 0, oh = 0; + double sx, sy; + + pointer = wl_container_of(listener, pointer, motion); + + server = pointer->server; + if (server->output) { + ow = server->output->width; + oh = server->output->height; + } + + if (server->output_x + event->delta_x >= ow) + server->output_x = ow; + else if(server->output_x + event->delta_x <= 0.f) + server->output_x = 0.f; + else + server->output_x = server->output_x + event->delta_x ; + if (server->output_y + event->delta_y >= oh) + server->output_y = oh; + else if(server->output_y + event->delta_y <= 0.f) + server->output_y = 0.f; + else + server->output_y = server->output_y + event->delta_y ; + + ds_inf("Pointer(%p) motion: (delta_x %.1f delta_y %.1f) output_x %.1f output_y %.1f", + pointer, event->delta_x, event->delta_y, server->output_x, server->output_y); + + view = server_view_at(pointer->server, server->output_x, server->output_y, &sx, &sy); + + if (pointer->focused_view != view) { + if (pointer->focused_view) { + ds_inf("Clear pointer focus from view(%p)", pointer->focused_view); + ds_seat_pointer_notify_clear_focus(pointer->server->seat); + pointer->focused_view = NULL; + } + + if (view) { + ds_inf("Set pointer focus to view(%p)", view); + ds_seat_pointer_notify_enter(pointer->server->seat, + ds_xdg_surface_get_surface(view->xdg_surface), sx, sy); + pointer->focused_view = view; + } + } + + if (view) { + ds_seat_pointer_notify_motion(pointer->server->seat, + event->time_msec, sx, sy); + } +} + +static void +pointer_handle_button(struct wl_listener *listener, void *data) +{ + struct tinyds_pointer *pointer; + struct ds_event_pointer_button *event = data; + + pointer = wl_container_of(listener, pointer, button); + + ds_inf("Pointer(%p) button(%d): state(%s) time(%d)", + pointer, event->button, + (event->state == DS_BUTTON_PRESSED) ? "Pressed" : "Released", + event->time_msec); + + ds_seat_pointer_notify_button(pointer->server->seat, event->time_msec, event->button, event->state); +} + +static void +pointer_handle_frame(struct wl_listener *listener, void *data) +{ + struct tinyds_pointer *pointer; + + pointer = wl_container_of(listener, pointer, frame); + + ds_inf("Pointer(%p) frame", pointer); + ds_seat_pointer_notify_frame(pointer->server->seat); +} + +static void +server_add_pointer(struct tinyds_server *server, struct ds_input_device *dev) +{ + struct tinyds_pointer *pointer; + + pointer = calloc(1, sizeof *pointer); + assert(pointer); + + pointer->dev = dev; + pointer->server = server; + server->output_x = 200; + server->output_y = 200; + + pointer->destroy.notify = pointer_handle_device_destroy; + ds_input_device_add_destroy_listener(dev, &pointer->destroy); + + pointer->motion.notify = pointer_handle_motion; + ds_pointer_add_motion_listener(ds_input_device_get_pointer(dev), + &pointer->motion); + + pointer->button.notify = pointer_handle_button; + ds_pointer_add_button_listener(ds_input_device_get_pointer(dev), + &pointer->button); + + pointer->frame.notify = pointer_handle_frame; + ds_pointer_add_frame_listener(ds_input_device_get_pointer(dev), + &pointer->frame); + + ds_inf("Pointer(%p) added", pointer); +} -- 2.7.4 From e38d54bf0825e4aa061e6d96ab925daec94b20e6 Mon Sep 17 00:00:00 2001 From: "duna.oh" Date: Thu, 19 May 2022 10:03:25 +0900 Subject: [PATCH 09/16] libinput: get event time from libinput_event Change-Id: I51c5ef5ca3d3a7b1bd27172617e484303205c46c --- src/libds/backend/libinput/backend.c | 4 ++++ src/libds/backend/libinput/backend.h | 2 ++ src/libds/backend/libinput/keyboard.c | 12 ++++++------ src/libds/backend/libinput/pointer.c | 9 ++++++--- src/libds/backend/libinput/touch.c | 9 ++++++--- 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/libds/backend/libinput/backend.c b/src/libds/backend/libinput/backend.c index f3ee044..b6dc4f1 100644 --- a/src/libds/backend/libinput/backend.c +++ b/src/libds/backend/libinput/backend.c @@ -225,3 +225,7 @@ libinput_backend_handle_display_destroy(struct wl_listener *listener, wl_container_of(listener, libinput_backend, display_destroy); libinput_backend_destroy(libinput_backend); } + +uint32_t usec_to_msec(uint64_t usec) { + return (uint32_t)(usec / 1000); +} \ No newline at end of file diff --git a/src/libds/backend/libinput/backend.h b/src/libds/backend/libinput/backend.h index 6fa2122..4d1c0ce 100644 --- a/src/libds/backend/libinput/backend.h +++ b/src/libds/backend/libinput/backend.h @@ -34,6 +34,8 @@ struct ds_libinput_input_device struct wl_list link; //ds_libinput_backend.devices }; +uint32_t usec_to_msec(uint64_t usec); + struct ds_libinput_backend * libinput_backend_from_backend(struct ds_backend *backend); diff --git a/src/libds/backend/libinput/keyboard.c b/src/libds/backend/libinput/keyboard.c index 53db0c9..9376f11 100644 --- a/src/libds/backend/libinput/keyboard.c +++ b/src/libds/backend/libinput/keyboard.c @@ -9,6 +9,7 @@ handle_keyboard_key(struct libinput_event *event, uint32_t key; enum libinput_key_state li_state; enum wl_keyboard_key_state state = WL_KEYBOARD_KEY_STATE_PRESSED; + struct ds_event_keyboard_key ds_event = {0}; kbevent = libinput_event_get_keyboard_event(event); @@ -24,12 +25,11 @@ handle_keyboard_key(struct libinput_event *event, ds_log(DS_DBG, "Keyboard(%p) key event key:%d, state:%s", kbd, key, (state == WL_KEYBOARD_KEY_STATE_PRESSED) ? "PRESSED":"RELEASED"); - struct ds_event_keyboard_key ds_event = { - .keycode = key, - .state = state, - .time_msec = -1, - .update_state = false, - }; + ds_event.keycode = key; + ds_event.state = state; + ds_event.time_msec = usec_to_msec( + libinput_event_keyboard_get_time_usec(kbevent)); + ds_event.update_state = false; ds_keyboard_notify_key(kbd, &ds_event); } diff --git a/src/libds/backend/libinput/pointer.c b/src/libds/backend/libinput/pointer.c index c118d06..efdbaa7 100644 --- a/src/libds/backend/libinput/pointer.c +++ b/src/libds/backend/libinput/pointer.c @@ -17,7 +17,8 @@ handle_pointer_motion(struct libinput_event *event, ds_log(DS_DBG, "pointer motion event delta_x:%f, delta_y:%f", delta_x, delta_y); ds_event.device = NULL; - ds_event.time_msec = -1; + ds_event.time_msec = usec_to_msec( + libinput_event_pointer_get_time_usec(pevent)); ds_event.delta_x = delta_x; ds_event.delta_y = delta_y; @@ -40,7 +41,8 @@ handle_pointer_motion_abs(struct libinput_event *event, ds_log(DS_DBG, "Pointer(%p) motion abs event x:%f, y:%f", pointer, x, y); ds_event.device = NULL; - ds_event.time_msec = -1; + ds_event.time_msec = usec_to_msec( + libinput_event_pointer_get_time_usec(pevent)); ds_event.x = x; ds_event.y = y; @@ -75,7 +77,8 @@ handle_pointer_button(struct libinput_event *event, (state == DS_BUTTON_PRESSED) ? "PRESSED" : "RELEASED"); ds_event.device = NULL; - ds_event.time_msec = -1; + ds_event.time_msec = usec_to_msec( + libinput_event_pointer_get_time_usec(pevent)); ds_event.button = button; ds_event.state = state; diff --git a/src/libds/backend/libinput/touch.c b/src/libds/backend/libinput/touch.c index 08b7b80..fd94c0b 100644 --- a/src/libds/backend/libinput/touch.c +++ b/src/libds/backend/libinput/touch.c @@ -19,7 +19,8 @@ handle_touch_down(struct libinput_event *event, ds_log(DS_DBG, "touch down event id:%d, x:%f, y:%f", touch_id, x, y); ds_event.device = NULL; - ds_event.time_msec = -1; + ds_event.time_msec = usec_to_msec( + libinput_event_touch_get_time_usec(tevent)); ds_event.id = touch_id; ds_event.x = x; ds_event.y = y; @@ -42,7 +43,8 @@ handle_touch_up(struct libinput_event *event, ds_log(DS_DBG, "touch up event id:%d", touch_id); ds_event.device = NULL; - ds_event.time_msec = -1; + ds_event.time_msec = usec_to_msec( + libinput_event_touch_get_time_usec(tevent)); ds_event.id = touch_id; wl_signal_emit(&touch->events.up, &ds_event); @@ -66,7 +68,8 @@ handle_touch_motion(struct libinput_event *event, ds_log(DS_DBG, "touch motion event id:%d, x:%f, y:%f", touch_id, x, y); ds_event.device = NULL; - ds_event.time_msec = -1; + ds_event.time_msec = usec_to_msec( + libinput_event_touch_get_time_usec(tevent)); ds_event.id = touch_id; ds_event.x = x; ds_event.y = y; -- 2.7.4 From bea3faea6e531643e9a9b037942b2345fef1a225 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Thu, 19 May 2022 19:34:06 +0900 Subject: [PATCH 10/16] keyrouter: change the prefix of the internal functions remove the tizen prefix. Change-Id: I060071ea4593d2dca366965b9ad9e94dc9d0c98f --- src/libds-tizen/keyrouter/keyrouter.c | 42 +++++++++++++++--------------- src/libds-tizen/keyrouter/keyrouter.h | 16 ++++++------ src/libds-tizen/keyrouter/keyrouter_grab.c | 16 ++++++------ 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/libds-tizen/keyrouter/keyrouter.c b/src/libds-tizen/keyrouter/keyrouter.c index eaa831a..6b5b220 100644 --- a/src/libds-tizen/keyrouter/keyrouter.c +++ b/src/libds-tizen/keyrouter/keyrouter.c @@ -13,13 +13,13 @@ #include "keyrouter.h" static void -tizen_keyrouter_bind(struct wl_client *client, void *data, uint32_t version, +keyrouter_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id); static bool keyrouter_check_privilege(struct ds_tizen_keyrouter_client *keyrouter_client, struct wl_client *client, uint32_t mode, uint32_t keycode); static void -tizen_keyrouter_options_set(struct ds_tizen_keyrouter *keyrouter); +keyrouter_options_set(struct ds_tizen_keyrouter *keyrouter); static void keyrouter_handle_display_destroy(struct wl_listener *listener, void *data) @@ -40,7 +40,7 @@ keyrouter_handle_display_destroy(struct wl_listener *listener, void *data) wl_global_destroy(keyrouter->global); - tizen_keyrouter_grab_destroy(keyrouter->keyrouter_grab); + keyrouter_grab_destroy(keyrouter->keyrouter_grab); free(keyrouter); } @@ -55,7 +55,7 @@ ds_tizen_keyrouter_create(struct wl_display *display) return NULL; } - keyrouter->keyrouter_grab = tizen_keyrouter_grab_create(); + keyrouter->keyrouter_grab = keyrouter_grab_create(); if (keyrouter->keyrouter_grab == NULL) { ds_err("Failed to create keyrouter."); free(keyrouter); @@ -63,9 +63,9 @@ ds_tizen_keyrouter_create(struct wl_display *display) } keyrouter->global = wl_global_create(display, &tizen_keyrouter_interface, - 2, keyrouter, tizen_keyrouter_bind); + 2, keyrouter, keyrouter_bind); if (!keyrouter->global) { - tizen_keyrouter_grab_destroy(keyrouter->keyrouter_grab); + keyrouter_grab_destroy(keyrouter->keyrouter_grab); free(keyrouter); return NULL; } @@ -77,7 +77,7 @@ ds_tizen_keyrouter_create(struct wl_display *display) keyrouter->display_destroy.notify = keyrouter_handle_display_destroy; wl_display_add_destroy_listener(display, &keyrouter->display_destroy); - tizen_keyrouter_options_set(keyrouter); + keyrouter_options_set(keyrouter); if (!tizen_security_init()) { ds_inf("tizen_security_init() is not sucessful. keyrouter works without security."); @@ -115,7 +115,7 @@ keyrouter_handle_keygrab_set(struct wl_client *client, return; } - res = tizen_keyrouter_grab_grab_key(keyrouter->keyrouter_grab, + res = keyrouter_grab_grab_key(keyrouter->keyrouter_grab, mode, key, (void *)client); if (res == TIZEN_KEYROUTER_ERROR_NONE && keyrouter_client->grabbed != true) keyrouter_client->grabbed = true; @@ -136,7 +136,7 @@ keyrouter_handle_keygrab_unset(struct wl_client *client, keyrouter = keyrouter_client->keyrouter; /* ungrab TOP POSITION grab first, this grab mode is not check privilege */ - tizen_keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, + keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, TIZEN_KEYROUTER_MODE_TOPMOST, key, (void *)client); ret = keyrouter_check_privilege(keyrouter_client, @@ -147,13 +147,13 @@ keyrouter_handle_keygrab_unset(struct wl_client *client, return; } - tizen_keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, + keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, TIZEN_KEYROUTER_MODE_EXCLUSIVE, key, (void *)client); - tizen_keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, + keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, TIZEN_KEYROUTER_MODE_OVERRIDABLE_EXCLUSIVE, key, (void *)client); - tizen_keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, + keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, TIZEN_KEYROUTER_MODE_TOPMOST, key, (void *)client); - tizen_keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, + keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, TIZEN_KEYROUTER_MODE_SHARED, key, (void *)client); tizen_keyrouter_send_keygrab_notify(resource, surface, key, TIZEN_KEYROUTER_MODE_NONE, res); @@ -205,7 +205,7 @@ keyrouter_handle_keygrab_set_list(struct wl_client *client, if (ret == false) { grab_data->err = TIZEN_KEYROUTER_ERROR_NO_PERMISSION; } else { - res = tizen_keyrouter_grab_grab_key(keyrouter->keyrouter_grab, + res = keyrouter_grab_grab_key(keyrouter->keyrouter_grab, grab_data->mode, grab_data->key, (void *)client); if (res == TIZEN_KEYROUTER_ERROR_NONE && keyrouter_client->grabbed != true) keyrouter_client->grabbed = true; @@ -240,7 +240,7 @@ keyrouter_handle_keygrab_unset_list(struct wl_client *client, } wl_array_for_each(ungrab_data, ungrab_list) { - tizen_keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, + keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, TIZEN_KEYROUTER_MODE_TOPMOST, ungrab_data->key, (void *)client); ret = keyrouter_check_privilege(keyrouter_client, client, @@ -248,16 +248,16 @@ keyrouter_handle_keygrab_unset_list(struct wl_client *client, if (!ret) { ungrab_data->err = TIZEN_KEYROUTER_ERROR_NO_PERMISSION; } else { - tizen_keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, + keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, TIZEN_KEYROUTER_MODE_EXCLUSIVE, ungrab_data->key, (void *)client); - tizen_keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, + keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, TIZEN_KEYROUTER_MODE_OVERRIDABLE_EXCLUSIVE, ungrab_data->key, (void *)client); - tizen_keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, + keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, TIZEN_KEYROUTER_MODE_TOPMOST, ungrab_data->key, (void *)client); - tizen_keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, + keyrouter_grab_ungrab_key(keyrouter->keyrouter_grab, TIZEN_KEYROUTER_MODE_SHARED, ungrab_data->key, (void *)client); ungrab_data->err = TIZEN_KEYROUTER_ERROR_NONE; @@ -329,7 +329,7 @@ keyrouter_handle_resource_destory(struct wl_resource *resource) } static void -tizen_keyrouter_bind(struct wl_client *client, void *data, uint32_t version, +keyrouter_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct ds_tizen_keyrouter *keyrouter = data; @@ -386,7 +386,7 @@ keyrouter_check_privilege(struct ds_tizen_keyrouter_client *keyrouter_client, } static void -tizen_keyrouter_options_set(struct ds_tizen_keyrouter *keyrouter) +keyrouter_options_set(struct ds_tizen_keyrouter *keyrouter) { FILE *file; int keycode; diff --git a/src/libds-tizen/keyrouter/keyrouter.h b/src/libds-tizen/keyrouter/keyrouter.h index c8dfa0f..ffbb3cd 100644 --- a/src/libds-tizen/keyrouter/keyrouter.h +++ b/src/libds-tizen/keyrouter/keyrouter.h @@ -83,26 +83,26 @@ struct ds_tizen_ungrab_data }; struct ds_tizen_keyrouter_grab * -tizen_keyrouter_grab_create(void); +keyrouter_grab_create(void); void -tizen_keyrouter_grab_destroy(struct ds_tizen_keyrouter_grab *keyrouter_grab); +keyrouter_grab_destroy(struct ds_tizen_keyrouter_grab *keyrouter_grab); int -tizen_keyrouter_grab_grab_key(struct ds_tizen_keyrouter_grab *keyrouter_grab, +keyrouter_grab_grab_key(struct ds_tizen_keyrouter_grab *keyrouter_grab, int type, int keycode, void *data); void -tizen_keyrouter_grab_ungrab_key(struct ds_tizen_keyrouter_grab *keyrouter_grab, +keyrouter_grab_ungrab_key(struct ds_tizen_keyrouter_grab *keyrouter_grab, int type, int keycode, void *data); int -tizen_keyrouter_grab_key_process(struct ds_tizen_keyrouter_grab *keyrouter_grab, +keyrouter_grab_key_process(struct ds_tizen_keyrouter_grab *keyrouter_grab, int keycode, int pressed, struct wl_list *delivery_list); void -tizen_keyrouter_grab_set_focus_client(struct ds_tizen_keyrouter_grab *keyrouter_grab, +keyrouter_grab_set_focus_client(struct ds_tizen_keyrouter_grab *keyrouter_grab, void *focus_client); void -tizen_keyrouter_grab_set_top_client(struct ds_tizen_keyrouter_grab *keyrouter_grab, +keyrouter_grab_set_top_client(struct ds_tizen_keyrouter_grab *keyrouter_grab, void *top_client); bool -tizen_keyrouter_grab_check_grabbed_key(struct ds_tizen_keyrouter_grab *keyrouter_grab, +keyrouter_grab_check_grabbed_key(struct ds_tizen_keyrouter_grab *keyrouter_grab, int keycode); #endif diff --git a/src/libds-tizen/keyrouter/keyrouter_grab.c b/src/libds-tizen/keyrouter/keyrouter_grab.c index 873f4b1..f5467ae 100644 --- a/src/libds-tizen/keyrouter/keyrouter_grab.c +++ b/src/libds-tizen/keyrouter/keyrouter_grab.c @@ -24,7 +24,7 @@ keyrouter_grab_get_grabbed_list(struct ds_tizen_keyrouter_grab *keyrouter_grab, } bool -tizen_keyrouter_grab_check_grabbed_key(struct ds_tizen_keyrouter_grab *keyrouter_grab, int keycode) +keyrouter_grab_check_grabbed_key(struct ds_tizen_keyrouter_grab *keyrouter_grab, int keycode) { struct wl_list *list; @@ -101,7 +101,7 @@ keyrouter_grab_check_grabbed(struct ds_tizen_keyrouter_grab *keyrouter_grab, } void -tizen_keyrouter_grab_set_focus_client(struct ds_tizen_keyrouter_grab *keyrouter_grab, +keyrouter_grab_set_focus_client(struct ds_tizen_keyrouter_grab *keyrouter_grab, void *focus_client) { keyrouter_grab->focus_client = focus_client; @@ -113,7 +113,7 @@ tizen_keyrouter_grab_set_focus_client(struct ds_tizen_keyrouter_grab *keyrouter_ } void -tizen_keyrouter_grab_set_top_client(struct ds_tizen_keyrouter_grab *keyrouter_grab, void *top_client) +keyrouter_grab_set_top_client(struct ds_tizen_keyrouter_grab *keyrouter_grab, void *top_client) { keyrouter_grab->top_client = top_client; @@ -124,7 +124,7 @@ tizen_keyrouter_grab_set_top_client(struct ds_tizen_keyrouter_grab *keyrouter_gr } int -tizen_keyrouter_grab_key_process(struct ds_tizen_keyrouter_grab *keyrouter_grab, +keyrouter_grab_key_process(struct ds_tizen_keyrouter_grab *keyrouter_grab, int keycode, int pressed, struct wl_list *delivery_list) { struct ds_tizen_keyrouter_key_info *info, *delivery; @@ -209,7 +209,7 @@ tizen_keyrouter_grab_key_process(struct ds_tizen_keyrouter_grab *keyrouter_grab, } int -tizen_keyrouter_grab_grab_key(struct ds_tizen_keyrouter_grab *keyrouter_grab, +keyrouter_grab_grab_key(struct ds_tizen_keyrouter_grab *keyrouter_grab, int type, int keycode, void *data) { struct ds_tizen_keyrouter_key_info *info = NULL; @@ -264,7 +264,7 @@ keyrouter_list_remove_data(struct wl_list *list, void *data) } void -tizen_keyrouter_grab_ungrab_key(struct ds_tizen_keyrouter_grab *keyrouter_grab, +keyrouter_grab_ungrab_key(struct ds_tizen_keyrouter_grab *keyrouter_grab, int type, int keycode, void *data) { struct wl_list *list; @@ -287,7 +287,7 @@ tizen_keyrouter_grab_ungrab_key(struct ds_tizen_keyrouter_grab *keyrouter_grab, } struct ds_tizen_keyrouter_grab * -tizen_keyrouter_grab_create(void) +keyrouter_grab_create(void) { struct ds_tizen_keyrouter_grab *keyrouter_grab = NULL; int i; @@ -334,7 +334,7 @@ keyrouter_grab_delete_list(struct wl_list *list) } void -tizen_keyrouter_grab_destroy(struct ds_tizen_keyrouter_grab *keyrouter_grab) +keyrouter_grab_destroy(struct ds_tizen_keyrouter_grab *keyrouter_grab) { int i; -- 2.7.4 From 68b858cbaf85f370bcd9241d3ae42d7f58485f41 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Wed, 18 May 2022 14:32:31 +0900 Subject: [PATCH 11/16] fix the build warning Change-Id: I43f5101b85b807abef226169940d4cba5955caa9 --- src/libds-tizen/util/security.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libds-tizen/util/security.c b/src/libds-tizen/util/security.c index 735caf5..ad19027 100644 --- a/src/libds-tizen/util/security.c +++ b/src/libds-tizen/util/security.c @@ -145,18 +145,16 @@ tizen_security_finish(void) #ifdef HAVE_CYNARA if (g_cynara_refcount < 1) { ds_err("%s called without tizen_security_init\n", __FUNCTION__); - return 0; + return; } if (--g_cynara_refcount != 0) - return 1; + return; if (g_cynara) { cynara_finish(g_cynara); g_cynara = NULL; } #endif - - return 1; } -- 2.7.4 From 939403b95d4b122e256c25f4fa9d90b7c3a343e5 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Mon, 16 May 2022 17:39:29 +0900 Subject: [PATCH 12/16] shell: implement wl_shell_interface ds_shell and ds_shell_surface are resources which implement the wl_shell_interface and wl_shell_surface_interface. Change-Id: I551324b8f14a3aa77c920cece19e15a12e8d9981 --- include/libds/shell.h | 50 +++++ src/libds/meson.build | 2 + src/libds/shell.c | 175 +++++++++++++++ src/libds/shell.h | 157 ++++++++++++++ src/libds/shell_surface.c | 540 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 924 insertions(+) create mode 100644 include/libds/shell.h create mode 100644 src/libds/shell.c create mode 100644 src/libds/shell.h create mode 100644 src/libds/shell_surface.c diff --git a/include/libds/shell.h b/include/libds/shell.h new file mode 100644 index 0000000..b4fa688 --- /dev/null +++ b/include/libds/shell.h @@ -0,0 +1,50 @@ +#ifndef LIBDS_SHELL_H +#define LIBDS_SHELL_H + +#include +#include + +#include "surface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct ds_shell; + +struct ds_shell_surface; + +struct ds_shell * +ds_shell_create(struct wl_display *display); + +void +ds_shell_add_destroy_listener(struct ds_shell *shell, + struct wl_listener *listener); + +void +ds_shell_add_new_surface_listener(struct ds_shell *shell, + struct wl_listener *listener); + +void +ds_shell_surface_add_destroy_listener(struct ds_shell_surface *surface, + struct wl_listener *listener); + +void +ds_shell_surface_add_map_listener(struct ds_shell_surface *surface, + struct wl_listener *listener); + +void +ds_shell_surface_add_unmap_listener(struct ds_shell_surface *surface, + struct wl_listener *listener); + +void +ds_shell_surface_ping(struct ds_shell_surface *surface); + +struct ds_surface * +ds_shell_surface_get_surface(struct ds_shell_surface *surface); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libds/meson.build b/src/libds/meson.build index 43bdeae..b59fda2 100644 --- a/src/libds/meson.build +++ b/src/libds/meson.build @@ -26,6 +26,8 @@ libds_files = [ 'seat/seat_pointer.c', 'seat/seat_keyboard.c', 'seat/seat_touch.c', + 'shell.c', + 'shell_surface.c', ] protocols = { diff --git a/src/libds/shell.c b/src/libds/shell.c new file mode 100644 index 0000000..3adb519 --- /dev/null +++ b/src/libds/shell.c @@ -0,0 +1,175 @@ +#include +#include +#include + +#include "libds/log.h" +#include "libds/shell.h" + +#include "shell.h" + +#define WL_SHELL_VERSION 1 +#define SHELL_PING_TIMEOUT 10000 + +static void shell_handle_display_destroy(struct wl_listener *listener, + void *data); +static void shell_bind(struct wl_client *wl_client, void *data, + uint32_t verison, uint32_t id); + +WL_EXPORT struct ds_shell * +ds_shell_create(struct wl_display *display) +{ + struct ds_shell *shell; + + shell = calloc(1, sizeof *shell); + if (!shell) { + return NULL; + } + + shell->global = wl_global_create(display, &wl_shell_interface, + WL_SHELL_VERSION, shell, shell_bind); + if (!shell->global) { + free(shell); + return NULL; + } + + wl_list_init(&shell->clients); + + shell->display_destroy.notify = shell_handle_display_destroy; + wl_display_add_destroy_listener(display, &shell->display_destroy); + + wl_signal_init(&shell->events.destroy); + wl_signal_init(&shell->events.new_surface); + + shell->ping_timeout = SHELL_PING_TIMEOUT; + + ds_inf("Global created: xdg_wm_base shell(%p)", shell); + + return shell; +} + +WL_EXPORT void +ds_shell_add_destroy_listener(struct ds_shell *shell, + struct wl_listener *listener) +{ + wl_signal_add(&shell->events.destroy, listener); +} + +WL_EXPORT void +ds_shell_add_new_surface_listener(struct ds_shell *shell, + struct wl_listener *listener) +{ + wl_signal_add(&shell->events.new_surface, listener); +} + +static void +shell_handle_display_destroy(struct wl_listener *listener, void *data) +{ + struct ds_shell *shell; + + shell = wl_container_of(listener, shell, display_destroy); + + ds_inf("Global destroy: xdg_wm_base shell(%p)", shell); + + wl_signal_emit(&shell->events.destroy, shell); + wl_list_remove(&shell->display_destroy.link); + wl_global_destroy(shell->global); + free(shell); +} + +static void +shell_handle_get_shell_surface(struct wl_client *wl_client, + struct wl_resource *resource, uint32_t id, + struct wl_resource *surface_resource) +{ + struct ds_shell_client *client; + struct ds_surface *surface; + + client = wl_resource_get_user_data(resource); + surface = ds_surface_from_resource(surface_resource); + create_shell_surface(client, surface, id); +} + +static const struct wl_shell_interface shell_impl = +{ + .get_shell_surface = shell_handle_get_shell_surface, +}; + +static void +shell_client_handle_resource_destroy(struct wl_resource *resource) +{ + struct ds_shell_client *client; + struct ds_shell_surface *shell_surface, *tmp; + + client = wl_resource_get_user_data(resource); + + wl_list_for_each_safe(shell_surface, tmp, &client->shell_surfaces, link) + destroy_shell_surface(shell_surface); + + if (client->ping_timer != NULL) + wl_event_source_remove(client->ping_timer); + + wl_list_remove(&client->link); + free(client); +} + +static int +shell_client_handle_ping_timeout(void *user_data) +{ + struct ds_shell_client *client = user_data; + struct ds_shell_surface *surface; + + wl_list_for_each(surface, &client->shell_surfaces, link) + wl_signal_emit(&surface->events.ping_timeout, surface); + + client->ping_serial = 0; + + return 1; +} + +static void +shell_client_init_ping_timer(struct ds_shell_client *client) +{ + struct wl_display *display; + struct wl_event_loop *loop; + + display = wl_client_get_display(client->wl_client); + loop = wl_display_get_event_loop(display); + client->ping_timer = wl_event_loop_add_timer(loop, + shell_client_handle_ping_timeout, client); + if (client->ping_timer == NULL) + wl_client_post_no_memory(client->wl_client); +} + +static void +shell_bind(struct wl_client *wl_client, void *data, uint32_t version, + uint32_t id) +{ + struct ds_shell *shell = data; + struct ds_shell_client *client; + + client = calloc(1, sizeof *client); + if (client == NULL) { + wl_client_post_no_memory(wl_client); + return; + } + + client->wl_client = wl_client; + client->shell = shell; + + wl_list_init(&client->shell_surfaces); + + client->resource = + wl_resource_create(wl_client, &wl_shell_interface, version, id); + if (client->resource == NULL) { + free(client); + wl_client_post_no_memory(wl_client); + return; + } + + wl_resource_set_implementation(client->resource, &shell_impl, client, + shell_client_handle_resource_destroy); + + wl_list_insert(&shell->clients, &client->link); + + shell_client_init_ping_timer(client); +} diff --git a/src/libds/shell.h b/src/libds/shell.h new file mode 100644 index 0000000..c2306af --- /dev/null +++ b/src/libds/shell.h @@ -0,0 +1,157 @@ +#ifndef DS_SHELL_H +#define DS_SHELL_H + +#include + +#include "libds/output.h" + +#include "surface.h" + +enum ds_shell_surface_role +{ + DS_SHELL_SURFACE_ROLE_NONE, + DS_SHELL_SURFACE_ROLE_TOPLEVEL, + DS_SHELL_SURFACE_ROLE_POPUP, +}; + +struct ds_shell +{ + struct wl_global *global; + + struct wl_list clients; + + struct wl_listener display_destroy; + + struct { + struct wl_signal destroy; + struct wl_signal new_surface; + } events; + + uint32_t ping_timeout; +}; + +struct ds_shell_client +{ + struct ds_shell *shell; + + struct wl_resource *resource; + struct wl_client *wl_client; + struct wl_event_source *ping_timer; + + struct wl_list shell_surfaces; + + struct wl_list link; // ds_shell::clients + + uint32_t ping_serial; +}; + +struct ds_shell_toplevel_state +{ + bool maximized, fullscreen, resizing, activated; + uint32_t tiled; + uint32_t width, height; + uint32_t max_width, max_height; + uint32_t min_width, min_height; +}; + +struct ds_shell_toplevel_requested +{ + bool maximized, minimized, fullscreen; + struct ds_output *fullscreen_output; + struct wl_listener fullscreen_output_destroy; +}; + +struct ds_shell_toplevel +{ + struct ds_shell_surface *base; + bool added; + + struct ds_shell_surface *parent; + struct wl_listener parent_unmap; + + struct ds_shell_toplevel_state current, pending; + struct ds_shell_toplevel_requested requested; + + char *title; + char *app_id; + + struct { + struct wl_signal request_maximize; + struct wl_signal request_fullscreen; + struct wl_signal request_minimize; + struct wl_signal request_move; + struct wl_signal request_resize; + struct wl_signal request_show_window_menu; + struct wl_signal set_parent; + struct wl_signal set_title; + struct wl_signal set_app_id; + } events; +}; + +struct ds_xdg_popup +{ + +}; + +struct ds_shell_surface_state +{ + uint32_t configure_serial; + struct { + int x, y; + int width, height; + } geometry; +}; + +struct ds_shell_surface +{ + struct ds_shell_client *client; + struct ds_surface *surface; + + enum ds_shell_surface_role role; + + union { + struct ds_shell_toplevel *toplevel; + struct ds_xdg_popup *popup; + }; + + struct wl_resource *resource; + + struct wl_event_source *configure_idle; + uint32_t scheduled_serial; + struct wl_list configure_list; + + struct ds_shell_surface_state current, pending; + + struct wl_list link; // ds_shell_client::surfaces + + struct { + struct wl_listener surface_destroy; + struct wl_listener surface_commit; + } listener; + + struct { + struct wl_signal destroy; + struct wl_signal ping_timeout; + struct wl_signal new_popup; + struct wl_signal map; + struct wl_signal unmap; + struct wl_signal configure; + } events; + + bool added, configured, mapped; +}; + +struct ds_shell_surface_configure +{ + struct ds_shell_surface *shell_surface; + struct wl_list link; + uint32_t serial; +}; + +struct ds_shell_surface * +create_shell_surface(struct ds_shell_client *client, struct ds_surface *surface, + uint32_t id); + +void destroy_shell_surface(struct ds_shell_surface *surface); + +#endif diff --git a/src/libds/shell_surface.c b/src/libds/shell_surface.c new file mode 100644 index 0000000..73c498b --- /dev/null +++ b/src/libds/shell_surface.c @@ -0,0 +1,540 @@ +#include +#include +#include + +#include "libds/log.h" + +#include "shell.h" + +static const struct wl_shell_surface_interface shell_surface_impl; + +static void reset_shell_surface(struct ds_shell_surface *shell_surface); +static void shell_surface_handle_surface_destroy(struct wl_listener *listener, + void *data); +static void shell_surface_handle_surface_commit(struct wl_listener *listener, + void *data); +static void shell_surface_handle_resource_destroy(struct wl_resource *resource); +static void shell_surface_configure_destroy(struct ds_shell_surface_configure *configure); +static void surface_send_configure(void *user_data); +static void handle_shell_surface_commit(struct ds_surface *surface); + +WL_EXPORT void +ds_shell_surface_add_destroy_listener(struct ds_shell_surface *shell_surface, + struct wl_listener *listener) +{ + wl_signal_add(&shell_surface->events.destroy, listener); +} + +WL_EXPORT void +ds_shell_surface_add_map_listener(struct ds_shell_surface *shell_surface, + struct wl_listener *listener) +{ + wl_signal_add(&shell_surface->events.map, listener); +} + +WL_EXPORT void +ds_shell_surface_add_unmap_listener(struct ds_shell_surface *shell_surface, + struct wl_listener *listener) +{ + wl_signal_add(&shell_surface->events.unmap, listener); +} + +WL_EXPORT struct ds_surface * +ds_shell_surface_get_surface(struct ds_shell_surface *shell_surface) +{ + return shell_surface->surface; +} + +static const struct ds_surface_role shell_surface_role = +{ + .name = "shell_surface", + .commit = handle_shell_surface_commit, +}; + +struct ds_shell_surface * +create_shell_surface(struct ds_shell_client *client, struct ds_surface *surface, + uint32_t id) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = calloc(1, sizeof *shell_surface); + if (!shell_surface) { + wl_client_post_no_memory(client->wl_client); + return NULL; + } + + shell_surface->client = client; + shell_surface->role = DS_SHELL_SURFACE_ROLE_NONE; + shell_surface->surface = surface; + shell_surface->resource = wl_resource_create(client->wl_client, + &wl_shell_surface_interface, wl_resource_get_version(client->resource), + id); + if (!shell_surface->resource) { + free(shell_surface); + wl_client_post_no_memory(client->wl_client); + return NULL; + } + + if (!ds_surface_set_role(shell_surface->surface, &shell_surface_role, + shell_surface, shell_surface->resource, -1)) { + free(shell_surface); + return NULL; + } + + wl_list_init(&shell_surface->configure_list); + + wl_signal_init(&shell_surface->events.destroy); + wl_signal_init(&shell_surface->events.ping_timeout); + wl_signal_init(&shell_surface->events.new_popup); + wl_signal_init(&shell_surface->events.map); + wl_signal_init(&shell_surface->events.unmap); + wl_signal_init(&shell_surface->events.configure); + + shell_surface->listener.surface_destroy.notify = + shell_surface_handle_surface_destroy; + ds_surface_add_destroy_listener(surface, + &shell_surface->listener.surface_destroy); + + shell_surface->listener.surface_commit.notify = + shell_surface_handle_surface_commit; + ds_surface_add_commit_listener(surface, + &shell_surface->listener.surface_commit); + + wl_resource_set_implementation(shell_surface->resource, &shell_surface_impl, + shell_surface, shell_surface_handle_resource_destroy); + + wl_list_insert(&client->shell_surfaces, &shell_surface->link); + + ds_inf("New shell_surface %p (res %p)", shell_surface, shell_surface->resource); + + return shell_surface; +} + +void +destroy_shell_surface(struct ds_shell_surface *shell_surface) +{ + reset_shell_surface(shell_surface); + + wl_resource_set_user_data(shell_surface->resource, NULL); + + ds_surface_reset_role_data(shell_surface->surface); + + wl_list_remove(&shell_surface->link); + wl_list_remove(&shell_surface->listener.surface_destroy.link); + wl_list_remove(&shell_surface->listener.surface_commit.link); + + free(shell_surface); +} + +static void +unmap_shell_surface(struct ds_shell_surface *shell_surface) +{ + struct ds_shell_surface_configure *configure, *tmp; + + // TODO handle popup + + if (shell_surface->mapped) + wl_signal_emit(&shell_surface->events.unmap, shell_surface); + + switch (shell_surface->role) { + case DS_SHELL_SURFACE_ROLE_TOPLEVEL: + if (shell_surface->toplevel->parent) { + wl_list_remove(&shell_surface->toplevel->parent_unmap.link); + shell_surface->toplevel->parent = NULL; + } + free(shell_surface->toplevel->title); + shell_surface->toplevel->title = NULL; + free(shell_surface->toplevel->app_id); + shell_surface->toplevel->app_id = NULL; + break; + case DS_SHELL_SURFACE_ROLE_POPUP: + // TODO + break; + case DS_SHELL_SURFACE_ROLE_NONE: + assert(false && "not reached"); + } + + wl_list_for_each_safe(configure, tmp, &shell_surface->configure_list, link) + shell_surface_configure_destroy(configure); + + if (shell_surface->configure_idle) { + wl_event_source_remove(shell_surface->configure_idle); + shell_surface->configure_idle = NULL; + } + + shell_surface->configured = false; + shell_surface->mapped = false; +} + +static void +reset_shell_surface(struct ds_shell_surface *shell_surface) +{ + struct ds_shell_toplevel_requested *req; + + if (shell_surface->role != DS_SHELL_SURFACE_ROLE_NONE) + unmap_shell_surface(shell_surface); + + if (shell_surface->added) { + wl_signal_emit(&shell_surface->events.destroy, shell_surface); + shell_surface->added = false; + } + + switch (shell_surface->role) { + case DS_SHELL_SURFACE_ROLE_TOPLEVEL: + req = &shell_surface->toplevel->requested; + if (req->fullscreen_output) + wl_list_remove(&req->fullscreen_output_destroy.link); + free(shell_surface->toplevel); + shell_surface->toplevel = NULL; + break; + case DS_SHELL_SURFACE_ROLE_POPUP: + // TODO + break; + case DS_SHELL_SURFACE_ROLE_NONE: + // This space is intentionally left blank + break; + } + + shell_surface->role = DS_SHELL_SURFACE_ROLE_NONE; +} + +static uint32_t +ds_shell_surface_schedule_configure(struct ds_shell_surface *shell_surface) +{ + struct wl_display *display; + struct wl_event_loop *loop; + + display = wl_client_get_display(shell_surface->client->wl_client); + loop = wl_display_get_event_loop(display); + + if (!shell_surface->configure_idle) { + shell_surface->scheduled_serial = wl_display_next_serial(display); + shell_surface->configure_idle = wl_event_loop_add_idle(loop, + surface_send_configure, shell_surface); + if (!shell_surface->configure_idle) + wl_client_post_no_memory(shell_surface->client->wl_client); + } + + return shell_surface->scheduled_serial; +} + +static void +handle_shell_surface_commit(struct ds_surface *surface) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = ds_surface_get_role_data(surface); + shell_surface->current = shell_surface->pending; + + switch (shell_surface->role) { + case DS_SHELL_SURFACE_ROLE_NONE: + // inert toplevel or popup + break; + case DS_SHELL_SURFACE_ROLE_TOPLEVEL: + if (!shell_surface->toplevel->added) { + ds_shell_surface_schedule_configure(shell_surface); + shell_surface->toplevel->added = true; + } + // TODO + break; + case DS_SHELL_SURFACE_ROLE_POPUP: + // TODO + break; + } + + if (!shell_surface->added) { + shell_surface->added = true; + wl_signal_emit(&shell_surface->client->shell->events.new_surface, shell_surface); + } + + if (shell_surface->configured && + ds_surface_has_buffer(shell_surface->surface) && + !shell_surface->mapped) { + shell_surface->mapped = true; + wl_signal_emit(&shell_surface->events.map, shell_surface); + } +} + +void handle_shell_surface_precommit(struct ds_surface *surface) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = ds_surface_get_role_data(surface); + + // TODO + (void)shell_surface; +} + +void +ds_shell_surface_ping(struct ds_shell_surface *shell_surface) +{ +} + +static void +shell_surface_handle_surface_destroy(struct wl_listener *listener, void *data) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_container_of(listener, shell_surface, listener.surface_destroy); + destroy_shell_surface(shell_surface); +} + +static void +shell_surface_handle_surface_commit(struct wl_listener *listener, void *data) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_container_of(listener, shell_surface, listener.surface_commit); + + if (ds_surface_has_buffer(shell_surface->surface) && + !shell_surface->configured) { + wl_resource_post_error(shell_surface->resource, + -1, + "shell_surface has never been configured"); + return; + } + + if (!ds_surface_get_role(shell_surface->surface)) { + wl_resource_post_error(shell_surface->resource, + -1, + "shell_surface must have a role"); + return; + } +} + +static void +shell_surface_handle_resource_destroy(struct wl_resource *resource) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_resource_get_user_data(resource); + if (!shell_surface) + return; + + destroy_shell_surface(shell_surface); +} + +static void +shell_surface_configure_destroy(struct ds_shell_surface_configure *configure) +{ + wl_list_remove(&configure->link); + free(configure); +} + +static void +create_shell_surface_toplevel(struct ds_shell_surface *shell_surface) +{ + assert(shell_surface->toplevel == NULL); + + shell_surface->toplevel = calloc(1, sizeof *shell_surface->toplevel); + if (!shell_surface->toplevel) { + wl_resource_post_no_memory(shell_surface->resource); + return; + } + + shell_surface->toplevel->base = shell_surface; + + wl_signal_init(&shell_surface->toplevel->events.request_maximize); + wl_signal_init(&shell_surface->toplevel->events.request_fullscreen); + wl_signal_init(&shell_surface->toplevel->events.request_minimize); + wl_signal_init(&shell_surface->toplevel->events.request_move); + wl_signal_init(&shell_surface->toplevel->events.request_resize); + wl_signal_init(&shell_surface->toplevel->events.request_show_window_menu); + wl_signal_init(&shell_surface->toplevel->events.set_parent); + wl_signal_init(&shell_surface->toplevel->events.set_title); + wl_signal_init(&shell_surface->toplevel->events.set_app_id); + + shell_surface->role = DS_SHELL_SURFACE_ROLE_TOPLEVEL; +} + +static void +shell_surface_handle_pong(struct wl_client *wl_client, + struct wl_resource *resource, uint32_t serial) +{ + struct ds_shell_surface *shell_surface; + struct ds_shell_client *client; + + shell_surface = wl_resource_get_user_data(resource); + + client = shell_surface->client; + if (client->ping_serial != serial) + return; + + wl_event_source_timer_update(client->ping_timer, 0); + client->ping_serial = 0; +} + +static void +shell_surface_handle_move(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *seat_resource, + uint32_t serial) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_resource_get_user_data(resource); + + // TODO + (void)shell_surface; +} + +static void +shell_surface_handle_resize(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *seat_resource, + uint32_t serial, uint32_t edges) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_resource_get_user_data(resource); + + // TODO + (void)shell_surface; +} + +static void +shell_surface_handle_set_toplevel(struct wl_client *client, struct wl_resource *resource) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_resource_get_user_data(resource); + create_shell_surface_toplevel(shell_surface); +} + +static void +shell_surface_handle_set_transient(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *parent_resource, + int32_t x, int32_t y, uint32_t flags) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_resource_get_user_data(resource); + + // TODO + (void)shell_surface; + ds_err("Not implemented yet."); +} + +static void +shell_surface_handle_set_fullscreen(struct wl_client *client, + struct wl_resource *resource, uint32_t method, uint32_t framerate, + struct wl_resource *output_resource) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_resource_get_user_data(resource); + + // TODO + (void)shell_surface; + ds_err("Not implemented yet."); +} + +static void +shell_surface_handle_set_popup(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *seat_resource, + uint32_t serial, struct wl_resource *parent_resource, + int32_t x, int32_t y, uint32_t flags) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_resource_get_user_data(resource); + + // TODO + (void)shell_surface; + ds_err("Not implemented yet."); +} + +static void +shell_surface_handle_set_maximized(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *output_resource) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_resource_get_user_data(resource); + + shell_surface->toplevel->requested.maximized = true; + wl_signal_emit(&shell_surface->toplevel->events.request_maximize, shell_surface); + ds_shell_surface_schedule_configure(shell_surface); +} + +static void +shell_surface_handle_set_title(struct wl_client *client, + struct wl_resource *resource, const char *title) +{ + struct ds_shell_surface *shell_surface; + char *tmp; + + shell_surface = wl_resource_get_user_data(resource); + tmp = strdup(title); + if (!tmp) { + wl_resource_post_no_memory(resource); + return; + } + + if (shell_surface->toplevel->title) + free(shell_surface->toplevel->title); + + shell_surface->toplevel->title = tmp; + wl_signal_emit(&shell_surface->toplevel->events.set_title, shell_surface); +} + +static void +shell_surface_handle_set_class(struct wl_client *client, + struct wl_resource *resource, const char *clas) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_resource_get_user_data(resource); + + // TODO + (void)shell_surface; + ds_err("Not implemented yet."); +} + +static const struct wl_shell_surface_interface shell_surface_impl = +{ + .pong = shell_surface_handle_pong, + .move = shell_surface_handle_move, + .resize = shell_surface_handle_resize, + .set_toplevel = shell_surface_handle_set_toplevel, + .set_transient = shell_surface_handle_set_transient, + .set_fullscreen = shell_surface_handle_set_fullscreen, + .set_popup = shell_surface_handle_set_popup, + .set_maximized = shell_surface_handle_set_maximized, + .set_title = shell_surface_handle_set_title, + .set_class = shell_surface_handle_set_class, +}; + +static void +surface_send_configure(void *user_data) +{ + struct ds_shell_surface *shell_surface; + struct ds_shell_surface_configure *configure; + uint32_t width, height; + uint32_t edges = 0; + + shell_surface = user_data; + shell_surface->configure_idle = NULL; + + // TDOO: Not sure if shell needs the struct ds_shell_surface_configure. + configure = calloc(1, sizeof *configure); + if (!configure) { + wl_client_post_no_memory(shell_surface->client->wl_client); + return; + } + + wl_list_insert(shell_surface->configure_list.prev, &configure->link); + configure->serial = shell_surface->scheduled_serial; + configure->shell_surface = shell_surface; + + wl_signal_emit(&shell_surface->events.configure, configure); + + edges = (WL_SHELL_SURFACE_RESIZE_TOP | WL_SHELL_SURFACE_RESIZE_LEFT); // fixed default value + width = shell_surface->current.geometry.width; + height = shell_surface->current.geometry.height; + + wl_shell_surface_send_configure(shell_surface->resource, edges, width, height); + + shell_surface->configured = true; + + // TDOO: Not sure if shell needs the struct ds_shell_surface_configure. + shell_surface_configure_destroy(configure); +} -- 2.7.4 From 024636d86aa5e2c43ba2f6c5b68f751cad237b78 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Thu, 19 May 2022 10:38:44 +0900 Subject: [PATCH 13/16] client: add simple-shm-shell sample This is a simple wayland-client which use wl_shm and wl_shell interface. Change-Id: I3d27d37d56097ab84e46d7c59fc52329b5c9605e --- packaging/libds.spec | 1 + src/clients/meson.build | 12 ++ src/clients/simple-shm-shell.c | 339 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 352 insertions(+) create mode 100644 src/clients/simple-shm-shell.c diff --git a/packaging/libds.spec b/packaging/libds.spec index 2ffdca0..5258295 100644 --- a/packaging/libds.spec +++ b/packaging/libds.spec @@ -103,6 +103,7 @@ ninja -C builddir install %{_bindir}/tinyds-tdm %{_bindir}/ds-simple-tbm %{_bindir}/tinyds-tdm-libinput +%{_bindir}/ds-simple-shm-shell %files tizen-keyrouter %manifest %{name}.manifest diff --git a/src/clients/meson.build b/src/clients/meson.build index 360c425..009016c 100644 --- a/src/clients/meson.build +++ b/src/clients/meson.build @@ -1,3 +1,15 @@ +simple_shm_shell_files = ['simple-shm-shell.c'] +simple_shm_shell_deps = [ + dependency('wayland-client', required: true), +] + +executable('ds-simple-shm-shell', + simple_shm_shell_files, + dependencies: simple_shm_shell_deps, + install_dir: libds_bindir, + install: true, +) + wayland_tbm_client = dependency('wayland-tbm-client', required: false) libtbm = dependency('libtbm', required: false) diff --git a/src/clients/simple-shm-shell.c b/src/clients/simple-shm-shell.c new file mode 100644 index 0000000..52d9ed2 --- /dev/null +++ b/src/clients/simple-shm-shell.c @@ -0,0 +1,339 @@ +/* +Copyright (C) 2015 - 2016 Samsung Electronics co., Ltd. All Rights Reserved. + +Contact: + SooChan Lim + Changyeon Lee + JunKyeong Kim + Boram Park + +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, sublicense, +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 NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS 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 +#include +#include +#include +#include +#include +#include + +#include + +#define BUF_WIDTH 1920 +#define BUF_HEIGHT 1080 + +#define RETURN_VAL_IF_FAIL(c,v) {\ + if(!((c))){\ + fprintf(stderr, "[%s(%d)] '%s' failed.", __func__, __LINE__, #c);\ + return (v);\ + }\ +} + +#define GOTO_IF_FAIL(c,l) {\ + if(!(c)) {\ + fprintf(stderr, "[%s(%d)] '%s' failed.", __func__, __LINE__, #c);\ + goto l;\ + }\ +} + +#define EXIT_IF_FAIL(c) {\ + if(!(c)) {\ + fprintf (stderr, "[%s(%d)] '%s' failed.\n",__func__,__LINE__,#c);\ + exit(0);\ + }\ +} + +struct wl_test_info { + int width; + int height; + int stride; + int format; + int size; + + struct wl_display *display; + struct wl_registry *registry; + + /* global objects */ + struct wl_compositor *compositor; + struct wl_shm *shm; + struct wl_shell *shell; + struct wl_shm_pool *shm_pool; + + /* objects */ + struct wl_surface *surface; + struct wl_shell_surface *shell_surface; + struct wl_buffer *buffer; +}; + +static int +_create_anonymous_file (off_t size) +{ + static const char template[] = + "/shooter-XXXXXX"; + const char *path; + char *name = NULL; + int fd = -1; + int ret = -1; + + path = getenv("XDG_RUNTIME_DIR"); + if (!path) { + errno = ENOENT; + return -1; + } + + name = malloc(strlen(path) + sizeof(template)); + GOTO_IF_FAIL(name != NULL, fail); + + strcpy(name, path); + strcat(name, template); + + fd = mkstemp(name); + if (fd >= 0) + unlink(name); + + ret = ftruncate(fd, size); + GOTO_IF_FAIL(ret >= 0, fail); + + free(name); + + return fd; +fail: + if (fd >= 0) + close(fd); + + if (name) + free(name); + + return -1; +} + +static void +_destroy_anonymous_file(int fd) +{ + if (fd < 0) return; + + close(fd); +} + + +static struct wl_test_info * +_create_wl_test_info (void) +{ + struct wl_test_info *test_info = NULL; + + test_info = calloc(1, sizeof(struct wl_test_info)); + RETURN_VAL_IF_FAIL(test_info != NULL, NULL); + + return test_info; +} + +static void +_destroy_wl_test_info (struct wl_test_info *test_info) +{ + if (!test_info) return; + + free(test_info); +} + +static struct wl_shm_pool * +_create_shm_pool(struct wl_shm *shm, int size) +{ + struct wl_shm_pool *shm_pool = NULL; + void *data = NULL; + int fd = -1; + + fd = _create_anonymous_file(size); + GOTO_IF_FAIL(fd >= 0, fail); + + data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + GOTO_IF_FAIL(data != NULL, fail); + + memset(data, 0xff, size); + munmap(data, size); + + shm_pool = wl_shm_create_pool(shm, fd, size); + GOTO_IF_FAIL(shm_pool != NULL, fail); + + _destroy_anonymous_file(fd); + + return shm_pool; + +fail: + if (fd > 0) + _destroy_anonymous_file(fd); + + return NULL; +} + +void +_destroy_shm_pool(struct wl_shm_pool *shm_pool) +{ + if (!shm_pool) return; + + wl_shm_pool_destroy(shm_pool); +} + +static void +handle_global(void *data, struct wl_registry *registry, uint32_t name, + const char *interface, uint32_t version) +{ + struct wl_test_info *ti = (struct wl_test_info *)data; + + if (strcmp(interface, "wl_compositor") == 0) { + ti->compositor = wl_registry_bind(registry, name, + &wl_compositor_interface, 3); + if (!ti->compositor) + printf("%s(%d): Error. fail to bind %s.\n", + __func__, __LINE__, interface); + else + printf("%s(%d): bind %s.\n", __func__, __LINE__, interface); + } else if (strcmp(interface, "wl_shm") == 0) { + ti->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); + if (!ti->shm) + printf("%s(%d): Error. fail to bind %s.\n", + __func__, __LINE__, interface); + else + printf("%s(%d): bind %s.\n", __func__, __LINE__, interface); + + ti->shm_pool = _create_shm_pool(ti->shm, ti->size); + if (!ti->shm_pool) + printf("%s(%d): Error. fail to create wl_shm_pool.\n", + __func__, __LINE__); + else + printf("%s(%d): success to create wl_shm_pool.\n", + __func__, __LINE__); + + } else if (strcmp(interface, "wl_shell") == 0) { + ti->shell = wl_registry_bind(registry, name, &wl_shell_interface, 1); + if (!ti->shell) + printf("%s(%d): Error. fail to bind %s.\n", + __func__, __LINE__, interface); + else + printf("%s(%d): bind %s.\n", __func__, __LINE__, interface); + } else { + printf("%s(%d): Not bind %s.\n", __func__, __LINE__, interface); + } +} + +static void +handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) +{ + +} + +static const struct wl_registry_listener registry_listener = { + handle_global, + handle_global_remove +}; + +int main (void) +{ + struct wl_test_info *ti = NULL; + int ret = 0; + + /* create test info */ + ti = _create_wl_test_info (); + GOTO_IF_FAIL(ti != NULL, fail); + + /* init */ + ti->width = BUF_WIDTH; + ti->height = BUF_HEIGHT; + ti->stride = ti->width * 4; + ti->format = WL_SHM_FORMAT_XRGB8888; + ti->size = ti->stride * ti->height; + + /* connect display */ + ti->display = wl_display_connect(NULL); + GOTO_IF_FAIL(ti->display != NULL, fail); + + /* get the registry */ + ti->registry = wl_display_get_registry(ti->display); + GOTO_IF_FAIL(ti->registry != NULL, fail); + + /* get the global objects */ + wl_registry_add_listener(ti->registry, ®istry_listener, ti); + wl_display_dispatch(ti->display); + wl_display_roundtrip(ti->display); + + /* check the global objects */ + GOTO_IF_FAIL(ti->compositor != NULL, fail); + GOTO_IF_FAIL(ti->shm != NULL, fail); + GOTO_IF_FAIL(ti->shell != NULL, fail); + GOTO_IF_FAIL(ti->shm_pool != NULL, fail); + + /* create objects */ + ti->surface = wl_compositor_create_surface(ti->compositor); + GOTO_IF_FAIL(ti->surface != NULL, fail); + + ti->shell_surface = wl_shell_get_shell_surface(ti->shell, ti->surface); + GOTO_IF_FAIL(ti->shell_surface != NULL, fail); + + wl_shell_surface_set_toplevel(ti->shell_surface); + + ti->buffer = wl_shm_pool_create_buffer(ti->shm_pool, 0, + ti->width, ti->height, ti->stride, ti->format); + GOTO_IF_FAIL(ti->buffer != NULL, fail); + + wl_surface_attach(ti->surface, ti->buffer, 0, 0); + wl_surface_damage(ti->surface, 0, 0, ti->width, ti->height); + wl_surface_commit(ti->surface); + + wl_display_roundtrip(ti->display); + + /* main loop */ + printf("%s(%d): loop start.\n", __func__, __LINE__); + while (ret >= 0) { + ret = wl_display_dispatch(ti->display); + printf("%s(%d): loop running(ret=%d).\n", __func__, __LINE__, ret); + } + printf("%s(%d): loop end.\n", __func__, __LINE__); + +fail: + /* destory objects */ + if (ti->shell_surface) + wl_shell_surface_destroy(ti->shell_surface); + if (ti->surface) + wl_surface_destroy(ti->surface); + if (ti->buffer) + wl_buffer_destroy(ti->buffer); + + /* destroy global objects */ + if (ti->shell) + wl_shell_destroy(ti->shell); + if (ti->shm_pool) + _destroy_shm_pool(ti->shm_pool); + if (ti->shm) + wl_shm_destroy(ti->shm); + if (ti->compositor) + wl_compositor_destroy(ti->compositor); + + /* destory registry and display */ + if (ti->registry) + wl_registry_destroy(ti->registry); + if (ti->display) + wl_display_disconnect(ti->display); + + /* destroy test_info */ + if (ti) + _destroy_wl_test_info(ti); + + return 0; +} -- 2.7.4 From 57b204030d832866d60c8c10613ebafe2d494796 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Fri, 27 May 2022 11:39:45 +0900 Subject: [PATCH 14/16] add review-bot as a reviewer Change-Id: Id353d4198b4dfd5a06daf33b2580110161aa405a --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index d52bfc2..06c492b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -5,5 +5,5 @@ # the repo. Unless a later match takes precedence, # @global-owner1 and @global-owner2 will be requested for # review when someone opens a pull request. -* @sc1-lim @shiin-lee @joonbum-ko @cyeon-lee @doyoun-kang @gl77-lee @duna-oh @jinbong-lee @jk0430-kim @juns-kim @TizenWS/ws_members +* @sc1-lim @shiin-lee @joonbum-ko @cyeon-lee @doyoun-kang @gl77-lee @duna-oh @jinbong-lee @jk0430-kim @juns-kim @TizenWS/ws_members @review-bot -- 2.7.4 From 73292cd943a431496e4b05f618d7e8529c089ae5 Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Thu, 2 Jun 2022 13:14:29 +0900 Subject: [PATCH 15/16] security: Remove meaningless double check In the function tizen_security_check_privilege(), it double-checks a g_cynara to see if it exists and then return different value if it doesn't exist which is contradictory. Let's just return false if initializing the cynara has been failed or not been initialized. Change-Id: I24755e5b86c5e1f4bf5cf6333a4d4686a880e942 --- src/libds-tizen/util/security.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/libds-tizen/util/security.c b/src/libds-tizen/util/security.c index ad19027..8544951 100644 --- a/src/libds-tizen/util/security.c +++ b/src/libds-tizen/util/security.c @@ -55,11 +55,6 @@ tizen_security_check_privilege(pid_t pid, uid_t uid, const char *privilege) int len = -1; int ret = -1; - /* If cynara_initialize() has been (retried) and failed, we suppose that cynara is not available. */ - /* Then we return true as if there is no security check available. */ - if (!g_cynara) - return true; - if (!g_cynara) { ds_err("security has not been initialized.\n"); return false; -- 2.7.4 From 7dcf6a1c868fd2c893401c745c461e0e551221d3 Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Thu, 2 Jun 2022 13:50:54 +0900 Subject: [PATCH 16/16] security: Refactor implementation for cynara No functional changes Change-Id: If95362c31dae167d7d11eef1b1b2847747e0636a --- src/libds-tizen/util.h | 2 +- src/libds-tizen/util/security.c | 180 +++++++++++++++++++++++----------------- 2 files changed, 104 insertions(+), 78 deletions(-) diff --git a/src/libds-tizen/util.h b/src/libds-tizen/util.h index 18ad811..82e178b 100644 --- a/src/libds-tizen/util.h +++ b/src/libds-tizen/util.h @@ -5,7 +5,7 @@ #define MIN(a,b) ((a)<(b)?(a):(b)) -int +bool tizen_security_init(void); void diff --git a/src/libds-tizen/util/security.c b/src/libds-tizen/util/security.c index 8544951..9983e24 100644 --- a/src/libds-tizen/util/security.c +++ b/src/libds-tizen/util/security.c @@ -1,4 +1,3 @@ - #include #include #include @@ -8,20 +7,61 @@ #include "util.h" #ifdef HAVE_CYNARA +#include +#include + #include #include #include #include -#include -#include #define CYNARA_BUFSIZE 128 -static cynara *g_cynara = NULL; -static int g_cynara_refcount = 0; +struct ds_cynara +{ + cynara *handle; + int references; +}; + +static struct ds_cynara ds_cynara; + +static bool ds_cynara_init(void); +static void ds_cynara_finish(void); +static bool ds_cynara_check_privilege(pid_t pid, uid_t uid, + const char *privilege); +#endif + +bool +tizen_security_check_privilege(pid_t pid, uid_t uid, const char *privilege) +{ +#ifdef HAVE_CYNARA + return ds_cynara_check_privilege(pid, uid, privilege); +#else + return true; +#endif +} + +bool +tizen_security_init(void) +{ +#ifdef HAVE_CYNARA + return ds_cynara_init(); +#else + return true; +#endif +} + +void +tizen_security_finish(void) +{ +#ifdef HAVE_CYNARA + ds_cynara_finish(); +#endif +} +#ifdef HAVE_CYNARA static void -__security_log_print(int err, const char *fmt, ...) +print_cynara_error(int err, const char *fmt, ...) { int ret; va_list args; @@ -42,12 +82,56 @@ __security_log_print(int err, const char *fmt, ...) ds_err("%s is failed. (%s)\n", tmp, buf); } -#endif -bool -tizen_security_check_privilege(pid_t pid, uid_t uid, const char *privilege) +static bool +ds_cynara_init(void) +{ + int ret = CYNARA_API_SUCCESS; + int retry_cnt = 0; + + if (++ds_cynara.references != 1) + return true; + + for (retry_cnt = 0; retry_cnt < 5; retry_cnt++) { + ds_dbg("Retry cynara initialize: %d\n", retry_cnt + 1); + + ret = cynara_initialize(&ds_cynara.handle, NULL); + + if (CYNARA_API_SUCCESS == ret) { + ds_dbg("Succeed to initialize cynara !\n"); + return true; + } + + print_cynara_error(ret, "cynara_initialize"); + } + + ds_err("Failed to initialize cynara! (error:%d, retry_cnt=%d)\n", + ret, retry_cnt); + + --ds_cynara.references; + + return false; + +} + +static void +ds_cynara_finish(void) +{ + if (ds_cynara.references < 1) { + ds_err("%s called without ds_cynara_init\n", __FUNCTION__); + return; + } + + if (--ds_cynara.references != 0) + return; + + cynara_finish(ds_cynara.handle); + ds_cynara.handle = NULL; +} + +static bool +ds_cynara_check_privilege(pid_t pid, uid_t uid, const char *privilege) { -#ifdef HAVE_CYNARA bool res = false; char *client_smack = NULL; char *client_session = NULL; @@ -55,8 +139,8 @@ tizen_security_check_privilege(pid_t pid, uid_t uid, const char *privilege) int len = -1; int ret = -1; - if (!g_cynara) { - ds_err("security has not been initialized.\n"); + if (!ds_cynara.handle) { + ds_err("ds_cynara has not been initialized.\n"); return false; } @@ -70,86 +154,28 @@ tizen_security_check_privilege(pid_t pid, uid_t uid, const char *privilege) if (!client_session) goto finish; - ret = cynara_check(g_cynara, client_smack, client_session, - uid_str, privilege); + ret = cynara_check(ds_cynara.handle, client_smack, client_session, + uid_str, privilege); if (ret == CYNARA_API_ACCESS_ALLOWED) res = true; else - __security_log_print(ret, "privilege: %s, client_smack: %s, pid: %d", privilege, client_smack, pid); + print_cynara_error(ret, "privilege: %s, client_smack: %s, pid: %d", + privilege, client_smack, pid); finish: - ds_dbg("Privilege Check For '%s' %s pid:%u uid:%u client_smack:%s(len:%d) client_session:%s ret:%d", + ds_dbg("Privilege Check For '%s' %s pid:%u uid:%u client_smack:%s(len:%d) " + "client_session:%s ret:%d", privilege, res ? "SUCCESS" : "FAIL", pid, uid, client_smack ? client_smack : "N/A", len, client_session ? client_session: "N/A", ret); if (client_session) free(client_session); + if (client_smack) free(client_smack); return res; -#else - return true; -#endif } - -int -tizen_security_init(void) -{ -#ifdef HAVE_CYNARA - int ret = CYNARA_API_SUCCESS; - int retry_cnt = 0; - static bool retried = false; - - if (++g_cynara_refcount != 1) - return g_cynara_refcount; - - if (!g_cynara && false == retried) { - retried = true; - - for (retry_cnt = 0; retry_cnt < 5; retry_cnt++) { - ds_dbg("Retry cynara initialize: %d\n", retry_cnt + 1); - - ret = cynara_initialize(&g_cynara, NULL); - - if (CYNARA_API_SUCCESS == ret) { - ds_dbg("Succeed to initialize cynara !\n"); - return 1; - } - - __security_log_print(ret, "cynara_initialize"); - g_cynara = NULL; - } - } - - ds_err("Failed to initialize _security ! (error:%d, retry_cnt=%d)\n", - ret, retry_cnt); - --g_cynara_refcount; - - return 0; -#else - return 1; #endif -} - -void -tizen_security_finish(void) -{ -#ifdef HAVE_CYNARA - if (g_cynara_refcount < 1) { - ds_err("%s called without tizen_security_init\n", __FUNCTION__); - return; - } - - if (--g_cynara_refcount != 0) - return; - - if (g_cynara) { - cynara_finish(g_cynara); - g_cynara = NULL; - } -#endif -} - -- 2.7.4