From: duna.oh Date: Mon, 22 Aug 2022 10:44:53 +0000 (+0900) Subject: clients: add ime-keyboard(input_method), text-entry(text_input) X-Git-Tag: accepted/tizen/unified/20220829.062550~4 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f854b9fd11277eac9ac471c9357698df2ca98c2d;p=platform%2Fcore%2Fuifw%2Flibds-tizen.git clients: add ime-keyboard(input_method), text-entry(text_input) ime-keyboard - deals with input_method - input key event usage) key [keys index] [down=1/up=0] ( keys = [q,w,e,r,t,,,,] ) ex) key 1 1 (input 'w' key) key 1 0 key 2 1 (input 'e' key) key 2 0 text-entry - deals with text_input - when mouse enters into the surface and mouse is pressed, text_input is activated. when mouse is released, text_input is deactivated. - usage) when text_input is activated, this gets key event from input_method. Change-Id: Idbea6f4d5756cfcb65f2810f4a3967669ff04045 --- diff --git a/clients/ime-keyboard.c b/clients/ime-keyboard.c new file mode 100644 index 0000000..4d6346d --- /dev/null +++ b/clients/ime-keyboard.c @@ -0,0 +1,957 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define MAX_STR 1024 +#define SIZE_EPOLL 16 + +enum keyboard_state { + KEYBOARD_STATE_DEFAULT, + KEYBOARD_STATE_UPPERCASE, + KEYBOARD_STATE_SYMBOLS +}; + +struct display +{ + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct wl_event_queue *queue; + + int run; + int fd_epoll; + int fd_display; + + int enable_log; + + struct zwp_input_method_manager_v1 *input_method_mgr; + struct zwp_input_method_v1 *input_method; + struct zwp_input_method_context_v1 *context; + + char *preedit_string; + uint32_t preedit_style; + struct { + xkb_mod_mask_t shift_mask; + } keysym; + uint32_t serial; + uint32_t content_hint; + int32_t content_purpose; + char *preferred_language; + char *surrounding_text; + uint32_t surrounding_cursor; + enum keyboard_state state; //struct keyboard *keyboard; + + //struct zwp_input_panel_surface_v1 *ips; +}; + +struct layout { + const struct key *keys; + uint32_t count; + + uint32_t columns; + uint32_t rows; + + const char *language; + uint32_t text_direction; +}; + +enum key_type { + keytype_default, + keytype_backspace, + keytype_enter, + keytype_space, + keytype_switch, + keytype_symbols, + keytype_tab, + keytype_arrow_up, + keytype_arrow_left, + keytype_arrow_right, + keytype_arrow_down, + keytype_style +}; + +struct key { + enum key_type key_type; + + char *label; + char *uppercase; + char *symbol; + + unsigned int width; +}; + +static const struct key normal_keys[] = { + { keytype_default, "q", "Q", "1", 1}, + { keytype_default, "w", "W", "2", 1}, + { keytype_default, "e", "E", "3", 1}, + { keytype_default, "r", "R", "4", 1}, + { keytype_default, "t", "T", "5", 1}, + { keytype_default, "y", "Y", "6", 1}, + { keytype_default, "u", "U", "7", 1}, + { keytype_default, "i", "I", "8", 1}, + { keytype_default, "o", "O", "9", 1}, + { keytype_default, "p", "P", "0", 1}, + { keytype_backspace, "<--", "<--", "<--", 2}, + + { keytype_tab, "->|", "->|", "->|", 1}, + { keytype_default, "a", "A", "-", 1}, + { keytype_default, "s", "S", "@", 1}, + { keytype_default, "d", "D", "*", 1}, + { keytype_default, "f", "F", "^", 1}, + { keytype_default, "g", "G", ":", 1}, + { keytype_default, "h", "H", ";", 1}, + { keytype_default, "j", "J", "(", 1}, + { keytype_default, "k", "K", ")", 1}, + { keytype_default, "l", "L", "~", 1}, + { keytype_enter, "Enter", "Enter", "Enter", 2}, + + { keytype_switch, "ABC", "abc", "ABC", 2}, + { keytype_default, "z", "Z", "/", 1}, + { keytype_default, "x", "X", "\'", 1}, + { keytype_default, "c", "C", "\"", 1}, + { keytype_default, "v", "V", "+", 1}, + { keytype_default, "b", "B", "=", 1}, + { keytype_default, "n", "N", "?", 1}, + { keytype_default, "m", "M", "!", 1}, + { keytype_default, ",", ",", "\\", 1}, + { keytype_default, ".", ".", "|", 1}, + { keytype_switch, "ABC", "abc", "ABC", 1}, + + { keytype_symbols, "?123", "?123", "abc", 1}, + { keytype_space, "", "", "", 5}, + { keytype_arrow_up, "/\\", "/\\", "/\\", 1}, + { keytype_arrow_left, "<", "<", "<", 1}, + { keytype_arrow_right, ">", ">", ">", 1}, + { keytype_arrow_down, "\\/", "\\/", "\\/", 1}, + { keytype_style, "", "", "", 2} +}; + +static const struct key numeric_keys[] = { + { keytype_default, "1", "1", "1", 1}, + { keytype_default, "2", "2", "2", 1}, + { keytype_default, "3", "3", "3", 1}, + { keytype_default, "4", "4", "4", 1}, + { keytype_default, "5", "5", "5", 1}, + { keytype_default, "6", "6", "6", 1}, + { keytype_default, "7", "7", "7", 1}, + { keytype_default, "8", "8", "8", 1}, + { keytype_default, "9", "9", "9", 1}, + { keytype_default, "0", "0", "0", 1}, + { keytype_backspace, "<--", "<--", "<--", 2}, + + { keytype_space, "", "", "", 4}, + { keytype_enter, "Enter", "Enter", "Enter", 2}, + { keytype_arrow_up, "/\\", "/\\", "/\\", 1}, + { keytype_arrow_left, "<", "<", "<", 1}, + { keytype_arrow_right, ">", ">", ">", 1}, + { keytype_arrow_down, "\\/", "\\/", "\\/", 1}, + { keytype_style, "", "", "", 2} +}; + +static const struct layout normal_layout = { + normal_keys, + sizeof(normal_keys) / sizeof(*normal_keys), + 12, + 4, + "en", + WL_TEXT_INPUT_TEXT_DIRECTION_LTR +}; + +static const struct layout numeric_layout = { + numeric_keys, + sizeof(numeric_keys) / sizeof(*numeric_keys), + 12, + 2, + "en", + WL_TEXT_INPUT_TEXT_DIRECTION_LTR +}; + +struct display data_wl; +static void +request_key(uint32_t time, const struct key *key, enum wl_pointer_button_state state); + +static void +usage(void) +{ + printf(" This is virtual keyboard. (refer to weston/clients/keyboard.c)\n"); + printf("\n"); +} + +static const struct layout * +get_current_layout(void) +{ + switch (data_wl.content_purpose) { + case WL_TEXT_INPUT_CONTENT_PURPOSE_DIGITS: + case WL_TEXT_INPUT_CONTENT_PURPOSE_NUMBER: + return &numeric_layout; + default: + return &normal_layout; + } +} + +static void +stdin_read(void) +{ + int c; + char buf[MAX_STR] = {0, }, *tmp, *buf_ptr; + int count = 0; + static uint32_t time = 12345; + const struct layout *layout; + uint32_t key_type = 0, key_idx = 0; + + while ((c = getchar()) != EOF) { + if (c == '\n') break; + if (count >= MAX_STR) break; + + buf[count] = c; + count++; + } + + count = 0; + tmp = strtok_r(buf, " ", &buf_ptr); + if (!tmp) return; + + if (!strncmp(buf, "q", MAX_STR) || !strncmp(buf, "quit", MAX_STR)) { + data_wl.run = 0; + } else if (!strncmp(buf, "help", MAX_STR)) { + usage(); + } else if (!strncmp(buf, "log", MAX_STR)) { + if (data_wl.enable_log) + printf("Disable detailed logs\n"); + else + printf("Enable detailed logs\n"); + + data_wl.enable_log = !data_wl.enable_log; + } else if (!strncmp(tmp, "key", MAX_STR)) { + while (tmp) { + tmp = strtok_r(NULL, " ", &buf_ptr); + if (tmp) { + switch (count) { + case 0: + key_idx = atoi(tmp); + break; + case 1: + key_type = atoi(tmp); + break; + default: + break; + } + } + count++; + } + if (data_wl.enable_log) + printf("request key() key_idx:%u, key_type:%u\n", key_idx, key_type); + + layout = get_current_layout(); + request_key(time++, &layout->keys[key_idx], key_type); + + } else { + printf("Invalid arguments\n"); + usage(); + } +} + +static const char * +prev_utf8_char(const char *s, const char *p) +{ + for (--p; p >= s; --p) { + if ((*p & 0xc0) != 0x80) + return p; + } + return NULL; +} + +static void +delete_before_cursor(void) +{ + const char *start, *end; + + if (!data_wl.surrounding_text) { + printf("delete_before_cursor: No surrounding text available\n"); + return; + } + + start = prev_utf8_char(data_wl.surrounding_text, + data_wl.surrounding_text + data_wl.surrounding_cursor); + if (!start) { + printf("delete_before_cursor: No previous character to delete\n"); + return; + } + + end = data_wl.surrounding_text + data_wl.surrounding_cursor; + + zwp_input_method_context_v1_delete_surrounding_text(data_wl.context, + (start - data_wl.surrounding_text) - data_wl.surrounding_cursor, + end - start); + zwp_input_method_context_v1_commit_string(data_wl.context, + data_wl.serial, + ""); + + /* Update surrounding text */ + data_wl.surrounding_cursor = start - data_wl.surrounding_text; + data_wl.surrounding_text[data_wl.surrounding_cursor] = '\0'; + if (*end) + memmove(data_wl.surrounding_text + data_wl.surrounding_cursor, end, strlen(end)); +} + +static char * +append(char *s1, const char *s2) +{ + int len1, len2; + char *s = NULL; + + len1 = strlen(s1); + len2 = strlen(s2); + s = realloc(s1, len1 + len2 + 1); + if (s == NULL) { + printf("alloc fail"); + return NULL; + } + memcpy(s + len1, s2, len2); + s[len1 + len2] = '\0'; + + return s; +} + +static void +request_preedit(int32_t cursor) +{ + printf("request_preedit() preedit_string:%s\n", data_wl.preedit_string); + uint32_t index = strlen(data_wl.preedit_string); + + if (!data_wl.context) return; + + if (data_wl.preedit_style) + zwp_input_method_context_v1_preedit_styling(data_wl.context, + 0, + strlen(data_wl.preedit_string), + data_wl.preedit_style); + if (cursor > 0) + index = cursor; + zwp_input_method_context_v1_preedit_cursor(data_wl.context, + index); + zwp_input_method_context_v1_preedit_string(data_wl.context, + data_wl.serial, + data_wl.preedit_string, + data_wl.preedit_string); +} + +static char * +insert_text(const char *text, uint32_t offset, const char *insert) +{ + int tlen = strlen(text), ilen = strlen(insert); + char *new_text = NULL; + + new_text = malloc(tlen + ilen + 1); + if (new_text == NULL) { + printf("alloc fail"); + return NULL; + } + + memcpy(new_text, text, offset); + memcpy(new_text + offset, insert, ilen); + memcpy(new_text + offset + ilen, text + offset, tlen - offset); + new_text[tlen + ilen] = '\0'; + + return new_text; +} + +static void +request_commit_string(void) +{ + char *surrounding_text; + + printf("request_commit_string()\n"); + + if (!data_wl.preedit_string || + strlen(data_wl.preedit_string) == 0) + return; + printf("request_commit_string() preedit_string:%s\n", data_wl.preedit_string); + + if (!data_wl.context) return; + + zwp_input_method_context_v1_cursor_position(data_wl.context, + 0, 0); + zwp_input_method_context_v1_commit_string(data_wl.context, + data_wl.serial, + data_wl.preedit_string); + + if (data_wl.surrounding_text) { + surrounding_text = insert_text(data_wl.surrounding_text, + data_wl.surrounding_cursor, + data_wl.preedit_string); + free(data_wl.surrounding_text); + data_wl.surrounding_text = surrounding_text; + data_wl.surrounding_cursor += strlen(data_wl.preedit_string); + } else { + data_wl.surrounding_text = strdup(data_wl.preedit_string); + data_wl.surrounding_cursor = strlen(data_wl.preedit_string); + } + + free(data_wl.preedit_string); + data_wl.preedit_string = strdup(""); +} + +static void +request_key(uint32_t time, const struct key *key, enum wl_pointer_button_state state) +{ + const char *label = NULL; + static uint32_t serial = 9999; + + switch(data_wl.state) { + case KEYBOARD_STATE_DEFAULT : + label = key->label; + break; + case KEYBOARD_STATE_UPPERCASE : + label = key->uppercase; + break; + case KEYBOARD_STATE_SYMBOLS : + label = key->symbol; + break; + default : + label = key->label; + break; + } + + xkb_mod_mask_t mod_mask = data_wl.state == KEYBOARD_STATE_DEFAULT ? 0 : data_wl.keysym.shift_mask; + uint32_t key_state = (state == WL_POINTER_BUTTON_STATE_PRESSED) ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED; + + printf("request_key() data_wl.state:%d, key->key_type:%d, pressed:%u, label:%s\n", data_wl.state, key->key_type, key_state, label); + switch (key->key_type) { + case keytype_default: + if (state != WL_POINTER_BUTTON_STATE_PRESSED) + break; + + printf("request_key() keytype_default. append label(%s) to preedit_string(%s)\n", label, data_wl.preedit_string); + data_wl.preedit_string = + append(data_wl.preedit_string, + label); + request_preedit(-1); + break; + case keytype_backspace: + if (state != WL_POINTER_BUTTON_STATE_PRESSED) + break; + + if (strlen(data_wl.preedit_string) == 0) { + delete_before_cursor(); + } else { + data_wl.preedit_string[strlen(data_wl.preedit_string) - 1] = '\0'; + request_preedit(-1); + } + break; + case keytype_enter: + request_commit_string(); + + if (!data_wl.context) break; + zwp_input_method_context_v1_keysym(data_wl.context, + serial++, + time, + XKB_KEY_Return, key_state, mod_mask); + break; + case keytype_space: + if (state != WL_POINTER_BUTTON_STATE_PRESSED) + break; + data_wl.preedit_string = + append(data_wl.preedit_string, " "); + request_commit_string(); + break; + case keytype_switch: + if (state != WL_POINTER_BUTTON_STATE_PRESSED) + break; + switch(data_wl.state) { + case KEYBOARD_STATE_DEFAULT: + data_wl.state = KEYBOARD_STATE_UPPERCASE; + break; + case KEYBOARD_STATE_UPPERCASE: + data_wl.state = KEYBOARD_STATE_DEFAULT; + break; + case KEYBOARD_STATE_SYMBOLS: + data_wl.state = KEYBOARD_STATE_UPPERCASE; + break; + } + break; + case keytype_symbols: + if (state != WL_POINTER_BUTTON_STATE_PRESSED) + break; + switch(data_wl.state) { + case KEYBOARD_STATE_DEFAULT: + data_wl.state = KEYBOARD_STATE_SYMBOLS; + break; + case KEYBOARD_STATE_UPPERCASE: + data_wl.state = KEYBOARD_STATE_SYMBOLS; + break; + case KEYBOARD_STATE_SYMBOLS: + data_wl.state = KEYBOARD_STATE_DEFAULT; + break; + } + break; + case keytype_tab: + request_commit_string(); + if (!data_wl.context) break; + zwp_input_method_context_v1_keysym(data_wl.context, + serial++, + time, + XKB_KEY_Tab, key_state, mod_mask); + break; + case keytype_arrow_up: + request_commit_string(); + if (!data_wl.context) break; + zwp_input_method_context_v1_keysym(data_wl.context, + serial++, + time, + XKB_KEY_Up, key_state, mod_mask); + break; + case keytype_arrow_left: + request_commit_string(); + if (!data_wl.context) break; + zwp_input_method_context_v1_keysym(data_wl.context, + serial++, + time, + XKB_KEY_Left, key_state, mod_mask); + break; + case keytype_arrow_right: + request_commit_string(); + if (!data_wl.context) break; + zwp_input_method_context_v1_keysym(data_wl.context, + serial++, + time, + XKB_KEY_Right, key_state, mod_mask); + break; + case keytype_arrow_down: + request_commit_string(); + if (!data_wl.context) break; + zwp_input_method_context_v1_keysym(data_wl.context, + serial++, + time, + XKB_KEY_Down, key_state, mod_mask); + break; + case keytype_style: + if (state != WL_POINTER_BUTTON_STATE_PRESSED) + break; + data_wl.preedit_style = (data_wl.preedit_style + 1) % 8; /* TODO */ + request_preedit(-1); + break; + } +} + +static void +im_context_cb_surrounding_text (void *data, struct zwp_input_method_context_v1 *im_ctx, const char *text, uint32_t cursor, uint32_t anchor) +{ + printf("im_context_cb_surrounding_text\n"); + free(data_wl.surrounding_text); + data_wl.surrounding_text = strdup(text); + + data_wl.surrounding_cursor = cursor; +} + +static void +im_context_cb_reset (void *data, struct zwp_input_method_context_v1 *im_ctx) +{ + printf("im_context_cb_reset\n"); + + if (strlen(data_wl.preedit_string)) { + free(data_wl.preedit_string); + data_wl.preedit_string = strdup(""); + } +} + +static void +im_context_cb_content_type (void *data, struct zwp_input_method_context_v1 *im_ctx, uint32_t hint, uint32_t purpose) +{ + printf("im_context_cb_content_type\n"); + + data_wl.content_hint = hint; + data_wl.content_purpose = purpose; +} + +static void +im_context_cb_invoke_action (void *data, struct zwp_input_method_context_v1 *im_ctx, uint32_t button, uint32_t index) +{ + printf("im_context_cb_invoke_action\n"); + + if (button != BTN_LEFT) + return; + + request_preedit(index); +} + +static void +im_context_cb_commit_state (void *data, struct zwp_input_method_context_v1 *context, uint32_t serial) +{ + printf("im_context_cb_commit_state\n"); + + const struct layout *layout; + + data_wl.serial = serial; + + layout = get_current_layout(); + + if (data_wl.surrounding_text) + printf("Surrounding text updated: %s\n", data_wl.surrounding_text); + + if (!context) return; + zwp_input_method_context_v1_language(context, + data_wl.serial, + layout->language); + zwp_input_method_context_v1_text_direction(context, + data_wl.serial, + layout->text_direction); +} + +static void +im_context_cb_preferred_language (void *data, struct zwp_input_method_context_v1 *context, const char *language) +{ + printf("im_context_cb_preferred_language\n"); + + if (data_wl.preferred_language) + free(data_wl.preferred_language); + + data_wl.preferred_language = NULL; + + if (language) + data_wl.preferred_language = strdup(language); +} + +static const struct zwp_input_method_context_v1_listener input_method_context_cb_listener = { + .surrounding_text = im_context_cb_surrounding_text, + .reset = im_context_cb_reset, + .content_type = im_context_cb_content_type, + .invoke_action = im_context_cb_invoke_action, + .commit_state = im_context_cb_commit_state, + .preferred_language = im_context_cb_preferred_language, + //for tizen only + .return_key_type = NULL, + .return_key_disabled = NULL, + .input_panel_data = NULL, + .bidi_direction = NULL, + .cursor_position = NULL, + .process_input_device_event = NULL, + .filter_key_event = NULL, + .capital_mode = NULL, + .prediction_hint = NULL, + .mime_type = NULL, + .finalized_content = NULL, + .prediction_hint_data = NULL, + .input_panel_enabled = NULL, +}; + +static void +keysym_modifiers_add(struct wl_array *modifiers_map, + const char *name) +{ + size_t len = strlen(name) + 1; + char *p; + + p = wl_array_add(modifiers_map, len); + + if (p == NULL) + return; + + strncpy(p, name, len); +} + +static xkb_mod_index_t +keysym_modifiers_get_index(struct wl_array *modifiers_map, + const char *name) +{ + xkb_mod_index_t index = 0; + char *p = modifiers_map->data; + + while ((const char *)p < (const char *)(modifiers_map->data + modifiers_map->size)) { + if (strcmp(p, name) == 0) + return index; + + index++; + p += strlen(p) + 1; + } + + return XKB_MOD_INVALID; +} + +xkb_mod_mask_t +keysym_modifiers_get_mask(struct wl_array *modifiers_map, + const char *name) +{ + xkb_mod_index_t index = keysym_modifiers_get_index(modifiers_map, name); + + if (index == XKB_MOD_INVALID) + return XKB_MOD_INVALID; + + return 1 << index; +} + +static void +im_cb_activate (void *data, struct zwp_input_method_v1 *input_method, struct zwp_input_method_context_v1 *context) +{ + printf("im_cb_activate\n"); + + struct wl_array modifiers_map; + const struct layout *layout; + + if (data_wl.context) + zwp_input_method_context_v1_destroy(data_wl.context); + + if (data_wl.preedit_string) + free(data_wl.preedit_string); + + data_wl.preedit_style = 0; + data_wl.preedit_string = strdup(""); + data_wl.content_hint = 0; + data_wl.content_purpose = 0; + free(data_wl.preferred_language); + data_wl.preferred_language = NULL; + free(data_wl.surrounding_text); + data_wl.surrounding_text = NULL; + + data_wl.serial = 0; + + data_wl.context = context; + zwp_input_method_context_v1_add_listener(context, + &input_method_context_cb_listener, + NULL); + + wl_array_init(&modifiers_map); + keysym_modifiers_add(&modifiers_map, "Shift"); + keysym_modifiers_add(&modifiers_map, "Control"); + keysym_modifiers_add(&modifiers_map, "Mod1"); + zwp_input_method_context_v1_modifiers_map(context, &modifiers_map); + data_wl.keysym.shift_mask = keysym_modifiers_get_mask(&modifiers_map, "Shift"); + wl_array_release(&modifiers_map); + + layout = get_current_layout(); + + zwp_input_method_context_v1_language(context, + data_wl.serial, + layout->language); + zwp_input_method_context_v1_text_direction(context, + data_wl.serial, + layout->text_direction); +} +static void +im_cb_deactivate (void *data, struct zwp_input_method_v1 *input_method, struct zwp_input_method_context_v1 *im_ctx) +{ + printf("im_cb_deactivate\n"); + + if (!data_wl.context) + return; + + zwp_input_method_context_v1_destroy(data_wl.context); + data_wl.context = NULL; +} + +static void +im_cb_destroy (void *data, struct zwp_input_method_v1 *input_method, struct zwp_input_method_context_v1 *im_ctx) +{ + printf("im_cb_destroy\n"); + zwp_input_method_v1_destroy(data_wl.input_method); + data_wl.input_method = NULL; +} + +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 = NULL, + .hide_input_panel = NULL, + .open_connection = NULL, + .close_connection = NULL, + .set_text_input_id = NULL, +}; + +static void +registry_handle_global(void * data, struct wl_registry * registry, uint32_t id, + const char * interface, uint32_t version) +{ + if (strcmp(interface, "wl_compositor") == 0) { + data_wl.compositor = wl_registry_bind(registry, id, + &wl_compositor_interface, version); + if (!data_wl.compositor) { + printf("Failed to bind compositor.\n"); + return; + } + if (data_wl.enable_log) + printf("Success to bind compositor.\n"); + } else if (!strcmp(interface, "zwp_input_method_manager_v1")) { + data_wl.input_method_mgr = wl_registry_bind(registry, id, + &zwp_input_method_manager_v1_interface, version); + } else if (!strcmp(interface, "zwp_input_method_v1")) { + data_wl.input_method = wl_registry_bind(registry, id, + &zwp_input_method_v1_interface, version); + zwp_input_method_v1_add_listener(data_wl.input_method, + &input_method_cb_listener, + NULL); + + //TODO: delte this code. this job should be done in cb_activate + data_wl.preedit_string = strdup(""); + data_wl.content_hint = 0; + data_wl.content_purpose = 0; + free(data_wl.preferred_language); + data_wl.preferred_language = NULL; + free(data_wl.surrounding_text); + data_wl.surrounding_text = NULL; + data_wl.serial = 0; + } +} + +static void +registry_handle_global_remove(void * data, struct wl_registry * registry, uint32_t id) +{ + if (data_wl.enable_log) + printf("registry is removed. id: %d !\n", id); +} + +static const struct wl_registry_listener _registry_listener = { + registry_handle_global, + registry_handle_global_remove +}; + +static int +wayland_init(void) +{ + memset(&data_wl, 0, sizeof(struct display)); + + data_wl.display = wl_display_connect(NULL); + if (!data_wl.display) { + printf("Failed to connect wayland display\n"); + return 0; + } + + data_wl.queue = wl_display_create_queue(data_wl.display); + if (!data_wl.queue) { + printf("Failed to create queue\n"); + return 0; + } + + data_wl.registry = wl_display_get_registry(data_wl.display); + if (!data_wl.registry) { + printf("Failed to get registry\n"); + return 0; + } + + wl_proxy_set_queue((struct wl_proxy*)data_wl.registry, data_wl.queue); + wl_registry_add_listener(data_wl.registry, &_registry_listener, NULL); + + if (wl_display_dispatch_queue(data_wl.display, data_wl.queue) == -1) { + printf("Failed to dispatch display\n"); + return 0; + } + if (wl_display_roundtrip_queue(data_wl.display, data_wl.queue) == -1) { + printf("Failed to roundtrip display\n"); + return 0; + } + + return 1; +} + +static void +wayland_deinit(void) +{ + if (data_wl.enable_log) + printf("Shutdown wayland system\n"); + + if (data_wl.queue) wl_event_queue_destroy(data_wl.queue); + if (data_wl.display) { + if (data_wl.input_method) + zwp_input_method_v1_destroy(data_wl.input_method); + zwp_input_method_manager_v1_destroy(data_wl.input_method_mgr); + wl_registry_destroy(data_wl.registry); + wl_display_flush(data_wl.display); + wl_display_disconnect(data_wl.display); + } +} + +static int +epoll_init(void) +{ + struct epoll_event ep[2]; + + data_wl.fd_epoll = epoll_create(SIZE_EPOLL); + if (data_wl.fd_epoll <= 0) { + printf("Failed to epoll create: %d\n", SIZE_EPOLL); + return 0; + } + + data_wl.fd_display = wl_display_get_fd(data_wl.display); + + memset(ep, 0, sizeof(struct epoll_event)*2); + + ep[0].events = EPOLLIN | EPOLLERR | EPOLLHUP; + ep[0].data.fd = data_wl.fd_display; + epoll_ctl(data_wl.fd_epoll, EPOLL_CTL_ADD, data_wl.fd_display, &ep[0]); + ep[1].events = EPOLLIN | EPOLLERR | EPOLLHUP; + ep[1].data.fd = STDIN_FILENO; + epoll_ctl(data_wl.fd_epoll, EPOLL_CTL_ADD, 0, &ep[1]); + + return 1; +} + +static void +mainloop(void) +{ + struct epoll_event ep[SIZE_EPOLL]; + int res, count, i; + + res = epoll_init(); + if (!res) { + printf("Failed to init epoll\n"); + return; + } + + data_wl.run = 1; + while (data_wl.run) { + res = wl_display_dispatch_queue_pending(data_wl.display, data_wl.queue); + if (res < 0) { + printf("Failed to dispatch pending. result: %d\n", res); + data_wl.run = 0; + break; + } + res = wl_display_flush(data_wl.display); + if (res < 0) { + printf("Failed to flush display. result: %d\n", res); + data_wl.run = 0; + break; + } + + count = epoll_wait(data_wl.fd_epoll, ep, SIZE_EPOLL, -1); + for (i = 0; i < count; i++) { + if (ep[i].events & EPOLLIN) { + if (ep[i].data.fd == data_wl.fd_display) { + wl_display_dispatch_queue(data_wl.display, data_wl.queue); + } else { + stdin_read(); + } + } + if (ep[i].events & EPOLLERR) { + data_wl.run = 0; + } + if (ep[i].events & EPOLLHUP) { + data_wl.run = 0; + } + } + } +} + +int +main(int argc, char **argv) +{ + int res; + + res = wayland_init(); + if (!res) return 0; + + mainloop(); + + wayland_deinit(); + + return 0; +} diff --git a/clients/meson.build b/clients/meson.build index c97e3ad..672792a 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -14,6 +14,13 @@ simple_tbm_deps = [ tizen_extension_client, ] +text_entry_files = ['text-entry.c'] +text_entry_deps = [ + dependency('wayland-client', required: true), + wayland_tbm_client, + libtbm, +] + protocols = { 'xdg-shell': wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', } @@ -28,6 +35,7 @@ foreach name, path : protocols command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], ) simple_tbm_files += code + text_entry_files += code client_header = custom_target( name.underscorify() + '_client_h', @@ -37,6 +45,7 @@ foreach name, path : protocols build_by_default: false, ) simple_tbm_files += client_header + text_entry_files += client_header endforeach executable('ds-simple-tbm', @@ -56,6 +65,15 @@ executable('ds-simple-dpms', install: true, ) +executable('text-entry', + text_entry_files, + dependencies: [text_entry_deps, + deps_libds_tizen_text_input, + ], + install_dir: libds_tizen_bindir, + install: true, +) + input_generator_files = ['input-generator.c'] input_generator_deps = [ dependency('wayland-client', required: true), @@ -68,3 +86,17 @@ executable('input-generator', install_dir: libds_tizen_bindir, install: true, ) + +ime_keyboard_files = ['ime-keyboard.c'] +ime_keyboard_deps = [ + dependency('wayland-client', required: true), +] + +executable('ime-keyboard', + ime_keyboard_files, + dependencies: [ime_keyboard_deps, + deps_libds_tizen_input_method, + ], + install_dir: libds_tizen_bindir, + install: true, +) diff --git a/clients/text-entry.c b/clients/text-entry.c new file mode 100644 index 0000000..a806f0e --- /dev/null +++ b/clients/text-entry.c @@ -0,0 +1,1071 @@ +/* + * Copyright © 2011 Benjamin Franzke + * Copyright © 2010 Intel Corporation + * + * 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 +#include +#include + +#include +#include +#include +#include +#include "xdg-shell-client-protocol.h" +#include +#include + +static uint64_t buffer_info_key; +#define BUFFER_INFO_KEY (unsigned long)(&buffer_info_key) + +struct display { + struct wl_display *display; + struct wl_registry *registry; + 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; + + struct wl_text_input_manager *text_input_mgr; + int notified; + bool blocked; + struct wl_surface *entered_surface; + + struct text_entry *entry; +}; + +struct window { + struct display *display; + int width, height; + struct wl_surface *surface; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; + struct wl_callback *callback; + tbm_surface_queue_h surface_queue; + bool wait_for_configure; +}; + +struct buffer_info { + struct window *window; + struct wl_buffer *wl_buffer; +}; + +struct text_entry { + char *text; + int active; + bool panel_visible; + uint32_t cursor; + uint32_t anchor; + struct { + char *text; + int32_t cursor; + char *commit; + } preedit; + struct { + int32_t cursor; + } preedit_info; + struct { + int32_t cursor; + int32_t anchor; + uint32_t delete_index; + uint32_t delete_length; + bool invalid_delete; + } pending_commit; + struct wl_text_input *text_input; + struct { + xkb_mod_mask_t shift_mask; + } keysym; + uint32_t serial; + uint32_t reset_serial; + uint32_t content_purpose; + bool click_to_show; + char *preferred_language; + bool button_pressed; +}; + +struct rectangle { + int32_t x; + int32_t y; + int32_t width; + int32_t height; +}; + +static int running = 1; + +static void +redraw(void *data, struct wl_callback *callback, uint32_t time); +static void +text_entry_activate(struct display *d); +static void +text_entry_deactivate(struct display *d); + +static void +handle_xdg_surface_configure(void *data, struct xdg_surface *surface, + uint32_t serial) +{ + struct window *window = data; + + xdg_surface_ack_configure(surface, serial); + + if (window->wait_for_configure) { + redraw(window, NULL, 0); + window->wait_for_configure = false; + } +} + +static const struct xdg_surface_listener xdg_surface_listener = { + handle_xdg_surface_configure, +}; + +static void +handle_xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, + int32_t width, int32_t height, + struct wl_array *state) +{ +} + +static void +handle_xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel) +{ + running = 0; +} + +static const struct xdg_toplevel_listener xdg_toplevel_listener = { + handle_xdg_toplevel_configure, + handle_xdg_toplevel_close, +}; + +static struct window * +create_window(struct display *display, int width, int height) +{ + struct window *window; + + window = calloc(1, sizeof *window); + if (!window) + return NULL; + + window->callback = NULL; + window->display = display; + window->width = width; + window->height = height; + window->surface = wl_compositor_create_surface(display->compositor); + + if (display->wm_base) { + window->xdg_surface = + xdg_wm_base_get_xdg_surface(display->wm_base, + window->surface); + assert(window->xdg_surface); + xdg_surface_add_listener(window->xdg_surface, + &xdg_surface_listener, window); + + window->xdg_toplevel = + xdg_surface_get_toplevel(window->xdg_surface); + assert(window->xdg_toplevel); + xdg_toplevel_add_listener(window->xdg_toplevel, + &xdg_toplevel_listener, window); + + xdg_toplevel_set_title(window->xdg_toplevel, "simple-tbm"); + wl_surface_commit(window->surface); + window->wait_for_configure = true; + } else { + assert(0); + } + + window->surface_queue = + wayland_tbm_client_create_surface_queue(display->wl_tbm, + window->surface, + 3, + width, + height, + TBM_FORMAT_XRGB8888); + assert(window->surface_queue); + + return window; +} + +static void +destroy_window(struct window *window) +{ + tbm_surface_queue_destroy(window->surface_queue); + + if (window->callback) + wl_callback_destroy(window->callback); + + if (window->xdg_toplevel) + xdg_toplevel_destroy(window->xdg_toplevel); + if (window->xdg_surface) + xdg_surface_destroy(window->xdg_surface); + wl_surface_destroy(window->surface); + free(window); +} + +static void +paint_pixels(void *image, int padding, int width, int height, uint32_t time) +{ + const int halfh = padding + (height - padding * 2) / 2; + const int halfw = padding + (width - padding * 2) / 2; + int ir, or; + uint32_t *pixel = image; + int y; + + /* squared radii thresholds */ + or = (halfw < halfh ? halfw : halfh) - 8; + ir = or - 32; + or *= or; + ir *= ir; + + pixel += padding * width; + for (y = padding; y < height - padding; y++) { + int x; + int y2 = (y - halfh) * (y - halfh); + + pixel += padding; + for (x = padding; x < width - padding; x++) { + uint32_t v; + + /* squared distance from center */ + int r2 = (x - halfw) * (x - halfw) + y2; + + if (r2 < ir) + v = (r2 / 32 + time / 64) * 0x0080401; + else if (r2 < or) + v = (y + time / 32) * 0x0080401; + else + v = (x + time / 16) * 0x0080401; + v &= 0x00ffffff; + + /* cross if compositor uses X from XRGB as alpha */ + if (abs(x - y) > 6 && abs(x + y - height) > 6) + v |= 0xff000000; + + *pixel++ = v; + } + + pixel += padding; + } +} + +static void +buffer_info_free_cb(void *data) +{ + struct buffer_info *buffer_info = data; + + if (!buffer_info) + return; + + wayland_tbm_client_destroy_buffer(buffer_info->window->display->wl_tbm, + buffer_info->wl_buffer); + free(buffer_info); +} + +static void +buffer_handle_release(void *data, struct wl_buffer *wl_buffer) +{ + tbm_surface_h surface = data; + struct buffer_info *buffer_info; + + tbm_surface_internal_get_user_data(surface, BUFFER_INFO_KEY, + (void **)&buffer_info); + if (buffer_info) + tbm_surface_queue_release(buffer_info->window->surface_queue, surface); +} + +static const struct wl_buffer_listener buffer_listener = { + .release = buffer_handle_release, +}; + +static const struct wl_callback_listener frame_listener; + +static void +redraw(void *data, struct wl_callback *callback, uint32_t time) +{ + struct window *window = data; + struct buffer_info *buffer_info = NULL; + tbm_surface_h surface = NULL; + tbm_surface_info_s surface_info; + + if (!tbm_surface_queue_can_dequeue(window->surface_queue, 0)) + return; + + tbm_surface_queue_dequeue(window->surface_queue, &surface); + assert(surface); + + tbm_surface_internal_get_user_data(surface, BUFFER_INFO_KEY, + (void **)&buffer_info); + if (!buffer_info) { + buffer_info = calloc(1, sizeof *buffer_info); + assert(buffer_info); + + tbm_surface_internal_add_user_data(surface, BUFFER_INFO_KEY, buffer_info_free_cb); + tbm_surface_internal_set_user_data(surface, BUFFER_INFO_KEY, buffer_info); + + buffer_info->wl_buffer = + wayland_tbm_client_create_buffer(window->display->wl_tbm, surface); + assert(buffer_info->wl_buffer); + + wl_buffer_add_listener(buffer_info->wl_buffer, &buffer_listener, + surface); + + buffer_info->window = window; + } + + tbm_surface_map(surface, TBM_SURF_OPTION_WRITE, &surface_info); + + paint_pixels(surface_info.planes[0].ptr, 20, + (surface_info.planes[0].stride/4), surface_info.height, time); + + tbm_surface_unmap(surface); + + wl_surface_attach(window->surface, buffer_info->wl_buffer, 0, 0); + wl_surface_damage(window->surface, + 20, 20, window->width - 40, window->height - 40); + + if (callback) + wl_callback_destroy(callback); + + window->callback = wl_surface_frame(window->surface); + wl_callback_add_listener(window->callback, &frame_listener, window); + wl_surface_commit(window->surface); +} + +static const struct wl_callback_listener frame_listener = { + redraw +}; + +static void +shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) +{ + struct display *d = data; + + if (format == WL_SHM_FORMAT_XRGB8888) + d->has_xrgb = true; +} + +struct wl_shm_listener shm_listener = { + shm_format +}; + +static void +xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial) +{ + xdg_wm_base_pong(shell, serial); +} + +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) +{ + struct display *d = data; + + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + fprintf(stderr, "pointer_handle_button: PRESSED\n"); + + text_entry_activate(d); + } + else { + fprintf(stderr, "pointer_handle_button: RELEASED\n"); + + text_entry_deactivate(d); + } +} + +static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface, + wl_fixed_t surface_x, wl_fixed_t surface_y) +{ + struct display *d = data; + + fprintf(stderr, "pointer_handle_enter surface_x:%d, surface_y:%d\n", + wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y)); + d->entered_surface = surface; +} + +static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer, + 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: key:%d, PRESSED\n", key); + } else { + fprintf(stderr, "keyboard_handle_key: key:%d, RELEASED\n", key); + } +} + +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) +{ + struct display *d = data; + 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, d); + 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, d); + 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 +text_entry_get_cursor_rectangle(struct text_entry *entry, struct rectangle *rectangle) +{ + if (entry->preedit.text && entry->preedit.cursor < 0) { + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = 0; + rectangle->height = 0; + return; + } + + rectangle->x = 10; + rectangle->y = 20; + rectangle->width = 50; + rectangle->height = 50; +} + +static void +text_entry_update(struct text_entry *entry) +{ + struct rectangle cursor_rectangle; + + fprintf(stderr, "text_entry_update()\n"); + + wl_text_input_set_content_type(entry->text_input, + WL_TEXT_INPUT_CONTENT_HINT_NONE, + entry->content_purpose); + + if (entry->preferred_language) + wl_text_input_set_preferred_language(entry->text_input, + entry->preferred_language); + + text_entry_get_cursor_rectangle(entry, &cursor_rectangle); + wl_text_input_set_cursor_rectangle(entry->text_input, + cursor_rectangle.x, + cursor_rectangle.y, + cursor_rectangle.width, + cursor_rectangle.height); + + wl_text_input_commit_state(entry->text_input, ++entry->serial); +} + +static void +text_entry_reset_preedit(struct text_entry *entry) +{ + fprintf(stderr, "text_entry_reset_preedit()\n"); + entry->preedit.cursor = 0; + + free(entry->preedit.text); + entry->preedit.text = NULL; + + free(entry->preedit.commit); + entry->preedit.commit = NULL; +} + +static void +text_entry_insert_at_cursor(struct text_entry *entry, const char *text, + int32_t cursor, int32_t anchor) +{ + fprintf(stderr, "text_entry_insert_at_cursor()\n"); + char *new_text = malloc(strlen(entry->text) + strlen(text) + 1); + if (new_text == NULL) { + fprintf(stderr, "alloc fail"); + return; + } + + strncpy(new_text, entry->text, entry->cursor); + strcpy(new_text + entry->cursor, text); + strcpy(new_text + entry->cursor + strlen(text), + entry->text + entry->cursor); + + free(entry->text); + entry->text = new_text; + if (anchor >= 0) + entry->anchor = entry->cursor + strlen(text) + anchor; + else + entry->anchor = entry->cursor + 1 + anchor; + + if (cursor >= 0) + entry->cursor += strlen(text) + cursor; + else + entry->cursor += 1 + cursor; + + text_entry_update(entry); +} + +static void +text_entry_commit_and_reset(struct text_entry *entry) +{ + char *commit = NULL; + + fprintf(stderr, "text_entry_commit_and_reset()\n"); + + if (entry->preedit.commit) + commit = strdup(entry->preedit.commit); + + text_entry_reset_preedit(entry); + if (commit) { + text_entry_insert_at_cursor(entry, commit, 0, 0); + free(commit); + } + + wl_text_input_reset(entry->text_input); + text_entry_update(entry); + entry->reset_serial = entry->serial; +} + +static void +text_input_enter(void *data, struct wl_text_input *text_input, + struct wl_surface *surface) +{ + fprintf(stderr, "text_input_enter()\n"); + + struct display *d = data; + struct text_entry *entry = d->entry; + + entry->active++; + + text_entry_update(entry); + entry->reset_serial = entry->serial; +} +static void +text_input_leave(void *data, struct wl_text_input *text_input) +{ + fprintf(stderr, "text_input_leave()\n"); + + struct display *d = data; + struct text_entry *entry = d->entry; + + text_entry_commit_and_reset(entry); + d->entry->active--; + + if (!entry->active) { + wl_text_input_hide_input_panel(text_input); + entry->panel_visible = false; + } +} + +static void +text_input_modifiers_map(void *data, struct wl_text_input *text_input, + struct wl_array *map) +{ + fprintf(stderr, "text_input_modifiers_map()\n"); +} + +static void +text_input_input_panel_state(void *data, struct wl_text_input *text_input, + uint32_t state) +{ + fprintf(stderr, "text_input_input_panel_state() state:%u\n", state); +} + +static void +text_input_preedit_string(void *data, struct wl_text_input *text_input, + uint32_t serial, const char *text, const char *commit) +{ + fprintf(stderr, "text_input_preedit_string() serial(%u), text(%s), commit(%s)\n", serial, text, commit); +} + +static void +text_input_preedit_styling(void *data, struct wl_text_input *text_input, + uint32_t index, uint32_t length, uint32_t style) +{ + fprintf(stderr, "text_input_preedit_styling() index(%u), length(%u), style(%u)\n", index, length, style); +} + +static void +text_input_preedit_cursor(void *data, struct wl_text_input *text_input, + int32_t index) +{ + fprintf(stderr, "text_input_preedit_cursor() index(%u)\n", index); +} + +static void +text_input_commit_string(void *data, struct wl_text_input *text_input, + uint32_t serial, const char *text) +{ + fprintf(stderr, "text_input_commit_string() serial(%u), text(%s)\n", serial, text); +} + +static void +text_input_cursor_position(void *data, struct wl_text_input *text_input, + int32_t index, int32_t anchor) +{ + fprintf(stderr, "text_input_cursor_position() index(%d), anchor(%d)\n", index, anchor); +} + +static void +text_input_delete_surrounding_text(void *data, + struct wl_text_input *text_input, int32_t index, uint32_t length) +{ + fprintf(stderr, "text_input_delete_surrounding_text() index(%d), length(%u)\n", index, length); +} + +static void +text_input_keysym(void *data, struct wl_text_input *text_input, + uint32_t serial, uint32_t time, uint32_t sym, uint32_t state, + uint32_t modifiers) +{ + fprintf(stderr, "text_input_keysym() serial(%u), time(%u), sym(%u), state(%u), modifiers(%u)\n", + serial, time, sym, state, modifiers); +} + +static void +text_input_language(void *data, struct wl_text_input *text_input, + uint32_t serial, const char *language) +{ + fprintf(stderr, "text_input_language() serial(%u), language(%s)\n", serial, language); +} + +static void +text_input_text_direction(void *data, struct wl_text_input *text_input, + uint32_t serial, uint32_t direction) +{ + fprintf(stderr, "text_input_text_direction() serial(%d), direction(%d)\n", serial, direction); +} + +static const struct wl_text_input_listener text_input_listener = { + .enter = text_input_enter, + .leave = text_input_leave, + .modifiers_map = text_input_modifiers_map, + .input_panel_state = text_input_input_panel_state, + .preedit_string = text_input_preedit_string, + .preedit_styling = text_input_preedit_styling, + .preedit_cursor = text_input_preedit_cursor, + .commit_string = text_input_commit_string, + .cursor_position = text_input_cursor_position, + .delete_surrounding_text = text_input_delete_surrounding_text, + .keysym = text_input_keysym, + .language = text_input_language, + .text_direction = text_input_text_direction, + // TIZEN_ONLY(20150918): Support to set the selection region + .selection_region = NULL, + .private_command = NULL, + .input_panel_geometry = NULL, + .input_panel_data = NULL, + .get_selection_text = NULL, + .get_surrounding_text = NULL, + .filter_key_event_done = NULL, + .hide_permission = NULL, + .recapture_string = NULL, + .input_panel_event = NULL, + .commit_content = NULL, + // +}; + +static void +text_entry_activate(struct display *d) +{ + struct wl_surface *surface = d->entered_surface; + struct text_entry *entry = d->entry; + + fprintf(stderr, "text_entry_activate\n"); + if (entry->click_to_show && entry->active) { + entry->panel_visible = !entry->panel_visible; + + if (entry->panel_visible) + wl_text_input_show_input_panel(entry->text_input); + else + wl_text_input_hide_input_panel(entry->text_input); + + return; + } + + if (!entry->click_to_show) + wl_text_input_show_input_panel(entry->text_input); + + wl_text_input_activate(entry->text_input, + d->seat, + surface); +} + +static void +text_entry_deactivate(struct display *d) +{ + struct text_entry *entry = d->entry; + + fprintf(stderr, "text_entry_deactivate\n"); + wl_text_input_deactivate(entry->text_input, + d->seat); +} + +static void +text_entry_destroy(struct display *d) +{ + struct text_entry *entry; + + entry = d->entry; + d->entry = NULL; + + wl_text_input_destroy(entry->text_input); + free(entry->text); + free(entry->preferred_language); + free(entry); +} + +static struct text_entry* +text_entry_create(struct display *d, const char *text) +{ + struct text_entry *entry; + + entry = calloc(1, sizeof *entry); + if (!entry) + return NULL; + + entry->text = strdup(text); + entry->active = 0; + entry->panel_visible = false; + entry->cursor = strlen(text); + entry->anchor = entry->cursor; + entry->click_to_show = true; + entry->text_input = + wl_text_input_manager_create_text_input(d->text_input_mgr); + wl_text_input_add_listener(entry->text_input, + &text_input_listener, d); + + d->entry = entry; + fprintf(stderr, "text_entry_create() entry(%p) created.\n", entry); + + return entry; +} + +static void +registry_handle_global(void *data, struct wl_registry *registry, + uint32_t id, const char *interface, uint32_t version) +{ + struct display *d = data; + + if (strcmp(interface, "wl_compositor") == 0) { + d->compositor = + wl_registry_bind(registry, + id, &wl_compositor_interface, 1); + } else if (strcmp(interface, "xdg_wm_base") == 0) { + d->wm_base = wl_registry_bind(registry, + id, &xdg_wm_base_interface, 1); + xdg_wm_base_add_listener(d->wm_base, &xdg_wm_base_listener, d); + } else if (strcmp(interface, "wl_shm") == 0) { + 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"); + } else if (strcmp(interface, "wl_text_input_manager") == 0) { + d->text_input_mgr = wl_registry_bind(registry, + id, &wl_text_input_manager_interface, version); + fprintf(stderr, "wl_text_input_manager bound!\n"); + + text_entry_create(d, "Entry"); + } +} + +static void +registry_handle_global_remove(void *data, struct wl_registry *registry, + uint32_t name) +{ +} + +static const struct wl_registry_listener registry_listener = { + registry_handle_global, + registry_handle_global_remove +}; + +static struct display * +create_display(void) +{ + struct display *display; + + display = calloc(1, sizeof *display); + if (display == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + display->display = wl_display_connect(NULL); + assert(display->display); + + display->has_xrgb = false; + display->registry = wl_display_get_registry(display->display); + wl_registry_add_listener(display->registry, + ®istry_listener, display); + wl_display_roundtrip(display->display); + if (display->shm == NULL) { + fprintf(stderr, "No wl_shm global\n"); + exit(1); + } + + wl_display_roundtrip(display->display); + + /* + * Why do we need two roundtrips here? + * + * wl_display_get_registry() sends a request to the server, to which + * the server replies by emitting the wl_registry.global events. + * The first wl_display_roundtrip() sends wl_display.sync. The server + * first processes the wl_display.get_registry which includes sending + * the global events, and then processes the sync. Therefore when the + * sync (roundtrip) returns, we are guaranteed to have received and + * processed all the global events. + * + * While we are inside the first wl_display_roundtrip(), incoming + * events are dispatched, which causes registry_handle_global() to + * be called for each global. One of these globals is wl_shm. + * registry_handle_global() sends wl_registry.bind request for the + * wl_shm global. However, wl_registry.bind request is sent after + * the first wl_display.sync, so the reply to the sync comes before + * the initial events of the wl_shm object. + * + * The initial events that get sent as a reply to binding to wl_shm + * include wl_shm.format. These tell us which pixel formats are + * supported, and we need them before we can create buffers. They + * don't change at runtime, so we receive them as part of init. + * + * When the reply to the first sync comes, the server may or may not + * have sent the initial wl_shm events. Therefore we need the second + * wl_display_roundtrip() call here. + * + * The server processes the wl_registry.bind for wl_shm first, and + * the second wl_display.sync next. During our second call to + * wl_display_roundtrip() the initial wl_shm events are received and + * processed. Finally, when the reply to the second wl_display.sync + * arrives, it guarantees we have processed all wl_shm initial events. + * + * This sequence contains two examples on how wl_display_roundtrip() + * can be used to guarantee, that all reply events to a request + * have been received and processed. This is a general Wayland + * technique. + */ + + if (!display->has_xrgb) { + fprintf(stderr, "WL_SHM_FORMAT_XRGB32 not available\n"); + exit(1); + } + + display->wl_tbm = wayland_tbm_client_init(display->display); + if (!display->wl_tbm) { + fprintf(stderr, "failed wayland_tbm_client_init()\n"); + exit(1); + } + + display->notified = -1; + + return display; +} + +static void +destroy_display(struct display *display) +{ + text_entry_destroy(display); + + if (display->text_input_mgr) + wl_text_input_manager_destroy(display->text_input_mgr); + + if (display->seat) + wl_seat_destroy(display->seat); + + if (display->shm) + wl_shm_destroy(display->shm); + + if (display->wm_base) + xdg_wm_base_destroy(display->wm_base); + + if (display->compositor) + wl_compositor_destroy(display->compositor); + + wayland_tbm_client_deinit(display->wl_tbm); + wl_registry_destroy(display->registry); + wl_display_flush(display->display); + wl_display_disconnect(display->display); + free(display); +} + +static void +signal_int(int signum) +{ + running = 0; +} + +int +main(int argc, char **argv) +{ + struct sigaction sigint; + struct display *display; + struct window *window; + int ret = 0; + + display = create_display(); + window = create_window(display, 250, 250); + if (!window) + return 1; + + sigint.sa_handler = signal_int; + sigemptyset(&sigint.sa_mask); + sigint.sa_flags = SA_RESETHAND; + sigaction(SIGINT, &sigint, NULL); + + /* Initialise damage to full surface, so the padding gets painted */ + wl_surface_damage(window->surface, 0, 0, + window->width, window->height); + + if (!window->wait_for_configure) + redraw(window, NULL, 0); + + while (running && ret != -1) + ret = wl_display_dispatch(display->display); + + fprintf(stderr, "simple-shm exiting\n"); + + destroy_window(window); + destroy_display(display); + + return 0; +} diff --git a/examples/meson.build b/examples/meson.build index 899529e..2d5c09c 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -31,6 +31,8 @@ executable('tinyds-tdm', deps_libds_tizen_dpms, deps_libds_tizen_input_devicemgr, deps_libds_tizen_launch, + deps_libds_tizen_input_method, + deps_libds_tizen_text_input, dependency('pixman-1', required: true), dependency('threads', required: true), ], diff --git a/examples/tinyds-tdm.c b/examples/tinyds-tdm.c index a7428f1..56d9412 100644 --- a/examples/tinyds-tdm.c +++ b/examples/tinyds-tdm.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #define USE_TDM_BUFFER_QUEUE @@ -118,6 +120,9 @@ struct tinyds_server struct wl_list keyboards; struct wl_list pointers; + + struct tinyds_text_input *text_input; + struct tinyds_input_method *input_method; }; struct tinyds_view @@ -177,6 +182,69 @@ struct tinyds_touch struct wl_listener motion; }; +struct tinyds_text_input { + struct ds_tizen_text_input *input; + struct ds_tizen_text_input_manager *text_input_mgr; + + struct tinyds_server *server; + struct ds_surface *surface; + + struct wl_list input_methods; + + struct wl_listener destroy; + struct wl_listener mgr_destroy; + + struct wl_listener new_text_input; + + struct wl_listener text_input_activate; + struct wl_listener text_input_deactivate; + struct wl_listener text_input_reset; + struct wl_listener text_input_set_content_type; + struct wl_listener text_input_invoke_action; + struct wl_listener text_input_commit_state; + struct wl_listener text_input_set_preferred_language; +}; + +struct tinyds_input_method { + struct ds_tizen_input_method *input_method; + struct ds_tizen_input_method_manager *input_method_mgr; + + struct tinyds_server *server; + struct tinyds_text_input *input; + struct tinyds_input_method_context *context; + + struct wl_list link; + + struct wl_listener destroy; + struct wl_listener mgr_destroy; + + struct wl_listener new_im_context; +}; + +struct tinyds_input_method_context { + struct ds_tizen_input_method_context *context; + + struct tinyds_server *server; + struct tinyds_text_input *input; + struct tinyds_input_method *input_method; + + struct wl_listener destroy; + + struct wl_listener im_context_commit_string; + struct wl_listener im_context_preedit_string; + struct wl_listener im_context_preedit_styling; + struct wl_listener im_context_preedit_cursor; + struct wl_listener im_context_delete_surrounding_text; + struct wl_listener im_context_cursor_position; + struct wl_listener im_context_modifiers_map; + struct wl_listener im_context_keysym; + struct wl_listener im_context_grab_keyboard; + struct wl_listener im_context_key; + struct wl_listener im_context_modifiers; + struct wl_listener im_context_language; + struct wl_listener im_context_text_direction; +}; + struct tinyds_server tinyds; static bool init_server(struct tinyds_server *server, struct wl_display *display); @@ -215,6 +283,22 @@ static struct tinyds_view * server_view_at(struct tinyds_server *server, double lx, double ly, double *sx, double *sy); +static bool add_new_text_input(struct tinyds_server *server); +static bool add_new_input_method(struct tinyds_server *server); + +static void text_input_mgr_handle_destroy(struct wl_listener *listener, + void *data TINYDS_UNUSED); +static void text_input_mgr_handle_new_text_input(struct wl_listener *listener, + void *data TINYDS_UNUSED); + +static void input_method_mgr_handle_destroy(struct wl_listener *listener, + void *data TINYDS_UNUSED); + +static void input_method_handle_destroy(struct wl_listener *listener, + void *data TINYDS_UNUSED); +static void input_method_handle_new_im_context(struct wl_listener *listener, + void *data TINYDS_UNUSED); + int main(void) { @@ -820,6 +904,12 @@ init_server(struct tinyds_server *server, struct wl_display *display) ds_tizen_launch_effect_add_new_splash_listener(server->effect, &server->new_splash); + if (!add_new_text_input(server)) + goto err; + + if (!add_new_input_method(server)) + goto err; + return true; err: @@ -1717,7 +1807,6 @@ server_add_pointer(struct tinyds_server *server, struct ds_input_device *dev) ds_inf("Pointer(%p) added", pointer); } - static void output_schedule_commit_handle_idle_timer(void *data) { @@ -1737,3 +1826,655 @@ output_schedule_commit(struct tinyds_output *output) output->idle_commit = wl_event_loop_add_idle(ev, output_schedule_commit_handle_idle_timer, output); } + +static bool +add_new_text_input(struct tinyds_server *server) +{ + struct tinyds_text_input *text_input; + + text_input = calloc(1, sizeof *text_input); + if (!text_input) + return false; + + text_input->text_input_mgr = ds_tizen_text_input_manager_create(server->display); + if (!text_input->text_input_mgr) { + free(text_input); + ds_err("Could not create ds_tizen_text_input_manager"); + return false; + } + + wl_list_init(&text_input->input_methods); + + text_input->destroy.notify = text_input_mgr_handle_destroy; + ds_tizen_text_input_manager_add_destroy_listener(text_input->text_input_mgr, + &text_input->destroy); + + text_input->new_text_input.notify = text_input_mgr_handle_new_text_input; + ds_tizen_text_input_manager_add_new_text_input_listener(text_input->text_input_mgr, + &text_input->new_text_input); + + text_input->server = server; + server->text_input = text_input; + + ds_inf("Text_Input (%p) added", text_input); + + return true; +} + +static bool +add_new_input_method(struct tinyds_server *server) +{ + struct tinyds_input_method *input_method; + + input_method = calloc(1, sizeof *input_method); + if (!input_method) + return false; + + input_method->input_method = ds_tizen_input_method_create(server->display); + if (!input_method->input_method) { + free(input_method); + ds_err("Could not create ds_tizen_input_method"); + return false; + } + input_method->destroy.notify = input_method_handle_destroy; + ds_tizen_input_method_add_destroy_listener(input_method->input_method, + &input_method->destroy); + + input_method->input_method_mgr = ds_tizen_input_method_manager_create(server->display); + if (!input_method->input_method_mgr) { + free(input_method); + ds_err("Could not create ds_tizen_input_method_manager"); + return false; + } + + input_method->mgr_destroy.notify = input_method_mgr_handle_destroy; + ds_tizen_input_method_manager_add_destroy_listener(input_method->input_method_mgr, + &input_method->mgr_destroy); + + input_method->new_im_context.notify = input_method_handle_new_im_context; + ds_tizen_input_method_add_new_input_method_context_listener(input_method->input_method, + &input_method->new_im_context); + + input_method->server = server; + server->input_method = input_method; + + ds_inf("Input_Method (%p) added", input_method); + + return true; +} + +static void +text_input_mgr_handle_destroy(struct wl_listener *listener, void *data) +{ + struct tinyds_text_input *text_input; + struct tinyds_server *server; + + ds_inf("text_input_mgr_handle_destroy"); + text_input = wl_container_of(listener, text_input, mgr_destroy); + + wl_list_remove(&text_input->mgr_destroy.link); + wl_list_remove(&text_input->destroy.link); + + wl_list_remove(&text_input->new_text_input.link); + + wl_list_remove(&text_input->text_input_activate.link); + wl_list_remove(&text_input->text_input_deactivate.link); + wl_list_remove(&text_input->text_input_reset.link); + wl_list_remove(&text_input->text_input_set_content_type.link); + wl_list_remove(&text_input->text_input_invoke_action.link); + wl_list_remove(&text_input->text_input_commit_state.link); + wl_list_remove(&text_input->text_input_set_preferred_language.link); + + server = text_input->server; + server->text_input = NULL; + + free(text_input); +} + +static void +text_input_handle_destroy(struct wl_listener *listener, void *data) +{ + struct tinyds_text_input *text_input; + + ds_inf("text_input_handle_destroy"); + + text_input = wl_container_of(listener, text_input, destroy); + + wl_list_remove(&text_input->destroy.link); +} + +static void +text_input_handle_activate(struct wl_listener *listener, void *data) +{ + struct tinyds_text_input *text_input; + struct tinyds_input_method *input_method; + struct tinyds_input_method_context *context; + struct ds_tizen_text_input_event_activate *event = data; + + text_input = wl_container_of(listener, text_input, text_input_activate); + + input_method = text_input->server->input_method; + + ds_inf("text_input_handle_activate. text_input(%p) seat(%p) surface(%p) text_input(%p)", + text_input, event->seat, event->surface, event->text_input); + + if (input_method->input == text_input) + return; + if (input_method->input) + ;//deactivate_input_method(server->input_method); + input_method->input = text_input; + wl_list_insert(&text_input->input_methods, &input_method->link); + + text_input->surface = event->surface; + + context = calloc(1, sizeof *context); + if (context == NULL) + { + ds_err("calloc is failed. tinyds_input_method_context"); + return; + } + input_method->context = context; + context->server = text_input->server; + context->context = ds_tizen_input_method_create_context(input_method->input_method); + context->input = text_input; + context->input_method = input_method; + + // ds_tizen_input_method_send_set_text_input_id(); +} + +static void +text_input_handle_deactivate(struct wl_listener *listener, void *data) +{ + struct tinyds_text_input *text_input; + struct tinyds_input_method *input_method, *tmp; + struct ds_tizen_text_input_event_deactivate *event = data; + + text_input = wl_container_of(listener, text_input, text_input_deactivate); + ds_inf("text_input_handle_deactivate. text_input(%p) seat(%p) text_input(%p)", + text_input, event->seat, event->text_input); + + wl_list_for_each_safe(input_method, tmp, &text_input->input_methods, link) { + if (!input_method->input_method || !input_method->context->context) continue; + ds_tizen_input_method_send_deactivate(input_method->input_method, input_method->context->context); + input_method->input = NULL; + input_method->context = NULL; + wl_list_remove(&input_method->link); + } + + text_input->surface = NULL; + // ds_tizen_input_method_send_close_connection(); +} + +static void +text_input_handle_reset(struct wl_listener *listener, void *data) +{ + struct tinyds_text_input *text_input; + struct tinyds_input_method *input_method; + + text_input = wl_container_of(listener, text_input, text_input_reset); + + ds_inf("text_input_handle_reset. text_input(%p)", text_input); + + wl_list_for_each(input_method, &text_input->input_methods, link) { + if (!input_method->context || !input_method->context->context) continue; + ds_tizen_input_method_context_send_reset(input_method->context->context); + } +} + +static void +text_input_handle_set_content_type(struct wl_listener *listener, void *data) +{ + struct tinyds_text_input *text_input; + struct ds_tizen_text_input_event_set_content_type *event = data; + struct tinyds_input_method *input_method; + + text_input = wl_container_of(listener, text_input, text_input_set_content_type); + + ds_inf("text_input_handle_content_type. text_input(%p) hint(%u) purpose(%u)", + text_input, event->hint, event->purpose); + + wl_list_for_each(input_method, &text_input->input_methods, link) { + if (!input_method->context || !input_method->context->context) continue; + ds_tizen_input_method_context_send_content_type(input_method->context->context, + event->hint, event->purpose); + } +} + +static void +text_input_handle_invoke_action(struct wl_listener *listener, void *data) +{ + struct tinyds_text_input *text_input; + struct ds_tizen_text_input_event_invoke_action *event = data; + struct tinyds_input_method *input_method; + + text_input = wl_container_of(listener, text_input, text_input_invoke_action); + + ds_inf("text_input_handle_invoke_action. text_input(%p) button(%u) index(%u)", + text_input, event->button, event->index); + + wl_list_for_each(input_method, &text_input->input_methods, link) { + if (!input_method->context || !input_method->context->context) continue; + ds_tizen_input_method_context_send_invoke_action(input_method->context->context, + event->button, event->index); + } +} + +static void +text_input_handle_commit_state(struct wl_listener *listener, void *data) +{ + struct tinyds_text_input *text_input; + struct ds_tizen_text_input_event_commit_state *event = data; + struct tinyds_input_method *input_method; + + text_input = wl_container_of(listener, text_input, text_input_commit_state); + + ds_inf("text_input_handle_commit_state. text_input(%p) serial(%u)", + text_input, event->serial); + + wl_list_for_each(input_method, &text_input->input_methods, link) { + if (!input_method->context || !input_method->context->context) continue; + ds_tizen_input_method_context_send_commit_state(input_method->context->context, + event->serial); + } +} + +static void +text_input_handle_set_preferred_language(struct wl_listener *listener, void *data) +{ + struct tinyds_text_input *text_input; + struct ds_tizen_text_input_event_set_preferred_language *event = data; + struct tinyds_input_method *input_method; + + text_input = wl_container_of(listener, text_input, text_input_set_preferred_language); + + ds_inf("text_input_handle_set_preferred_language. text_input(%p) language(%s)", + text_input, event->language); + + wl_list_for_each(input_method, &text_input->input_methods, link) { + if (!input_method->context || !input_method->context->context) continue; + ds_tizen_input_method_context_send_preferred_language(input_method->context->context, + event->language); + } +} + +static void +text_input_mgr_handle_new_text_input(struct wl_listener *listener, void *data) +{ + struct tinyds_text_input *text_input; + struct ds_tizen_text_input *input = data; + + text_input = wl_container_of(listener, text_input, new_text_input); + + ds_inf("text_input_mgr_handle_new_text_input"); + + text_input->input = input; + + text_input->destroy.notify = text_input_handle_destroy; + ds_tizen_text_input_add_destroy_listener(text_input->input, + &text_input->destroy); + + text_input->text_input_activate.notify = text_input_handle_activate; + ds_tizen_text_input_add_activate_listener(text_input->input, + &text_input->text_input_activate); + + text_input->text_input_deactivate.notify = text_input_handle_deactivate; + ds_tizen_text_input_add_deactivate_listener(text_input->input, + &text_input->text_input_deactivate); + + text_input->text_input_reset.notify = text_input_handle_reset; + ds_tizen_text_input_add_reset_listener(text_input->input, + &text_input->text_input_reset); + + text_input->text_input_set_content_type.notify = text_input_handle_set_content_type; + ds_tizen_text_input_add_set_content_type_listener(text_input->input, + &text_input->text_input_set_content_type); + + text_input->text_input_invoke_action.notify = text_input_handle_invoke_action; + ds_tizen_text_input_add_invoke_action_listener(text_input->input, + &text_input->text_input_invoke_action); + + text_input->text_input_commit_state.notify = text_input_handle_commit_state; + ds_tizen_text_input_add_commit_state_listener(text_input->input, + &text_input->text_input_commit_state); + + text_input->text_input_set_preferred_language.notify = text_input_handle_set_preferred_language; + ds_tizen_text_input_add_set_preferred_language_listener(text_input->input, + &text_input->text_input_set_preferred_language); +} + +static void +input_method_mgr_handle_destroy(struct wl_listener *listener, void *data) +{ + struct tinyds_input_method *input_method; + + ds_inf("input_method_mgr_handle_destroy"); + + input_method = wl_container_of(listener, input_method, mgr_destroy); + + wl_list_remove(&input_method->mgr_destroy.link); +} + +static void +input_method_handle_destroy(struct wl_listener *listener, void *data) +{ + struct tinyds_input_method *input_method; + struct tinyds_server *server; + + ds_inf("input_method_handle_destroy"); + + input_method = wl_container_of(listener, input_method, destroy); + + wl_list_remove(&input_method->destroy.link); + wl_list_remove(&input_method->new_im_context.link); + + server = input_method->server; + server->input_method = NULL; + + free(input_method); +} + +static void +context_handle_destroy(struct wl_listener *listener, void *data) +{ + struct tinyds_input_method_context *context; + struct tinyds_server *server; + + ds_inf("context_handle_destroy"); + + context = wl_container_of(listener, context, destroy); + + wl_list_remove(&context->destroy.link); + + wl_list_remove(&context->im_context_commit_string.link); + wl_list_remove(&context->im_context_preedit_string.link); + wl_list_remove(&context->im_context_preedit_styling.link); + wl_list_remove(&context->im_context_preedit_cursor.link); + wl_list_remove(&context->im_context_delete_surrounding_text.link); + wl_list_remove(&context->im_context_cursor_position.link); + wl_list_remove(&context->im_context_modifiers_map.link); + wl_list_remove(&context->im_context_keysym.link); + wl_list_remove(&context->im_context_grab_keyboard.link); + wl_list_remove(&context->im_context_key.link); + wl_list_remove(&context->im_context_modifiers.link); + wl_list_remove(&context->im_context_language.link); + wl_list_remove(&context->im_context_text_direction.link); + + server = context->server; + server->input_method->context = NULL; + + free(context); +} + +static void +context_handle_commit_string(struct wl_listener *listener, void *data) +{ + struct tinyds_text_input *text_input; + struct tinyds_input_method_context *context; + struct ds_tizen_input_method_context_event_commit_string *event = data; + + context = wl_container_of(listener, context, im_context_commit_string); + text_input = context->server->text_input; + + ds_inf("context_handle_commit_string. text_input(%p) serial(%u) text(%s)", + text_input, event->serial, event->text); + + ds_tizen_text_input_send_commit_string(text_input->input, event->serial, event->text); +} + +static void +context_handle_preedit_string(struct wl_listener *listener, void *data) +{ + struct tinyds_input_method_context *context; + struct tinyds_text_input *text_input; + struct ds_tizen_input_method_context_event_preedit_string *event = data; + + context = wl_container_of(listener, context, im_context_preedit_string); + text_input = context->server->text_input; + + ds_inf("context_handle_preedit_string. text_input(%p) serial(%u) text(%s) commit(%s)", + text_input, event->serial, event->text, event->commit); + + ds_tizen_text_input_send_preedit_string(text_input->input, event->serial, event->text, event->commit); +} + +static void +context_handle_preedit_styling(struct wl_listener *listener, void *data) +{ + struct tinyds_input_method_context *context; + struct tinyds_text_input *text_input; + struct ds_tizen_input_method_context_event_preedit_styling *event = data; + + context = wl_container_of(listener, context, im_context_preedit_styling); + text_input = context->server->text_input; + + ds_inf("context_handle_preedit_styling. text_input(%p) index(%u) length(%u) style(%u)", + text_input, event->index, event->length, event->style); + + ds_tizen_text_input_send_preedit_styling(text_input->input, event->index, event->length, event->style); +} + +static void +context_handle_preedit_cursor(struct wl_listener *listener, void *data) +{ + struct tinyds_input_method_context *context; + struct tinyds_text_input *text_input; + struct ds_tizen_input_method_context_event_preedit_cursor *event = data; + + context = wl_container_of(listener, context, im_context_preedit_cursor); + text_input = context->server->text_input; + + ds_inf("context_handle_preedit_cursor. text_input(%p) index(%u)", + text_input, event->index); + + ds_tizen_text_input_send_preedit_cursor(text_input->input, event->index); +} + +static void +context_handle_delete_surrounding_text(struct wl_listener *listener, void *data) +{ + struct tinyds_input_method_context *context; + struct tinyds_text_input *text_input; + struct ds_tizen_input_method_context_event_delete_surrounding_text *event = data; + + context = wl_container_of(listener, context, im_context_delete_surrounding_text); + text_input = context->server->text_input; + + ds_inf("context_handle_delete_surrounding_text. text_input(%p) index(%d) length(%u)", + text_input, event->index, event->length); + + ds_tizen_text_input_send_delete_surrounding_text(text_input->input, event->index, event->length); +} + +static void +context_handle_cursor_position(struct wl_listener *listener, void *data) +{ + struct tinyds_input_method_context *context; + struct tinyds_text_input *text_input; + struct ds_tizen_input_method_context_event_cursor_position *event = data; + + context = wl_container_of(listener, context, im_context_cursor_position); + text_input = context->server->text_input; + + ds_inf("context_handle_cursor_position. text_input(%p) index(%d) length(%d)", + text_input, event->index, event->anchor); + + ds_tizen_text_input_send_cursor_position(text_input->input, event->index, event->anchor); +} + +static void +context_handle_modifiers_map(struct wl_listener *listener, void *data) +{ + struct tinyds_input_method_context *context; + struct tinyds_text_input *text_input; + struct ds_tizen_input_method_context_event_modifiers_map *event = data; + + context = wl_container_of(listener, context, im_context_modifiers_map); + text_input = context->server->text_input; + + ds_inf("context_handle_modifiers_map. text_input(%p) map(%p)", + text_input, event->map); + + ds_tizen_text_input_send_modifiers_map(text_input->input, event->map); +} + +static void +context_handle_keysym(struct wl_listener *listener, void *data) +{ + struct tinyds_input_method_context *context; + struct tinyds_text_input *text_input; + struct ds_tizen_input_method_context_event_keysym *event = data; + + context = wl_container_of(listener, context, im_context_keysym); + text_input = context->server->text_input; + + ds_inf("context_handle_keysym. text_input(%p) serial(%u) time(%u) sysm(%u) state(%u) modifiers(%u)", + text_input, event->serial, event->time, event->sym, event->state, event->modifiers); + + ds_tizen_text_input_send_keysym(text_input->input, event->serial, event->time, event->sym, event->state, event->modifiers); +} + +static void +context_handle_grab_keyboard(struct wl_listener *listener, void *data) +{ + struct tinyds_input_method_context *context; + struct tinyds_text_input *text_input; + + context = wl_container_of(listener, context, im_context_grab_keyboard); + text_input = context->server->text_input; + + ds_inf("context_handle_grab_keyboard. text_input(%p)", + text_input); + + //TODO +} + +static void +context_handle_key(struct wl_listener *listener, void *data) +{ + struct tinyds_input_method_context *context; + struct tinyds_text_input *text_input; + + context = wl_container_of(listener, context, im_context_key); + text_input = context->server->text_input; + + ds_inf("context_handle_key. text_input(%p)", + text_input); + + //TODO +} + +static void +context_handle_modifiers(struct wl_listener *listener, void *data) +{ + struct tinyds_input_method_context *context; + struct tinyds_text_input *text_input; + + context = wl_container_of(listener, context, im_context_modifiers); + text_input = context->server->text_input; + + ds_inf("context_handle_modifiers. text_input(%p)", + text_input); + + //TODO +} + +static void +context_handle_language(struct wl_listener *listener, void *data) +{ + struct tinyds_input_method_context *context; + struct tinyds_text_input *text_input; + struct ds_tizen_input_method_context_event_language *event = data; + + context = wl_container_of(listener, context, im_context_language); + text_input = context->server->text_input; + + ds_inf("context_handle_language. text_input(%p) serial(%u), language(%s)", + text_input, event->serial, event->language); + + ds_tizen_text_input_send_language(text_input->input, event->serial, event->language); +} + +static void +context_handle_text_direction(struct wl_listener *listener, void *data) +{ + struct tinyds_input_method_context *context; + struct tinyds_text_input *text_input; + struct ds_tizen_input_method_context_event_text_direction *event = data; + + context = wl_container_of(listener, context, im_context_text_direction); + text_input = context->server->text_input; + + ds_inf("context_handle_text_direction. text_input(%p) serial(%u), direction(%u)", + text_input, event->serial, event->direction); + + ds_tizen_text_input_send_text_direction(text_input->input, event->serial, event->direction); +} + +static void +input_method_handle_new_im_context(struct wl_listener *listener, void *data) +{ + struct ds_tizen_input_method_context *context = data; + struct tinyds_input_method *input_method; + struct tinyds_input_method_context *ctx; + + ds_inf("input_method_handle_new_im_context"); + + input_method = wl_container_of(listener, input_method, new_im_context); + ctx = input_method->context; + + ctx->destroy.notify = context_handle_destroy; + ds_tizen_input_method_context_add_destroy_listener(context, + &ctx->destroy); + + ctx->im_context_commit_string.notify = context_handle_commit_string; + ds_tizen_input_method_context_add_commit_string_listener(context, + &ctx->im_context_commit_string); + + ctx->im_context_preedit_string.notify = context_handle_preedit_string; + ds_tizen_input_method_context_add_preedit_string_listener(context, + &ctx->im_context_preedit_string); + + ctx->im_context_preedit_styling.notify = context_handle_preedit_styling; + ds_tizen_input_method_context_add_preedit_styling_listener(context, + &ctx->im_context_preedit_styling); + + ctx->im_context_preedit_cursor.notify = context_handle_preedit_cursor; + ds_tizen_input_method_context_add_preedit_cursor_listener(context, + &ctx->im_context_preedit_cursor); + + ctx->im_context_delete_surrounding_text.notify = context_handle_delete_surrounding_text; + ds_tizen_input_method_context_add_delete_surrounding_text_listener(context, + &ctx->im_context_delete_surrounding_text); + + ctx->im_context_cursor_position.notify = context_handle_cursor_position; + ds_tizen_input_method_context_add_cursor_position_listener(context, + &ctx->im_context_cursor_position); + + ctx->im_context_modifiers_map.notify = context_handle_modifiers_map; + ds_tizen_input_method_context_add_modifiers_map_listener(context, + &ctx->im_context_modifiers_map); + + ctx->im_context_keysym.notify = context_handle_keysym; + ds_tizen_input_method_context_add_keysym_listener(context, + &ctx->im_context_keysym); + + ctx->im_context_grab_keyboard.notify = context_handle_grab_keyboard; + ds_tizen_input_method_context_add_grab_keyboard_listener(context, + &ctx->im_context_grab_keyboard); + + ctx->im_context_key.notify = context_handle_key; + ds_tizen_input_method_context_add_key_listener(context, + &ctx->im_context_key); + + ctx->im_context_modifiers.notify = context_handle_modifiers; + ds_tizen_input_method_context_add_modifiers_listener(context, + &ctx->im_context_modifiers); + + ctx->im_context_language.notify = context_handle_language; + ds_tizen_input_method_context_add_language_listener(context, + &ctx->im_context_language); + + ctx->im_context_text_direction.notify = context_handle_text_direction; + ds_tizen_input_method_context_add_text_direction_listener(context, + &ctx->im_context_text_direction); +} diff --git a/packaging/libds-tizen.spec b/packaging/libds-tizen.spec index f392041..fc126c7 100644 --- a/packaging/libds-tizen.spec +++ b/packaging/libds-tizen.spec @@ -553,6 +553,7 @@ ninja -C builddir install %{_libdir}/pkgconfig/libds-tizen-text-input.pc %{_libdir}/libds-tizen-text-input.so %{_bindir}/libds-tizen-text-input-tests +%{_bindir}/text-entry %files input-method %manifest %{name}.manifest @@ -569,3 +570,4 @@ ninja -C builddir install %{_libdir}/libds-tizen-input-method.so %{_bindir}/libds-tizen-input-method-tests %{_bindir}/libds-tizen-input-method-manager-tests +%{_bindir}/ime-keyboard