input_method: implement zwp_input_method and context 45/280245/1
authorduna.oh <duna.oh@samsung.com>
Fri, 19 Aug 2022 10:08:17 +0000 (19:08 +0900)
committerTizen Window System <tizen.windowsystem@gmail.com>
Fri, 26 Aug 2022 00:33:05 +0000 (09:33 +0900)
Change-Id: Icc35ccd4f9e6285c37f9143630cca852b8edfd90

include/libds-tizen/input_method.h [new file with mode: 0644]
packaging/libds-tizen.spec
src/input_method/input_method.c [new file with mode: 0644]
src/input_method/meson.build [new file with mode: 0644]
src/meson.build
tests/meson.build
tests/tc_input_method.cpp [new file with mode: 0644]

diff --git a/include/libds-tizen/input_method.h b/include/libds-tizen/input_method.h
new file mode 100644 (file)
index 0000000..c79c534
--- /dev/null
@@ -0,0 +1,152 @@
+#ifndef LIBDS_TIZEN_INPUT_METHOD_H
+#define LIBDS_TIZEN_INPUT_METHOD_H
+
+#include <stdint.h>
+#include <wayland-server.h>
+#include <libds/seat.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ds_tizen_input_method_context_event_commit_string
+{
+    uint32_t serial;
+    const char *text;
+};
+
+struct ds_tizen_input_method_context_event_preedit_string
+{
+    uint32_t serial;
+    const char *text, *commit;
+};
+
+struct ds_tizen_input_method_context_event_preedit_styling
+{
+    uint32_t index, length, style;
+};
+
+struct ds_tizen_input_method_context_event_preedit_cursor
+{
+    int32_t index;
+};
+
+struct ds_tizen_input_method_context_event_delete_surrounding_text
+{
+    int32_t index;
+    uint32_t length;
+};
+
+struct ds_tizen_input_method_context_event_cursor_position
+{
+    int32_t index, anchor;
+};
+
+struct ds_tizen_input_method_context_event_modifiers_map
+{
+    struct wl_array *map;
+};
+
+struct ds_tizen_input_method_context_event_keysym
+{
+    uint32_t serial, time, sym, state, modifiers;
+};
+
+struct ds_tizen_input_method_context_event_language
+{
+    uint32_t serial;
+    const char *language;
+};
+
+struct ds_tizen_input_method_context_event_text_direction
+{
+    uint32_t serial, direction;
+};
+
+//input_method
+struct ds_tizen_input_method *
+ds_tizen_input_method_create(struct wl_display *display);
+
+struct ds_tizen_input_method_context *
+ds_tizen_input_method_create_context(struct ds_tizen_input_method *input_method);
+
+void
+ds_tizen_input_method_add_destroy_listener(
+    struct ds_tizen_input_method *im, struct wl_listener *listener);
+void
+ds_tizen_input_method_add_new_input_method_context_listener(
+    struct ds_tizen_input_method *im, struct wl_listener *listener);
+
+// events
+void ds_tizen_input_method_send_activate(struct ds_tizen_input_method *im,
+    struct ds_tizen_input_method_context *context);
+void ds_tizen_input_method_send_deactivate(struct ds_tizen_input_method *im,
+    struct ds_tizen_input_method_context *context);
+
+//input_method_context
+void
+ds_tizen_input_method_context_add_destroy_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener);
+void
+ds_tizen_input_method_context_add_commit_string_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener);
+void
+ds_tizen_input_method_context_add_preedit_string_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener);
+void
+ds_tizen_input_method_context_add_preedit_styling_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener);
+void
+ds_tizen_input_method_context_add_preedit_cursor_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener);
+void
+ds_tizen_input_method_context_add_delete_surrounding_text_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener);
+void
+ds_tizen_input_method_context_add_cursor_position_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener);
+void
+ds_tizen_input_method_context_add_modifiers_map_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener);
+void
+ds_tizen_input_method_context_add_keysym_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener);
+void
+ds_tizen_input_method_context_add_grab_keyboard_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener);
+void
+ds_tizen_input_method_context_add_key_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener);
+void
+ds_tizen_input_method_context_add_modifiers_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener);
+void
+ds_tizen_input_method_context_add_language_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener);
+void
+ds_tizen_input_method_context_add_text_direction_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener);
+
+// events
+void
+ds_tizen_input_method_context_send_surrounding_text(struct ds_tizen_input_method_context *context,
+    const char *text, uint32_t cursor, uint32_t anchor);
+void
+ds_tizen_input_method_context_send_reset(struct ds_tizen_input_method_context *context);
+void
+ds_tizen_input_method_context_send_content_type(
+    struct ds_tizen_input_method_context *context, uint32_t hint, uint32_t purpose);
+void
+ds_tizen_input_method_context_send_invoke_action(
+    struct ds_tizen_input_method_context *context, uint32_t button, uint32_t index);
+void
+ds_tizen_input_method_context_send_commit_state(
+    struct ds_tizen_input_method_context *context, uint32_t serial);
+void
+ds_tizen_input_method_context_send_preferred_language(
+    struct ds_tizen_input_method_context *context, const char *launguage);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
\ No newline at end of file
index 09fbf34..65b6c33 100644 (file)
@@ -16,6 +16,8 @@ BuildRequires:  pkgconfig(tizen-extension-server)
 BuildRequires:  pkgconfig(tizen-extension-client)
 BuildRequires:  pkgconfig(tizen-launch-server)
 BuildRequires:  pkgconfig(tizen-launch-client)
