/* vim:set et sts=4: */ /* * Keyman Input Method for IBUS (The Input Bus) * * Copyright (C) 2009-2018 SIL International * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * */ #include #include #include #include #include #include "keymanutil.h" #include "keyman-service.h" #include "engine.h" #define MAXCONTEXT_ITEMS 128 #define KEYMAN_BACKSPACE 14 #define KEYMAN_BACKSPACE_KEYSYM 0xff08 #define KEYMAN_LCTRL 29 #define KEYMAN_LALT 56 #define KEYMAN_RCTRL 97 #define KEYMAN_RALT 100 typedef struct _IBusKeymanEngine IBusKeymanEngine; typedef struct _IBusKeymanEngineClass IBusKeymanEngineClass; struct _IBusKeymanEngine { IBusEngine parent; /* members */ km_kbp_keyboard *keyboard; km_kbp_state *state; gchar *ldmlfile; gchar *kb_name; gchar *char_buffer; gunichar firstsurrogate; gboolean lctrl_pressed; gboolean rctrl_pressed; gboolean lalt_pressed; gboolean ralt_pressed; gboolean emitting_keystroke; IBusLookupTable *table; IBusProperty *status_prop; IBusPropList *prop_list; }; struct _IBusKeymanEngineClass { IBusEngineClass parent; }; /* functions prototype */ static void ibus_keyman_engine_class_init (IBusKeymanEngineClass *klass); static void ibus_keyman_engine_init (IBusKeymanEngine *kmfl); static GObject* ibus_keyman_engine_constructor (GType type, guint n_construct_params, GObjectConstructParam *construct_params); static void ibus_keyman_engine_destroy (IBusKeymanEngine *kmfl); static gboolean ibus_keyman_engine_process_key_event (IBusEngine *engine, guint keyval, guint keycode, guint state); static void ibus_keyman_engine_focus_in (IBusEngine *engine); static void ibus_keyman_engine_focus_out (IBusEngine *engine); static void ibus_keyman_engine_reset (IBusEngine *engine); static void ibus_keyman_engine_enable (IBusEngine *engine); static void ibus_keyman_engine_disable (IBusEngine *engine); static void ibus_keyman_engine_set_surrounding_text( IBusEngine *engine, IBusText *text, guint cursor_pos, guint anchor_pos); // static void ibus_keyman_engine_set_cursor_location (IBusEngine *engine, // gint x, // gint y, // gint w, // gint h); static void ibus_keyman_engine_set_capabilities( IBusEngine *engine, guint caps); // static void ibus_keyman_engine_page_up (IBusEngine *engine); // static void ibus_keyman_engine_page_down (IBusEngine *engine); // static void ibus_keyman_engine_cursor_up (IBusEngine *engine); // static void ibus_keyman_engine_cursor_down (IBusEngine *engine); static void ibus_keyman_engine_property_activate (IBusEngine *engine, const gchar *prop_name, guint prop_state); static void ibus_keyman_engine_property_show (IBusEngine *engine, const gchar *prop_name); static void ibus_keyman_engine_property_hide (IBusEngine *engine, const gchar *prop_name); static void ibus_keyman_engine_commit_string (IBusKeymanEngine *kmfl, const gchar *string); static IBusEngineClass *parent_class = NULL; GType ibus_keyman_engine_get_type (void) { static GType type = 0; static const GTypeInfo type_info = { sizeof (IBusKeymanEngineClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) ibus_keyman_engine_class_init, NULL, NULL, sizeof (IBusKeymanEngine), 0, (GInstanceInitFunc) ibus_keyman_engine_init, }; if (type == 0) { type = g_type_register_static (IBUS_TYPE_ENGINE, "IBusKeymanEngine", &type_info, (GTypeFlags) 0); } return type; } static void ibus_keyman_engine_class_init (IBusKeymanEngineClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass); IBusEngineClass *engine_class = IBUS_ENGINE_CLASS (klass); parent_class = (IBusEngineClass *) g_type_class_peek_parent (klass); object_class->constructor = ibus_keyman_engine_constructor; ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_keyman_engine_destroy; engine_class->process_key_event = ibus_keyman_engine_process_key_event; engine_class->reset = ibus_keyman_engine_reset; engine_class->enable = ibus_keyman_engine_enable; engine_class->disable = ibus_keyman_engine_disable; engine_class->set_surrounding_text = ibus_keyman_engine_set_surrounding_text; // engine_class->set_cursor_location = ibus_keyman_engine_set_cursor_location; engine_class->focus_in = ibus_keyman_engine_focus_in; engine_class->focus_out = ibus_keyman_engine_focus_out; // engine_class->page_up = ibus_keyman_engine_page_up; // engine_class->page_down = ibus_keyman_engine_page_down; // engine_class->cursor_up = ibus_keyman_engine_cursor_up; // engine_class->cursor_down = ibus_keyman_engine_cursor_down; engine_class->property_activate = ibus_keyman_engine_property_activate; } static gchar *get_current_context_text(km_kbp_context *context) { size_t buf_size = 512; km_kbp_context_item *context_items; gchar *current_context_utf8 = g_new0(gchar, buf_size); if (km_kbp_context_get(context, &context_items) == KM_KBP_STATUS_OK) { km_kbp_context_items_to_utf8(context_items, current_context_utf8, &buf_size); } km_kbp_context_items_dispose(context_items); g_message("current context is:%lu:%lu:%s:", km_kbp_context_length(context), buf_size, current_context_utf8); return current_context_utf8; } static void reset_context(IBusEngine *engine) { IBusKeymanEngine *keyman = (IBusKeymanEngine *) engine; IBusText *text; gchar *surrounding_text, *current_context_utf8; guint cursor_pos, anchor_pos, context_start, context_pos; km_kbp_context_item *context_items; km_kbp_context *context; g_message("reset_context"); keyman->firstsurrogate = 0; context = km_kbp_state_context(keyman->state); if ((engine->client_capabilities & IBUS_CAP_SURROUNDING_TEXT) != 0) { current_context_utf8 = get_current_context_text(context); ibus_engine_get_surrounding_text(engine, &text, &cursor_pos, &anchor_pos); context_pos = anchor_pos < cursor_pos ? anchor_pos : cursor_pos; context_start = context_pos > MAXCONTEXT_ITEMS ? context_pos - MAXCONTEXT_ITEMS : 0; surrounding_text = g_utf8_substring(ibus_text_get_text(text), context_start, context_pos); g_message("new context is:%u:%s: cursor:%d anchor:%d", context_pos - context_start, surrounding_text, cursor_pos, anchor_pos); g_message(":%s:%s:", surrounding_text, current_context_utf8); if (g_strcmp0(surrounding_text, current_context_utf8) != 0) { g_message("setting context because it has changed from expected"); if (km_kbp_context_items_from_utf8(surrounding_text, &context_items) == KM_KBP_STATUS_OK) { km_kbp_context_set(context, context_items); } km_kbp_context_items_dispose(context_items); } g_free(surrounding_text); g_free(current_context_utf8); } else { km_kbp_context_clear(context); } } static void ibus_keyman_engine_init (IBusKeymanEngine *kmfl) { kmfl->status_prop = ibus_property_new ("status", PROP_TYPE_NORMAL, NULL, NULL, NULL, TRUE, FALSE, 0, NULL); g_object_ref_sink(kmfl->status_prop); kmfl->prop_list = ibus_prop_list_new (); g_object_ref_sink(kmfl->prop_list); ibus_prop_list_append (kmfl->prop_list, kmfl->status_prop); kmfl->table = ibus_lookup_table_new (9, 0, TRUE, TRUE); g_object_ref_sink(kmfl->table); kmfl->state = NULL; } static GObject* ibus_keyman_engine_constructor (GType type, guint n_construct_params, GObjectConstructParam *construct_params) { IBusKeymanEngine *keyman; IBusEngine *engine; IBusText *text; const gchar *engine_name; gchar *surrounding_text, *p, *abs_kmx_path; guint cursor_pos, anchor_pos; km_kbp_context_item *context_items; g_debug("DAR: ibus_keyman_engine_constructor"); keyman = (IBusKeymanEngine *) G_OBJECT_CLASS (parent_class)->constructor (type, n_construct_params, construct_params); engine = (IBusEngine *) keyman; engine_name = ibus_engine_get_name (engine); g_assert (engine_name); g_message("DAR: ibus_keyman_engine_constructor %s", engine_name); keyman->kb_name = NULL; keyman->ldmlfile = NULL; keyman->firstsurrogate = 0; keyman->lalt_pressed = FALSE; keyman->lctrl_pressed = FALSE; keyman->ralt_pressed = FALSE; keyman->rctrl_pressed = FALSE; keyman->emitting_keystroke = FALSE; gchar **split_name = g_strsplit(engine_name, ":", 2); if (split_name[0] == NULL) { IBUS_OBJECT_CLASS (parent_class)->destroy ((IBusObject *)keyman); return NULL; } else if (split_name[1] == NULL) { abs_kmx_path = g_strdup(split_name[0]); } else { abs_kmx_path = g_strdup(split_name[1]); } g_strfreev(split_name); gchar *kmx_file = g_path_get_basename(abs_kmx_path); p = rindex(kmx_file, '.'); // get id to use as dbus service name if (p) { keyman->kb_name = g_strndup(kmx_file, p-kmx_file); p = rindex(abs_kmx_path, '.'); if (p) { gchar *dir = g_path_get_dirname(abs_kmx_path); gchar *ldmlfile = g_strdup_printf("%s/%s.ldml", dir, keyman->kb_name); if (g_file_test(ldmlfile, G_FILE_TEST_EXISTS)) { keyman->ldmlfile = g_strdup(ldmlfile); } g_free(dir); g_free(ldmlfile); } } g_free(kmx_file); km_kbp_option_item *keyboard_opts = g_new0(km_kbp_option_item, 4); keyboard_opts[0].scope = KM_KBP_OPT_ENVIRONMENT; km_kbp_cp *cp = g_utf8_to_utf16 ("platform", -1, NULL, NULL, NULL); keyboard_opts[0].key = cp; cp = g_utf8_to_utf16 ("linux desktop hardware native", -1, NULL, NULL, NULL); keyboard_opts[0].value = cp; keyboard_opts[1].scope = KM_KBP_OPT_ENVIRONMENT; cp = g_utf8_to_utf16 ("baseLayout", -1, NULL, NULL, NULL); keyboard_opts[1].key = cp; cp = g_utf8_to_utf16 ("kbdus.dll", -1, NULL, NULL, NULL); keyboard_opts[1].value = cp; keyboard_opts[2].scope = KM_KBP_OPT_ENVIRONMENT; cp = g_utf8_to_utf16 ("baseLayoutAlt", -1, NULL, NULL, NULL); keyboard_opts[2].key = cp; #if 0 // in the future when mnemonic layouts are to be supported const gchar *lang_env = g_getenv("LANG"); gchar *lang; if (lang_env != NULL) { g_message("LANG=%s", lang_env); gchar **splitlang = g_strsplit(lang_env, ".", 2); g_message("before . is %s", splitlang[0]); if (g_strrstr(splitlang[0], "_")) { g_message("splitting %s", splitlang[0]); gchar **taglang = g_strsplit(splitlang[0], "_", 2); g_message("lang of tag is %s", taglang[0]); g_message("country of tag is %s", taglang[1]); lang = g_strjoin("-", taglang[0], taglang[1], NULL); g_strfreev(taglang); } else { lang = g_strdup(splitlang[0]); } g_strfreev(splitlang); } else { lang = strdup("en-US"); } g_message("lang is %s", lang); #endif cp = g_utf8_to_utf16 ("en-US", -1, NULL, NULL, NULL); // g_free(lang); keyboard_opts[2].value = cp; // keyboard_opts[3] already initialised to {0, 0, 0} km_kbp_status status_keyboard = km_kbp_keyboard_load(abs_kmx_path, &(keyman->keyboard)); g_free(abs_kmx_path); if (status_keyboard != KM_KBP_STATUS_OK) { g_warning("problem creating km_kbp_keyboard"); } km_kbp_status status_state = km_kbp_state_create(keyman->keyboard, keyboard_opts, &(keyman->state)); if (status_state != KM_KBP_STATUS_OK) { g_warning("problem creating km_kbp_state"); } for (int i =0; i < 3; i++) { g_free((km_kbp_cp *)keyboard_opts[i].key); g_free((km_kbp_cp *)keyboard_opts[i].value); } g_free(keyboard_opts); reset_context(engine); return (GObject *) keyman; } static void ibus_keyman_engine_destroy (IBusKeymanEngine *keyman) { const gchar *engine_name; g_debug("DAR: ibus_keyman_engine_destroy"); engine_name = ibus_engine_get_name ((IBusEngine *) keyman); g_assert (engine_name); g_message("DAR: ibus_keyman_engine_destroy %s", engine_name); if (keyman->prop_list) { g_debug("DAR: unref keyman->prop_list"); g_object_unref (keyman->prop_list); keyman->prop_list = NULL; } if (keyman->status_prop) { g_debug("DAR: unref keyman->status_prop"); g_object_unref (keyman->status_prop); keyman->status_prop = NULL; } if (keyman->table) { g_debug("DAR: unref keyman->table"); g_object_unref (keyman->table); keyman->table = NULL; } if (keyman->state) { km_kbp_state_dispose(keyman->state); keyman->state = NULL; } if (keyman->keyboard) { km_kbp_keyboard_dispose(keyman->keyboard); keyman->keyboard = NULL; } g_free(keyman->kb_name); g_free(keyman->ldmlfile); IBUS_OBJECT_CLASS (parent_class)->destroy ((IBusObject *)keyman); } static void ibus_keyman_engine_commit_string (IBusKeymanEngine *kmfl, const gchar *string) { IBusText *text; g_message("DAR: ibus_keyman_engine_commit_string - %s", string); text = ibus_text_new_from_static_string (string); g_object_ref_sink(text); ibus_engine_commit_text ((IBusEngine *)kmfl, text); g_object_unref (text); } static void forward_backspace(IBusKeymanEngine *engine, unsigned int state) { g_message("DAR: forward_backspace %d no keysym state %d", KEYMAN_BACKSPACE, state); ibus_engine_forward_key_event((IBusEngine *)engine, KEYMAN_BACKSPACE_KEYSYM, KEYMAN_BACKSPACE, state); } // from android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java // uses kernel keycodes which are X11 keycode - 8 //private static final // int scanCodeMap[] = { static km_kbp_virtual_key const keycode_to_vk[256] = { 0, // padding = 0x00; KM_KBP_VKEY_ESC, // public static final int KEY_ESC = 0x01; KM_KBP_VKEY_1, // public static final int KEY_1 = 0x02; KM_KBP_VKEY_2, // public static final int KEY_2 = 0x03; KM_KBP_VKEY_3, // public static final int KEY_3 = 0x04; KM_KBP_VKEY_4, // public static final int KEY_4 = 0x05; KM_KBP_VKEY_5, // public static final int KEY_5 = 0x06; KM_KBP_VKEY_6, // public static final int KEY_6 = 0x07; KM_KBP_VKEY_7, // public static final int KEY_7 = 0x08; KM_KBP_VKEY_8, // public static final int KEY_8 = 0x09; KM_KBP_VKEY_9, // public static final int KEY_9 = 0x0A; KM_KBP_VKEY_0, // public static final int KEY_0 = 0x0B; KM_KBP_VKEY_HYPHEN, // public static final int KEY_MINUS = 0x0C; KM_KBP_VKEY_EQUAL, // public static final int KEY_EQUALS = 0x0D; KM_KBP_VKEY_BKSP, // public static final int KEY_BACKSPACE = 0x0E; KM_KBP_VKEY_TAB, // public static final int KEY_TAB = 0x0F; KM_KBP_VKEY_Q, // public static final int KEY_Q = 0x10; KM_KBP_VKEY_W, // public static final int KEY_W = 0x11; KM_KBP_VKEY_E, // public static final int KEY_E = 0x12; KM_KBP_VKEY_R, // public static final int KEY_R = 0x13; KM_KBP_VKEY_T, // public static final int KEY_T = 0x14; KM_KBP_VKEY_Y, // public static final int KEY_Y = 0x15; KM_KBP_VKEY_U, // public static final int KEY_U = 0x16; KM_KBP_VKEY_I, // public static final int KEY_I = 0x17; KM_KBP_VKEY_O, // public static final int KEY_O = 0x18; KM_KBP_VKEY_P, // public static final int KEY_P = 0x19; KM_KBP_VKEY_LBRKT, // public static final int KEY_LEFTBRACE = 0x1A; KM_KBP_VKEY_RBRKT, // public static final int KEY_RIGHTBRACE = 0x1B; KM_KBP_VKEY_ENTER, // public static final int KEY_ENTER = 0x1C; 0, // public static final int KEY_LEFTCTRL = 0x1D; KM_KBP_VKEY_A, // public static final int KEY_A = 0x1E; KM_KBP_VKEY_S, // public static final int KEY_S = 0x1F; KM_KBP_VKEY_D, // public static final int KEY_D = 0x20; KM_KBP_VKEY_F, // public static final int KEY_F = 0x21; KM_KBP_VKEY_G, // public static final int KEY_G = 0x22; KM_KBP_VKEY_H, // public static final int KEY_H = 0x23; KM_KBP_VKEY_J, // public static final int KEY_J = 0x24; KM_KBP_VKEY_K, // public static final int KEY_K = 0x25; KM_KBP_VKEY_L, // public static final int KEY_L = 0x26; KM_KBP_VKEY_COLON, // public static final int KEY_SEMICOLON = 0x27; KM_KBP_VKEY_QUOTE, // public static final int KEY_APOSTROPHE = 0x28; KM_KBP_VKEY_BKQUOTE, // public static final int KEY_GRAVE = 0x29; 0, // public static final int KEY_LEFTSHIFT = 0x2A; KM_KBP_VKEY_BKSLASH, // public static final int KEY_BACKSLASH = 0x2B; KM_KBP_VKEY_Z, // public static final int KEY_Z = 0x2C; KM_KBP_VKEY_X, // public static final int KEY_X = 0x2D; KM_KBP_VKEY_C, // public static final int KEY_C = 0x2E; KM_KBP_VKEY_V, // public static final int KEY_V = 0x2F; KM_KBP_VKEY_B, // public static final int KEY_B = 0x30; KM_KBP_VKEY_N, // public static final int KEY_N = 0x31; KM_KBP_VKEY_M, // public static final int KEY_M = 0x32; KM_KBP_VKEY_COMMA, // public static final int KEY_COMMA = 0x33; KM_KBP_VKEY_PERIOD, // public static final int KEY_DOT = 0x34; KM_KBP_VKEY_SLASH, // public static final int KEY_SLASH = 0x35; 0, // public static final int KEY_RIGHTSHIFT = 0x36; KM_KBP_VKEY_NPSTAR, // public static final int KEY_KPASTERISK = 0x37; 0, // public static final int KEY_LEFTALT = 0x38; KM_KBP_VKEY_SPACE, // public static final int KEY_SPACE = 0x39; 0, // public static final int KEY_CAPSLOCK = 0x3A; KM_KBP_VKEY_F1, // public static final int KEY_F1 = 0x3B; KM_KBP_VKEY_F2, // public static final int KEY_F2 = 0x3C; KM_KBP_VKEY_F3, // public static final int KEY_F3 = 0x3D; KM_KBP_VKEY_F4, // public static final int KEY_F4 = 0x3E; KM_KBP_VKEY_F5, // public static final int KEY_F5 = 0x3F; KM_KBP_VKEY_F6, // public static final int KEY_F6 = 0x40; KM_KBP_VKEY_F7, // public static final int KEY_F7 = 0x41; KM_KBP_VKEY_F8, // public static final int KEY_F8 = 0x42; KM_KBP_VKEY_F9, // public static final int KEY_F9 = 0x43; KM_KBP_VKEY_F10, // public static final int KEY_F10 = 0x44; 0, // public static final int KEY_NUMLOCK = 0x45; 0, // public static final int KEY_SCROLLLOCK = 0x46; KM_KBP_VKEY_NP7, // public static final int KEY_KP7 = 0x47; KM_KBP_VKEY_NP8, // public static final int KEY_KP8 = 0x48; KM_KBP_VKEY_NP9, // public static final int KEY_KP9 = 0x49; KM_KBP_VKEY_NPMINUS, // public static final int KEY_KPMINUS = 0x4A; KM_KBP_VKEY_NP4, // public static final int KEY_KP4 = 0x4B; KM_KBP_VKEY_NP5, // public static final int KEY_KP5 = 0x4C; KM_KBP_VKEY_NP6, // public static final int KEY_KP6 = 0x4D; KM_KBP_VKEY_NPPLUS, // public static final int KEY_KPPLUS = 0x4E; KM_KBP_VKEY_NP1, // public static final int KEY_KP1 = 0x4F; KM_KBP_VKEY_NP2, // public static final int KEY_KP2 = 0x50; KM_KBP_VKEY_NP3, // public static final int KEY_KP3 = 0x51; KM_KBP_VKEY_NP0, // public static final int KEY_KP0 = 0x52; KM_KBP_VKEY_NPDOT, // public static final int KEY_KPDOT = 0x53; 0, // padding 0x54; 0, // public static final int KEY_ZENKAKUHANKAKU = 0x55; KM_KBP_VKEY_oE2, // public static final int KEY_102ND = 0x56; // additional on linux KM_KBP_VKEY_F11, // public static final int KEY_F11 = 0x57; KM_KBP_VKEY_F12 // public static final int KEY_F12 = 0x58; // Many more KEYS currently not used by KMW... }; static gboolean ok_for_single_backspace(const km_kbp_action_item *action_items, int i, size_t num_actions) { for (int j=i+1; j < num_actions; j++) { if (action_items[i].type == KM_KBP_IT_BACK || action_items[i].type == KM_KBP_IT_CHAR || action_items[i].type == KM_KBP_IT_EMIT_KEYSTROKE) { return FALSE; } } return TRUE; } static gboolean ibus_keyman_engine_process_key_event (IBusEngine *engine, guint keyval, guint keycode, guint state) { IBusKeymanEngine *keyman = (IBusKeymanEngine *) engine; g_message("DAR: ibus_keyman_engine_process_key_event - keyval=%02i, keycode=%02i, state=%02x", keyval, keycode, state); if (state & IBUS_RELEASE_MASK) { switch(keycode) { case KEYMAN_LCTRL: keyman->lctrl_pressed = FALSE; break; case KEYMAN_RCTRL: keyman->rctrl_pressed = FALSE; break; case KEYMAN_LALT: keyman->lalt_pressed = FALSE; break; case KEYMAN_RALT: keyman->ralt_pressed = FALSE; break; default: break; } return FALSE; } if (keycode < 0 || keycode > 255) { g_warning("keycode %d out of range", keycode); return FALSE; } if (keycode_to_vk[keycode] == 0) // key we don't handle { //save if a possible Ctrl or Alt modifier switch(keycode) { case KEYMAN_LCTRL: keyman->lctrl_pressed = TRUE; break; case KEYMAN_RCTRL: keyman->rctrl_pressed = TRUE; break; case KEYMAN_LALT: keyman->lalt_pressed = TRUE; break; case KEYMAN_RALT: keyman->ralt_pressed = TRUE; break; default: break; } return FALSE; } // keyman modifiers are different from X11/ibus uint16_t km_mod_state = 0; if (state & IBUS_SHIFT_MASK) { km_mod_state |= KM_KBP_MODIFIER_SHIFT; } if (state & IBUS_MOD5_MASK) { km_mod_state |= KM_KBP_MODIFIER_RALT; g_message("modstate KM_KBP_MODIFIER_RALT from IBUS_MOD5_MASK"); } if (state & IBUS_MOD1_MASK) { if (keyman->ralt_pressed) { km_mod_state |= KM_KBP_MODIFIER_RALT; g_message("modstate KM_KBP_MODIFIER_RALT from ralt_pressed"); } if (keyman->lalt_pressed) { km_mod_state |= KM_KBP_MODIFIER_LALT; g_message("modstate KM_KBP_MODIFIER_LALT from lalt_pressed"); } } if (state & IBUS_CONTROL_MASK) { if (keyman->rctrl_pressed) { km_mod_state |= KM_KBP_MODIFIER_RCTRL; g_message("modstate KM_KBP_MODIFIER_RCTRL from rctrl_pressed"); } if (keyman->lctrl_pressed) { km_mod_state |= KM_KBP_MODIFIER_LCTRL; g_message("modstate KM_KBP_MODIFIER_LCTRL from lctrl_pressed"); } } g_message("before process key event"); km_kbp_context *context = km_kbp_state_context(keyman->state); g_free(get_current_context_text(context)); g_message("DAR: ibus_keyman_engine_process_key_event - km_mod_state=%x", km_mod_state); km_kbp_status event_status = km_kbp_process_event(keyman->state, keycode_to_vk[keycode], km_mod_state); context = km_kbp_state_context(keyman->state); g_message("after process key event"); g_free(get_current_context_text(context)); // km_kbp_state_action_items to get action items size_t num_action_items; gint numbytes; g_free(keyman->char_buffer); keyman->char_buffer = NULL; const km_kbp_action_item *action_items = km_kbp_state_action_items(keyman->state, &num_action_items); for (int i = 0; i < num_action_items; i++) { switch(action_items[i].type) { case KM_KBP_IT_CHAR: g_message("CHAR action %d/%d", i+1, (int)num_action_items); if (g_unichar_type(action_items[i].character) == G_UNICODE_SURROGATE) { if (keyman->firstsurrogate == 0) { keyman->firstsurrogate = action_items[i].character; g_message("first surrogate %d", keyman->firstsurrogate); } else { glong items_read, items_written; gunichar2 utf16_pair[2] = {keyman->firstsurrogate, action_items[i].character}; gchar *utf8_pair = g_utf16_to_utf8 (utf16_pair, 2, &items_read, &items_written, NULL); if (keyman->char_buffer == NULL) { keyman->char_buffer = utf8_pair; } else { gchar *new_buffer = g_strjoin("", keyman->char_buffer, utf8_pair, NULL); g_free(keyman->char_buffer); g_free(utf8_pair); keyman->char_buffer = new_buffer; } keyman->firstsurrogate = 0; } } else { gchar *utf8 = (gchar *) g_new0(gchar, 12); numbytes = g_unichar_to_utf8(action_items[i].character, utf8); if (numbytes > 12) { g_error("g_unichar_to_utf8 overflowing buffer"); g_free(utf8); } else { g_message("unichar:U+%04x, bytes:%d, string:%s", action_items[i].character, numbytes, utf8); if (keyman->char_buffer == NULL) { g_message("setting buffer to converted unichar"); keyman->char_buffer = utf8; } else { g_message("appending converted unichar to CHAR buffer"); gchar *new_buffer = g_strjoin("", keyman->char_buffer, utf8, NULL); g_free(keyman->char_buffer); g_free(utf8); keyman->char_buffer = new_buffer; } g_message("CHAR buffer is now %s", keyman->char_buffer); } } break; case KM_KBP_IT_MARKER: g_message("MARKER action %d/%d", i+1, (int)num_action_items); break; case KM_KBP_IT_ALERT: g_message("ALERT action %d/%d", i+1, (int)num_action_items); GdkDisplay *display = gdk_display_open(NULL); if (display != NULL) { gdk_display_beep(display); gdk_display_close(display); } break; case KM_KBP_IT_BACK: g_message("BACK action %d/%d", i+1, (int)num_action_items); if (keyman->char_buffer != NULL) { // ibus_keyman_engine_commit_string(keyman, keyman->char_buffer); g_message("removing one utf8 char from CHAR buffer"); glong end_pos = g_utf8_strlen(keyman->char_buffer, -1); gchar *new_buffer; if (end_pos == 1) { new_buffer = NULL; g_message("resetting CHAR buffer to NULL"); } else { new_buffer = g_utf8_substring(keyman->char_buffer, 0 , end_pos - 1); g_message("changing CHAR buffer to :%s:", new_buffer); } if (g_strcmp0(keyman->char_buffer, new_buffer) == 0) { g_message("oops, CHAR buffer hasn't changed"); } g_free(keyman->char_buffer); keyman->char_buffer = new_buffer; } else if (ok_for_single_backspace(action_items, i, num_action_items)) { // single backspace can be handled by ibus as normal g_message("no char actions, just single back"); return FALSE; } else { g_message("DAR: ibus_keyman_engine_process_key_event - client_capabilities=%x, %x", engine->client_capabilities, IBUS_CAP_SURROUNDING_TEXT); if ((engine->client_capabilities & IBUS_CAP_SURROUNDING_TEXT) != 0) { g_message("deleting surrounding text 1 char"); ibus_engine_delete_surrounding_text(engine, -1, 1); } else { g_message("forwarding backspace with reset context"); km_kbp_context_item *context_items; km_kbp_context_get(km_kbp_state_context(keyman->state), &context_items); reset_context(engine); forward_backspace(keyman, 0); km_kbp_context_set(km_kbp_state_context(keyman->state), context_items); km_kbp_context_items_dispose(context_items); } } break; case KM_KBP_IT_PERSIST_OPT: g_message("PERSIST_OPT action %d/%d", i+1, (int)num_action_items); break; case KM_KBP_IT_EMIT_KEYSTROKE: if (keyman->char_buffer != NULL) { ibus_keyman_engine_commit_string(keyman, keyman->char_buffer); g_free(keyman->char_buffer); keyman->char_buffer = NULL; } g_message("EMIT_KEYSTROKE action %d/%d", i+1, (int)num_action_items); keyman->emitting_keystroke = TRUE; break; case KM_KBP_IT_INVALIDATE_CONTEXT: g_message("INVALIDATE_CONTEXT action %d/%d", i+1, (int)num_action_items); km_kbp_context_clear(km_kbp_state_context(keyman->state)); reset_context(engine); break; case KM_KBP_IT_END: g_message("END action %d/%d", i+1, (int)num_action_items); keyman->firstsurrogate = 0; if (keyman->char_buffer != NULL) { ibus_keyman_engine_commit_string(keyman, keyman->char_buffer); g_free(keyman->char_buffer); keyman->char_buffer = NULL; } if (keyman->emitting_keystroke) { keyman->emitting_keystroke = FALSE; return FALSE; } break; default: g_warning("Unknown action %d/%d(%d)", i+1, (int)num_action_items, action_items[i].type); } } context = km_kbp_state_context(keyman->state); g_message("after processing all actions"); g_free(get_current_context_text(context)); return TRUE; } static void ibus_keyman_engine_set_surrounding_text (IBusEngine *engine, IBusText *text, guint cursor_pos, guint anchor_pos) { gchar *surrounding_text; guint context_start = cursor_pos > MAXCONTEXT_ITEMS ? cursor_pos - MAXCONTEXT_ITEMS : 0; g_message("ibus_keyman_engine_set_surrounding_text"); if (cursor_pos != anchor_pos){ g_message("ibus_keyman_engine_set_surrounding_text: There is a selection"); } parent_class->set_surrounding_text (engine, text, cursor_pos, anchor_pos); surrounding_text = g_utf8_substring(ibus_text_get_text(text), context_start, cursor_pos); g_message("surrounding context is:%u:%s:", cursor_pos - context_start, surrounding_text); g_free(surrounding_text); reset_context(engine); } // static void ibus_keyman_engine_set_cursor_location (IBusEngine *engine, // gint x, // gint y, // gint w, // gint h) // { // g_message("ibus_keyman_engine_set_cursor_location"); // //ibus_keyman_engine_reset(engine); // parent_class->set_cursor_location (engine, x, y, w, h); // } static void ibus_keyman_engine_focus_in (IBusEngine *engine) { IBusKeymanEngine *keyman = (IBusKeymanEngine *) engine; g_message("ibus_keyman_engine_focus_in"); ibus_engine_register_properties (engine, keyman->prop_list); reset_context(engine); parent_class->focus_in (engine); } static void ibus_keyman_engine_focus_out (IBusEngine *engine) { IBusKeymanEngine *keyman = (IBusKeymanEngine *) engine; g_message("ibus_keyman_engine_focus_out"); km_kbp_context_clear(km_kbp_state_context(keyman->state)); parent_class->focus_out (engine); } static void ibus_keyman_engine_reset (IBusEngine *engine) { g_message("ibus_keyman_engine_reset"); parent_class->reset (engine); ibus_keyman_engine_focus_in (engine); } static void ibus_keyman_engine_enable (IBusEngine *engine) { const gchar *engine_name; IBusKeymanEngine *keyman = (IBusKeymanEngine *) engine; engine_name = ibus_engine_get_name (engine); g_assert (engine_name); g_message("WDG: ibus_keyman_engine_enable %s", engine_name); g_message("enabling surrounding context"); ibus_engine_get_surrounding_text(engine, NULL, NULL, NULL); if (keyman->ldmlfile) { // own dbus name com.Keyman // expose properties LDMLFile and Name KeymanService *service = km_service_get_default(); km_service_set_ldmlfile (service, keyman->ldmlfile); km_service_set_name (service, keyman->kb_name); } parent_class->enable (engine); } static void ibus_keyman_engine_disable (IBusEngine *engine) { const gchar *engine_name; engine_name = ibus_engine_get_name (engine); g_assert (engine_name); g_message("WDG: ibus_keyman_engine_disable %s", engine_name); ibus_keyman_engine_focus_out (engine); // stop owning dbus name com.Keyman KeymanService *service = km_service_get_default(); km_service_set_ldmlfile (service, ""); km_service_set_name (service, "None"); // g_clear_object(&service); parent_class->disable (engine); } // static void // ibus_keyman_engine_page_up (IBusEngine *engine) // { // g_message("ibus_keyman_engine_page_up"); // parent_class->page_up (engine); // reset_context(engine); // } // static void // ibus_keyman_engine_page_down (IBusEngine *engine) // { // g_message("ibus_keyman_engine_page_down"); // parent_class->page_down (engine); // reset_context(engine); // } // static void // ibus_keyman_engine_cursor_up (IBusEngine *engine) // { // g_message("ibus_keyman_engine_cursor_up"); // parent_class->cursor_up (engine); // reset_context(engine); // } // static void // ibus_keyman_engine_cursor_down (IBusEngine *engine) // { // g_message("ibus_keyman_engine_cursor_down"); // parent_class->cursor_down (engine); // reset_context(engine); // } static void ibus_keyman_engine_property_activate (IBusEngine *engine, const gchar *prop_name, guint prop_state) { g_message("ibus_keyman_engine_property_activate"); parent_class->property_activate (engine, prop_name, prop_state); }