/* vim:set et sts=4: */ /* * KMFL Input Method for IBUS (The Input Bus) * * Copyright (C) 2009 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 #include "kmflutil.h" #include "engine.h" typedef struct _IBusKMFLEngine IBusKMFLEngine; typedef struct _IBusKMFLEngineClass IBusKMFLEngineClass; struct _IBusKMFLEngine { IBusEngine parent; /* members */ KInputContext *context; IBusLookupTable *table; IBusProperty *status_prop; IBusPropList *prop_list; Display *display; }; struct _IBusKMFLEngineClass { IBusEngineClass parent; }; /* functions prototype */ static void ibus_kmfl_engine_class_init (IBusKMFLEngineClass *klass); static void ibus_kmfl_engine_init (IBusKMFLEngine *kmfl); static GObject* ibus_kmfl_engine_constructor (GType type, guint n_construct_params, GObjectConstructParam *construct_params); static void ibus_kmfl_engine_destroy (IBusKMFLEngine *kmfl); static gboolean ibus_kmfl_engine_process_key_event (IBusEngine *engine, guint keyval, guint keycode, guint state); static void ibus_kmfl_engine_focus_in (IBusEngine *engine); static void ibus_kmfl_engine_focus_out (IBusEngine *engine); static void ibus_kmfl_engine_reset (IBusEngine *engine); static void ibus_kmfl_engine_enable (IBusEngine *engine); static void ibus_kmfl_engine_disable (IBusEngine *engine); static void ibus_engine_set_cursor_location (IBusEngine *engine, gint x, gint y, gint w, gint h); static void ibus_kmfl_engine_set_capabilities (IBusEngine *engine, guint caps); static void ibus_kmfl_engine_page_up (IBusEngine *engine); static void ibus_kmfl_engine_page_down (IBusEngine *engine); static void ibus_kmfl_engine_cursor_up (IBusEngine *engine); static void ibus_kmfl_engine_cursor_down (IBusEngine *engine); static void ibus_kmfl_engine_property_activate (IBusEngine *engine, const gchar *prop_name, guint prop_state); static void ibus_kmfl_engine_property_show (IBusEngine *engine, const gchar *prop_name); static void ibus_kmfl_engine_property_hide (IBusEngine *engine, const gchar *prop_name); static void ibus_kmfl_engine_commit_string (IBusKMFLEngine *kmfl, const gchar *string); static IBusEngineClass *parent_class = NULL; static GHashTable *im_table = NULL; GType ibus_kmfl_engine_get_type (void) { static GType type = 0; static const GTypeInfo type_info = { sizeof (IBusKMFLEngineClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) ibus_kmfl_engine_class_init, NULL, NULL, sizeof (IBusKMFLEngine), 0, (GInstanceInitFunc) ibus_kmfl_engine_init, }; if (type == 0) { type = g_type_register_static (IBUS_TYPE_ENGINE, "IBusKMFLEngine", &type_info, (GTypeFlags) 0); } return type; } static void ibus_kmfl_engine_class_init (IBusKMFLEngineClass *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_kmfl_engine_constructor; ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_kmfl_engine_destroy; engine_class->process_key_event = ibus_kmfl_engine_process_key_event; engine_class->reset = ibus_kmfl_engine_reset; engine_class->enable = ibus_kmfl_engine_enable; engine_class->disable = ibus_kmfl_engine_disable; engine_class->focus_in = ibus_kmfl_engine_focus_in; engine_class->focus_out = ibus_kmfl_engine_focus_out; engine_class->page_up = ibus_kmfl_engine_page_up; engine_class->page_down = ibus_kmfl_engine_page_down; engine_class->cursor_up = ibus_kmfl_engine_cursor_up; engine_class->cursor_down = ibus_kmfl_engine_cursor_down; engine_class->property_activate = ibus_kmfl_engine_property_activate; } static void ibus_kmfl_engine_init (IBusKMFLEngine *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->context = NULL; } static GObject* ibus_kmfl_engine_constructor (GType type, guint n_construct_params, GObjectConstructParam *construct_params) { IBusKMFLEngine *kmfl; KInputMethod *im; const gchar *engine_name; g_debug("DAR: ibus_kmfl_engine_constructor"); kmfl = (IBusKMFLEngine *) G_OBJECT_CLASS (parent_class)->constructor (type, n_construct_params, construct_params); engine_name = ibus_engine_get_name ((IBusEngine *) kmfl); g_assert (engine_name); g_message("DAR: ibus_kmfl_engine_constructor %s", engine_name); if (im_table == NULL) { im_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) kinput_close_im); } im = (KInputMethod *) g_hash_table_lookup (im_table, engine_name); if (im == NULL) { im = kinput_open_im(engine_name); if (im != NULL) g_hash_table_insert (im_table, g_strdup (engine_name), im); } if (im == NULL) { g_warning ("Can not find kmfl keymap %s", engine_name); g_object_unref (kmfl); return NULL; } kmfl->context = kmfl_create_ic (im); kmfl->display = XOpenDisplay(NULL); return (GObject *) kmfl; } static void ibus_kmfl_engine_destroy (IBusKMFLEngine *kmfl) { const gchar *engine_name; g_debug("DAR: ibus_kmfl_engine_destroy"); engine_name = ibus_engine_get_name ((IBusEngine *) kmfl); g_assert (engine_name); g_message("DAR: ibus_kmfl_engine_destroy %s", engine_name); if (kmfl->prop_list) { g_debug("DAR: unref kmfl->prop_list"); g_object_unref (kmfl->prop_list); kmfl->prop_list = NULL; } if (kmfl->status_prop) { g_debug("DAR: unref kmfl->status_prop"); g_object_unref (kmfl->status_prop); kmfl->status_prop = NULL; } if (kmfl->table) { g_debug("DAR: unref kmfl->table"); g_object_unref (kmfl->table); kmfl->table = NULL; } if (kmfl->context) { kmfl_destroy_ic (kmfl->context); kmfl->context = NULL; } if (kmfl->display) { XCloseDisplay(kmfl->display); kmfl->display = NULL; } g_hash_table_remove(im_table, engine_name); IBUS_OBJECT_CLASS (parent_class)->destroy ((IBusObject *)kmfl); } static void ibus_kmfl_engine_commit_string (IBusKMFLEngine *kmfl, const gchar *string) { IBusText *text; g_debug("DAR: ibus_kmfl_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); } int is_key_pressed(Display * display, char *key_vec, KeySym keysym) { unsigned char keycode; keycode = XKeysymToKeycode(display, keysym); return key_vec[keycode >> 3] & (1 << (keycode & 7)); } static void forward_key(IBusKMFLEngine *engine, unsigned int keysym, unsigned int state) { unsigned char keycode; g_debug("DAR: forward_key"); keycode = XKeysymToKeycode(engine->display, keysym); ibus_engine_forward_key_event((IBusEngine *)engine, keysym, keycode-8, state); } static gboolean ibus_kmfl_engine_process_key_event (IBusEngine *engine, guint keyval, guint keycode, guint state) { IBusKMFLEngine *kmfl = (IBusKMFLEngine *) engine; if (state & IBUS_RELEASE_MASK) return FALSE; g_debug("DAR: ibus_kmfl_engine_process_key_event - keyval=%x, keycode=%x, state=%x", keyval, keycode, state); // If a modifier key is pressed, check to see if it is a right modifier key // This is rather expensive so only do it if a shift state is active int right_modifier_state = 0; if (state & (IBUS_SHIFT_MASK | IBUS_CONTROL_MASK | IBUS_MOD1_MASK)) { Display * m_display = XOpenDisplay(NULL);; char key_vec[32]; XQueryKeymap(m_display, key_vec); if ((state & IBUS_MOD1_MASK) && is_key_pressed(m_display, key_vec, IBUS_Alt_R)) { right_modifier_state |= (IBUS_MOD1_MASK << 8); } if ((state & IBUS_CONTROL_MASK) && is_key_pressed(m_display, key_vec, IBUS_Control_R)) { right_modifier_state |= (IBUS_CONTROL_MASK << 8); } if ((state & IBUS_SHIFT_MASK) && is_key_pressed(m_display, key_vec, IBUS_Shift_R)) { right_modifier_state |= (IBUS_SHIFT_MASK << 8); } XCloseDisplay(m_display); } g_debug("DAR: ibus_kmfl_engine_process_key_event - right_modifier_state=%x", right_modifier_state); // Let the application handle user generated backspaces after resetting the kmfl history if (keyval == IBUS_BackSpace) { clear_history(kmfl->context->p_kmsi); } else if (kmfl_interpret(kmfl->context->p_kmsi, keyval, state | right_modifier_state) == 1) { GList * p; for (p=kmfl->context->cmds; p != NULL; p = p->next) { Cmd * cmd = (Cmd *) p->data; if (cmd) { switch (cmd->opcode) { case OUTPUT_STRING: ibus_kmfl_engine_commit_string(kmfl, cmd->cmdarg); break; case ERASE_CHAR: g_debug("DAR: ibus_kmfl_engine_process_key_event - client_capabilities=%x, %x, %x", engine->client_capabilities, IBUS_CAP_SURROUNDING_TEXT, engine->client_capabilities & IBUS_CAP_SURROUNDING_TEXT); if ((engine->client_capabilities & IBUS_CAP_SURROUNDING_TEXT) != 0) { ibus_engine_delete_surrounding_text(engine, -1, 1); if ((engine->client_capabilities & IBUS_CAP_SURROUNDING_TEXT) == 0) { forward_key(kmfl, IBUS_BackSpace, 0); } } else forward_key(kmfl, IBUS_BackSpace, 0); break; case FORWARD_KEYEVENT: break; case OUTPUT_BEEP: break; } if (cmd->cmdarg) g_free(cmd->cmdarg); g_free(cmd); } } g_list_free(kmfl->context->cmds); kmfl->context->cmds=NULL; return TRUE; } return FALSE; } static void ibus_kmfl_engine_focus_in (IBusEngine *engine) { IBusKMFLEngine *kmfl = (IBusKMFLEngine *) engine; ibus_engine_register_properties (engine, kmfl->prop_list); parent_class->focus_in (engine); } static void ibus_kmfl_engine_focus_out (IBusEngine *engine) { IBusKMFLEngine *kmfl = (IBusKMFLEngine *) engine; parent_class->focus_out (engine); } static void ibus_kmfl_engine_reset (IBusEngine *engine) { IBusKMFLEngine *kmfl = (IBusKMFLEngine *) engine; parent_class->reset (engine); ibus_kmfl_engine_focus_in (engine); } static void ibus_kmfl_engine_enable (IBusEngine *engine) { const gchar *engine_name; IBusKMFLEngine *kmfl = (IBusKMFLEngine *) engine; KInputMethod *im; engine_name = ibus_engine_get_name (engine); g_assert (engine_name); g_message("WDG: ibus_kmfl_engine_enable %s", engine_name); parent_class->enable (engine); } static void ibus_kmfl_engine_disable (IBusEngine *engine) { const gchar *engine_name; IBusKMFLEngine *kmfl = (IBusKMFLEngine *) engine; engine_name = ibus_engine_get_name (engine); g_assert (engine_name); g_message("WDG: ibus_kmfl_engine_disable %s", engine_name); ibus_kmfl_engine_focus_out (engine); parent_class->disable (engine); } static void ibus_kmfl_engine_page_up (IBusEngine *engine) { IBusKMFLEngine *kmfl = (IBusKMFLEngine *) engine; parent_class->page_up (engine); } static void ibus_kmfl_engine_page_down (IBusEngine *engine) { IBusKMFLEngine *kmfl = (IBusKMFLEngine *) engine; parent_class->page_down (engine); } static void ibus_kmfl_engine_cursor_up (IBusEngine *engine) { IBusKMFLEngine *kmfl = (IBusKMFLEngine *) engine; parent_class->cursor_up (engine); } static void ibus_kmfl_engine_cursor_down (IBusEngine *engine) { IBusKMFLEngine *kmfl = (IBusKMFLEngine *) engine; parent_class->cursor_down (engine); } static void ibus_kmfl_engine_property_activate (IBusEngine *engine, const gchar *prop_name, guint prop_state) { parent_class->property_activate (engine, prop_name, prop_state); }