+BuildRequires:  pkgconfig(input-method-server)
+BuildRequires:  pkgconfig(input-method-client)
 BuildRequires:  pkgconfig(pixman-1)
 BuildRequires:  pkgconfig(libdrm)
 BuildRequires:  pkgconfig(xkbcommon)
@@ -261,6 +263,21 @@ Group:   Development/Libraries
 %description embedded-compositor-devel
 Development package for tizen embedded compositor
 
+## libds-tizen-input-method
+%package input-method
+Summary: Library for tizen input-method
+Group:   Development/Libraries
+
+%description input-method
+Library for tizen input-method
+
+%package input-method-devel
+Summary: Development package for tizen input-method
+Group:   Development/Libraries
+
+%description input-method-devel
+Development package for tizen input-method
+
 %prep
 %setup -q
 cp %{SOURCE1001} .
@@ -504,3 +521,18 @@ ninja -C builddir install
 %{_libdir}/pkgconfig/libds-tizen-embedded-compositor.pc
 %{_libdir}/libds-tizen-embedded-compositor.so
 %{_bindir}/libds-tizen-embedded-compositor-tests
+
+%files input-method
+%manifest %{name}.manifest
+%defattr(-,root,root,-)
+%license LICENSE
+%{_libdir}/libds-tizen-input-method.so.*
+
+%files input-method-devel
+%manifest %{name}.manifest
+%defattr(-,root,root,-)
+%license LICENSE
+%{_includedir}/libds-tizen/input_method.h
+%{_libdir}/pkgconfig/libds-tizen-input-method.pc
+%{_libdir}/libds-tizen-input-method.so
+%{_bindir}/libds-tizen-input-method-tests
diff --git a/src/input_method/input_method.c b/src/input_method/input_method.c
new file mode 100644 (file)
index 0000000..0481e1a
--- /dev/null
@@ -0,0 +1,598 @@
+#include <stdlib.h>
+#include <wayland-server.h>
+#include <input-method-server-protocol.h>
+#include <libds/log.h>
+
+#include "util.h"
+#include <libds/seat.h>
+#include <libds-tizen/input_method.h>
+
+#define INPUT_METHOD_VERSION 1
+
+struct ds_tizen_input_method;
+
+struct ds_tizen_input_method_context {
+    struct wl_resource *resource;
+    struct ds_tizen_input_method *input_method;
+
+    struct {
+        struct wl_signal destroy;
+        struct wl_signal commit_string;
+        struct wl_signal preedit_string;
+        struct wl_signal preedit_styling;
+        struct wl_signal preedit_cursor;
+        struct wl_signal delete_surrounding_text;
+        struct wl_signal cursor_position;
+        struct wl_signal modifiers_map;
+        struct wl_signal keysym;
+        struct wl_signal grab_keyboard;
+        struct wl_signal key;
+        struct wl_signal modifiers;
+        struct wl_signal language;
+        struct wl_signal text_direction;
+    } events;
+};
+
+struct ds_tizen_input_method {
+    struct wl_global *global;
+    struct wl_resource *resource;
+    struct ds_tizen_input_method_context *context;
+
+    struct wl_listener destroy;
+
+    struct {
+        struct wl_signal destroy;
+        struct wl_signal new_input_method_context;
+    } events;
+};
+
+static const struct zwp_input_method_context_v1_interface context_impl;
+
+WL_EXPORT void
+ds_tizen_input_method_add_destroy_listener(
+    struct ds_tizen_input_method *im, struct wl_listener *listener)
+{
+    wl_signal_add(&im->events.destroy, listener);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_add_new_input_method_context_listener(
+    struct ds_tizen_input_method *im, struct wl_listener *listener)
+{
+    wl_signal_add(&im->events.new_input_method_context, listener);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_send_activate(struct ds_tizen_input_method *im,
+    struct ds_tizen_input_method_context *context)
+{
+    if (!im || !context) return;
+
+    ds_inf("ds_tizen_input_method_send_activate");
+    zwp_input_method_v1_send_activate(im->resource, context->resource);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_send_deactivate(struct ds_tizen_input_method *im,
+    struct ds_tizen_input_method_context *context)
+{
+    if (!im || !context) return;
+
+    ds_inf("ds_tizen_input_method_send_deactivate");
+    zwp_input_method_v1_send_deactivate(im->resource, context->resource);
+}
+
+static void
+context_destroy(struct ds_tizen_input_method_context *context)
+{
+    ds_inf("input_method_context(%p) destroy", context);
+
+    wl_signal_emit(&context->events.destroy, context);
+
+    free(context);
+}
+
+static void
+input_method_context_client_handle_destroy(struct wl_resource *resource)
+{
+    struct ds_tizen_input_method_context *context = wl_resource_get_user_data(resource);
+    struct ds_tizen_input_method *input_method;
+   
+    ds_inf("input_method_context_client_handle_destroy");
+
+    input_method = context->input_method;
+    input_method->context = NULL;
+
+    context_destroy(context);
+}
+
+WL_EXPORT struct ds_tizen_input_method_context *
+ds_tizen_input_method_create_context(struct ds_tizen_input_method *input_method)
+{
+    struct ds_tizen_input_method_context *context;
+    struct wl_resource *binding;
+
+    ds_inf("ds_tizen_input_method_create_context");
+    context = calloc(1, sizeof *context);
+    if (context == NULL)
+        return NULL;
+
+    binding = input_method->resource;
+    if (!binding) return NULL;
+    context->resource = wl_resource_create(wl_resource_get_client(binding),
+            &zwp_input_method_context_v1_interface, INPUT_METHOD_VERSION, 0);
+    wl_resource_set_implementation(context->resource, &context_impl,
+        context, input_method_context_client_handle_destroy);
+
+    wl_signal_init(&context->events.destroy);
+    wl_signal_init(&context->events.commit_string);
+    wl_signal_init(&context->events.preedit_string);
+    wl_signal_init(&context->events.preedit_styling);
+    wl_signal_init(&context->events.preedit_cursor);
+    wl_signal_init(&context->events.delete_surrounding_text);
+    wl_signal_init(&context->events.cursor_position);
+    wl_signal_init(&context->events.modifiers_map);
+    wl_signal_init(&context->events.keysym);
+    wl_signal_init(&context->events.grab_keyboard);
+    wl_signal_init(&context->events.key);
+    wl_signal_init(&context->events.modifiers);
+    wl_signal_init(&context->events.language);
+    wl_signal_init(&context->events.text_direction);
+
+    wl_signal_emit(&input_method->events.new_input_method_context, context);
+
+    ds_tizen_input_method_send_activate(input_method, context);
+
+    input_method->context = context;
+    context->input_method = input_method;
+
+    return context;
+}
+
+WL_EXPORT void
+ds_tizen_input_method_context_add_destroy_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener)
+{
+    wl_signal_add(&context->events.destroy, listener);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_context_add_commit_string_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener)
+{
+    wl_signal_add(&context->events.commit_string, listener);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_context_add_preedit_string_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener)
+{
+    wl_signal_add(&context->events.preedit_string, listener);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_context_add_preedit_styling_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener)
+{
+    wl_signal_add(&context->events.preedit_styling, listener);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_context_add_preedit_cursor_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener)
+{
+    wl_signal_add(&context->events.preedit_cursor, listener);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_context_add_delete_surrounding_text_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener)
+{
+    wl_signal_add(&context->events.delete_surrounding_text, listener);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_context_add_cursor_position_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener)
+{
+    wl_signal_add(&context->events.cursor_position, listener);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_context_add_modifiers_map_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener)
+{
+    wl_signal_add(&context->events.modifiers_map, listener);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_context_add_keysym_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener)
+{
+    wl_signal_add(&context->events.keysym, listener);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_context_add_grab_keyboard_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener)
+{
+    wl_signal_add(&context->events.grab_keyboard, listener);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_context_add_key_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener)
+{
+    wl_signal_add(&context->events.key, listener);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_context_add_modifiers_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener)
+{
+    wl_signal_add(&context->events.modifiers, listener);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_context_add_language_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener)
+{
+    wl_signal_add(&context->events.language, listener);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_context_add_text_direction_listener(
+    struct ds_tizen_input_method_context *context, struct wl_listener *listener)
+{
+    wl_signal_add(&context->events.text_direction, listener);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_context_send_surrounding_text(struct ds_tizen_input_method_context *context,
+    const char *text, uint32_t cursor, uint32_t anchor)
+{
+    if (!context || !context->resource) return;
+
+    ds_inf("ds_tizen_input_method_context_send_surrounding_text");
+    zwp_input_method_context_v1_send_surrounding_text(context->resource, text, cursor, anchor);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_context_send_reset(
+    struct ds_tizen_input_method_context *context)
+{
+    if (!context || !context->resource) return;
+
+    ds_inf("ds_tizen_input_method_context_send_reset");
+    zwp_input_method_context_v1_send_reset(context->resource);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_context_send_content_type(
+    struct ds_tizen_input_method_context *context, uint32_t hint, uint32_t purpose)
+{
+    if (!context || !context->resource) return;
+
+    ds_inf("ds_tizen_input_method_context_send_content_type");
+    zwp_input_method_context_v1_send_content_type(context->resource, hint, purpose);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_context_send_invoke_action(
+    struct ds_tizen_input_method_context *context, uint32_t button, uint32_t index)
+{
+    if (!context || !context->resource) return;
+
+    ds_inf("ds_tizen_input_method_context_send_invoke_action");
+    zwp_input_method_context_v1_send_invoke_action(context->resource, button, index);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_context_send_commit_state(
+    struct ds_tizen_input_method_context *context, uint32_t serial)
+{
+    if (!context || !context->resource) return;
+
+    ds_inf("ds_tizen_input_method_context_send_commit_state");
+    zwp_input_method_context_v1_send_commit_state(context->resource, serial);
+}
+
+WL_EXPORT void
+ds_tizen_input_method_context_send_preferred_language(
+    struct ds_tizen_input_method_context *context, const char *language)
+{
+    if (!context || !context->resource) return;
+
+    ds_inf("ds_tizen_input_method_context_send_preferred_language");
+    zwp_input_method_context_v1_send_preferred_language(context->resource, language);
+}
+
+static void
+context_handle_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+    ds_inf("context_handle_destroy");
+    wl_resource_destroy(resource);
+}
+
+static void
+context_handle_commit_string(struct wl_client *client,
+    struct wl_resource *resource, uint32_t serial, const char *text)
+{
+    struct ds_tizen_input_method_context *context = wl_resource_get_user_data(resource);
+    struct ds_tizen_input_method_context_event_commit_string ds_event;
+
+    ds_inf("context_handle_commit_string");
+    ds_event.serial = serial;
+    ds_event.text = text;
+
+    wl_signal_emit(&context->events.commit_string, &ds_event);
+}
+
+static void
+context_handle_preedit_string(struct wl_client *client,
+    struct wl_resource *resource, uint32_t serial, const char *text,
+    const char *commit)
+{
+    struct ds_tizen_input_method_context *context = wl_resource_get_user_data(resource);
+    struct ds_tizen_input_method_context_event_preedit_string ds_event;
+
+    ds_inf("context_handle_preedit_string() serial:%u, text:%s, commit:%s",
+        serial, text, commit);
+
+    ds_event.serial = serial;
+    ds_event.text = text;
+    ds_event.commit = commit;
+
+    wl_signal_emit(&context->events.preedit_string, &ds_event);
+}
+
+static void
+context_handle_preedit_styling(struct wl_client *client,
+    struct wl_resource *resource, uint32_t index, uint32_t length,
+    uint32_t style)
+{
+    struct ds_tizen_input_method_context *context = wl_resource_get_user_data(resource);
+    struct ds_tizen_input_method_context_event_preedit_styling ds_event;
+
+    ds_inf("context_handle_preedit_styling() index:%u, length:%u, style:%u",
+        index, length, style);
+
+    ds_event.index = index;
+    ds_event.length = length;
+    ds_event.style = style;
+
+    wl_signal_emit(&context->events.preedit_styling, &ds_event);
+}
+
+static void
+context_handle_preedit_cursor(struct wl_client *client,
+    struct wl_resource *resource, int32_t index)
+{
+    struct ds_tizen_input_method_context *context = wl_resource_get_user_data(resource);
+    struct ds_tizen_input_method_context_event_preedit_cursor ds_event;
+
+    ds_inf("context_handle_preedit_cursor() cursor:%d", index);
+
+    ds_event.index = index;
+    wl_signal_emit(&context->events.preedit_styling, &ds_event);
+}
+
+static void
+context_handle_delete_surrounding_text(struct wl_client *client,
+    struct wl_resource *resource, int32_t index, uint32_t length)
+{
+    struct ds_tizen_input_method_context *context = wl_resource_get_user_data(resource);
+    struct ds_tizen_input_method_context_event_delete_surrounding_text ds_event;
+
+    ds_inf("context_handle_delete_surrounding_text() index:%d, length:%u", index, length);
+
+    ds_event.index = index;
+    ds_event.length = length;
+    wl_signal_emit(&context->events.delete_surrounding_text, &ds_event);
+}
+
+static void
+context_handle_cursor_position(struct wl_client *client,
+    struct wl_resource *resource, int32_t index, int32_t anchor)
+{
+    struct ds_tizen_input_method_context *context = wl_resource_get_user_data(resource);
+    struct ds_tizen_input_method_context_event_cursor_position ds_event;
+
+    ds_inf("context_handle_cursor_position() index:%d anchor:%d", index, anchor);
+
+    ds_event.index = index;
+    ds_event.anchor = anchor;
+    wl_signal_emit(&context->events.cursor_position, &ds_event);
+}
+
+static void
+context_handle_modifiers_map(struct wl_client *client,
+    struct wl_resource *resource, struct wl_array *map)
+{
+    struct ds_tizen_input_method_context *context = wl_resource_get_user_data(resource);
+    struct ds_tizen_input_method_context_event_modifiers_map ds_event;
+
+    ds_inf("context_handle_modifiers_map() map(%p)", map);
+
+    ds_event.map = map;
+    wl_signal_emit(&context->events.modifiers_map, &ds_event);
+}
+
+static void
+context_handle_keysym(struct wl_client *client, struct wl_resource *resource,
+    uint32_t serial, uint32_t time, uint32_t sym,
+    uint32_t state, uint32_t modifiers)
+{
+    struct ds_tizen_input_method_context *context = wl_resource_get_user_data(resource);
+    struct ds_tizen_input_method_context_event_keysym ds_event;
+
+    ds_inf("context_handle_keysym() serial(%u), time(%u), sym(%u), state(%u), modifiers(%u)",
+        serial, time, sym, state, modifiers);
+
+    ds_event.serial = serial;
+    ds_event.time = time;
+    ds_event.sym = sym;
+    ds_event.state = state;
+    ds_event.modifiers = modifiers;
+    wl_signal_emit(&context->events.keysym, &ds_event);
+}
+
+static void
+context_handle_grab_keyboard(struct wl_client *client,
+    struct wl_resource *resource, uint32_t id)
+{
+    ds_inf("context_handle_grab_keyboard() nothing done");
+    //TODO
+}
+
+static void
+context_handle_key(struct wl_client *client, struct wl_resource *resource,
+    uint32_t serial, uint32_t time, uint32_t key, uint32_t state_w)
+{
+    ds_inf("context_handle_key() nothing done");
+    //TODO
+}
+
+static void
+context_handle_modifiers(struct wl_client *client,
+    struct wl_resource *resource, uint32_t serial, uint32_t mods_depressed,
+    uint32_t mods_latched, uint32_t mods_locked, uint32_t group)
+{
+    ds_inf("context_handle_modifiers() nothing done");
+    //TODO
+}
+
+static void
+context_handle_language(struct wl_client *client,
+    struct wl_resource *resource, uint32_t serial, const char *language)
+{
+    struct ds_tizen_input_method_context *context = wl_resource_get_user_data(resource);
+    struct ds_tizen_input_method_context_event_language ds_event;
+
+    ds_inf("context_handle_language() serial(%u) language(%s)", serial, language);
+
+    ds_event.serial = serial;
+    ds_event.language = language;
+    wl_signal_emit(&context->events.language, &ds_event);
+}
+
+static void
+context_handle_text_direction(struct wl_client *client,
+    struct wl_resource *resource, uint32_t serial, uint32_t direction)
+{
+    struct ds_tizen_input_method_context *context = wl_resource_get_user_data(resource);
+    struct ds_tizen_input_method_context_event_text_direction ds_event;
+
+    ds_inf("context_handle_text_direction() serial(%u) direction(%u)", serial, direction);
+
+    ds_event.serial = serial;
+    ds_event.direction = direction;
+    wl_signal_emit(&context->events.text_direction, &ds_event);
+}
+
+static const struct zwp_input_method_context_v1_interface context_impl = {
+    .destroy = context_handle_destroy,
+    .commit_string = context_handle_commit_string,
+    .preedit_string = context_handle_preedit_string,
+    .preedit_styling = context_handle_preedit_styling,
+    .preedit_cursor = context_handle_preedit_cursor,
+    .delete_surrounding_text = context_handle_delete_surrounding_text,
+    .cursor_position = context_handle_cursor_position,
+    .modifiers_map = context_handle_modifiers_map,
+    .keysym = context_handle_keysym,
+    .grab_keyboard = context_handle_grab_keyboard,
+    .key = context_handle_key,
+    .modifiers = context_handle_modifiers,
+    .language = context_handle_language,
+    .text_direction = context_handle_text_direction,
+    //for tizen_only
+    .selection_region = NULL,
+    .private_command = NULL,
+    .update_input_panel_data = NULL,
+    .hide_input_panel = NULL,
+    .get_selection_text = NULL,
+    .get_surrounding_text = NULL,
+    .filter_key_event_done = NULL,
+    .update_ise_geometry = NULL,
+    .recapture_string = NULL,
+    .input_panel_event = NULL,
+    .commit_content = NULL,
+    .update_candidate_state = NULL,
+    .reshow_input_panel = NULL,
+    .set_floating_panel = NULL,
+    .set_floating_drag_enabled = NULL,
+};
+
+static void
+input_method_handle_display_destroy(struct wl_listener *listener, void *data)
+{
+    struct ds_tizen_input_method *input_method;
+
+    input_method = wl_container_of(listener, input_method, destroy);
+
+    ds_inf("Global destroy: input_method(%p)", input_method);
+
+    if (input_method->context)
+        context_destroy(input_method->context);
+
+    wl_signal_emit(&input_method->events.destroy, input_method);
+    wl_list_remove(&input_method->destroy.link);
+
+    wl_global_destroy(input_method->global);
+    free(input_method);
+}
+
+static void
+input_method_client_handle_destroy(struct wl_resource *resource)
+{
+    ds_inf("input_method_client_handle_destroy");
+}
+
+static void
+input_method_bind(struct wl_client *wl_client, void *data,
+    uint32_t version, uint32_t id)
+{
+    struct ds_tizen_input_method *input_method = data;
+
+    ds_inf("input_method. client binds. (client:%p)", wl_client);
+
+    input_method->resource = wl_resource_create(wl_client,
+            &zwp_input_method_v1_interface,
+            version, id);
+    if (input_method->resource == NULL) {
+        ds_err("input_method. wl_resource_create() failed.");
+        wl_client_post_no_memory(wl_client);
+        return;
+    }
+    wl_resource_set_implementation(input_method->resource, NULL,
+        input_method, input_method_client_handle_destroy);
+}
+
+WL_EXPORT struct ds_tizen_input_method *
+ds_tizen_input_method_create(struct wl_display *display)
+{
+    struct ds_tizen_input_method *input_method;
+
+    input_method = calloc(1, sizeof *input_method);
+    if (input_method == NULL) {
+        ds_err("calloc() failed. ds_tizen_input_method");
+        return NULL;
+    }
+
+    input_method->global = wl_global_create(display,
+        &zwp_input_method_v1_interface, INPUT_METHOD_VERSION, input_method, input_method_bind);
+    if (!input_method->global) {
+        free(input_method);
+        return NULL;
+    }
+
+    wl_signal_init(&input_method->events.destroy);
+    wl_signal_init(&input_method->events.new_input_method_context);
+
+    input_method->destroy.notify = input_method_handle_display_destroy;
+    wl_display_add_destroy_listener(display, &input_method->destroy);
+
+    ds_inf("Global create: zwp_input_method_v1. input_method(%p)", input_method);
+
+    return input_method;
+}
diff --git a/src/input_method/meson.build b/src/input_method/meson.build
new file mode 100644 (file)
index 0000000..eb20352
--- /dev/null
@@ -0,0 +1,29 @@
+libds_tizen_input_method_files = [
+  'input_method.c',
+]
+
+libds_tizen_input_method_deps = [
+  deps_libds_tizen,
+  dependency('input-method-server', required: true),
+]
+
+lib_libds_tizen_input_method = shared_library('ds-tizen-input-method', libds_tizen_input_method_files,
+  dependencies: libds_tizen_input_method_deps,
+  include_directories: [ common_inc, include_directories('.'), include_directories('..') ],
+  version: meson.project_version(),
+  install: true
+)
+
+deps_libds_tizen_input_method = declare_dependency(
+  link_with: lib_libds_tizen_input_method,
+  dependencies: libds_tizen_input_method_deps,
+  include_directories: [ common_inc, include_directories('.') ],
+)
+
+pkgconfig = import('pkgconfig')
+pkgconfig.generate(lib_libds_tizen_input_method,
+  version: meson.project_version(),
+  filebase: 'libds-tizen-input-method',
+  name: 'libds-tizen-input-method',
+  description: 'tizen input-method extension of libds-tizen for tizen platform',
+)
index a1ba1ab..37ef631 100644 (file)
@@ -42,3 +42,4 @@ subdir('renderer')
 subdir('screen_rotation')
 subdir('global_resource')
 subdir('embedded_compositor')
+subdir('input_method')
index 34b9245..9ec62fa 100644 (file)
@@ -226,4 +226,23 @@ executable('libds-tizen-embedded-compositor-tests',
   ],
   install_dir: libds_tizen_bindir,
   install : true
-)
\ No newline at end of file
+)
+
+## input method tests
+tc_input_method_files = [
+  'tc_main.cpp',
+  'tc_input_method.cpp',
+]
+
+executable('libds-tizen-input-method-tests',
+  [
+    tc_mock_files,
+    tc_input_method_files
+  ],
+  dependencies: [
+    deps_test_common,
+    deps_libds_tizen_input_method,
+  ],
+  install_dir: libds_tizen_bindir,
+  install : true
+)
diff --git a/tests/tc_input_method.cpp b/tests/tc_input_method.cpp
new file mode 100644 (file)
index 0000000..1bbc1be
--- /dev/null
@@ -0,0 +1,346 @@
+#include "tc_main.h"
+#include "mockclient.h"
+#include "mockcompositor.h"
+#include <libds-tizen/input_method.h>
+#include <input-method-server-protocol.h>
+#include <input-method-client-protocol.h>
+
+#define INPUT_METHOD_VERSION 1
+
+class MockInputMethodCompositor : public MockCompositor
+{
+public:
+    MockInputMethodCompositor()
+        : MockCompositor(&MockInputMethodCompositor::TestSetup, this)
+    {
+        ds_inf("%s : this(%p)", __func__, this);
+
+        // initialize the flags to check
+        bDestroyed = false;
+        bNewInputMethodContext = false;
+    }
+
+    ~MockInputMethodCompositor()
+    {
+        ds_inf("%s : this(%p)", __func__, this);
+    }
+
+    static void TestSetup(void *data)
+    {
+        MockInputMethodCompositor *mockComp =
+            static_cast<MockInputMethodCompositor *>(data);
+        Compositor *comp = mockComp->compositor;
+
+        ds_inf("%s: mockComp(%p)", __func__, mockComp);
+
+        mockComp->mInputMethod = ds_tizen_input_method_create(comp->display);
+
+        // destroy listener
+        mockComp->mDestroyListener.notify =
+            MockInputMethodCompositor::DestroyCallback;
+        mockComp->mDestroyListener.parent = mockComp;
+        ds_tizen_input_method_add_destroy_listener(mockComp->mInputMethod,
+            &mockComp->mDestroyListener);
+
+        // new input method context listener
+        mockComp->mNewInputMethodContextListener.notify =
+            MockInputMethodCompositor::NewInputMethodContextCallback;
+        mockComp->mNewInputMethodContextListener.parent = mockComp;
+        ds_tizen_input_method_add_new_input_method_context_listener(mockComp->mInputMethod,
+            &mockComp->mNewInputMethodContextListener);
+    }
+
+    static void DestroyCallback(struct wl_listener *listener, void *data)
+    {
+        ds_inf("%s", __func__);
+
+        MockInputMethodCompositor *mockComp =
+            reinterpret_cast<DestroyListener *>(listener)->parent;
+
+        mockComp->bDestroyed = true;
+    }
+
+    static void NewInputMethodContextCallback(struct wl_listener *listener, void *data)
+    {
+        ds_inf("%s", __func__);
+
+        MockInputMethodCompositor *mockComp =
+            reinterpret_cast<NewInputMethodContextListener *>(listener)->parent;
+        struct ds_tizen_input_method_context *context =
+            static_cast<struct ds_tizen_input_method_context *>(data);
+
+        ds_inf("%s: mockComp(%p), input_method_context(%p)", __func__, mockComp, context);
+
+        mockComp->mInputMethodContext = context;
+        mockComp->bNewInputMethodContext = true;
+    }
+
+    void CreateInputMethodContext()
+    {
+        ds_inf("%s", __func__);
+
+        ds_tizen_input_method_create_context(mInputMethod);
+    }
+
+    void SendActivate()
+    {
+        ds_inf("%s", __func__);
+
+        ds_tizen_input_method_send_activate(mInputMethod, mInputMethodContext);
+    }
+
+    void SendDeactivate()
+    {
+        ds_inf("%s", __func__);
+
+        ds_tizen_input_method_send_deactivate(mInputMethod, mInputMethodContext);
+    }
+
+public:
+    bool bDestroyed;
+    bool bNewInputMethodContext;
+
+private:
+    struct ds_tizen_input_method_manager *mInputMethodMgr;
+    struct ds_tizen_input_method *mInputMethod;
+    struct ds_tizen_input_method_context *mInputMethodContext;
+
+    struct DestroyListener: ::wl_listener {
+        MockInputMethodCompositor *parent;
+    };
+    DestroyListener mDestroyListener;
+
+    struct NewInputMethodContextListener: ::wl_listener {
+        MockInputMethodCompositor *parent;
+    };
+    NewInputMethodContextListener mNewInputMethodContextListener;
+};
+
+class MockInputMethodClient : public MockClient
+{
+public:
+    MockInputMethodClient()
+        : bActivate(false),
+          bDeactivate(false),
+          compositor_res(nullptr),
+          zwp_input_method(nullptr)
+    {}
+
+    MockInputMethodClient(const struct wl_registry_listener *listener)
+        : MockClient(listener, this)
+    {
+        ds_inf("%s", __func__);
+    }
+
+    ~MockInputMethodClient()
+    {
+        ds_inf("%s", __func__);
+    }
+
+    void SetWlCompositor(struct wl_compositor *global_res)
+    {
+        ds_inf("%s", __func__);
+
+        compositor_res = global_res;
+    }
+
+    struct wl_compositor *GetWlCompositor()
+    {
+        ds_inf("%s", __func__);
+
+        return compositor_res;
+    }
+
+    void SetInputMethod(struct zwp_input_method_v1 *global_res)
+    {
+        ds_inf("%s", __func__);
+        zwp_input_method = global_res;
+    }
+
+    struct zwp_input_method_v1 *GetInputMethod()
+    {
+        ds_inf("%s", __func__);
+
+        return zwp_input_method;
+    }
+
+public:
+    bool bActivate;
+    bool bDeactivate;
+
+private:
+    struct wl_compositor *compositor_res;
+    struct zwp_input_method_v1 *zwp_input_method;
+};
+
+static void
+client_registry_cb_global(void *data, struct wl_registry *registry,
+    uint32_t name, const char *interface, uint32_t version)
+{
+    ds_inf("%s", __func__);
+
+    MockInputMethodClient *client = static_cast<MockInputMethodClient *>(data);
+    struct wl_compositor *compositor_res;
+    struct zwp_input_method_v1 *zwp_input_method;
+
+    if (!strcmp(interface, "wl_compositor")) {
+        compositor_res = (struct wl_compositor *)wl_registry_bind(registry,
+            name, &wl_compositor_interface, 1);
+        if (compositor_res == nullptr) {
+            ds_err("wl_registry_bind() failed. wl_compositor resource.");
+            return;
+        }
+        client->SetWlCompositor(compositor_res);
+    } else if (!strcmp(interface, "zwp_input_method_v1")) {
+        zwp_input_method = (struct zwp_input_method_v1 *)wl_registry_bind(registry,
+            name, &zwp_input_method_v1_interface, INPUT_METHOD_VERSION);
+        if (zwp_input_method == nullptr) {
+            ds_err("wl_registry_bind() failed. zwp_input_method_v1 resource.");
+            return;
+        }
+        client->SetInputMethod(zwp_input_method);
+    }
+}
+
+static void
+client_registry_cb_global_remove(void *data, struct wl_registry *registry,
+    uint32_t name)
+{
+    ds_inf("%s", __func__);
+
+    MockInputMethodClient *client = static_cast<MockInputMethodClient *>(data);
+    struct wl_compositor *compositor_res = client->GetWlCompositor();
+    struct zwp_input_method_v1 *input_method_res = client->GetInputMethod();
+
+    zwp_input_method_v1_destroy(input_method_res);
+    wl_compositor_destroy(compositor_res);
+}
+
+static const struct wl_registry_listener registry_listener = {
+    .global = client_registry_cb_global,
+    .global_remove = client_registry_cb_global_remove
+};
+
+static void
+im_cb_activate (void *data, struct zwp_input_method_v1 *input_method, struct zwp_input_method_context_v1 *im_ctx)
+{
+    ds_inf("%s", __func__);
+
+    MockInputMethodClient *client = static_cast<MockInputMethodClient *>(data);
+    client->bActivate = true;
+}
+static void
+im_cb_deactivate (void *data, struct zwp_input_method_v1 *input_method, struct zwp_input_method_context_v1 *im_ctx)
+{
+    ds_inf("%s", __func__);
+
+    MockInputMethodClient *client = static_cast<MockInputMethodClient *>(data);
+    client->bDeactivate = true;
+}
+static void
+im_cb_destroy (void *data, struct zwp_input_method_v1 *input_method, struct zwp_input_method_context_v1 *im_ctx)
+{}
+static void
+im_cb_show_input_panel (void *data, struct zwp_input_method_v1 *input_method, struct zwp_input_method_context_v1 *im_ctx, uint32_t angle)
+{}
+static void
+im_cb_hide_input_panel (void *data, struct zwp_input_method_v1 *input_method, struct zwp_input_method_context_v1 *im_ctx)
+{}
+static void
+im_cb_open_connection (void *data, struct zwp_input_method_v1 *input_method, struct zwp_input_method_context_v1 *im_ctx)
+{}
+static void
+im_cb_close_connection (void *data, struct zwp_input_method_v1 *input_method, struct zwp_input_method_context_v1 *im_ctx)
+{}
+static void
+im_cb_set_text_input_id (void *data, struct zwp_input_method_v1 *input_method, struct zwp_input_method_context_v1 *im_ctx, uint32_t text_input_id)
+{}
+static const struct zwp_input_method_v1_listener input_method_cb_listener = {
+    .activate = im_cb_activate,
+    .deactivate = im_cb_deactivate,
+    //for tizen only
+    .destroy = im_cb_destroy,
+    .show_input_panel = im_cb_show_input_panel,
+    .hide_input_panel = im_cb_hide_input_panel,
+    .open_connection = im_cb_open_connection,
+    .close_connection = im_cb_close_connection,
+    .set_text_input_id = im_cb_set_text_input_id,
+};
+
+class InputMethodTest : public ::testing::Test
+{
+public:
+    void SetUp(void) override;
+    void TearDown(void) override;
+
+    MockInputMethodCompositor *comp;
+    MockInputMethodClient *client;
+    struct wl_compositor *compositor_res;
+    struct zwp_input_method_v1 *input_method_res;
+};
+
+void
+InputMethodTest::SetUp(void)
+{
+    ds_log_init(DS_INF, NULL);
+
+    ds_inf("%s", __func__);
+
+    comp = new MockInputMethodCompositor();
+    client = new MockInputMethodClient(&registry_listener);
+    compositor_res = client->GetWlCompositor();
+    input_method_res = client->GetInputMethod();
+
+    zwp_input_method_v1_add_listener(input_method_res,
+        &input_method_cb_listener, client);
+
+    client->RoundTrip();
+}
+
+void
+InputMethodTest::TearDown(void)
+{
+    ds_inf("%s", __func__);
+
+    client->RoundTrip();
+
+    delete client;
+    delete comp;
+}
+
+TEST_F(InputMethodTest, Create_P)
+{
+    EXPECT_TRUE(true);
+}
+
+TEST_F(InputMethodTest, CreateInputMethodContext)
+{
+    comp->CreateInputMethodContext();
+    comp->Process();
+
+    EXPECT_TRUE(comp->bNewInputMethodContext);
+}
+
+TEST_F(InputMethodTest, Ev_Activate)
+{
+    comp->CreateInputMethodContext();
+    comp->Process();
+
+    comp->SendActivate();
+    comp->Process();
+
+    client->RoundTrip();
+    EXPECT_TRUE(client->bActivate);
+}
+
+TEST_F(InputMethodTest, Ev_Deactivate)
+{
+    comp->CreateInputMethodContext();
+    comp->Process();
+
+    comp->SendDeactivate();
+    comp->Process();
+
+    client->RoundTrip();
+    EXPECT_TRUE(client->bDeactivate);
+}