void scim_bridge_client_imcontext_finalize (GObject *object)
{
    scim_bridge_pdebugln (5, "scim_bridge_client_imcontext_finalize ()");

    ScimBridgeClientIMContext *imcontext = SCIM_BRIDGE_CLIENT_IMCONTEXT (object);

    if (imcontext == focused_imcontext) scim_bridge_client_imcontext_focus_out (GTK_IM_CONTEXT (imcontext));

    if (!scim_bridge_client_is_messenger_opened ()) {
        scim_bridge_perrorln ("The messenger is now down");
    } else if (scim_bridge_client_deregister_imcontext (imcontext)) {
        scim_bridge_perrorln ("Failed to deregister an IMContext");
    } else {
        scim_bridge_pdebugln (3, "IMContext deregistered: id = %d", imcontext->id);
    }

    if (imcontext->client_window) g_object_unref (imcontext->client_window);

    free (imcontext->preedit_string);
    free (imcontext->commit_string);

    if (imcontext->preedit_attributes != NULL)
        pango_attr_list_unref (imcontext->preedit_attributes);
    
    imcontext->preedit_attributes = NULL;
    
    root_klass->finalize (object);
}
void scim_bridge_client_imcontext_initialize (ScimBridgeClientIMContext *imcontext, ScimBridgeClientIMContextClass *klass)
{
    scim_bridge_pdebugln (5, "scim_bridge_client_imcontext_initialize  ()");

    imcontext->preedit_shown = FALSE;
    imcontext->preedit_started = FALSE;
    
    imcontext->preedit_cursor_position = 0;
    imcontext->preedit_cursor_flicking = FALSE;

    imcontext->preedit_string = malloc (sizeof (char));
    imcontext->preedit_string[0] = '\0';
    imcontext->preedit_string_capacity = 0;

    imcontext->preedit_attributes = NULL;

    imcontext->commit_string = malloc (sizeof (char));
    imcontext->commit_string[0] = '\0';
    imcontext->commit_string_capacity = 0;

    imcontext->enabled = FALSE;

    imcontext->client_window = NULL;

    imcontext->id = -1;

    if (!scim_bridge_client_is_messenger_opened ()) {
        scim_bridge_perrorln ("The messenger is now down");
    } else if (scim_bridge_client_register_imcontext (imcontext)) {
        scim_bridge_perrorln ("Failed to register the IMContext");
    } else {
        scim_bridge_pdebugln (1, "IMContext registered: id = %d", imcontext->id);
    }
}
/* Class functions */
gboolean scim_bridge_client_imcontext_filter_key_event (ClutterIMContext *context, ClutterKeyEvent *event)
{
    scim_bridge_pdebugln (8, "scim_bridge_client_imcontext_filter_key_event ()");

    ScimBridgeClientIMContext *imcontext = SCIM_BRIDGE_CLIENT_IMCONTEXT (context);

    if (scim_bridge_client_is_messenger_opened () && imcontext != NULL ) {
        if (context->actor != NULL) {
	    ClutterActor *stage = clutter_actor_get_stage (context->actor);
	    Window current_window, root, parent, *childs;
	    unsigned int nchild;
	    XWindowAttributes winattr;
	    Display *xdpy;
            int new_window_x;
            int new_window_y;

            clutter_actor_get_transformed_position (context->actor, &new_window_x, &new_window_y);
	    xdpy = clutter_x11_get_default_display ();
	    current_window = clutter_x11_get_stage_window(CLUTTER_STAGE(stage));

	    while(1) {
                XGetWindowAttributes (xdpy, current_window, &winattr);
                new_window_x += winattr.x;
                new_window_y += winattr.y;

                XQueryTree(xdpy, current_window, &root, &parent, &childs, &nchild);
                current_window = parent;
                if (root == parent)
                    break;
            }

            if (imcontext->window_x != new_window_x || imcontext->window_y != new_window_y) {
                imcontext->window_x = new_window_x;
                imcontext->window_y = new_window_y;

                scim_bridge_pdebugln (1,
                    "The cursor location is changed: x = %d + %d\ty = %d + %d",
                    imcontext->window_x, imcontext->cursor_x, imcontext->window_y, imcontext->cursor_y);

                if (set_cursor_location (imcontext, new_window_x, new_window_y, imcontext->cursor_x, imcontext->cursor_y)) {
                    scim_bridge_perrorln ("An IOException occurred at scim_bridge_client_imcontext_filter_key_event ()");
                    return clutter_im_context_filter_keypress (fallback_imcontext, event);
                }
            }
        }

        boolean consumed = FALSE;
        if (filter_key_event (imcontext, event, &consumed)) {
            scim_bridge_perrorln ("An IOException occurred at scim_bridge_client_imcontext_filter_key_event ()");
        } else if (consumed) {
            return TRUE;
        }
    }

    if (imcontext == NULL || !imcontext->enabled) {
        return clutter_im_context_filter_keypress (fallback_imcontext, event);
    }

    return FALSE;
}
static retval_t filter_key_event (ScimBridgeClientIMContext *imcontext, ClutterKeyEvent *event, boolean *consumed)
{
    scim_bridge_pdebugln (5, "filter_key_event ()");

    if (focused_imcontext != imcontext) scim_bridge_client_imcontext_focus_in (CLUTTER_IM_CONTEXT (imcontext));

/* if the source is null, then it's the event we forward out, and we do not handle it again */
    if (clutter_event_get_source ((ClutterEvent*) event) == NULL)
	return RETVAL_SUCCEEDED;

    focused_actor = clutter_event_get_source ((ClutterEvent*) event);

    if (scim_bridge_client_is_messenger_opened ()) {
        ScimBridgeKeyEvent *bridge_key_event = scim_bridge_alloc_key_event ();
        scim_bridge_key_event_clutter_to_bridge (bridge_key_event, imcontext->client_stage, event);

        *consumed = FALSE;
        const retval_t retval_error = scim_bridge_client_handle_key_event (imcontext, bridge_key_event, consumed);
        scim_bridge_free_key_event (bridge_key_event);

        if (retval_error) {
            scim_bridge_perrorln ("An IOException at filter_key_event ()");
        } else {
            return RETVAL_SUCCEEDED;
        }
    }

    return RETVAL_FAILED;
}
}


