| /* |
| * Copyright © 2012 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 "config.h" |
| |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <sys/mman.h> |
| |
| #include <linux/input.h> |
| |
| #include "window.h" |
| #include "input-method-unstable-v1-client-protocol.h" |
| #include "shared/helpers.h" |
| |
| enum compose_state { |
| state_normal, |
| state_compose |
| }; |
| |
| struct compose_seq { |
| uint32_t keys[4]; |
| |
| const char *text; |
| }; |
| |
| struct simple_im; |
| |
| typedef void (*keyboard_input_key_handler_t)(struct simple_im *keyboard, |
| uint32_t serial, |
| uint32_t time, uint32_t key, uint32_t unicode, |
| enum wl_keyboard_key_state state); |
| |
| struct simple_im { |
| struct zwp_input_method_v1 *input_method; |
| struct zwp_input_method_context_v1 *context; |
| struct wl_display *display; |
| struct wl_registry *registry; |
| struct wl_keyboard *keyboard; |
| enum compose_state compose_state; |
| struct compose_seq compose_seq; |
| |
| struct xkb_context *xkb_context; |
| |
| uint32_t modifiers; |
| |
| struct xkb_keymap *keymap; |
| struct xkb_state *state; |
| xkb_mod_mask_t control_mask; |
| xkb_mod_mask_t alt_mask; |
| xkb_mod_mask_t shift_mask; |
| |
| keyboard_input_key_handler_t key_handler; |
| |
| uint32_t serial; |
| }; |
| |
| static const struct compose_seq compose_seqs[] = { |
| { { XKB_KEY_quotedbl, XKB_KEY_A, 0 }, "Ä" }, |
| { { XKB_KEY_quotedbl, XKB_KEY_O, 0 }, "Ö" }, |
| { { XKB_KEY_quotedbl, XKB_KEY_U, 0 }, "Ü" }, |
| { { XKB_KEY_quotedbl, XKB_KEY_a, 0 }, "ä" }, |
| { { XKB_KEY_quotedbl, XKB_KEY_o, 0 }, "ö" }, |
| { { XKB_KEY_quotedbl, XKB_KEY_u, 0 }, "ü" }, |
| { { XKB_KEY_apostrophe, XKB_KEY_A, 0 }, "Á" }, |
| { { XKB_KEY_apostrophe, XKB_KEY_a, 0 }, "á" }, |
| { { XKB_KEY_slash, XKB_KEY_O, 0 }, "Ø" }, |
| { { XKB_KEY_slash, XKB_KEY_o, 0 }, "ø" }, |
| { { XKB_KEY_less, XKB_KEY_3, 0 }, "♥" }, |
| { { XKB_KEY_A, XKB_KEY_A, 0 }, "Å" }, |
| { { XKB_KEY_A, XKB_KEY_E, 0 }, "Æ" }, |
| { { XKB_KEY_O, XKB_KEY_C, 0 }, "©" }, |
| { { XKB_KEY_O, XKB_KEY_R, 0 }, "®" }, |
| { { XKB_KEY_s, XKB_KEY_s, 0 }, "ß" }, |
| { { XKB_KEY_a, XKB_KEY_e, 0 }, "æ" }, |
| { { XKB_KEY_a, XKB_KEY_a, 0 }, "å" }, |
| }; |
| |
| static const uint32_t ignore_keys_on_compose[] = { |
| XKB_KEY_Shift_L, |
| XKB_KEY_Shift_R |
| }; |
| |
| static void |
| handle_surrounding_text(void *data, |
| struct zwp_input_method_context_v1 *context, |
| const char *text, |
| uint32_t cursor, |
| uint32_t anchor) |
| { |
| fprintf(stderr, "Surrounding text updated: %s\n", text); |
| } |
| |
| static void |
| handle_reset(void *data, |
| struct zwp_input_method_context_v1 *context) |
| { |
| struct simple_im *keyboard = data; |
| |
| fprintf(stderr, "Reset pre-edit buffer\n"); |
| |
| keyboard->compose_state = state_normal; |
| } |
| |
| static void |
| handle_content_type(void *data, |
| struct zwp_input_method_context_v1 *context, |
| uint32_t hint, |
| uint32_t purpose) |
| { |
| } |
| |
| static void |
| handle_invoke_action(void *data, |
| struct zwp_input_method_context_v1 *context, |
| uint32_t button, |
| uint32_t index) |
| { |
| } |
| |
| static void |
| handle_commit_state(void *data, |
| struct zwp_input_method_context_v1 *context, |
| uint32_t serial) |
| { |
| struct simple_im *keyboard = data; |
| |
| keyboard->serial = serial; |
| } |
| |
| static void |
| handle_preferred_language(void *data, |
| struct zwp_input_method_context_v1 *context, |
| const char *language) |
| { |
| } |
| |
| static const struct zwp_input_method_context_v1_listener input_method_context_listener = { |
| handle_surrounding_text, |
| handle_reset, |
| handle_content_type, |
| handle_invoke_action, |
| handle_commit_state, |
| handle_preferred_language |
| }; |
| |
| static void |
| input_method_keyboard_keymap(void *data, |
| struct wl_keyboard *wl_keyboard, |
| uint32_t format, |
| int32_t fd, |
| uint32_t size) |
| { |
| struct simple_im *keyboard = data; |
| char *map_str; |
| |
| if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { |
| close(fd); |
| return; |
| } |
| |
| map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); |
| if (map_str == MAP_FAILED) { |
| close(fd); |
| return; |
| } |
| |
| keyboard->keymap = |
| xkb_keymap_new_from_string(keyboard->xkb_context, |
| map_str, |
| XKB_KEYMAP_FORMAT_TEXT_V1, |
| XKB_KEYMAP_COMPILE_NO_FLAGS); |
| |
| munmap(map_str, size); |
| close(fd); |
| |
| if (!keyboard->keymap) { |
| fprintf(stderr, "Failed to compile keymap\n"); |
| return; |
| } |
| |
| keyboard->state = xkb_state_new(keyboard->keymap); |
| if (!keyboard->state) { |
| fprintf(stderr, "Failed to create XKB state\n"); |
| xkb_keymap_unref(keyboard->keymap); |
| return; |
| } |
| |
| keyboard->control_mask = |
| 1 << xkb_keymap_mod_get_index(keyboard->keymap, "Control"); |
| keyboard->alt_mask = |
| 1 << xkb_keymap_mod_get_index(keyboard->keymap, "Mod1"); |
| keyboard->shift_mask = |
| 1 << xkb_keymap_mod_get_index(keyboard->keymap, "Shift"); |
| } |
| |
| static void |
| input_method_keyboard_key(void *data, |
| struct wl_keyboard *wl_keyboard, |
| uint32_t serial, |
| uint32_t time, |
| uint32_t key, |
| uint32_t state_w) |
| { |
| struct simple_im *keyboard = data; |
| uint32_t code; |
| uint32_t num_syms; |
| const xkb_keysym_t *syms; |
| xkb_keysym_t sym; |
| enum wl_keyboard_key_state state = state_w; |
| |
| if (!keyboard->state) |
| return; |
| |
| code = key + 8; |
| num_syms = xkb_state_key_get_syms(keyboard->state, code, &syms); |
| |
| sym = XKB_KEY_NoSymbol; |
| if (num_syms == 1) |
| sym = syms[0]; |
| |
| if (keyboard->key_handler) |
| (*keyboard->key_handler)(keyboard, serial, time, key, sym, |
| state); |
| } |
| |
| static void |
| input_method_keyboard_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) |
| { |
| struct simple_im *keyboard = data; |
| struct zwp_input_method_context_v1 *context = keyboard->context; |
| xkb_mod_mask_t mask; |
| |
| xkb_state_update_mask(keyboard->state, mods_depressed, |
| mods_latched, mods_locked, 0, 0, group); |
| mask = xkb_state_serialize_mods(keyboard->state, |
| XKB_STATE_MODS_DEPRESSED | |
| XKB_STATE_MODS_LATCHED); |
| |
| keyboard->modifiers = 0; |
| if (mask & keyboard->control_mask) |
| keyboard->modifiers |= MOD_CONTROL_MASK; |
| if (mask & keyboard->alt_mask) |
| keyboard->modifiers |= MOD_ALT_MASK; |
| if (mask & keyboard->shift_mask) |
| keyboard->modifiers |= MOD_SHIFT_MASK; |
| |
| zwp_input_method_context_v1_modifiers(context, serial, |
| mods_depressed, mods_depressed, |
| mods_latched, group); |
| } |
| |
| static const struct wl_keyboard_listener input_method_keyboard_listener = { |
| input_method_keyboard_keymap, |
| NULL, /* enter */ |
| NULL, /* leave */ |
| input_method_keyboard_key, |
| input_method_keyboard_modifiers |
| }; |
| |
| static void |
| input_method_activate(void *data, |
| struct zwp_input_method_v1 *input_method, |
| struct zwp_input_method_context_v1 *context) |
| { |
| struct simple_im *keyboard = data; |
| |
| if (keyboard->context) |
| zwp_input_method_context_v1_destroy(keyboard->context); |
| |
| keyboard->compose_state = state_normal; |
| |
| keyboard->serial = 0; |
| |
| keyboard->context = context; |
| zwp_input_method_context_v1_add_listener(context, |
| &input_method_context_listener, |
| keyboard); |
| keyboard->keyboard = zwp_input_method_context_v1_grab_keyboard(context); |
| wl_keyboard_add_listener(keyboard->keyboard, |
| &input_method_keyboard_listener, |
| keyboard); |
| } |
| |
| static void |
| input_method_deactivate(void *data, |
| struct zwp_input_method_v1 *input_method, |
| struct zwp_input_method_context_v1 *context) |
| { |
| struct simple_im *keyboard = data; |
| |
| if (!keyboard->context) |
| return; |
| |
| zwp_input_method_context_v1_destroy(keyboard->context); |
| keyboard->context = NULL; |
| } |
| |
| static const struct zwp_input_method_v1_listener input_method_listener = { |
| input_method_activate, |
| input_method_deactivate |
| }; |
| |
| static void |
| registry_handle_global(void *data, struct wl_registry *registry, |
| uint32_t name, const char *interface, uint32_t version) |
| { |
| struct simple_im *keyboard = data; |
| |
| if (!strcmp(interface, "zwp_input_method_v1")) { |
| keyboard->input_method = |
| wl_registry_bind(registry, name, |
| &zwp_input_method_v1_interface, 1); |
| zwp_input_method_v1_add_listener(keyboard->input_method, |
| &input_method_listener, keyboard); |
| } |
| } |
| |
| 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 int |
| compare_compose_keys(const void *c1, const void *c2) |
| { |
| const struct compose_seq *cs1 = c1; |
| const struct compose_seq *cs2 = c2; |
| int i; |
| |
| for (i = 0; cs1->keys[i] != 0 && cs2->keys[i] != 0; i++) { |
| if (cs1->keys[i] != cs2->keys[i]) |
| return cs1->keys[i] - cs2->keys[i]; |
| } |
| |
| if (cs1->keys[i] == cs2->keys[i] |
| || cs1->keys[i] == 0) |
| return 0; |
| |
| return cs1->keys[i] - cs2->keys[i]; |
| } |
| |
| static void |
| simple_im_key_handler(struct simple_im *keyboard, |
| uint32_t serial, uint32_t time, uint32_t key, uint32_t sym, |
| enum wl_keyboard_key_state state) |
| { |
| struct zwp_input_method_context_v1 *context = keyboard->context; |
| char text[64]; |
| |
| if (sym == XKB_KEY_Multi_key && |
| state == WL_KEYBOARD_KEY_STATE_RELEASED && |
| keyboard->compose_state == state_normal) { |
| keyboard->compose_state = state_compose; |
| memset(&keyboard->compose_seq, 0, sizeof(struct compose_seq)); |
| return; |
| } |
| |
| if (keyboard->compose_state == state_compose) { |
| uint32_t i = 0; |
| struct compose_seq *cs; |
| |
| if (state == WL_KEYBOARD_KEY_STATE_PRESSED) |
| return; |
| |
| for (i = 0; i < ARRAY_LENGTH(ignore_keys_on_compose); i++) { |
| if (sym == ignore_keys_on_compose[i]) { |
| zwp_input_method_context_v1_key(context, |
| keyboard->serial, |
| time, |
| key, |
| state); |
| return; |
| } |
| } |
| |
| for (i = 0; keyboard->compose_seq.keys[i] != 0; i++); |
| |
| keyboard->compose_seq.keys[i] = sym; |
| |
| cs = bsearch (&keyboard->compose_seq, compose_seqs, |
| ARRAY_LENGTH(compose_seqs), |
| sizeof(compose_seqs[0]), compare_compose_keys); |
| |
| if (cs) { |
| if (cs->keys[i + 1] == 0) { |
| zwp_input_method_context_v1_preedit_cursor(keyboard->context, |
| 0); |
| zwp_input_method_context_v1_preedit_string(keyboard->context, |
| keyboard->serial, |
| "", ""); |
| zwp_input_method_context_v1_cursor_position(keyboard->context, |
| 0, 0); |
| zwp_input_method_context_v1_commit_string(keyboard->context, |
| keyboard->serial, |
| cs->text); |
| keyboard->compose_state = state_normal; |
| } else { |
| uint32_t j = 0, idx = 0; |
| |
| for (; j <= i; j++) { |
| idx += xkb_keysym_to_utf8(cs->keys[j], text + idx, sizeof(text) - idx); |
| } |
| |
| zwp_input_method_context_v1_preedit_cursor(keyboard->context, |
| strlen(text)); |
| zwp_input_method_context_v1_preedit_string(keyboard->context, |
| keyboard->serial, |
| text, |
| text); |
| } |
| } else { |
| uint32_t j = 0, idx = 0; |
| |
| for (; j <= i; j++) { |
| idx += xkb_keysym_to_utf8(keyboard->compose_seq.keys[j], text + idx, sizeof(text) - idx); |
| } |
| zwp_input_method_context_v1_preedit_cursor(keyboard->context, |
| 0); |
| zwp_input_method_context_v1_preedit_string(keyboard->context, |
| keyboard->serial, |
| "", ""); |
| zwp_input_method_context_v1_cursor_position(keyboard->context, |
| 0, 0); |
| zwp_input_method_context_v1_commit_string(keyboard->context, |
| keyboard->serial, |
| text); |
| keyboard->compose_state = state_normal; |
| } |
| return; |
| } |
| |
| if (xkb_keysym_to_utf8(sym, text, sizeof(text)) <= 0) { |
| zwp_input_method_context_v1_key(context, serial, time, key, state); |
| return; |
| } |
| |
| if (state == WL_KEYBOARD_KEY_STATE_PRESSED) |
| return; |
| |
| zwp_input_method_context_v1_cursor_position(keyboard->context, |
| 0, 0); |
| zwp_input_method_context_v1_commit_string(keyboard->context, |
| keyboard->serial, |
| text); |
| } |
| |
| int |
| main(int argc, char *argv[]) |
| { |
| struct simple_im simple_im; |
| int ret = 0; |
| |
| memset(&simple_im, 0, sizeof(simple_im)); |
| |
| simple_im.display = wl_display_connect(NULL); |
| if (simple_im.display == NULL) { |
| fprintf(stderr, "Failed to connect to server: %m\n"); |
| return -1; |
| } |
| |
| simple_im.registry = wl_display_get_registry(simple_im.display); |
| wl_registry_add_listener(simple_im.registry, |
| ®istry_listener, &simple_im); |
| wl_display_roundtrip(simple_im.display); |
| if (simple_im.input_method == NULL) { |
| fprintf(stderr, "No input_method global\n"); |
| return -1; |
| } |
| |
| simple_im.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); |
| if (simple_im.xkb_context == NULL) { |
| fprintf(stderr, "Failed to create XKB context\n"); |
| return -1; |
| } |
| |
| simple_im.context = NULL; |
| simple_im.key_handler = simple_im_key_handler; |
| |
| while (ret != -1) |
| ret = wl_display_dispatch(simple_im.display); |
| |
| if (ret == -1) { |
| fprintf(stderr, "Dispatch error: %m\n"); |
| return -1; |
| } |
| |
| return 0; |
| } |