mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-10 02:45:32 +00:00
473 lines
16 KiB
C
473 lines
16 KiB
C
/* 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 <ibus.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <X11/X.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/keysym.h>
|
|
|
|
#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);
|
|
}
|
|
|