void scim_bridge_client_imcontext_focus_out (GtkIMContext *context)
{
    scim_bridge_pdebugln (8, "scim_bridge_client_imcontext_focus_out ()");

    ScimBridgeClientIMContext *imcontext = SCIM_BRIDGE_CLIENT_IMCONTEXT (context);
    focused_widget = NULL;

    focused_imcontext = imcontext;
    if (imcontext->preedit_shown) {
        if (imcontext->enabled) {
            scim_bridge_client_imcontext_set_preedit_shown (imcontext, FALSE);
            scim_bridge_client_imcontext_update_preedit (imcontext);
        } else {
            gtk_im_context_reset (GTK_IM_CONTEXT (fallback_imcontext));
        }
    }
    if (scim_bridge_client_is_messenger_opened () && imcontext != NULL) {
        if (scim_bridge_client_change_focus (imcontext, FALSE)) {
            scim_bridge_perrorln ("An IOException occurred at scim_bridge_client_imcontext_focus_out ()");
        }
    }
    if (key_snooper_used) {
        gtk_key_snooper_remove (key_snooper_id);
        key_snooper_id = 0;
        key_snooper_used = FALSE;
    }
static gboolean key_snooper (GtkWidget *widget, GdkEventKey *event, gpointer data)
{
    scim_bridge_pdebugln (7, "key_snooper ()");

    if (!(event->send_event & SEND_EVENT_MASK) && scim_bridge_client_is_messenger_opened () && focused_imcontext != NULL) {
        if (focused_imcontext->client_window != NULL) {
            int new_window_x;
            int new_window_y;
            gdk_window_get_origin (focused_imcontext->client_window, &new_window_x, &new_window_y);

            if (focused_imcontext->window_x != new_window_x || focused_imcontext->window_y != new_window_y) {
                if (set_cursor_location (focused_imcontext, new_window_x, new_window_y, focused_imcontext->cursor_x, focused_imcontext->cursor_y)) {
                    scim_bridge_perrorln ("An IOException at key_snooper ()");
                    return FALSE;
                }
            }
        }

        boolean consumed = FALSE;
        if (filter_key_event (focused_imcontext, event, &consumed)) {
            scim_bridge_perrorln ("An IOException at key_snooper ()");
            return FALSE;
        } else {
            if (consumed)
                return TRUE;
        }
    }

    return FALSE;
}
static retval_t set_cursor_location (ScimBridgeClientIMContext *imcontext, int window_x, int window_y, int cursor_x, int cursor_y)
{
    scim_bridge_pdebugln (5, "set_cursor_location ()");

    if (imcontext->window_x == window_x && imcontext->window_y == window_y && imcontext->cursor_x == cursor_x && imcontext->cursor_y == cursor_y) {
        return RETVAL_SUCCEEDED;
    } else {
        imcontext->cursor_x = cursor_x;
        imcontext->cursor_y = cursor_y;
        imcontext->window_x = window_x;
        imcontext->window_y = window_y;

        scim_bridge_pdebugln (3, "The cursor location is changed: x = %d + %d\ty = %d + %d", imcontext->window_x, imcontext->cursor_x, imcontext->window_y, imcontext->cursor_y);

        if (scim_bridge_client_is_messenger_opened ()) {
            if (scim_bridge_client_set_cursor_location (imcontext, imcontext->window_x + imcontext->cursor_x, imcontext->window_y + imcontext->cursor_y)) {
                scim_bridge_perrorln ("An IOException occurred at set_cursor_location ()");
                return RETVAL_FAILED;
            } else {
                return RETVAL_SUCCEEDED;
            }
        }
    }

    return RETVAL_FAILED;
}
void scim_bridge_client_imcontext_initialize (ScimBridgeClientIMContext *imcontext, ScimBridgeClientIMContextClass *klass)
{
    scim_bridge_pdebugln (5, "scim_bridge_client_imcontext_initialize  ()");

    /* slave exists for using gtk+'s table based input method */
    imcontext->slave_preedit = FALSE;
    imcontext->slave = gtk_im_context_simple_new ();
    g_signal_connect(G_OBJECT(imcontext->slave),
                     "commit",
                     G_CALLBACK(gtk_im_slave_commit_cb),
                     imcontext);
    
    g_signal_connect(G_OBJECT(imcontext->slave),
                     "preedit-changed",
                     G_CALLBACK(gtk_im_slave_preedit_changed_cb),
                     imcontext);
    
    g_signal_connect(G_OBJECT(imcontext->slave),
                     "preedit-start",
                     G_CALLBACK(gtk_im_slave_preedit_start_cb),
                     imcontext);
    
    g_signal_connect(G_OBJECT(imcontext->slave),
                     "preedit-end",
                     G_CALLBACK(gtk_im_slave_preedit_end_cb),
                     imcontext);

    imcontext->preedit_shown = FALSE;
    imcontext->preedit_started = FALSE;
    
    imcontext->preedit_cursor_position = 0;
    imcontext->preedit_cursor_flicking = FALSE;

    imcontext->preedit_string = malloc (sizeof (char));
    imcontext->preedit_string[0] = '\0';
    imcontext->preedit_string_capacity = 0;

    imcontext->preedit_attributes = NULL;

    imcontext->commit_string = malloc (sizeof (char));
    imcontext->commit_string[0] = '\0';
    imcontext->commit_string_capacity = 0;

    imcontext->enabled = FALSE;

    imcontext->client_window = NULL;

    imcontext->id = -1;

    if (!scim_bridge_client_is_messenger_opened ()) {
        scim_bridge_perrorln ("The messenger is now down");
    } else if (scim_bridge_client_register_imcontext (imcontext)) {
        scim_bridge_perrorln ("Failed to register the IMContext");
    } else {
        scim_bridge_pdebugln (1, "IMContext registered: id = %d", imcontext->id);
    }
}
void scim_bridge_client_imcontext_focus_in (ClutterIMContext *context)
{
    scim_bridge_pdebugln (8, "scim_bridge_client_imcontext_focus_in ()");

    ScimBridgeClientIMContext *imcontext = SCIM_BRIDGE_CLIENT_IMCONTEXT (context);

    if (focused_imcontext != NULL && focused_imcontext != imcontext) scim_bridge_client_imcontext_focus_out (CLUTTER_IM_CONTEXT (focused_imcontext));
    focused_imcontext = imcontext;

    if (!scim_bridge_client_is_messenger_opened () && scim_bridge_client_is_reconnection_enabled ()) {
        scim_bridge_pdebugln (7, "Trying to open the connection again...");
        scim_bridge_client_open_messenger ();
    }

    if (scim_bridge_client_is_messenger_opened () && imcontext != NULL) {
        if (scim_bridge_client_change_focus (imcontext, TRUE)) {
            scim_bridge_perrorln ("An IOException occurred at scim_bridge_client_imcontext_focus_in ()");
        }
    }
}
void scim_bridge_client_imcontext_show(ClutterIMContext *context)
{
    scim_bridge_pdebugln (8, "scim_bridge_client_imcontext_show ()");

    ScimBridgeClientIMContext *imcontext = SCIM_BRIDGE_CLIENT_IMCONTEXT (context);

    if (scim_bridge_client_is_messenger_opened () && imcontext != NULL && !imcontext->enabled) {
		scim_bridge_client_imcontext_focus_in (context);
        if (scim_bridge_client_set_imcontext_enabled (imcontext, TRUE)) {
            scim_bridge_perrorln ("An IOException occurred at scim_bridge_client_imcontext_show ()");
        }
    }
}
}


