/* vim:set et sts=4: */ /* ibus * Copyright (C) 2007-2008 Huang Peng * * main.c: * * This tool is free software; you can redistribute it and/or * modify it under the terms of the GNU Library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _GNU_SOURCES #include #define LOG(level, fmt_args...) \ if (g_debug_level >= (level)) { \ g_debug (fmt_args); \ } #include #include "gdk-private.h" struct _X11ICONN { GList *clients; }; typedef struct _X11ICONN X11ICONN; typedef struct _X11IC X11IC; struct _X11IC { IBusInputContext *context; Window client_window; Window focus_window; gint32 input_style; X11ICONN *conn; gint icid; gint connect_id; gchar *lang; gboolean has_preedit_area; GdkRectangle preedit_area; gchar *preedit_string; IBusAttrList *preedit_attrs; gint preedit_cursor; gboolean preedit_visible; gboolean preedit_started; gint onspot_preedit_length; }; static void _xim_set_cursor_location (X11IC *x11ic); static void _context_commit_text_cb (IBusInputContext *context, IBusText *text, X11IC *x11ic); static void _context_forward_key_event_cb (IBusInputContext *context, X11IC *x11ic); static void _context_update_preedit_text_cb (IBusInputContext *context, IBusText *text, gint cursor_pos, gboolean visible, X11IC *x11ic); static void _context_show_preedit_text_cb (IBusInputContext *context, X11IC *x11ic); static void _context_hide_preedit_text_cb (IBusInputContext *context, X11IC *x11ic); static void _context_enabled_cb (IBusInputContext *context, X11IC *x11ic); static void _context_disabled_cb (IBusInputContext *context, X11IC *x11ic); static GHashTable *_x11_ic_table = NULL; static GHashTable *_connections = NULL; static XIMS _xims = NULL; static gchar _server_name[128] = "ibus"; static gchar _locale[1024] = "aa,af,am,an,ar,as,az,be,bg,bn,br,bs," "ca,cs,cy,da,de,dz,el,en,es,et,eu," "fa,fi,fo,fr,fy,ga,gd,gl,gu,gv," "he,hi,hr,hu,hy,id,is,it,iw,ja," "ka,kk,kl,km,kn,ko,ku,kw,ky,lg,lo,lt,lv," "mg,mi,mk,ml,mn,mr,ms,mt,nb,ne,nl,nn,no,nr," "oc,om,or,pa,pl,pt,ro,ru,rw," "se,si,sk,sl,so,sq,sr,ss,st,sv," "ta,te,tg,th,ti,tl,tn,tr,ts,tt," "uk,ur,uz,ve,vi,wa,xh,yi,zh,zu"; static gboolean _kill_daemon = FALSE; static gint g_debug_level = 0; static IBusBus *_bus = NULL; static void _xim_preedit_start (XIMS xims, const X11IC *x11ic) { IMPreeditStateStruct ips; ips.major_code = 0; ips.minor_code = 0; ips.icid = x11ic->icid; ips.connect_id = x11ic->connect_id; IMPreeditStart (xims, (XPointer)&ips); } static void _xim_preedit_end (XIMS xims, const X11IC *x11ic) { IMPreeditStateStruct ips; ips.major_code = 0; ips.minor_code = 0; ips.icid = x11ic->icid; ips.connect_id = x11ic->connect_id; IMPreeditEnd (xims, (XPointer)&ips); } static void _xim_preedit_callback_start (XIMS xims, const X11IC *x11ic) { IMPreeditCBStruct pcb; pcb.major_code = XIM_PREEDIT_START; pcb.minor_code = 0; pcb.connect_id = x11ic->connect_id; pcb.icid = x11ic->icid; pcb.todo.return_value = 0; IMCallCallback (xims, (XPointer) & pcb); } static void _xim_preedit_callback_done (XIMS xims, const X11IC *x11ic) { IMPreeditCBStruct pcb; pcb.major_code = XIM_PREEDIT_DONE; pcb.minor_code = 0; pcb.connect_id = x11ic->connect_id; pcb.icid = x11ic->icid; pcb.todo.return_value = 0; IMCallCallback (xims, (XPointer) & pcb); } static void _xim_preedit_callback_draw (XIMS xims, X11IC *x11ic, const gchar *preedit_string, IBusAttrList *attr_list) { IMPreeditCBStruct pcb; XIMText text; XTextProperty tp; static XIMFeedback *feedback; static gint feedback_len = 0; guint j, i, len; if (preedit_string == NULL) return; len = g_utf8_strlen (preedit_string, -1); if (len + 1 > feedback_len) { feedback_len = (len + 1 + 63) & ~63; if (feedback) { feedback = g_renew (XIMFeedback, feedback, feedback_len); } else { feedback = g_new (XIMFeedback, feedback_len); } } for (i = 0; i < len; i++) { feedback[i] = 0; } if (attr_list != NULL) { for (i = 0;; i++) { XIMFeedback attr = 0; IBusAttribute *ibus_attr = ibus_attr_list_get (attr_list, i); if (ibus_attr == NULL) { break; } switch (ibus_attr->type) { case IBUS_ATTR_TYPE_UNDERLINE: if (ibus_attr->value == IBUS_ATTR_UNDERLINE_SINGLE) { attr = XIMUnderline; } break; case IBUS_ATTR_TYPE_BACKGROUND: { if (ibus_attr->value != 0xffffff) { attr = XIMReverse; } break; } default: continue; } for (j = ibus_attr->start_index; j < ibus_attr->end_index; j++) { feedback[j] |= attr; } } } for (i = 0; i < len; i++) { if (feedback[i] == 0) { feedback[i] = XIMUnderline; } } feedback[len] = 0; pcb.major_code = XIM_PREEDIT_DRAW; pcb.connect_id = x11ic->connect_id; pcb.icid = x11ic->icid; pcb.todo.draw.caret = len; pcb.todo.draw.chg_first = 0; pcb.todo.draw.chg_length = x11ic->onspot_preedit_length; pcb.todo.draw.text = &text; text.feedback = feedback; if (len > 0) { Xutf8TextListToTextProperty (GDK_DISPLAY (), (char **)&preedit_string, 1, XCompoundTextStyle, &tp); text.encoding_is_wchar = 0; text.length = strlen ((char*)tp.value); text.string.multi_byte = (char*)tp.value; IMCallCallback (xims, (XPointer) & pcb); XFree (tp.value); } else { text.encoding_is_wchar = 0; text.length = 0; text.string.multi_byte = ""; IMCallCallback (xims, (XPointer) & pcb); len = 0; } x11ic->onspot_preedit_length = len; } static int _xim_store_ic_values (X11IC *x11ic, IMChangeICStruct *call_data) { XICAttribute *ic_attr = call_data->ic_attr; XICAttribute *pre_attr = call_data->preedit_attr; XICAttribute *sts_attr = call_data->status_attr; gint i; guint32 attrs = 1; g_return_val_if_fail (x11ic != NULL, 0); for (i=0; i< (int) call_data->ic_attr_num; ++i, ++ic_attr) { if (g_strcmp0 (XNInputStyle, ic_attr->name) == 0) { x11ic->input_style = *(gint32 *) ic_attr->value; } else if (g_strcmp0 (XNClientWindow, ic_attr->name) == 0) { x11ic->client_window = *(Window *) call_data->ic_attr[i].value; } else if (g_strcmp0 (XNFocusWindow, ic_attr->name) == 0) { x11ic->focus_window = *(Window *) call_data->ic_attr[i].value; } else { LOG (1, "Unknown ic attribute: %s", ic_attr->name); } } for (i=0; i< (int) call_data->preedit_attr_num; ++i, ++pre_attr) { if (g_strcmp0 (XNSpotLocation, pre_attr->name) == 0) { x11ic->has_preedit_area = TRUE; x11ic->preedit_area.x = ((XPoint *)pre_attr->value)->x; x11ic->preedit_area.y = ((XPoint *)pre_attr->value)->y; } else { LOG (1, "Unknown preedit attribute: %s", pre_attr->name); } } for (i=0; i< (int) call_data->status_attr_num; ++i, ++sts_attr) { LOG (1, "Unkown status attribute: %s", sts_attr->name); } return attrs; } static int xim_create_ic (XIMS xims, IMChangeICStruct *call_data) { static int base_icid = 1; X11IC *x11ic; int i; call_data->icid = base_icid ++; LOG (1, "XIM_CREATE_IC ic=%d connect_id=%d", call_data->icid, call_data->connect_id); x11ic = g_slice_new0 (X11IC); g_return_val_if_fail (x11ic != NULL, 0); x11ic->icid = call_data->icid; x11ic->connect_id = call_data->connect_id; x11ic->conn = (X11ICONN *)g_hash_table_lookup (_connections, (gconstpointer)(unsigned long)call_data->connect_id); if (x11ic->conn == NULL) { g_slice_free (X11IC, x11ic); g_return_val_if_reached (0); } i = _xim_store_ic_values (x11ic, call_data); x11ic->context = ibus_bus_create_input_context (_bus, "xim"); if (x11ic->context == NULL) { g_slice_free (X11IC, x11ic); g_return_val_if_reached (0); } g_signal_connect (x11ic->context, "commit-text", G_CALLBACK (_context_commit_text_cb), x11ic); g_signal_connect (x11ic->context, "forward-key-event", G_CALLBACK (_context_forward_key_event_cb), x11ic); g_signal_connect (x11ic->context, "update-preedit-text", G_CALLBACK (_context_update_preedit_text_cb), x11ic); g_signal_connect (x11ic->context, "show-preedit-text", G_CALLBACK (_context_show_preedit_text_cb), x11ic); g_signal_connect (x11ic->context, "hide-preedit-text", G_CALLBACK (_context_hide_preedit_text_cb), x11ic); g_signal_connect (x11ic->context, "enabled", G_CALLBACK (_context_enabled_cb), x11ic); g_signal_connect (x11ic->context, "disabled", G_CALLBACK (_context_disabled_cb), x11ic); if (x11ic->input_style & XIMPreeditCallbacks) { ibus_input_context_set_capabilities (x11ic->context, IBUS_CAP_FOCUS | IBUS_CAP_PREEDIT_TEXT); } else { ibus_input_context_set_capabilities (x11ic->context, IBUS_CAP_FOCUS); } g_hash_table_insert (_x11_ic_table, (gpointer)x11ic->icid, (gpointer)x11ic); x11ic->conn->clients = g_list_append (x11ic->conn->clients, (gpointer)x11ic); return 1; } static int xim_destroy_ic (XIMS xims, IMChangeICStruct *call_data) { X11IC *x11ic; LOG (1, "XIM_DESTROY_IC ic=%d connect_id=%d", call_data->icid, call_data->connect_id); x11ic = (X11IC *)g_hash_table_lookup (_x11_ic_table, (gconstpointer)(unsigned long)call_data->icid); g_return_val_if_fail (x11ic != NULL, 0); if (x11ic->context) { ibus_object_destroy ((IBusObject *)x11ic->context); g_object_unref (x11ic->context); x11ic->context = NULL; } g_hash_table_remove (_x11_ic_table, (gconstpointer)(unsigned long)call_data->icid); x11ic->conn->clients = g_list_remove (x11ic->conn->clients, (gconstpointer)x11ic); g_free (x11ic->preedit_string); x11ic->preedit_string = NULL; if (x11ic->preedit_attrs) { g_object_unref (x11ic->preedit_attrs); x11ic->preedit_attrs = NULL; } g_slice_free (X11IC, x11ic); return 1; } static int xim_set_ic_focus (XIMS xims, IMChangeFocusStruct *call_data) { X11IC *x11ic; LOG (1, "XIM_SET_IC_FOCUS ic=%d connect_id=%d", call_data->icid, call_data->connect_id); x11ic = (X11IC *)g_hash_table_lookup (_x11_ic_table, (gconstpointer)(unsigned long)call_data->icid); g_return_val_if_fail (x11ic != NULL, 0); ibus_input_context_focus_in (x11ic->context); _xim_set_cursor_location (x11ic); return 1; } static int xim_unset_ic_focus (XIMS xims, IMChangeFocusStruct *call_data) { X11IC *x11ic; LOG (1, "XIM_UNSET_IC_FOCUS ic=%d connect_id=%d", call_data->icid, call_data->connect_id); x11ic = (X11IC *)g_hash_table_lookup (_x11_ic_table, (gconstpointer)(unsigned long)call_data->icid); g_return_val_if_fail (x11ic != NULL, 0); ibus_input_context_focus_out (x11ic->context); return 1; } static int xim_forward_event (XIMS xims, IMForwardEventStruct *call_data) { X11IC *x11ic; XKeyEvent *xevent; GdkEventKey event; LOG (1, "XIM_FORWARD_EVENT ic=%d connect_id=%d", call_data->icid, call_data->connect_id); x11ic = (X11IC *)g_hash_table_lookup (_x11_ic_table, (gconstpointer)(unsigned long)call_data->icid); g_return_val_if_fail (x11ic != NULL, 0); xevent = (XKeyEvent*) &(call_data->event); translate_key_event (gdk_display_get_default (), (GdkEvent *)&event, (XEvent *)xevent); event.send_event = xevent->send_event; event.window = NULL; if (event.type == GDK_KEY_RELEASE) { event.state |= IBUS_RELEASE_MASK; } if (ibus_input_context_process_key_event (x11ic->context, event.keyval, event.state)) { if (! x11ic->has_preedit_area) { _xim_set_cursor_location (x11ic); } return 1; } IMForwardEventStruct fe; memset (&fe, 0, sizeof (fe)); fe.major_code = XIM_FORWARD_EVENT; fe.icid = x11ic->icid; fe.connect_id = x11ic->connect_id; fe.sync_bit = 0; fe.serial_number = 0L; fe.event = call_data->event; IMForwardEvent (_xims, (XPointer) &fe); return 1; } static int xim_open (XIMS xims, IMOpenStruct *call_data) { X11ICONN *conn; LOG (1, "XIM_OPEN connect_id=%d", call_data->connect_id); conn = (X11ICONN *)g_hash_table_lookup (_connections, (gconstpointer)(unsigned long)call_data->connect_id); g_return_val_if_fail (conn == NULL, 0); conn = g_slice_new0 (X11ICONN); g_hash_table_insert (_connections, (gpointer)(unsigned long)call_data->connect_id, (gpointer) conn); return 1; } static void _free_ic (gpointer data, gpointer user_data) { X11IC *x11ic = (X11IC *) data; g_return_if_fail (x11ic != NULL); g_free (x11ic->preedit_string); if (x11ic->preedit_attrs) { g_object_unref (x11ic->preedit_attrs); x11ic->preedit_attrs = NULL; } if (x11ic->context) { ibus_object_destroy ((IBusObject *)x11ic->context); g_object_unref (x11ic->context); x11ic->context = NULL; } /* Remove the IC from g_client dictionary */ g_hash_table_remove (_x11_ic_table, (gconstpointer)(unsigned long)x11ic->icid); g_slice_free (X11IC, x11ic); } static int xim_close (XIMS ims, IMCloseStruct *call_data) { X11ICONN *conn; LOG (1, "XIM_CLOSE connect_id=%d", call_data->connect_id); conn = (X11ICONN *)g_hash_table_lookup (_connections, (gconstpointer)(unsigned long)call_data->connect_id); g_return_val_if_fail (conn != NULL, 0); g_list_foreach (conn->clients, _free_ic, NULL); g_list_free (conn->clients); g_hash_table_remove (_connections, (gconstpointer)(unsigned long)call_data->connect_id); g_slice_free (X11ICONN, conn); return 1; } static void _xim_set_cursor_location (X11IC *x11ic) { g_return_if_fail (x11ic != NULL); GdkRectangle preedit_area = x11ic->preedit_area; Window w = x11ic->focus_window ? x11ic->focus_window :x11ic->client_window; if (w) { XWindowAttributes xwa; Window child; XGetWindowAttributes (GDK_DISPLAY(), w, &xwa); if (preedit_area.x <= 0 && preedit_area.y <= 0) { XTranslateCoordinates (GDK_DISPLAY(), w, xwa.root, 0, xwa.height, &preedit_area.x, &preedit_area.y, &child); } else { XTranslateCoordinates (GDK_DISPLAY(), w, xwa.root, preedit_area.x, preedit_area.y, &preedit_area.x, &preedit_area.y, &child); } } ibus_input_context_set_cursor_location (x11ic->context, preedit_area.x, preedit_area.y, preedit_area.width, preedit_area.height); } static int xim_set_ic_values (XIMS xims, IMChangeICStruct *call_data) { X11IC *x11ic; gint i; LOG (1, "XIM_SET_IC_VALUES ic=%d connect_id=%d", call_data->icid, call_data->connect_id); x11ic = (X11IC *)g_hash_table_lookup (_x11_ic_table, (gconstpointer)(unsigned long)call_data->icid); g_return_val_if_fail (x11ic != NULL, 0); i = _xim_store_ic_values (x11ic, call_data); if (i) { _xim_set_cursor_location (x11ic); } return 1; } static int xim_get_ic_values (XIMS xims, IMChangeICStruct *call_data) { X11IC *x11ic; gint i; LOG (1, "XIM_GET_IC_VALUES ic=%d connect_id=%d", call_data->icid, call_data->connect_id); x11ic = (X11IC *)g_hash_table_lookup (_x11_ic_table, (gconstpointer)(unsigned long)call_data->icid); g_return_val_if_fail (x11ic != NULL, 0); XICAttribute *ic_attr = call_data->ic_attr; for (i = 0; i < (int) call_data->ic_attr_num; ++i, ++ic_attr) { if (g_strcmp0 (XNFilterEvents, ic_attr->name) == 0) { ic_attr->value = (void *) malloc (sizeof (CARD32)); *(CARD32 *) ic_attr->value = KeyPressMask | KeyReleaseMask; ic_attr->value_length = sizeof (CARD32); } } return 1; } static int xim_reset_ic (XIMS xims, IMResetICStruct *call_data) { X11IC *x11ic; LOG (1, "XIM_RESET_IC ic=%d connect_id=%d", call_data->icid, call_data->connect_id); x11ic = (X11IC *)g_hash_table_lookup (_x11_ic_table, (gconstpointer)(unsigned long)call_data->icid); g_return_val_if_fail (x11ic != NULL, 0); ibus_input_context_reset (x11ic->context); return 1; } int ims_protocol_handler (XIMS xims, IMProtocol *call_data) { g_return_val_if_fail (xims != NULL, 1); g_return_val_if_fail (call_data != NULL, 1); switch (call_data->major_code) { case XIM_OPEN: return xim_open (xims, (IMOpenStruct *)call_data); case XIM_CLOSE: return xim_close (xims, (IMCloseStruct *)call_data); case XIM_CREATE_IC: return xim_create_ic (xims, (IMChangeICStruct *)call_data); case XIM_DESTROY_IC: return xim_destroy_ic (xims, (IMChangeICStruct *)call_data); case XIM_SET_IC_VALUES: return xim_set_ic_values (xims, (IMChangeICStruct *)call_data); case XIM_GET_IC_VALUES: return xim_get_ic_values (xims, (IMChangeICStruct *)call_data); case XIM_FORWARD_EVENT: return xim_forward_event (xims, (IMForwardEventStruct *)call_data); case XIM_SET_IC_FOCUS: return xim_set_ic_focus (xims, (IMChangeFocusStruct *)call_data); case XIM_UNSET_IC_FOCUS: return xim_unset_ic_focus (xims, (IMChangeFocusStruct *)call_data); case XIM_RESET_IC: return xim_reset_ic (xims, (IMResetICStruct *)call_data); case XIM_TRIGGER_NOTIFY: LOG (1, "XIM_TRIGGER_NOTIFY"); return 0; case XIM_PREEDIT_START_REPLY: LOG (1, "XIM_PREEDIT_START_REPLY"); return 0; case XIM_PREEDIT_CARET_REPLY: LOG (1, "XIM_PREEDIT_CARET_REPLY"); return 0; case XIM_SYNC_REPLY: LOG (1, "XIM_SYNC_REPLY"); return 0; default: LOG (1, "Unkown (%d)", call_data->major_code); return 0; } } static void _xim_forward_gdk_event (GdkEventKey *event, X11IC *x11ic) { g_return_if_fail (x11ic != NULL); IMForwardEventStruct fe = {0}; XEvent xkp = {0}; xkp.xkey.type = (event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease; xkp.xkey.serial = 0L; xkp.xkey.send_event = False; xkp.xkey.same_screen = True; xkp.xkey.display = GDK_DISPLAY(); xkp.xkey.window = x11ic->focus_window ? x11ic->focus_window : x11ic->client_window; xkp.xkey.subwindow = None; xkp.xkey.root = DefaultRootWindow (GDK_DISPLAY()); xkp.xkey.time = 0; xkp.xkey.state = event->state; xkp.xkey.keycode = event->hardware_keycode; fe.major_code = XIM_FORWARD_EVENT; fe.icid = x11ic->icid; fe.connect_id = x11ic->connect_id; fe.sync_bit = 0; fe.serial_number = 0L; fe.event = xkp; IMForwardEvent (_xims, (XPointer) & fe); } static void _bus_disconnected_cb (IBusBus *bus, gpointer user_data) { g_warning ("Connection closed by ibus-daemon"); g_object_unref (_bus); _bus = NULL; exit(EXIT_SUCCESS); } static void _context_commit_text_cb (IBusInputContext *context, IBusText *text, X11IC *x11ic) { g_assert (IBUS_IS_INPUT_CONTEXT (context)); g_assert (IBUS_IS_TEXT (text)); g_assert (x11ic != NULL); XTextProperty tp; IMCommitStruct cms = {0}; Xutf8TextListToTextProperty (GDK_DISPLAY (), (gchar **)&(text->text), 1, XCompoundTextStyle, &tp); cms.major_code = XIM_COMMIT; cms.icid = x11ic->icid; cms.connect_id = x11ic->connect_id; cms.flag = XimLookupChars; cms.commit_string = (gchar *)tp.value; IMCommitString (_xims, (XPointer) & cms); XFree (tp.value); } static void _context_forward_key_event_cb (IBusInputContext *context, X11IC *x11ic) { g_assert (x11ic); // _xim_forward_gdk_event (&(event->key), x11ic); } static void _update_preedit (X11IC *x11ic) { if (x11ic->preedit_visible == FALSE && x11ic->preedit_started == TRUE) { _xim_preedit_callback_draw (_xims, x11ic, "", NULL); _xim_preedit_callback_done (_xims, x11ic); x11ic->preedit_started = FALSE; } if (x11ic->preedit_visible == TRUE && x11ic->preedit_started == FALSE) { _xim_preedit_callback_start (_xims, x11ic); x11ic->preedit_started = TRUE; } if (x11ic->preedit_visible == TRUE) { _xim_preedit_callback_draw (_xims, x11ic, x11ic->preedit_string, x11ic->preedit_attrs); } } static void _context_update_preedit_text_cb (IBusInputContext *context, IBusText *text, gint cursor_pos, gboolean visible, X11IC *x11ic) { g_assert (IBUS_IS_INPUT_CONTEXT (context)); g_assert (IBUS_IS_TEXT (text)); g_assert (x11ic); if (x11ic->preedit_string) { g_free(x11ic->preedit_string); } x11ic->preedit_string = g_strdup(text->text); if (x11ic->preedit_attrs) { g_object_unref (x11ic->preedit_attrs); } g_object_ref(text->attrs); x11ic->preedit_attrs = text->attrs; x11ic->preedit_cursor = cursor_pos; x11ic->preedit_visible = visible; _update_preedit (x11ic); } static void _context_show_preedit_text_cb (IBusInputContext *context, X11IC *x11ic) { g_assert (IBUS_IS_INPUT_CONTEXT (context)); g_assert (x11ic); x11ic->preedit_visible = TRUE; _update_preedit (x11ic); } static void _context_hide_preedit_text_cb (IBusInputContext *context, X11IC *x11ic) { g_assert (IBUS_IS_INPUT_CONTEXT (context)); g_assert (x11ic); x11ic->preedit_visible = FALSE; _update_preedit (x11ic); } static void _context_enabled_cb (IBusInputContext *context, X11IC *x11ic) { g_assert (IBUS_IS_INPUT_CONTEXT (context)); g_assert (x11ic); _xim_preedit_start (_xims, x11ic); } static void _context_disabled_cb (IBusInputContext *context, X11IC *x11ic) { g_assert (IBUS_IS_INPUT_CONTEXT (context)); g_assert (x11ic); _xim_preedit_end (_xims, x11ic); } static void _init_ibus (void) { if (_bus != NULL) return; ibus_init (); _bus = ibus_bus_new (); if (!ibus_bus_is_connected (_bus)) { g_error ("Can not connect to ibus-daemon!"); } g_signal_connect (_bus, "disconnected", G_CALLBACK (_bus_disconnected_cb), NULL); } static void _xim_init_IMdkit () { #if 0 XIMStyle ims_styles_overspot [] = { XIMPreeditPosition | XIMStatusNothing, XIMPreeditNothing | XIMStatusNothing, XIMPreeditPosition | XIMStatusCallbacks, XIMPreeditNothing | XIMStatusCallbacks, 0 }; #endif XIMStyle ims_styles_onspot [] = { XIMPreeditPosition | XIMStatusNothing, XIMPreeditCallbacks | XIMStatusNothing, XIMPreeditNothing | XIMStatusNothing, XIMPreeditPosition | XIMStatusCallbacks, XIMPreeditCallbacks | XIMStatusCallbacks, XIMPreeditNothing | XIMStatusCallbacks, 0 }; XIMEncoding ims_encodings[] = { "COMPOUND_TEXT", 0 }; GdkWindowAttr window_attr = { title : "ibus-xim", event_mask : GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK, wclass : GDK_INPUT_OUTPUT, window_type : GDK_WINDOW_TOPLEVEL, override_redirect : 1, }; XIMStyles styles; XIMEncodings encodings; GdkWindow *win; win = gdk_window_new (NULL, &window_attr, GDK_WA_TITLE); styles.count_styles = sizeof (ims_styles_onspot)/sizeof (XIMStyle) - 1; styles.supported_styles = ims_styles_onspot; encodings.count_encodings = sizeof (ims_encodings)/sizeof (XIMEncoding) - 1; encodings.supported_encodings = ims_encodings; _xims = IMOpenIM(GDK_DISPLAY(), IMModifiers, "Xi18n", IMServerWindow, GDK_WINDOW_XWINDOW(win), IMServerName, _server_name, IMLocale, _locale, IMServerTransport, "X/", IMInputStyles, &styles, IMEncodingList, &encodings, IMProtocolHandler, ims_protocol_handler, IMFilterEventMask, KeyPressMask | KeyReleaseMask, NULL); _init_ibus (); if (!ibus_bus_is_connected (_bus)) { g_warning ("Can not connect to ibus daemon"); exit (1); } } static void _atexit_cb () { if (_bus) { ibus_bus_kill(_bus); } } static void _sighandler (int sig) { exit(EXIT_FAILURE); } static void _print_usage (FILE *fp, gchar *name) { fprintf (fp, "Usage:\n" " %s --help Show this message\n" " --server-name= -n Setup xim sevrer name\n" " --locale= -l Setup support locale\n" " --kill-daemon -k Kill ibus daemon when exit\n" " --debug= -v Setup debug level\n", name); } static int _xerror_handler (Display *dpy, XErrorEvent *e) { g_debug ( "XError: " "serial=%lu error_code=%d request_code=%d minor_code=%d resourceid=%lu", e->serial, e->error_code, e->request_code, e->minor_code, e->resourceid); return 1; } int main (int argc, char **argv) { gint option_index = 0; gint c; gtk_init (&argc, &argv); XSetErrorHandler (_xerror_handler); while (1) { static struct option long_options [] = { {"debug", 1, 0, 0}, {"server-name", 1, 0, 0}, {"locale", 1, 0, 0}, {"help", 0, 0, 0}, {"kill-daemon", 0, 0, 0}, {0, 0, 0, 0}, }; c = getopt_long (argc, argv, "v:n:l:k", long_options, &option_index); if (c == -1) break; switch (c) { case 0: if (g_strcmp0 (long_options[option_index].name, "debug") == 0) { g_debug_level = atoi (optarg); } else if (g_strcmp0 (long_options[option_index].name, "server-name") == 0) { strncpy (_server_name, optarg, sizeof (_server_name)); } else if (g_strcmp0 (long_options[option_index].name, "locale") == 0) { strncpy (_locale, optarg, sizeof (_locale)); } else if (g_strcmp0 (long_options[option_index].name, "help") == 0) { _print_usage (stdout, argv[0]); exit (EXIT_SUCCESS); } else if (g_strcmp0 (long_options[option_index].name, "kill-daemon") == 0) { _kill_daemon = TRUE; } break; case 'v': g_debug_level = atoi (optarg); break; case 'n': strncpy (_server_name, optarg, sizeof (_server_name)); break; case 'l': strncpy (_locale, optarg, sizeof (_locale)); break; case 'k': _kill_daemon = TRUE; break; case '?': default: _print_usage (stderr, argv[0]); exit (EXIT_FAILURE); } } _x11_ic_table = g_hash_table_new (g_direct_hash, g_direct_equal); _connections = g_hash_table_new (g_direct_hash, g_direct_equal); signal (SIGINT, _sighandler); signal (SIGTERM, _sighandler); if (_kill_daemon) g_atexit (_atexit_cb); _xim_init_IMdkit (); gtk_main(); exit (EXIT_SUCCESS); }