void scim_bridge_client_imcontext_set_preedit_enabled (GtkIMContext *context, gboolean enabled)
{
    scim_bridge_pdebugln (8, "scim_bridge_client_imcontext_set_preedit_enabled ()");

    ScimBridgeClientIMContext *imcontext = SCIM_BRIDGE_CLIENT_IMCONTEXT (context);

    if (imcontext != NULL) {
        if (scim_bridge_client_is_messenger_opened ()) {
            if (scim_bridge_client_set_preedit_mode (imcontext, enabled ? PREEDIT_EMBEDDED:PREEDIT_ANY)) {
                scim_bridge_perrorln ("An IOException occurred at scim_bridge_client_imcontext_set_preedit_enabled ()");
            }
}


void scim_bridge_client_imcontext_reset (GtkIMContext *context)
{
    scim_bridge_pdebugln (8, "scim_bridge_client_imcontext_reset ()");

    ScimBridgeClientIMContext *imcontext = SCIM_BRIDGE_CLIENT_IMCONTEXT (context);

    if (imcontext != focused_imcontext) return;

    if (scim_bridge_client_is_messenger_opened () && imcontext != NULL) {
        if (scim_bridge_client_reset_imcontext (imcontext)) {
            scim_bridge_perrorln ("An IOException occurred at scim_bridge_client_imcontext_reset ()");
/* Class functions */
gboolean scim_bridge_client_imcontext_filter_key_event (GtkIMContext *context, GdkEventKey *event)
{
    scim_bridge_pdebugln (8, "scim_bridge_client_imcontext_filter_key_event ()");

    ScimBridgeClientIMContext *imcontext = SCIM_BRIDGE_CLIENT_IMCONTEXT (context);
    
    if (!(event->send_event & SEND_EVENT_MASK) && scim_bridge_client_is_messenger_opened () && imcontext != NULL && !key_snooper_used) {

        if (imcontext->client_window != NULL) {
            int new_window_x;
            int new_window_y;
            gdk_window_get_origin (imcontext->client_window, &new_window_x, &new_window_y);

            if (imcontext->window_x != new_window_x || imcontext->window_y != new_window_y) {
                imcontext->window_x = new_window_x;
                imcontext->window_y = new_window_y;

                scim_bridge_pdebugln (1,
                    "The cursor location is changed: x = %d + %d\ty = %d + %d",
                    imcontext->window_x, imcontext->cursor_x, imcontext->window_y, imcontext->cursor_y);

                if (set_cursor_location (imcontext, new_window_x, new_window_y, imcontext->cursor_x, imcontext->cursor_y)) {
                    scim_bridge_perrorln ("An IOException occurred at scim_bridge_client_imcontext_filter_key_event ()");
                    return gtk_im_context_filter_keypress (fallback_imcontext, event);
                }
            }
        }

        boolean consumed = FALSE;
        if (filter_key_event (imcontext, event, &consumed)) {
            scim_bridge_perrorln ("An IOException occurred at scim_bridge_client_imcontext_filter_key_event ()");
        } else if (consumed) {
            return TRUE;
        }
    }

    unsigned int accelerator_mask = (gtk_accelerator_get_default_mod_mask () & ~GDK_SHIFT_MASK);
    if (imcontext == NULL || !imcontext->enabled) {
        return gtk_im_context_filter_keypress (fallback_imcontext, event);
    } else if (event->type == GDK_KEY_PRESS && (event->state & accelerator_mask) == 0) {
        guint32 wchar = gdk_keyval_to_unicode (event->keyval);
        if (wchar != 0) {
            gchar buffer[10];
            const int buffer_length = g_unichar_to_utf8 (wchar, buffer);
            buffer[buffer_length] = '\0';
            g_signal_emit_by_name (focused_imcontext, "commit", &buffer);
            return TRUE;
        }
    }
}


void scim_bridge_client_imcontext_focus_in (GtkIMContext *context)
{
    scim_bridge_pdebugln (8, "scim_bridge_client_imcontext_focus_in ()");

    ScimBridgeClientIMContext *imcontext = SCIM_BRIDGE_CLIENT_IMCONTEXT (context);
    
    if (focused_imcontext != NULL && focused_imcontext != imcontext) scim_bridge_client_imcontext_focus_out (GTK_IM_CONTEXT (focused_imcontext));
    focused_imcontext = imcontext;

    if (!scim_bridge_client_is_messenger_opened () && scim_bridge_client_is_reconnection_enabled ()) {
        scim_bridge_pdebugln (7, "Trying to open the connection again...");
        scim_bridge_client_open_messenger ();
    }

    if (!key_snooper_used && is_key_snooper_enabled ()) {
        key_snooper_id = gtk_key_snooper_install ((GtkKeySnoopFunc) &key_snooper, NULL);
        key_snooper_used = TRUE;
    }
    if (scim_bridge_client_is_messenger_opened () && imcontext != NULL) {
        if (scim_bridge_client_change_focus (imcontext, TRUE)) {
            scim_bridge_perrorln ("An IOException occurred at scim_bridge_client_imcontext_focus_in ()");
void scim_bridge_client_imcontext_get_preedit_string (GtkIMContext *context, gchar **str, PangoAttrList **pango_attrs, gint *cursor_pos)
{
    scim_bridge_pdebugln (4, "scim_bridge_client_imcontext_get_preedit_string ()");

    ScimBridgeClientIMContext *imcontext = SCIM_BRIDGE_CLIENT_IMCONTEXT (context);

    if (imcontext->slave_preedit) {
        gtk_im_context_get_preedit_string (imcontext->slave, str, pango_attrs, cursor_pos);
        return;
    }
    
    if (scim_bridge_client_is_messenger_opened () && imcontext != NULL && imcontext->preedit_shown) {
        const size_t preedit_string_length = strlen (imcontext->preedit_string);
        const size_t preedit_wstring_length = g_utf8_strlen (imcontext->preedit_string, -1);
        
        if (str) {
            if (preedit_string_length > 0) {
                *str = g_strdup (imcontext->preedit_string);
            } else {
                *str = g_strdup ("");
            }
        }
        
        if (cursor_pos) {
            if (imcontext->preedit_cursor_position > preedit_wstring_length) {
                *cursor_pos = preedit_wstring_length;
            } else {
                *cursor_pos = imcontext->preedit_cursor_position;
            }
        }
        
        if (pango_attrs) {
            *pango_attrs = imcontext->preedit_attributes;
            pango_attr_list_ref (imcontext->preedit_attributes);
        }
    } else {
        if (str) *str = g_strdup ("");
        if (cursor_pos) *cursor_pos = 0;
        if (pango_attrs) *pango_attrs = pango_attr_list_new ();
    }
}
static gboolean key_snooper (GtkWidget *widget, GdkEventKey *event, gpointer data)
{
    scim_bridge_pdebugln (7, "key_snooper ()");

    if (focused_imcontext && scim_bridge_client_is_messenger_opened () &&
        (event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE) &&
        !(event->send_event & SEND_EVENT_MASK)) {
        if (focused_imcontext->client_window) {
            int new_window_x;
            int new_window_y;
            gdk_window_get_origin (focused_imcontext->client_window, &new_window_x, &new_window_y);

            if (focused_imcontext->window_x != new_window_x || focused_imcontext->window_y != new_window_y) {

                scim_bridge_pdebugln (1,
                    "The cursor location is changed: x = %d + %d\ty = %d + %d",
                    new_window_x, focused_imcontext->cursor_x, new_window_y, focused_imcontext->cursor_y);

                if (set_cursor_location (focused_imcontext, new_window_x, new_window_y, focused_imcontext->cursor_x, focused_imcontext->cursor_y)) {
                    scim_bridge_perrorln ("An IOException at key_snooper ()");
                    return FALSE;
                }
            }
        }

        boolean consumed = FALSE;
        if (filter_key_event (focused_imcontext, event, &consumed)) {
            scim_bridge_perrorln ("An IOException at key_snooper ()");
            return FALSE;
        } else {
            if (consumed) {
                g_signal_emit_by_name (focused_imcontext, "preedit-changed");
                return TRUE;
            }
        }
    }

    return FALSE;
}
void scim_bridge_client_imcontext_focus_out (ClutterIMContext *context)
{
    scim_bridge_pdebugln (8, "scim_bridge_client_imcontext_focus_out ()");

    ScimBridgeClientIMContext *imcontext = SCIM_BRIDGE_CLIENT_IMCONTEXT (context);
    focused_actor = NULL;

    focused_imcontext = imcontext;
    if (imcontext->preedit_shown) {
        if (imcontext->enabled) {
            scim_bridge_client_imcontext_set_preedit_shown (imcontext, FALSE);
            scim_bridge_client_imcontext_update_preedit (imcontext);
        } else {
            clutter_im_context_reset (CLUTTER_IM_CONTEXT (fallback_imcontext));
        }
    }
    if (scim_bridge_client_is_messenger_opened () && imcontext != NULL) {
        if (scim_bridge_client_change_focus (imcontext, FALSE)) {
            scim_bridge_perrorln ("An IOException occurred at scim_bridge_client_imcontext_focus_out ()");
        }
    }

    focused_imcontext = NULL;
}
static retval_t filter_key_event (ScimBridgeClientIMContext *imcontext, GdkEventKey *event, boolean *consumed)
{
    scim_bridge_pdebugln (5, "filter_key_event ()");
    
    if (focused_imcontext != imcontext) scim_bridge_client_imcontext_focus_in (GTK_IM_CONTEXT (imcontext));
    focused_widget = gtk_get_event_widget ((GdkEvent*) event);
    
    if (scim_bridge_client_is_messenger_opened ()) {
        ScimBridgeKeyEvent *bridge_key_event = scim_bridge_alloc_key_event ();
        scim_bridge_key_event_gdk_to_bridge (bridge_key_event, imcontext->client_window, event);

        *consumed = FALSE;
        const retval_t retval_error = scim_bridge_client_handle_key_event (imcontext, bridge_key_event, consumed);
        scim_bridge_free_key_event (bridge_key_event);

        if (retval_error) {
            scim_bridge_perrorln ("An IOException at filter_key_event ()");
        } else {
            return RETVAL_SUCCEEDED;
        }
    }

    return RETVAL_FAILED;
}