Blame client/gtk3/ibusimcontext.c

Packit 3ff832
/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
Packit 3ff832
/* vim:set et sts=4: */
Packit 3ff832
/* ibus - The Input Bus
Packit 3ff832
 * Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
Packit Service fd1217
 * Copyright (C) 2015-2019 Takao Fujiwara <takao.fujiwara1@gmail.com>
Packit Service fd1217
 * Copyright (C) 2008-2019 Red Hat, Inc.
Packit 3ff832
 *
Packit 3ff832
 * This library is free software; you can redistribute it and/or
Packit 3ff832
 * modify it under the terms of the GNU Lesser General Public
Packit 3ff832
 * License as published by the Free Software Foundation; either
Packit 3ff832
 * version 2.1 of the License, or (at your option) any later version.
Packit 3ff832
 *
Packit 3ff832
 * This library is distributed in the hope that it will be useful,
Packit 3ff832
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 3ff832
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 3ff832
 * Lesser General Public License for more details.
Packit 3ff832
 *
Packit 3ff832
 * You should have received a copy of the GNU Lesser General Public
Packit 3ff832
 * License along with this library; if not, write to the Free Software
Packit 3ff832
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
Packit 3ff832
 * USA
Packit 3ff832
 */
Packit 3ff832
Packit 3ff832
#ifdef HAVE_CONFIG_H
Packit 3ff832
#  include <config.h>
Packit 3ff832
#endif
Packit 3ff832
Packit 3ff832
#include <string.h>
Packit 3ff832
#include <gtk/gtk.h>
Packit 3ff832
#include <gdk/gdkkeysyms.h>
Packit 3ff832
#include <ibus.h>
Packit 3ff832
#include "ibusimcontext.h"
Packit 3ff832
Packit 3ff832
#ifdef GDK_WINDOWING_WAYLAND
Packit 3ff832
#include <gdk/gdkwayland.h>
Packit 3ff832
#endif
Packit 3ff832
Packit 3ff832
#if !GTK_CHECK_VERSION (2, 91, 0)
Packit 3ff832
#  define DEPRECATED_GDK_KEYSYMS 1
Packit 3ff832
#endif
Packit 3ff832
Packit 3ff832
#ifdef DEBUG
Packit 3ff832
#  define IDEBUG g_debug
Packit 3ff832
#else
Packit 3ff832
#  define IDEBUG(a...)
Packit 3ff832
#endif
Packit 3ff832
Packit 3ff832
#define MAX_QUEUED_EVENTS 20
Packit 3ff832
Packit 3ff832
struct _IBusIMContext {
Packit 3ff832
    GtkIMContext parent;
Packit 3ff832
Packit 3ff832
    /* instance members */
Packit 3ff832
    GtkIMContext *slave;
Packit 3ff832
    GdkWindow *client_window;
Packit 3ff832
Packit 3ff832
    IBusInputContext *ibuscontext;
Packit 3ff832
Packit 3ff832
    /* preedit status */
Packit 3ff832
    gchar           *preedit_string;
Packit 3ff832
    PangoAttrList   *preedit_attrs;
Packit 3ff832
    gint             preedit_cursor_pos;
Packit 3ff832
    gboolean         preedit_visible;
Packit Service fd1217
    guint            preedit_mode;
Packit 3ff832
Packit 3ff832
    GdkRectangle     cursor_area;
Packit 3ff832
    gboolean         has_focus;
Packit 3ff832
Packit 3ff832
    guint32          time;
Packit 3ff832
    gint             caps;
Packit 3ff832
Packit 3ff832
    /* cancellable */
Packit 3ff832
    GCancellable    *cancellable;
Packit 3ff832
    GQueue          *events_queue;
Packit Service fd1217
Packit Service fd1217
#if !GTK_CHECK_VERSION (3, 93, 0)
Packit Service fd1217
    gboolean         use_button_press_event;
Packit Service fd1217
#endif
Packit 3ff832
};
Packit 3ff832
Packit 3ff832
struct _IBusIMContextClass {
Packit 3ff832
GtkIMContextClass parent;
Packit 3ff832
    /* class members */
Packit 3ff832
};
Packit 3ff832
Packit 3ff832
static guint    _signal_commit_id = 0;
Packit 3ff832
static guint    _signal_preedit_changed_id = 0;
Packit 3ff832
static guint    _signal_preedit_start_id = 0;
Packit 3ff832
static guint    _signal_preedit_end_id = 0;
Packit 3ff832
static guint    _signal_delete_surrounding_id = 0;
Packit 3ff832
static guint    _signal_retrieve_surrounding_id = 0;
Packit 3ff832
Packit 3ff832
static const gchar *_no_snooper_apps = NO_SNOOPER_APPS;
Packit 3ff832
static gboolean _use_key_snooper = ENABLE_SNOOPER;
Packit 3ff832
static guint    _key_snooper_id = 0;
Packit 3ff832
Packit 3ff832
static gboolean _use_sync_mode = FALSE;
Packit 3ff832
Packit 3ff832
static const gchar *_discard_password_apps  = "";
Packit 3ff832
static gboolean _use_discard_password = FALSE;
Packit 3ff832
Packit 3ff832
static GtkIMContext *_focus_im_context = NULL;
Packit 3ff832
static IBusInputContext *_fake_context = NULL;
Packit 3ff832
static GdkWindow *_input_window = NULL;
Packit 3ff832
static GtkWidget *_input_widget = NULL;
Packit 3ff832
Packit 3ff832
/* functions prototype */
Packit 3ff832
static void     ibus_im_context_class_init  (IBusIMContextClass    *class);
Packit 3ff832
static void     ibus_im_context_class_fini  (IBusIMContextClass    *class);
Packit 3ff832
static void     ibus_im_context_init        (GObject               *obj);
Packit 3ff832
static void     ibus_im_context_notify      (GObject               *obj,
Packit 3ff832
                                             GParamSpec            *pspec);
Packit 3ff832
static void     ibus_im_context_finalize    (GObject               *obj);
Packit 3ff832
static void     ibus_im_context_reset       (GtkIMContext          *context);
Packit 3ff832
static gboolean ibus_im_context_filter_keypress
Packit 3ff832
                                            (GtkIMContext           *context,
Packit 3ff832
                                             GdkEventKey            *key);
Packit 3ff832
static void     ibus_im_context_focus_in    (GtkIMContext          *context);
Packit 3ff832
static void     ibus_im_context_focus_out   (GtkIMContext          *context);
Packit 3ff832
static void     ibus_im_context_get_preedit_string
Packit 3ff832
                                            (GtkIMContext           *context,
Packit 3ff832
                                             gchar                  **str,
Packit 3ff832
                                             PangoAttrList          **attrs,
Packit 3ff832
                                             gint                   *cursor_pos);
Packit 3ff832
static void     ibus_im_context_set_client_window
Packit 3ff832
                                            (GtkIMContext           *context,
Packit 3ff832
                                             GdkWindow              *client);
Packit 3ff832
static void     ibus_im_context_set_cursor_location
Packit 3ff832
                                            (GtkIMContext           *context,
Packit 3ff832
                                             GdkRectangle           *area);
Packit 3ff832
static void     ibus_im_context_set_use_preedit
Packit 3ff832
                                            (GtkIMContext           *context,
Packit 3ff832
                                             gboolean               use_preedit);
Packit 3ff832
static void     ibus_im_context_set_surrounding
Packit 3ff832
                                            (GtkIMContext  *slave,
Packit 3ff832
                                             const gchar   *text,
Packit 3ff832
                                             gint           len,
Packit 3ff832
                                             gint           cursor_index);
Packit 3ff832
Packit 3ff832
/* static methods*/
Packit Service fd1217
static void     _ibus_context_update_preedit_text_cb
Packit Service fd1217
                                            (IBusInputContext   *ibuscontext,
Packit Service fd1217
                                             IBusText           *text,
Packit Service fd1217
                                             gint                cursor_pos,
Packit Service fd1217
                                             gboolean            visible,
Packit Service fd1217
                                             guint               mode,
Packit Service fd1217
                                             IBusIMContext      *ibusimcontext);
Packit 3ff832
static void     _create_input_context       (IBusIMContext      *context);
Packit 3ff832
static gboolean _set_cursor_location_internal
Packit 3ff832
                                            (IBusIMContext      *context);
Packit 3ff832
Packit 3ff832
static void     _bus_connected_cb           (IBusBus            *bus,
Packit 3ff832
                                             IBusIMContext      *context);
Packit 3ff832
/* callback functions for slave context */
Packit 3ff832
static void     _slave_commit_cb            (GtkIMContext       *slave,
Packit 3ff832
                                             gchar              *string,
Packit 3ff832
                                             IBusIMContext       *context);
Packit 3ff832
static void     _slave_preedit_changed_cb   (GtkIMContext       *slave,
Packit 3ff832
                                             IBusIMContext       *context);
Packit 3ff832
static void     _slave_preedit_start_cb     (GtkIMContext       *slave,
Packit 3ff832
                                             IBusIMContext       *context);
Packit 3ff832
static void     _slave_preedit_end_cb       (GtkIMContext       *slave,
Packit 3ff832
                                             IBusIMContext       *context);
Packit 3ff832
static gboolean _slave_retrieve_surrounding_cb
Packit 3ff832
                                            (GtkIMContext       *slave,
Packit 3ff832
                                             IBusIMContext      *context);
Packit 3ff832
static gboolean _slave_delete_surrounding_cb
Packit 3ff832
                                            (GtkIMContext       *slave,
Packit 3ff832
                                             gint                offset_from_cursor,
Packit 3ff832
                                             guint               nchars,
Packit 3ff832
                                             IBusIMContext      *context);
Packit 3ff832
static void     _request_surrounding_text   (IBusIMContext      *context);
Packit 3ff832
static void     _create_fake_input_context  (void);
Packit 3ff832
static gboolean _set_content_type           (IBusIMContext      *context);
Packit 3ff832
Packit 3ff832
Packit 3ff832
static GType                _ibus_type_im_context = 0;
Packit 3ff832
static GtkIMContextClass    *parent_class = NULL;
Packit 3ff832
Packit 3ff832
static IBusBus              *_bus = NULL;
Packit 3ff832
static guint                _daemon_name_watch_id = 0;
Packit 3ff832
static gboolean             _daemon_is_running = FALSE;
Packit 3ff832
Packit 3ff832
void
Packit 3ff832
ibus_im_context_register_type (GTypeModule *type_module)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
Packit 3ff832
    static const GTypeInfo ibus_im_context_info = {
Packit 3ff832
        sizeof (IBusIMContextClass),
Packit 3ff832
        (GBaseInitFunc)      NULL,
Packit 3ff832
        (GBaseFinalizeFunc)  NULL,
Packit 3ff832
        (GClassInitFunc)     ibus_im_context_class_init,
Packit 3ff832
        (GClassFinalizeFunc) ibus_im_context_class_fini,
Packit 3ff832
        NULL,            /* class data */
Packit 3ff832
        sizeof (IBusIMContext),
Packit 3ff832
        0,
Packit 3ff832
        (GInstanceInitFunc)    ibus_im_context_init,
Packit 3ff832
    };
Packit 3ff832
Packit 3ff832
    if (!_ibus_type_im_context) {
Packit 3ff832
        if (type_module) {
Packit 3ff832
            _ibus_type_im_context =
Packit 3ff832
                g_type_module_register_type (type_module,
Packit 3ff832
                    GTK_TYPE_IM_CONTEXT,
Packit 3ff832
                    "IBusIMContext",
Packit 3ff832
                    &ibus_im_context_info,
Packit 3ff832
                    (GTypeFlags)0);
Packit 3ff832
        }
Packit 3ff832
        else {
Packit 3ff832
            _ibus_type_im_context =
Packit 3ff832
                g_type_register_static (GTK_TYPE_IM_CONTEXT,
Packit 3ff832
                    "IBusIMContext",
Packit 3ff832
                    &ibus_im_context_info,
Packit 3ff832
                    (GTypeFlags)0);
Packit 3ff832
        }
Packit 3ff832
    }
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
GType
Packit 3ff832
ibus_im_context_get_type (void)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
Packit 3ff832
    if (_ibus_type_im_context == 0) {
Packit 3ff832
        ibus_im_context_register_type (NULL);
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    g_assert (_ibus_type_im_context != 0);
Packit 3ff832
    return _ibus_type_im_context;
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
IBusIMContext *
Packit 3ff832
ibus_im_context_new (void)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
Packit 3ff832
    GObject *obj = g_object_new (IBUS_TYPE_IM_CONTEXT, NULL);
Packit 3ff832
    return IBUS_IM_CONTEXT (obj);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static gboolean
Packit 3ff832
_focus_in_cb (GtkWidget     *widget,
Packit 3ff832
              GdkEventFocus *event,
Packit 3ff832
              gpointer       user_data)
Packit 3ff832
{
Packit 3ff832
    if (_focus_im_context == NULL && _fake_context != NULL) {
Packit 3ff832
        ibus_input_context_focus_in (_fake_context);
Packit 3ff832
    }
Packit 3ff832
    return FALSE;
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static gboolean
Packit 3ff832
_focus_out_cb (GtkWidget     *widget,
Packit 3ff832
               GdkEventFocus *event,
Packit 3ff832
               gpointer       user_data)
Packit 3ff832
{
Packit 3ff832
    if (_focus_im_context == NULL && _fake_context != NULL) {
Packit 3ff832
        ibus_input_context_focus_out (_fake_context);
Packit 3ff832
    }
Packit 3ff832
    return FALSE;
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static gboolean
Packit 3ff832
ibus_im_context_commit_event (IBusIMContext *ibusimcontext,
Packit 3ff832
                              GdkEventKey   *event)
Packit 3ff832
{
Packit 3ff832
    int i;
Packit 3ff832
    GdkModifierType no_text_input_mask;
Packit 3ff832
    gunichar ch;
Packit 3ff832
Packit 3ff832
    if (event->type == GDK_KEY_RELEASE)
Packit 3ff832
        return FALSE;
Packit 3ff832
    /* Ignore modifier key presses */
Packit 3ff832
    for (i = 0; i < G_N_ELEMENTS (IBUS_COMPOSE_IGNORE_KEYLIST); i++)
Packit 3ff832
        if (event->keyval == IBUS_COMPOSE_IGNORE_KEYLIST[i])
Packit 3ff832
            return FALSE;
Packit 3ff832
#if GTK_CHECK_VERSION (3, 4, 0)
Packit 3ff832
    no_text_input_mask = gdk_keymap_get_modifier_mask (
Packit 3ff832
            gdk_keymap_get_for_display (gdk_display_get_default ()),
Packit 3ff832
            GDK_MODIFIER_INTENT_NO_TEXT_INPUT);
Packit 3ff832
#else
Packit 3ff832
#  ifndef GDK_WINDOWING_QUARTZ
Packit 3ff832
#    define _IBUS_NO_TEXT_INPUT_MOD_MASK (GDK_MOD1_MASK | GDK_CONTROL_MASK)
Packit 3ff832
#  else
Packit 3ff832
#    define _IBUS_NO_TEXT_INPUT_MOD_MASK (GDK_MOD2_MASK | GDK_CONTROL_MASK)
Packit 3ff832
#  endif
Packit 3ff832
Packit 3ff832
    no_text_input_mask = _IBUS_NO_TEXT_INPUT_MOD_MASK;
Packit 3ff832
Packit 3ff832
#  undef _IBUS_NO_TEXT_INPUT_MOD_MASK
Packit 3ff832
#endif
Packit 3ff832
    if (event->state & no_text_input_mask ||
Packit 3ff832
        event->keyval == GDK_KEY_Return ||
Packit 3ff832
        event->keyval == GDK_KEY_ISO_Enter ||
Packit 3ff832
        event->keyval == GDK_KEY_KP_Enter) {
Packit 3ff832
        return FALSE;
Packit 3ff832
    }
Packit 3ff832
    ch = ibus_keyval_to_unicode (event->keyval);
Packit 3ff832
    if (ch != 0 && !g_unichar_iscntrl (ch)) {
Packit 3ff832
        IBusText *text = ibus_text_new_from_unichar (ch);
Packit 3ff832
        g_signal_emit (ibusimcontext, _signal_commit_id, 0, text->text);
Packit 3ff832
        g_object_unref (text);
Packit Service fd1217
        _request_surrounding_text (ibusimcontext);
Packit 3ff832
        return TRUE;
Packit 3ff832
    }
Packit 3ff832
   return FALSE;
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
_process_key_event_done (GObject      *object,
Packit 3ff832
                         GAsyncResult *res,
Packit 3ff832
                         gpointer      user_data)
Packit 3ff832
{
Packit 3ff832
    IBusInputContext *context = (IBusInputContext *)object;
Packit 3ff832
    GdkEventKey *event = (GdkEventKey *) user_data;
Packit 3ff832
    GError *error = NULL;
Packit 3ff832
    gboolean retval = ibus_input_context_process_key_event_async_finish (
Packit 3ff832
            context,
Packit 3ff832
            res,
Packit 3ff832
            &error);
Packit 3ff832
Packit 3ff832
    if (error != NULL) {
Packit 3ff832
        g_warning ("Process Key Event failed: %s.", error->message);
Packit 3ff832
        g_error_free (error);
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    if (retval == FALSE) {
Packit 3ff832
        event->state |= IBUS_IGNORED_MASK;
Packit 3ff832
        gdk_event_put ((GdkEvent *)event);
Packit 3ff832
    }
Packit 3ff832
    gdk_event_free ((GdkEvent *)event);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static gboolean
Packit 3ff832
_process_key_event (IBusInputContext *context,
Packit 3ff832
                    GdkEventKey      *event)
Packit 3ff832
{
Packit 3ff832
    guint state = event->state;
Packit 3ff832
    gboolean retval = FALSE;
Packit 3ff832
Packit 3ff832
    if (event->type == GDK_KEY_RELEASE) {
Packit 3ff832
        state |= IBUS_RELEASE_MASK;
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    if (_use_sync_mode) {
Packit 3ff832
        retval = ibus_input_context_process_key_event (context,
Packit 3ff832
            event->keyval,
Packit 3ff832
            event->hardware_keycode - 8,
Packit 3ff832
            state);
Packit 3ff832
    }
Packit 3ff832
    else {
Packit 3ff832
        ibus_input_context_process_key_event_async (context,
Packit 3ff832
            event->keyval,
Packit 3ff832
            event->hardware_keycode - 8,
Packit 3ff832
            state,
Packit 3ff832
            -1,
Packit 3ff832
            NULL,
Packit 3ff832
            _process_key_event_done,
Packit 3ff832
            gdk_event_copy ((GdkEvent *) event));
Packit 3ff832
Packit 3ff832
        retval = TRUE;
Packit 3ff832
    }
Packit 3ff832
Packit Service fd1217
    if (retval)
Packit 3ff832
        event->state |= IBUS_HANDLED_MASK;
Packit Service fd1217
    else
Packit 3ff832
        event->state |= IBUS_IGNORED_MASK;
Packit 3ff832
Packit 3ff832
    return retval;
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
Packit 3ff832
/* emit "retrieve-surrounding" glib signal of GtkIMContext, if
Packit 3ff832
 * context->caps has IBUS_CAP_SURROUNDING_TEXT and the current IBus
Packit 3ff832
 * engine needs surrounding-text.
Packit 3ff832
 */
Packit 3ff832
static void
Packit 3ff832
_request_surrounding_text (IBusIMContext *context)
Packit 3ff832
{
Packit 3ff832
    if (context &&
Packit 3ff832
        (context->caps & IBUS_CAP_SURROUNDING_TEXT) != 0 &&
Packit 3ff832
        context->ibuscontext != NULL &&
Packit 3ff832
        ibus_input_context_needs_surrounding_text (context->ibuscontext)) {
Packit 3ff832
        gboolean return_value;
Packit 3ff832
        IDEBUG ("requesting surrounding text");
Packit 3ff832
        g_signal_emit (context, _signal_retrieve_surrounding_id, 0,
Packit 3ff832
                       &return_value);
Packit 3ff832
        if (!return_value) {
Packit Service fd1217
            /* #2054 firefox::IMContextWrapper::GetCurrentParagraph() could
Packit Service fd1217
             * fail with the first typing on firefox but it succeeds with
Packit Service fd1217
             * the second typing.
Packit Service fd1217
             */
Packit Service fd1217
            g_warning ("%s has no capability of surrounding-text feature",
Packit Service fd1217
                       g_get_prgname ());
Packit 3ff832
        }
Packit 3ff832
    }
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static gboolean
Packit 3ff832
_set_content_type (IBusIMContext *context)
Packit 3ff832
{
Packit 3ff832
#if GTK_CHECK_VERSION (3, 6, 0)
Packit 3ff832
    if (context->ibuscontext != NULL) {
Packit 3ff832
        GtkInputPurpose purpose;
Packit 3ff832
        GtkInputHints hints;
Packit 3ff832
Packit 3ff832
        g_object_get (G_OBJECT (context),
Packit 3ff832
                      "input-purpose", &purpose,
Packit 3ff832
                      "input-hints", &hints,
Packit 3ff832
                      NULL);
Packit 3ff832
Packit 3ff832
        if (_use_discard_password) {
Packit 3ff832
            if (purpose == GTK_INPUT_PURPOSE_PASSWORD ||
Packit 3ff832
                purpose == GTK_INPUT_PURPOSE_PIN) {
Packit 3ff832
                return FALSE;
Packit 3ff832
            }
Packit 3ff832
        }
Packit 3ff832
        ibus_input_context_set_content_type (context->ibuscontext,
Packit 3ff832
                                             purpose,
Packit 3ff832
                                             hints);
Packit 3ff832
    }
Packit 3ff832
#endif
Packit 3ff832
    return TRUE;
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
Packit 3ff832
static gint
Packit 3ff832
_key_snooper_cb (GtkWidget   *widget,
Packit 3ff832
                 GdkEventKey *event,
Packit 3ff832
                 gpointer     user_data)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
    gboolean retval = FALSE;
Packit 3ff832
Packit 3ff832
    IBusIMContext *ibusimcontext = NULL;
Packit 3ff832
    IBusInputContext *ibuscontext = NULL;
Packit 3ff832
Packit 3ff832
    if (!_use_key_snooper)
Packit 3ff832
        return FALSE;
Packit 3ff832
Packit 3ff832
    if (_focus_im_context != NULL &&
Packit 3ff832
        ((IBusIMContext *) _focus_im_context)->has_focus == TRUE) {
Packit 3ff832
        ibusimcontext = (IBusIMContext *) _focus_im_context;
Packit 3ff832
        /* has IC with focus */
Packit 3ff832
        ibuscontext = ibusimcontext->ibuscontext;
Packit 3ff832
    }
Packit 3ff832
    else {
Packit 3ff832
        /* If no IC has focus, and fake IC has been created, then pass key events to fake IC. */
Packit 3ff832
        ibuscontext = _fake_context;
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    if (ibuscontext == NULL)
Packit 3ff832
        return FALSE;
Packit 3ff832
Packit 3ff832
    if (G_UNLIKELY (event->state & IBUS_HANDLED_MASK))
Packit 3ff832
        return TRUE;
Packit 3ff832
Packit 3ff832
    if (G_UNLIKELY (event->state & IBUS_IGNORED_MASK))
Packit 3ff832
        return FALSE;
Packit 3ff832
Packit 3ff832
    do {
Packit 3ff832
        if (_fake_context != ibuscontext)
Packit 3ff832
            break;
Packit 3ff832
Packit 3ff832
        /* window has input focus is not changed */
Packit 3ff832
        if (_input_window == event->window)
Packit 3ff832
            break;
Packit 3ff832
Packit 3ff832
        if (_input_window != NULL) {
Packit 3ff832
            g_object_remove_weak_pointer ((GObject *) _input_window,
Packit 3ff832
                                          (gpointer *) &_input_window);
Packit 3ff832
        }
Packit 3ff832
        if (event->window != NULL) {
Packit 3ff832
            g_object_add_weak_pointer ((GObject *) event->window,
Packit 3ff832
                                       (gpointer *) &_input_window);
Packit 3ff832
        }
Packit 3ff832
        _input_window = event->window;
Packit 3ff832
Packit 3ff832
        /* Trace widget has input focus, and listen focus events of it.
Packit 3ff832
         * It is workaround for Alt+Shift+Tab shortcut key issue(crosbug.com/8855).
Packit 3ff832
         * gtk_get_event_widget returns the widget that is associated with the
Packit 3ff832
         * GdkWindow of the GdkEvent.
Packit 3ff832
         * */
Packit 3ff832
        GtkWidget *widget = gtk_get_event_widget ((GdkEvent *)event);
Packit 3ff832
        /* g_assert (_input_widget != widget). */
Packit 3ff832
        if (_input_widget == widget)
Packit 3ff832
            break;
Packit 3ff832
Packit 3ff832
        if (_input_widget != NULL) {
Packit 3ff832
            g_signal_handlers_disconnect_by_func (_input_widget,
Packit 3ff832
                                                  (GCallback) _focus_in_cb,
Packit 3ff832
                                                  NULL);
Packit 3ff832
            g_signal_handlers_disconnect_by_func (_input_widget,
Packit 3ff832
                                                  (GCallback) _focus_out_cb,
Packit 3ff832
                                                  NULL);
Packit 3ff832
            g_object_remove_weak_pointer ((GObject *) _input_widget,
Packit 3ff832
                                          (gpointer *) &_input_widget);
Packit 3ff832
        }
Packit 3ff832
Packit 3ff832
        if (widget != NULL) {
Packit 3ff832
            g_signal_connect (widget,
Packit 3ff832
                              "focus-in-event",
Packit 3ff832
                              (GCallback) _focus_in_cb,
Packit 3ff832
                              NULL);
Packit 3ff832
            g_signal_connect (widget,
Packit 3ff832
                              "focus-out-event",
Packit 3ff832
                              (GCallback) _focus_out_cb,
Packit 3ff832
                              NULL);
Packit 3ff832
            g_object_add_weak_pointer ((GObject *) widget,
Packit 3ff832
                                       (gpointer *) &_input_widget);
Packit 3ff832
        }
Packit 3ff832
        _input_widget = widget;
Packit 3ff832
Packit 3ff832
    } while (0);
Packit 3ff832
Packit 3ff832
    if (ibusimcontext != NULL) {
Packit 3ff832
        /* "retrieve-surrounding" signal sometimes calls unref by
Packit 3ff832
         * gtk_im_multicontext_get_slave() because priv->context_id is not
Packit 3ff832
         * the latest than global_context_id in GtkIMMulticontext.
Packit 3ff832
         * Since _focus_im_context is gotten by the focus_in event,
Packit 3ff832
         * it would be good to call ref here.
Packit 3ff832
         */
Packit 3ff832
        g_object_ref (ibusimcontext);
Packit 3ff832
        _request_surrounding_text (ibusimcontext);
Packit 3ff832
        ibusimcontext->time = event->time;
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    retval = _process_key_event (ibuscontext, event);
Packit 3ff832
Packit 3ff832
    if (ibusimcontext != NULL) {
Packit 3ff832
        /* unref ibusimcontext could call ibus_im_context_finalize here
Packit 3ff832
         * because "retrieve-surrounding" signal could call unref.
Packit 3ff832
         */
Packit 3ff832
        g_object_unref (ibusimcontext);
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    return retval;
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static gboolean
Packit 3ff832
_get_boolean_env(const gchar *name,
Packit 3ff832
                 gboolean     defval)
Packit 3ff832
{
Packit 3ff832
    const gchar *value = g_getenv (name);
Packit 3ff832
Packit 3ff832
    if (value == NULL)
Packit 3ff832
      return defval;
Packit 3ff832
Packit 3ff832
    if (g_strcmp0 (value, "") == 0 ||
Packit 3ff832
        g_strcmp0 (value, "0") == 0 ||
Packit 3ff832
        g_strcmp0 (value, "false") == 0 ||
Packit 3ff832
        g_strcmp0 (value, "False") == 0 ||
Packit 3ff832
        g_strcmp0 (value, "FALSE") == 0)
Packit 3ff832
      return FALSE;
Packit 3ff832
Packit 3ff832
    return TRUE;
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
daemon_name_appeared (GDBusConnection *connection,
Packit 3ff832
                      const gchar     *name,
Packit 3ff832
                      const gchar     *owner,
Packit 3ff832
                      gpointer         data)
Packit 3ff832
{
Packit Service fd1217
    if (!g_strcmp0 (ibus_bus_get_service_name (_bus), IBUS_SERVICE_PORTAL)) {
Packit Service fd1217
        _daemon_is_running = TRUE;
Packit Service fd1217
        return;
Packit Service fd1217
    }
Packit 3ff832
    /* If ibus-daemon is running and run ssh -X localhost,
Packit 3ff832
     * daemon_name_appeared() is called but ibus_get_address() == NULL
Packit 3ff832
     * because the hostname and display number are different between
Packit 3ff832
     * ibus-daemon and clients. So IBusBus would not be connected and
Packit 3ff832
     * ibusimcontext->ibuscontext == NULL and ibusimcontext->events_queue
Packit 3ff832
     * could go beyond MAX_QUEUED_EVENTS . */
Packit 3ff832
    _daemon_is_running = (ibus_get_address () != NULL);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
daemon_name_vanished (GDBusConnection *connection,
Packit 3ff832
                      const gchar     *name,
Packit 3ff832
                      gpointer         data)
Packit 3ff832
{
Packit 3ff832
    _daemon_is_running = FALSE;
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
ibus_im_context_class_init (IBusIMContextClass *class)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
Packit 3ff832
    GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class);
Packit 3ff832
    GObjectClass *gobject_class = G_OBJECT_CLASS (class);
Packit 3ff832
Packit 3ff832
    parent_class = (GtkIMContextClass *) g_type_class_peek_parent (class);
Packit 3ff832
Packit 3ff832
    im_context_class->reset = ibus_im_context_reset;
Packit 3ff832
    im_context_class->focus_in = ibus_im_context_focus_in;
Packit 3ff832
    im_context_class->focus_out = ibus_im_context_focus_out;
Packit 3ff832
    im_context_class->filter_keypress = ibus_im_context_filter_keypress;
Packit 3ff832
    im_context_class->get_preedit_string = ibus_im_context_get_preedit_string;
Packit 3ff832
    im_context_class->set_client_window = ibus_im_context_set_client_window;
Packit 3ff832
    im_context_class->set_cursor_location = ibus_im_context_set_cursor_location;
Packit 3ff832
    im_context_class->set_use_preedit = ibus_im_context_set_use_preedit;
Packit 3ff832
    im_context_class->set_surrounding = ibus_im_context_set_surrounding;
Packit 3ff832
    gobject_class->notify = ibus_im_context_notify;
Packit 3ff832
    gobject_class->finalize = ibus_im_context_finalize;
Packit 3ff832
Packit 3ff832
    _signal_commit_id =
Packit 3ff832
        g_signal_lookup ("commit", G_TYPE_FROM_CLASS (class));
Packit 3ff832
    g_assert (_signal_commit_id != 0);
Packit 3ff832
Packit 3ff832
    _signal_preedit_changed_id =
Packit 3ff832
        g_signal_lookup ("preedit-changed", G_TYPE_FROM_CLASS (class));
Packit 3ff832
    g_assert (_signal_preedit_changed_id != 0);
Packit 3ff832
Packit 3ff832
    _signal_preedit_start_id =
Packit 3ff832
        g_signal_lookup ("preedit-start", G_TYPE_FROM_CLASS (class));
Packit 3ff832
    g_assert (_signal_preedit_start_id != 0);
Packit 3ff832
Packit 3ff832
    _signal_preedit_end_id =
Packit 3ff832
        g_signal_lookup ("preedit-end", G_TYPE_FROM_CLASS (class));
Packit 3ff832
    g_assert (_signal_preedit_end_id != 0);
Packit 3ff832
Packit 3ff832
    _signal_delete_surrounding_id =
Packit 3ff832
        g_signal_lookup ("delete-surrounding", G_TYPE_FROM_CLASS (class));
Packit 3ff832
    g_assert (_signal_delete_surrounding_id != 0);
Packit 3ff832
Packit 3ff832
    _signal_retrieve_surrounding_id =
Packit 3ff832
        g_signal_lookup ("retrieve-surrounding", G_TYPE_FROM_CLASS (class));
Packit 3ff832
    g_assert (_signal_retrieve_surrounding_id != 0);
Packit 3ff832
Packit 3ff832
    _use_key_snooper = !_get_boolean_env ("IBUS_DISABLE_SNOOPER",
Packit 3ff832
                                          !(ENABLE_SNOOPER));
Packit 3ff832
    _use_sync_mode = _get_boolean_env ("IBUS_ENABLE_SYNC_MODE", FALSE);
Packit 3ff832
    _use_discard_password = _get_boolean_env ("IBUS_DISCARD_PASSWORD", FALSE);
Packit 3ff832
Packit 3ff832
#define CHECK_APP_IN_CSV_ENV_VARIABLES(retval,                          \
Packit 3ff832
                                       env_apps,                        \
Packit 3ff832
                                       fallback_apps,                   \
Packit 3ff832
                                       value_if_found)                  \
Packit 3ff832
{                                                                       \
Packit 3ff832
    const gchar * prgname = g_get_prgname ();                           \
Packit 3ff832
    gchar **p;                                                          \
Packit 3ff832
    gchar ** apps;                                                      \
Packit 3ff832
    if (g_getenv ((#env_apps))) {                                       \
Packit 3ff832
        fallback_apps = g_getenv (#env_apps);                           \
Packit 3ff832
    }                                                                   \
Packit 3ff832
    apps = g_strsplit ((fallback_apps), ",", 0);                        \
Packit 3ff832
    for (p = apps; *p != NULL; p++) {                                   \
Packit 3ff832
        if (g_regex_match_simple (*p, prgname, 0, 0)) {                 \
Packit 3ff832
            retval = (value_if_found);                                  \
Packit 3ff832
            break;                                                      \
Packit 3ff832
        }                                                               \
Packit 3ff832
    }                                                                   \
Packit 3ff832
    g_strfreev (apps);                                                  \
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
    /* env IBUS_DISABLE_SNOOPER does not exist */
Packit 3ff832
    if (_use_key_snooper) {
Packit 3ff832
        /* disable snooper if app is in _no_snooper_apps */
Packit 3ff832
        CHECK_APP_IN_CSV_ENV_VARIABLES (_use_key_snooper,
Packit 3ff832
                                        IBUS_NO_SNOOPER_APPS,
Packit 3ff832
                                        _no_snooper_apps,
Packit 3ff832
                                        FALSE);
Packit 3ff832
    }
Packit 3ff832
    if (!_use_discard_password) {
Packit 3ff832
        CHECK_APP_IN_CSV_ENV_VARIABLES (_use_discard_password,
Packit 3ff832
                                        IBUS_DISCARD_PASSWORD_APPS,
Packit 3ff832
                                        _discard_password_apps,
Packit 3ff832
                                        TRUE);
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
#undef CHECK_APP_IN_CSV_ENV_VARIABLES
Packit 3ff832
Packit 3ff832
    /* init bus object */
Packit 3ff832
    if (_bus == NULL) {
Packit 3ff832
        _bus = ibus_bus_new_async_client ();
Packit 3ff832
Packit 3ff832
        /* init the global fake context */
Packit 3ff832
        if (ibus_bus_is_connected (_bus)) {
Packit 3ff832
            _create_fake_input_context ();
Packit 3ff832
        }
Packit 3ff832
Packit 3ff832
        g_signal_connect (_bus, "connected", G_CALLBACK (_bus_connected_cb), NULL);
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
Packit 3ff832
    /* always install snooper */
Packit 3ff832
    if (_key_snooper_id == 0) {
Packit 3ff832
#pragma GCC diagnostic push
Packit 3ff832
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Packit 3ff832
        _key_snooper_id = gtk_key_snooper_install (_key_snooper_cb, NULL);
Packit 3ff832
#pragma GCC diagnostic pop
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    _daemon_name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
Packit 3ff832
                                              ibus_bus_get_service_name (_bus),
Packit 3ff832
                                              G_BUS_NAME_WATCHER_FLAGS_NONE,
Packit 3ff832
                                              daemon_name_appeared,
Packit 3ff832
                                              daemon_name_vanished,
Packit 3ff832
                                              NULL,
Packit 3ff832
                                              NULL);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
ibus_im_context_class_fini (IBusIMContextClass *class)
Packit 3ff832
{
Packit 3ff832
    if (_key_snooper_id != 0) {
Packit 3ff832
        IDEBUG ("snooper is terminated.");
Packit 3ff832
#pragma GCC diagnostic push
Packit 3ff832
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Packit 3ff832
        gtk_key_snooper_remove (_key_snooper_id);
Packit 3ff832
#pragma GCC diagnostic pop
Packit 3ff832
        _key_snooper_id = 0;
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    g_bus_unwatch_name (_daemon_name_watch_id);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
/* Copied from gtk+2.0-2.20.1/modules/input/imcedilla.c to fix crosbug.com/11421.
Packit 3ff832
 * Overwrite the original Gtk+'s compose table in gtk+-2.x.y/gtk/gtkimcontextsimple.c. */
Packit 3ff832
Packit 3ff832
/* The difference between this and the default input method is the handling
Packit 3ff832
 * of C+acute - this method produces C WITH CEDILLA rather than C WITH ACUTE.
Packit 3ff832
 * For languages that use CCedilla and not acute, this is the preferred mapping,
Packit 3ff832
 * and is particularly important for pt_BR, where the us-intl keyboard is
Packit 3ff832
 * used extensively.
Packit 3ff832
 */
Packit 3ff832
static guint16 cedilla_compose_seqs[] = {
Packit 3ff832
#ifdef DEPRECATED_GDK_KEYSYMS
Packit 3ff832
  GDK_dead_acute,	GDK_C,	0,	0,	0,	0x00C7,	/* LATIN_CAPITAL_LETTER_C_WITH_CEDILLA */
Packit 3ff832
  GDK_dead_acute,	GDK_c,	0,	0,	0,	0x00E7,	/* LATIN_SMALL_LETTER_C_WITH_CEDILLA */
Packit 3ff832
  GDK_Multi_key,	GDK_apostrophe,	GDK_C,  0,      0,      0x00C7, /* LATIN_CAPITAL_LETTER_C_WITH_CEDILLA */
Packit 3ff832
  GDK_Multi_key,	GDK_apostrophe,	GDK_c,  0,      0,      0x00E7, /* LATIN_SMALL_LETTER_C_WITH_CEDILLA */
Packit 3ff832
  GDK_Multi_key,	GDK_C,  GDK_apostrophe,	0,      0,      0x00C7, /* LATIN_CAPITAL_LETTER_C_WITH_CEDILLA */
Packit 3ff832
  GDK_Multi_key,	GDK_c,  GDK_apostrophe,	0,      0,      0x00E7, /* LATIN_SMALL_LETTER_C_WITH_CEDILLA */
Packit 3ff832
#else
Packit 3ff832
  GDK_KEY_dead_acute,	GDK_KEY_C,	0,	0,	0,	0x00C7,	/* LATIN_CAPITAL_LETTER_C_WITH_CEDILLA */
Packit 3ff832
  GDK_KEY_dead_acute,	GDK_KEY_c,	0,	0,	0,	0x00E7,	/* LATIN_SMALL_LETTER_C_WITH_CEDILLA */
Packit 3ff832
  GDK_KEY_Multi_key,	GDK_KEY_apostrophe,	GDK_KEY_C,  0,      0,      0x00C7, /* LATIN_CAPITAL_LETTER_C_WITH_CEDILLA */
Packit 3ff832
  GDK_KEY_Multi_key,	GDK_KEY_apostrophe,	GDK_KEY_c,  0,      0,      0x00E7, /* LATIN_SMALL_LETTER_C_WITH_CEDILLA */
Packit 3ff832
  GDK_KEY_Multi_key,	GDK_KEY_C,  GDK_KEY_apostrophe,	0,      0,      0x00C7, /* LATIN_CAPITAL_LETTER_C_WITH_CEDILLA */
Packit 3ff832
  GDK_KEY_Multi_key,	GDK_KEY_c,  GDK_KEY_apostrophe,	0,      0,      0x00E7, /* LATIN_SMALL_LETTER_C_WITH_CEDILLA */
Packit 3ff832
#endif
Packit 3ff832
};
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
ibus_im_context_init (GObject *obj)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
Packit 3ff832
    IBusIMContext *ibusimcontext = IBUS_IM_CONTEXT (obj);
Packit 3ff832
Packit 3ff832
    ibusimcontext->client_window = NULL;
Packit 3ff832
Packit 3ff832
    // Init preedit status
Packit 3ff832
    ibusimcontext->preedit_string = NULL;
Packit 3ff832
    ibusimcontext->preedit_attrs = NULL;
Packit 3ff832
    ibusimcontext->preedit_cursor_pos = 0;
Packit 3ff832
    ibusimcontext->preedit_visible = FALSE;
Packit Service fd1217
    ibusimcontext->preedit_mode = IBUS_ENGINE_PREEDIT_CLEAR;
Packit 3ff832
Packit 3ff832
    // Init cursor area
Packit 3ff832
    ibusimcontext->cursor_area.x = -1;
Packit 3ff832
    ibusimcontext->cursor_area.y = -1;
Packit 3ff832
    ibusimcontext->cursor_area.width = 0;
Packit 3ff832
    ibusimcontext->cursor_area.height = 0;
Packit 3ff832
Packit 3ff832
    ibusimcontext->ibuscontext = NULL;
Packit 3ff832
    ibusimcontext->has_focus = FALSE;
Packit 3ff832
    ibusimcontext->time = GDK_CURRENT_TIME;
Packit 3ff832
#ifdef ENABLE_SURROUNDING
Packit 3ff832
    ibusimcontext->caps = IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_FOCUS | IBUS_CAP_SURROUNDING_TEXT;
Packit 3ff832
#else
Packit 3ff832
    ibusimcontext->caps = IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_FOCUS;
Packit 3ff832
#endif
Packit 3ff832
Packit 3ff832
    ibusimcontext->events_queue = g_queue_new ();
Packit 3ff832
Packit 3ff832
    // Create slave im context
Packit 3ff832
    ibusimcontext->slave = gtk_im_context_simple_new ();
Packit 3ff832
    gtk_im_context_simple_add_table (GTK_IM_CONTEXT_SIMPLE (ibusimcontext->slave),
Packit 3ff832
                                     cedilla_compose_seqs,
Packit 3ff832
                                     4,
Packit 3ff832
                                     G_N_ELEMENTS (cedilla_compose_seqs) / (4 + 2));
Packit 3ff832
Packit 3ff832
    g_signal_connect (ibusimcontext->slave,
Packit 3ff832
                      "commit",
Packit 3ff832
                      G_CALLBACK (_slave_commit_cb),
Packit 3ff832
                      ibusimcontext);
Packit 3ff832
    g_signal_connect (ibusimcontext->slave,
Packit 3ff832
                      "preedit-start",
Packit 3ff832
                      G_CALLBACK (_slave_preedit_start_cb),
Packit 3ff832
                      ibusimcontext);
Packit 3ff832
    g_signal_connect (ibusimcontext->slave,
Packit 3ff832
                      "preedit-end",
Packit 3ff832
                      G_CALLBACK (_slave_preedit_end_cb),
Packit 3ff832
                      ibusimcontext);
Packit 3ff832
    g_signal_connect (ibusimcontext->slave,
Packit 3ff832
                      "preedit-changed",
Packit 3ff832
                      G_CALLBACK (_slave_preedit_changed_cb),
Packit 3ff832
                      ibusimcontext);
Packit 3ff832
    g_signal_connect (ibusimcontext->slave,
Packit 3ff832
                      "retrieve-surrounding",
Packit 3ff832
                      G_CALLBACK (_slave_retrieve_surrounding_cb),
Packit 3ff832
                      ibusimcontext);
Packit 3ff832
    g_signal_connect (ibusimcontext->slave,
Packit 3ff832
                      "delete-surrounding",
Packit 3ff832
                      G_CALLBACK (_slave_delete_surrounding_cb),
Packit 3ff832
                      ibusimcontext);
Packit 3ff832
Packit 3ff832
    if (ibus_bus_is_connected (_bus)) {
Packit 3ff832
        _create_input_context (ibusimcontext);
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    g_signal_connect (_bus, "connected", G_CALLBACK (_bus_connected_cb), obj);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
ibus_im_context_notify (GObject    *obj,
Packit 3ff832
                        GParamSpec *pspec)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
Packit 3ff832
    if (g_strcmp0 (pspec->name, "input-purpose") == 0 ||
Packit 3ff832
        g_strcmp0 (pspec->name, "input-hints") == 0) {
Packit 3ff832
        _set_content_type (IBUS_IM_CONTEXT (obj));
Packit 3ff832
    }
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
ibus_im_context_finalize (GObject *obj)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
Packit 3ff832
    IBusIMContext *ibusimcontext = IBUS_IM_CONTEXT (obj);
Packit 3ff832
Packit 3ff832
    g_signal_handlers_disconnect_by_func (_bus, G_CALLBACK (_bus_connected_cb), obj);
Packit 3ff832
Packit 3ff832
    if (ibusimcontext->cancellable != NULL) {
Packit 3ff832
        /* Cancel any ongoing create input context request */
Packit 3ff832
        g_cancellable_cancel (ibusimcontext->cancellable);
Packit 3ff832
        g_object_unref (ibusimcontext->cancellable);
Packit 3ff832
        ibusimcontext->cancellable = NULL;
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    if (ibusimcontext->ibuscontext) {
Packit 3ff832
        ibus_proxy_destroy ((IBusProxy *)ibusimcontext->ibuscontext);
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    ibus_im_context_set_client_window ((GtkIMContext *)ibusimcontext, NULL);
Packit 3ff832
Packit 3ff832
    if (ibusimcontext->slave) {
Packit 3ff832
        g_object_unref (ibusimcontext->slave);
Packit 3ff832
        ibusimcontext->slave = NULL;
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    // release preedit
Packit 3ff832
    if (ibusimcontext->preedit_string) {
Packit 3ff832
        g_free (ibusimcontext->preedit_string);
Packit 3ff832
    }
Packit 3ff832
    if (ibusimcontext->preedit_attrs) {
Packit 3ff832
        pango_attr_list_unref (ibusimcontext->preedit_attrs);
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    g_queue_free_full (ibusimcontext->events_queue,
Packit 3ff832
                       (GDestroyNotify)gdk_event_free);
Packit 3ff832
Packit 3ff832
    G_OBJECT_CLASS(parent_class)->finalize (obj);
Packit 3ff832
}
Packit 3ff832
Packit Service fd1217
static void
Packit Service fd1217
ibus_im_context_clear_preedit_text (IBusIMContext *ibusimcontext)
Packit Service fd1217
{
Packit Service fd1217
    gchar *preedit_string = NULL;
Packit Service fd1217
    g_assert (ibusimcontext->ibuscontext);
Packit Service fd1217
    if (ibusimcontext->preedit_visible &&
Packit Service fd1217
        ibusimcontext->preedit_mode == IBUS_ENGINE_PREEDIT_COMMIT) {
Packit Service fd1217
        preedit_string = g_strdup (ibusimcontext->preedit_string);
Packit Service fd1217
    }
Packit Service fd1217
Packit Service fd1217
    /* Clear the preedit_string but keep the preedit_cursor_pos and
Packit Service fd1217
     * preedit_visible because a time lag could happen, firefox commit
Packit Service fd1217
     * the preedit text before the preedit text is cleared and it cause
Packit Service fd1217
     * a double commits of the Hangul preedit in firefox if the preedit
Packit Service fd1217
     * would be located on the URL bar and click on anywhere of firefox
Packit Service fd1217
     * out of the URL bar.
Packit Service fd1217
     */
Packit Service fd1217
    _ibus_context_update_preedit_text_cb (ibusimcontext->ibuscontext,
Packit Service fd1217
                                          ibus_text_new_from_string (""),
Packit Service fd1217
                                          ibusimcontext->preedit_cursor_pos,
Packit Service fd1217
                                          ibusimcontext->preedit_visible,
Packit Service fd1217
                                          IBUS_ENGINE_PREEDIT_CLEAR,
Packit Service fd1217
                                          ibusimcontext);
Packit Service fd1217
    if (preedit_string) {
Packit Service fd1217
        g_signal_emit (ibusimcontext, _signal_commit_id, 0, preedit_string);
Packit Service fd1217
        g_free (preedit_string);
Packit Service fd1217
        _request_surrounding_text (ibusimcontext);
Packit Service fd1217
    }
Packit Service fd1217
}
Packit Service fd1217
Packit 3ff832
static gboolean
Packit 3ff832
ibus_im_context_filter_keypress (GtkIMContext *context,
Packit 3ff832
                                 GdkEventKey  *event)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
Packit 3ff832
    IBusIMContext *ibusimcontext = IBUS_IM_CONTEXT (context);
Packit 3ff832
Packit 3ff832
    if (!_daemon_is_running)
Packit 3ff832
        return gtk_im_context_filter_keypress (ibusimcontext->slave, event);
Packit 3ff832
Packit 3ff832
    /* If context does not have focus, ibus will process key event in
Packit 3ff832
     * sync mode.  It is a workaround for increase search in treeview.
Packit 3ff832
     */
Packit 3ff832
    if (!ibusimcontext->has_focus)
Packit 3ff832
        return gtk_im_context_filter_keypress (ibusimcontext->slave, event);
Packit 3ff832
Packit 3ff832
    if (event->state & IBUS_HANDLED_MASK)
Packit 3ff832
        return TRUE;
Packit 3ff832
Packit 3ff832
    /* Do not call gtk_im_context_filter_keypress() because
Packit 3ff832
     * gtk_im_context_simple_filter_keypress() binds Ctrl-Shift-u
Packit 3ff832
     */
Packit 3ff832
    if (event->state & IBUS_IGNORED_MASK)
Packit 3ff832
        return ibus_im_context_commit_event (ibusimcontext, event);
Packit 3ff832
Packit 3ff832
    /* XXX it is a workaround for some applications do not set client
Packit 3ff832
     * window. */
Packit 3ff832
    if (ibusimcontext->client_window == NULL && event->window != NULL)
Packit 3ff832
        gtk_im_context_set_client_window ((GtkIMContext *)ibusimcontext,
Packit 3ff832
                                          event->window);
Packit 3ff832
Packit 3ff832
    _request_surrounding_text (ibusimcontext);
Packit 3ff832
Packit 3ff832
    ibusimcontext->time = event->time;
Packit 3ff832
Packit 3ff832
    if (ibusimcontext->ibuscontext) {
Packit 3ff832
        if (_process_key_event (ibusimcontext->ibuscontext, event))
Packit 3ff832
            return TRUE;
Packit 3ff832
        else
Packit 3ff832
            return gtk_im_context_filter_keypress (ibusimcontext->slave,
Packit 3ff832
                                                   event);
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    /* At this point we _should_ be waiting for the IBus context to be
Packit 3ff832
     * created or the connection to IBus to be established. If that's
Packit 3ff832
     * the case we queue events to be processed when the IBus context
Packit 3ff832
     * is ready. */
Packit 3ff832
    g_return_val_if_fail (ibusimcontext->cancellable != NULL ||
Packit 3ff832
                          ibus_bus_is_connected (_bus) == FALSE,
Packit 3ff832
                          FALSE);
Packit 3ff832
    g_queue_push_tail (ibusimcontext->events_queue,
Packit 3ff832
                       gdk_event_copy ((GdkEvent *)event));
Packit 3ff832
Packit 3ff832
    if (g_queue_get_length (ibusimcontext->events_queue) > MAX_QUEUED_EVENTS) {
Packit 3ff832
        g_warning ("Events queue growing too big, will start to drop.");
Packit 3ff832
        gdk_event_free ((GdkEvent *)
Packit 3ff832
                        g_queue_pop_head (ibusimcontext->events_queue));
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    return TRUE;
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
ibus_im_context_focus_in (GtkIMContext *context)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
Packit 3ff832
    IBusIMContext *ibusimcontext = (IBusIMContext *) context;
Packit 3ff832
Packit 3ff832
    if (ibusimcontext->has_focus)
Packit 3ff832
        return;
Packit 3ff832
Packit 3ff832
    /* don't set focus on password entry */
Packit 3ff832
    if (ibusimcontext->client_window != NULL) {
Packit 3ff832
        GtkWidget *widget;
Packit 3ff832
Packit 3ff832
        gdk_window_get_user_data (ibusimcontext->client_window,
Packit 3ff832
                                  (gpointer *)&widget);
Packit 3ff832
Packit 3ff832
        if (GTK_IS_ENTRY (widget) &&
Packit 3ff832
            !gtk_entry_get_visibility (GTK_ENTRY (widget))) {
Packit 3ff832
            return;
Packit 3ff832
        }
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    /* Do not call gtk_im_context_focus_out() here.
Packit 3ff832
     * google-chrome's notification popup window (Pushbullet)
Packit 3ff832
     * takes the focus and the popup window disappears.
Packit 3ff832
     * So other applications lose the focus because
Packit 3ff832
     * ibusimcontext->has_focus is FALSE if
Packit 3ff832
     * gtk_im_context_focus_out() is called here when
Packit 3ff832
     * _focus_im_context != context.
Packit 3ff832
     */
Packit 3ff832
    if (_focus_im_context == NULL) {
Packit 3ff832
        /* focus out fake context */
Packit 3ff832
        if (_fake_context != NULL) {
Packit 3ff832
            ibus_input_context_focus_out (_fake_context);
Packit 3ff832
        }
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    ibusimcontext->has_focus = TRUE;
Packit 3ff832
    if (ibusimcontext->ibuscontext) {
Packit 3ff832
        if (!_set_content_type (ibusimcontext)) {
Packit 3ff832
            ibusimcontext->has_focus = FALSE;
Packit 3ff832
            return;
Packit 3ff832
        }
Packit 3ff832
        ibus_input_context_focus_in (ibusimcontext->ibuscontext);
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    gtk_im_context_focus_in (ibusimcontext->slave);
Packit 3ff832
Packit 3ff832
    /* set_cursor_location_internal() will get origin from X server,
Packit 3ff832
     * it blocks UI. So delay it to idle callback. */
Packit 3ff832
    gdk_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE,
Packit 3ff832
                               (GSourceFunc) _set_cursor_location_internal,
Packit 3ff832
                               g_object_ref (ibusimcontext),
Packit 3ff832
                               (GDestroyNotify) g_object_unref);
Packit 3ff832
Packit 3ff832
    /* retrieve the initial surrounding-text (regardless of whether
Packit 3ff832
     * the current IBus engine needs surrounding-text) */
Packit 3ff832
    _request_surrounding_text (ibusimcontext);
Packit 3ff832
Packit 3ff832
    g_object_add_weak_pointer ((GObject *) context,
Packit 3ff832
                               (gpointer *) &_focus_im_context);
Packit 3ff832
    _focus_im_context = context;
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
ibus_im_context_focus_out (GtkIMContext *context)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
    IBusIMContext *ibusimcontext = (IBusIMContext *) context;
Packit 3ff832
Packit 3ff832
    if (ibusimcontext->has_focus == FALSE) {
Packit 3ff832
        return;
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    /* If _use_discard_password is TRUE or GtkEntry has no visibility,
Packit 3ff832
     * _focus_im_context is NULL.
Packit 3ff832
     */
Packit 3ff832
    if (_focus_im_context) {
Packit 3ff832
        g_object_remove_weak_pointer ((GObject *) context,
Packit 3ff832
                                      (gpointer *) &_focus_im_context);
Packit 3ff832
        _focus_im_context = NULL;
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    ibusimcontext->has_focus = FALSE;
Packit 3ff832
    if (ibusimcontext->ibuscontext) {
Packit Service fd1217
        ibus_im_context_clear_preedit_text (ibusimcontext);
Packit 3ff832
        ibus_input_context_focus_out (ibusimcontext->ibuscontext);
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    gtk_im_context_focus_out (ibusimcontext->slave);
Packit 3ff832
Packit 3ff832
    /* focus in the fake ic */
Packit 3ff832
    if (_fake_context != NULL) {
Packit 3ff832
        ibus_input_context_focus_in (_fake_context);
Packit 3ff832
    }
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
ibus_im_context_reset (GtkIMContext *context)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
Packit 3ff832
    IBusIMContext *ibusimcontext = IBUS_IM_CONTEXT (context);
Packit 3ff832
Packit 3ff832
    if (ibusimcontext->ibuscontext) {
Packit Service fd1217
        /* Commented out ibus_im_context_clear_preedit_text().
Packit Service fd1217
         * Hangul needs to receive the reset callback with button press
Packit Service fd1217
         * but other IMEs should avoid to receive the reset callback
Packit Service fd1217
         * by themselves.
Packit Service fd1217
         * IBus uses button-press-event instead until GTK is fixed.
Packit Service fd1217
         * https://gitlab.gnome.org/GNOME/gtk/issues/1534
Packit Service fd1217
         */
Packit 3ff832
        ibus_input_context_reset (ibusimcontext->ibuscontext);
Packit 3ff832
    }
Packit 3ff832
    gtk_im_context_reset (ibusimcontext->slave);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
ibus_im_context_get_preedit_string (GtkIMContext   *context,
Packit 3ff832
                                    gchar         **str,
Packit 3ff832
                                    PangoAttrList **attrs,
Packit 3ff832
                                    gint           *cursor_pos)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
Packit 3ff832
    IBusIMContext *ibusimcontext = IBUS_IM_CONTEXT (context);
Packit 3ff832
Packit 3ff832
    if (ibusimcontext->preedit_visible) {
Packit 3ff832
        if (str) {
Packit 3ff832
            *str = g_strdup (ibusimcontext->preedit_string ? ibusimcontext->preedit_string: "");
Packit 3ff832
        }
Packit 3ff832
Packit 3ff832
        if (attrs) {
Packit 3ff832
            *attrs = ibusimcontext->preedit_attrs ?
Packit 3ff832
                        pango_attr_list_ref (ibusimcontext->preedit_attrs):
Packit 3ff832
                        pango_attr_list_new ();
Packit 3ff832
        }
Packit 3ff832
Packit 3ff832
        if (cursor_pos) {
Packit 3ff832
            *cursor_pos = ibusimcontext->preedit_cursor_pos;
Packit 3ff832
        }
Packit 3ff832
    }
Packit 3ff832
    else {
Packit 3ff832
        if (str) {
Packit 3ff832
            *str = g_strdup ("");
Packit 3ff832
        }
Packit 3ff832
        if (attrs) {
Packit 3ff832
            *attrs = pango_attr_list_new ();
Packit 3ff832
        }
Packit 3ff832
        if (cursor_pos) {
Packit 3ff832
            *cursor_pos = 0;
Packit 3ff832
        }
Packit 3ff832
    }
Packit 3ff832
    IDEBUG ("str=%s", *str);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
Packit Service fd1217
#if !GTK_CHECK_VERSION (3, 93, 0)
Packit Service fd1217
/* Use the button-press-event signal until GtkIMContext always emits the reset
Packit Service fd1217
 * signal.
Packit Service fd1217
 * https://gitlab.gnome.org/GNOME/gtk/merge_requests/460
Packit Service fd1217
 */
Packit Service fd1217
static gboolean
Packit Service fd1217
ibus_im_context_button_press_event_cb (GtkWidget      *widget,
Packit Service fd1217
                                       GdkEventButton *event,
Packit Service fd1217
                                       IBusIMContext  *ibusimcontext)
Packit Service fd1217
{
Packit Service fd1217
    if (event->button != 1)
Packit Service fd1217
        return FALSE;
Packit Service fd1217
Packit Service fd1217
    if (ibusimcontext->ibuscontext) {
Packit Service fd1217
        ibus_im_context_clear_preedit_text (ibusimcontext);
Packit Service fd1217
        ibus_input_context_reset (ibusimcontext->ibuscontext);
Packit Service fd1217
    }
Packit Service fd1217
    return FALSE;
Packit Service fd1217
}
Packit Service fd1217
Packit Service fd1217
static void
Packit Service fd1217
_connect_button_press_event (IBusIMContext *ibusimcontext,
Packit Service fd1217
                             gboolean       do_connect)
Packit Service fd1217
{
Packit Service fd1217
    GtkWidget *widget = NULL;
Packit Service fd1217
Packit Service fd1217
    g_assert (ibusimcontext->client_window);
Packit Service fd1217
    gdk_window_get_user_data (ibusimcontext->client_window,
Packit Service fd1217
                              (gpointer *)&widget);
Packit Service fd1217
    /* firefox needs GtkWidget instead of GtkWindow */
Packit Service fd1217
    if (GTK_IS_WIDGET (widget)) {
Packit Service fd1217
        if (do_connect) {
Packit Service fd1217
            g_signal_connect (
Packit Service fd1217
                    widget,
Packit Service fd1217
                    "button-press-event",
Packit Service fd1217
                    G_CALLBACK (ibus_im_context_button_press_event_cb),
Packit Service fd1217
                    ibusimcontext);
Packit Service fd1217
            ibusimcontext->use_button_press_event = TRUE;
Packit Service fd1217
        } else {
Packit Service fd1217
            g_signal_handlers_disconnect_by_func (
Packit Service fd1217
                    widget,
Packit Service fd1217
                    G_CALLBACK (ibus_im_context_button_press_event_cb),
Packit Service fd1217
                    ibusimcontext);
Packit Service fd1217
            ibusimcontext->use_button_press_event = FALSE;
Packit Service fd1217
        }
Packit Service fd1217
    }
Packit Service fd1217
}
Packit Service fd1217
#endif
Packit Service fd1217
Packit 3ff832
static void
Packit 3ff832
ibus_im_context_set_client_window (GtkIMContext *context, GdkWindow *client)
Packit 3ff832
{
Packit Service fd1217
    IBusIMContext *ibusimcontext;
Packit Service fd1217
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
Packit Service fd1217
    ibusimcontext = IBUS_IM_CONTEXT (context);
Packit 3ff832
Packit 3ff832
    if (ibusimcontext->client_window) {
Packit Service fd1217
#if !GTK_CHECK_VERSION (3, 93, 0)
Packit Service fd1217
        if (ibusimcontext->use_button_press_event)
Packit Service fd1217
            _connect_button_press_event (ibusimcontext, FALSE);
Packit Service fd1217
#endif
Packit 3ff832
        g_object_unref (ibusimcontext->client_window);
Packit 3ff832
        ibusimcontext->client_window = NULL;
Packit 3ff832
    }
Packit 3ff832
Packit Service fd1217
    if (client != NULL) {
Packit 3ff832
        ibusimcontext->client_window = g_object_ref (client);
Packit Service fd1217
#if !GTK_CHECK_VERSION (3, 93, 0)
Packit Service fd1217
        if (!ibusimcontext->use_button_press_event)
Packit Service fd1217
            _connect_button_press_event (ibusimcontext, TRUE);
Packit Service fd1217
#endif
Packit Service fd1217
    }
Packit 3ff832
    if (ibusimcontext->slave)
Packit 3ff832
        gtk_im_context_set_client_window (ibusimcontext->slave, client);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
_set_rect_scale_factor_with_window (GdkRectangle *area,
Packit 3ff832
                                    GdkWindow    *window)
Packit 3ff832
{
Packit 3ff832
#if GTK_CHECK_VERSION (3, 10, 0)
Packit 3ff832
    int scale_factor;
Packit 3ff832
Packit 3ff832
    g_assert (area);
Packit 3ff832
    g_assert (GDK_IS_WINDOW (window));
Packit 3ff832
Packit 3ff832
    scale_factor = gdk_window_get_scale_factor (window);
Packit 3ff832
    area->x *= scale_factor;
Packit 3ff832
    area->y *= scale_factor;
Packit 3ff832
    area->width *= scale_factor;
Packit 3ff832
    area->height *= scale_factor;
Packit 3ff832
#endif
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static gboolean
Packit 3ff832
_set_cursor_location_internal (IBusIMContext *ibusimcontext)
Packit 3ff832
{
Packit 3ff832
    GdkRectangle area;
Packit 3ff832
Packit 3ff832
    if(ibusimcontext->client_window == NULL ||
Packit 3ff832
       ibusimcontext->ibuscontext == NULL) {
Packit 3ff832
        return FALSE;
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    area = ibusimcontext->cursor_area;
Packit 3ff832
Packit 3ff832
#ifdef GDK_WINDOWING_WAYLAND
Packit 3ff832
    if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ())) {
Packit 3ff832
        gdouble px, py;
Packit 3ff832
        GdkWindow *parent;
Packit 3ff832
        GdkWindow *window = ibusimcontext->client_window;
Packit 3ff832
Packit 3ff832
        while ((parent = gdk_window_get_effective_parent (window)) != NULL) {
Packit 3ff832
            gdk_window_coords_to_parent (window, area.x, area.y, &px, &py;;
Packit 3ff832
            area.x = px;
Packit 3ff832
            area.y = py;
Packit 3ff832
            window = parent;
Packit 3ff832
        }
Packit 3ff832
Packit 3ff832
        _set_rect_scale_factor_with_window (&area,
Packit 3ff832
                                            ibusimcontext->client_window);
Packit 3ff832
        ibus_input_context_set_cursor_location_relative (
Packit 3ff832
            ibusimcontext->ibuscontext,
Packit 3ff832
            area.x,
Packit 3ff832
            area.y,
Packit 3ff832
            area.width,
Packit 3ff832
            area.height);
Packit 3ff832
        return FALSE;
Packit 3ff832
    }
Packit 3ff832
#endif
Packit 3ff832
Packit 3ff832
    if (area.x == -1 && area.y == -1 && area.width == 0 && area.height == 0) {
Packit 3ff832
#if GTK_CHECK_VERSION (2, 91, 0)
Packit 3ff832
        area.x = 0;
Packit 3ff832
        area.y += gdk_window_get_height (ibusimcontext->client_window);
Packit 3ff832
#else
Packit 3ff832
        gint w, h;
Packit 3ff832
        gdk_drawable_get_size (ibusimcontext->client_window, &w, &h);
Packit 3ff832
        area.y += h;
Packit 3ff832
        area.x = 0;
Packit 3ff832
#endif
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    gdk_window_get_root_coords (ibusimcontext->client_window,
Packit 3ff832
                                area.x, area.y,
Packit 3ff832
                                &area.x, &area.y);
Packit 3ff832
    _set_rect_scale_factor_with_window (&area, ibusimcontext->client_window);
Packit 3ff832
    ibus_input_context_set_cursor_location (ibusimcontext->ibuscontext,
Packit 3ff832
                                            area.x,
Packit 3ff832
                                            area.y,
Packit 3ff832
                                            area.width,
Packit 3ff832
                                            area.height);
Packit 3ff832
    return FALSE;
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
ibus_im_context_set_cursor_location (GtkIMContext *context, GdkRectangle *area)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
Packit 3ff832
    IBusIMContext *ibusimcontext = IBUS_IM_CONTEXT (context);
Packit 3ff832
Packit 3ff832
    if (ibusimcontext->cursor_area.x == area->x &&
Packit 3ff832
        ibusimcontext->cursor_area.y == area->y &&
Packit 3ff832
        ibusimcontext->cursor_area.width == area->width &&
Packit 3ff832
        ibusimcontext->cursor_area.height == area->height) {
Packit 3ff832
        return;
Packit 3ff832
    }
Packit 3ff832
    ibusimcontext->cursor_area = *area;
Packit 3ff832
    _set_cursor_location_internal (ibusimcontext);
Packit 3ff832
    gtk_im_context_set_cursor_location (ibusimcontext->slave, area);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
ibus_im_context_set_use_preedit (GtkIMContext *context, gboolean use_preedit)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
Packit 3ff832
    IBusIMContext *ibusimcontext = IBUS_IM_CONTEXT (context);
Packit 3ff832
Packit 3ff832
    if (use_preedit) {
Packit 3ff832
        ibusimcontext->caps |= IBUS_CAP_PREEDIT_TEXT;
Packit 3ff832
    }
Packit 3ff832
    else {
Packit 3ff832
        ibusimcontext->caps &= ~IBUS_CAP_PREEDIT_TEXT;
Packit 3ff832
    }
Packit 3ff832
    if(ibusimcontext->ibuscontext) {
Packit 3ff832
        ibus_input_context_set_capabilities (ibusimcontext->ibuscontext,
Packit 3ff832
                                             ibusimcontext->caps);
Packit 3ff832
    }
Packit 3ff832
    gtk_im_context_set_use_preedit (ibusimcontext->slave, use_preedit);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static guint
Packit 3ff832
get_selection_anchor_point (IBusIMContext *ibusimcontext,
Packit 3ff832
                            guint cursor_pos,
Packit 3ff832
                            guint surrounding_text_len)
Packit 3ff832
{
Packit 3ff832
    GtkWidget *widget;
Packit 3ff832
    if (ibusimcontext->client_window == NULL) {
Packit 3ff832
        return cursor_pos;
Packit 3ff832
    }
Packit 3ff832
    gdk_window_get_user_data (ibusimcontext->client_window, (gpointer *)&widget);
Packit 3ff832
Packit 3ff832
    if (!GTK_IS_TEXT_VIEW (widget)){
Packit 3ff832
        return cursor_pos;
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    GtkTextView *text_view = GTK_TEXT_VIEW (widget);
Packit 3ff832
    GtkTextBuffer *buffer = gtk_text_view_get_buffer (text_view);
Packit 3ff832
Packit 3ff832
    if (!gtk_text_buffer_get_has_selection (buffer)) {
Packit 3ff832
        return cursor_pos;
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    GtkTextIter start_iter, end_iter, cursor_iter;
Packit 3ff832
    if (!gtk_text_buffer_get_selection_bounds (buffer, &start_iter, &end_iter)) {
Packit 3ff832
        return cursor_pos;
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    gtk_text_buffer_get_iter_at_mark (buffer,
Packit 3ff832
                                      &cursor_iter,
Packit 3ff832
                                      gtk_text_buffer_get_insert (buffer));
Packit 3ff832
Packit 3ff832
    guint start_index = gtk_text_iter_get_offset (&start_iter);
Packit 3ff832
    guint end_index   = gtk_text_iter_get_offset (&end_iter);
Packit 3ff832
    guint cursor_index = gtk_text_iter_get_offset (&cursor_iter);
Packit 3ff832
Packit 3ff832
    guint anchor;
Packit 3ff832
Packit 3ff832
    if (start_index == cursor_index) {
Packit 3ff832
      anchor = end_index;
Packit 3ff832
    } else if (end_index == cursor_index) {
Packit 3ff832
      anchor = start_index;
Packit 3ff832
    } else {
Packit 3ff832
      return cursor_pos;
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    // Change absolute index to relative position.
Packit 3ff832
    guint relative_origin = cursor_index - cursor_pos;
Packit 3ff832
Packit 3ff832
    if (anchor < relative_origin) {
Packit 3ff832
      return cursor_pos;
Packit 3ff832
    }
Packit 3ff832
    anchor -= relative_origin;
Packit 3ff832
Packit 3ff832
    if (anchor > surrounding_text_len) {
Packit 3ff832
      return cursor_pos;
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    return anchor;
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
ibus_im_context_set_surrounding (GtkIMContext  *context,
Packit 3ff832
                                 const gchar   *text,
Packit 3ff832
                                 gint           len,
Packit 3ff832
                                 gint           cursor_index)
Packit 3ff832
{
Packit 3ff832
    g_return_if_fail (context != NULL);
Packit 3ff832
    g_return_if_fail (IBUS_IS_IM_CONTEXT (context));
Packit 3ff832
    g_return_if_fail (text != NULL);
Packit 3ff832
    g_return_if_fail (strlen (text) >= len);
Packit 3ff832
    g_return_if_fail (0 <= cursor_index && cursor_index <= len);
Packit 3ff832
Packit 3ff832
    IBusIMContext *ibusimcontext = IBUS_IM_CONTEXT (context);
Packit 3ff832
Packit 3ff832
    if (ibusimcontext->ibuscontext) {
Packit 3ff832
        IBusText *ibustext;
Packit 3ff832
        guint cursor_pos;
Packit 3ff832
        guint utf8_len;
Packit 3ff832
        gchar *p;
Packit 3ff832
Packit 3ff832
        p = g_strndup (text, len);
Packit 3ff832
        cursor_pos = g_utf8_strlen (p, cursor_index);
Packit 3ff832
        utf8_len = g_utf8_strlen(p, len);
Packit 3ff832
        ibustext = ibus_text_new_from_string (p);
Packit 3ff832
        g_free (p);
Packit 3ff832
Packit 3ff832
        guint anchor_pos = get_selection_anchor_point (ibusimcontext,
Packit 3ff832
                                                       cursor_pos,
Packit 3ff832
                                                       utf8_len);
Packit 3ff832
        ibus_input_context_set_surrounding_text (ibusimcontext->ibuscontext,
Packit 3ff832
                                                 ibustext,
Packit 3ff832
                                                 cursor_pos,
Packit 3ff832
                                                 anchor_pos);
Packit 3ff832
    }
Packit 3ff832
    gtk_im_context_set_surrounding (ibusimcontext->slave,
Packit 3ff832
                                    text,
Packit 3ff832
                                    len,
Packit 3ff832
                                    cursor_index);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
_bus_connected_cb (IBusBus          *bus,
Packit 3ff832
                   IBusIMContext    *ibusimcontext)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
    if (ibusimcontext)
Packit 3ff832
        _create_input_context (ibusimcontext);
Packit 3ff832
    else
Packit 3ff832
        _create_fake_input_context ();
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
_ibus_context_commit_text_cb (IBusInputContext *ibuscontext,
Packit 3ff832
                              IBusText         *text,
Packit 3ff832
                              IBusIMContext    *ibusimcontext)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
Packit 3ff832
    g_signal_emit (ibusimcontext, _signal_commit_id, 0, text->text);
Packit 3ff832
Packit 3ff832
    _request_surrounding_text (ibusimcontext);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static gboolean
Packit 3ff832
_key_is_modifier (guint keyval)
Packit 3ff832
{
Packit 3ff832
  /* See gdkkeys-x11.c:_gdk_keymap_key_is_modifier() for how this
Packit 3ff832
   * really should be implemented */
Packit 3ff832
Packit 3ff832
    switch (keyval) {
Packit 3ff832
#ifdef DEPRECATED_GDK_KEYSYMS
Packit 3ff832
    case GDK_Shift_L:
Packit 3ff832
    case GDK_Shift_R:
Packit 3ff832
    case GDK_Control_L:
Packit 3ff832
    case GDK_Control_R:
Packit 3ff832
    case GDK_Caps_Lock:
Packit 3ff832
    case GDK_Shift_Lock:
Packit 3ff832
    case GDK_Meta_L:
Packit 3ff832
    case GDK_Meta_R:
Packit 3ff832
    case GDK_Alt_L:
Packit 3ff832
    case GDK_Alt_R:
Packit 3ff832
    case GDK_Super_L:
Packit 3ff832
    case GDK_Super_R:
Packit 3ff832
    case GDK_Hyper_L:
Packit 3ff832
    case GDK_Hyper_R:
Packit 3ff832
    case GDK_ISO_Lock:
Packit 3ff832
    case GDK_ISO_Level2_Latch:
Packit 3ff832
    case GDK_ISO_Level3_Shift:
Packit 3ff832
    case GDK_ISO_Level3_Latch:
Packit 3ff832
    case GDK_ISO_Level3_Lock:
Packit 3ff832
    case GDK_ISO_Level5_Shift:
Packit 3ff832
    case GDK_ISO_Level5_Latch:
Packit 3ff832
    case GDK_ISO_Level5_Lock:
Packit 3ff832
    case GDK_ISO_Group_Shift:
Packit 3ff832
    case GDK_ISO_Group_Latch:
Packit 3ff832
    case GDK_ISO_Group_Lock:
Packit 3ff832
        return TRUE;
Packit 3ff832
#else
Packit 3ff832
    case GDK_KEY_Shift_L:
Packit 3ff832
    case GDK_KEY_Shift_R:
Packit 3ff832
    case GDK_KEY_Control_L:
Packit 3ff832
    case GDK_KEY_Control_R:
Packit 3ff832
    case GDK_KEY_Caps_Lock:
Packit 3ff832
    case GDK_KEY_Shift_Lock:
Packit 3ff832
    case GDK_KEY_Meta_L:
Packit 3ff832
    case GDK_KEY_Meta_R:
Packit 3ff832
    case GDK_KEY_Alt_L:
Packit 3ff832
    case GDK_KEY_Alt_R:
Packit 3ff832
    case GDK_KEY_Super_L:
Packit 3ff832
    case GDK_KEY_Super_R:
Packit 3ff832
    case GDK_KEY_Hyper_L:
Packit 3ff832
    case GDK_KEY_Hyper_R:
Packit 3ff832
    case GDK_KEY_ISO_Lock:
Packit 3ff832
    case GDK_KEY_ISO_Level2_Latch:
Packit 3ff832
    case GDK_KEY_ISO_Level3_Shift:
Packit 3ff832
    case GDK_KEY_ISO_Level3_Latch:
Packit 3ff832
    case GDK_KEY_ISO_Level3_Lock:
Packit 3ff832
    case GDK_KEY_ISO_Level5_Shift:
Packit 3ff832
    case GDK_KEY_ISO_Level5_Latch:
Packit 3ff832
    case GDK_KEY_ISO_Level5_Lock:
Packit 3ff832
    case GDK_KEY_ISO_Group_Shift:
Packit 3ff832
    case GDK_KEY_ISO_Group_Latch:
Packit 3ff832
    case GDK_KEY_ISO_Group_Lock:
Packit 3ff832
        return TRUE;
Packit 3ff832
#endif
Packit 3ff832
    default:
Packit 3ff832
        return FALSE;
Packit 3ff832
    }
Packit 3ff832
}
Packit Service fd1217
Packit 3ff832
/* Copy from gdk */
Packit 3ff832
static GdkEventKey *
Packit 3ff832
_create_gdk_event (IBusIMContext *ibusimcontext,
Packit 3ff832
                   guint          keyval,
Packit 3ff832
                   guint          keycode,
Packit 3ff832
                   guint          state)
Packit 3ff832
{
Packit 3ff832
    gunichar c = 0;
Packit 3ff832
    gchar buf[8];
Packit 3ff832
Packit 3ff832
    GdkEventKey *event = (GdkEventKey *)gdk_event_new ((state & IBUS_RELEASE_MASK) ? GDK_KEY_RELEASE : GDK_KEY_PRESS);
Packit 3ff832
Packit 3ff832
    if (ibusimcontext && ibusimcontext->client_window)
Packit 3ff832
        event->window = g_object_ref (ibusimcontext->client_window);
Packit 3ff832
    else if (_input_window)
Packit 3ff832
        event->window = g_object_ref (_input_window);
Packit 3ff832
Packit 3ff832
    /* The time is copied the latest value from the previous
Packit 3ff832
     * GdkKeyEvent in filter_keypress().
Packit 3ff832
     *
Packit 3ff832
     * We understand the best way would be to pass the all time value
Packit 3ff832
     * to IBus functions process_key_event() and IBus DBus functions
Packit 3ff832
     * ProcessKeyEvent() in IM clients and IM engines so that the
Packit 3ff832
     * _create_gdk_event() could get the correct time values.
Packit 3ff832
     * However it would causes to change many functions and the time value
Packit 3ff832
     * would not provide the useful meanings for each IBus engines but just
Packit 3ff832
     * pass the original value to ForwardKeyEvent().
Packit 3ff832
     * We use the saved value at the moment.
Packit 3ff832
     *
Packit 3ff832
     * Another idea might be to have the time implementation in X servers
Packit 3ff832
     * but some Xorg uses clock_gettime() and others use gettimeofday()
Packit 3ff832
     * and the values would be different in each implementation and 
Packit 3ff832
     * locale/remote X server. So probably that idea would not work. */
Packit 3ff832
    if (ibusimcontext) {
Packit 3ff832
        event->time = ibusimcontext->time;
Packit 3ff832
    } else {
Packit 3ff832
        event->time = GDK_CURRENT_TIME;
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    event->send_event = FALSE;
Packit 3ff832
    event->state = state;
Packit 3ff832
    event->keyval = keyval;
Packit 3ff832
    event->string = NULL;
Packit 3ff832
    event->length = 0;
Packit 3ff832
    event->hardware_keycode = (keycode != 0) ? keycode + 8 : 0;
Packit 3ff832
    event->group = 0;
Packit 3ff832
    event->is_modifier = _key_is_modifier (keyval);
Packit 3ff832
Packit 3ff832
#ifdef DEPRECATED_GDK_KEYSYMS
Packit 3ff832
    if (keyval != GDK_VoidSymbol)
Packit 3ff832
#else
Packit 3ff832
    if (keyval != GDK_KEY_VoidSymbol)
Packit 3ff832
#endif
Packit 3ff832
        c = gdk_keyval_to_unicode (keyval);
Packit 3ff832
Packit 3ff832
    if (c) {
Packit 3ff832
        gsize bytes_written;
Packit 3ff832
        gint len;
Packit 3ff832
Packit 3ff832
        /* Apply the control key - Taken from Xlib
Packit 3ff832
         */
Packit 3ff832
        if (event->state & GDK_CONTROL_MASK) {
Packit 3ff832
            if ((c >= '@' && c < '\177') || c == ' ') c &= 0x1F;
Packit 3ff832
            else if (c == '2') {
Packit 3ff832
                event->string = g_memdup ("\0\0", 2);
Packit 3ff832
                event->length = 1;
Packit 3ff832
                buf[0] = '\0';
Packit 3ff832
                goto out;
Packit 3ff832
            }
Packit 3ff832
            else if (c >= '3' && c <= '7') c -= ('3' - '\033');
Packit 3ff832
            else if (c == '8') c = '\177';
Packit 3ff832
            else if (c == '/') c = '_' & 0x1F;
Packit 3ff832
        }
Packit 3ff832
Packit 3ff832
        len = g_unichar_to_utf8 (c, buf);
Packit 3ff832
        buf[len] = '\0';
Packit 3ff832
Packit 3ff832
        event->string = g_locale_from_utf8 (buf, len,
Packit 3ff832
                                            NULL, &bytes_written,
Packit 3ff832
                                            NULL);
Packit 3ff832
        if (event->string)
Packit 3ff832
            event->length = bytes_written;
Packit 3ff832
#ifdef DEPRECATED_GDK_KEYSYMS
Packit 3ff832
    } else if (keyval == GDK_Escape) {
Packit 3ff832
#else
Packit 3ff832
    } else if (keyval == GDK_KEY_Escape) {
Packit 3ff832
#endif
Packit 3ff832
        event->length = 1;
Packit 3ff832
        event->string = g_strdup ("\033");
Packit 3ff832
    }
Packit 3ff832
#ifdef DEPRECATED_GDK_KEYSYMS
Packit 3ff832
    else if (keyval == GDK_Return ||
Packit 3ff832
             keyval == GDK_KP_Enter) {
Packit 3ff832
#else
Packit 3ff832
    else if (keyval == GDK_KEY_Return ||
Packit 3ff832
             keyval == GDK_KEY_KP_Enter) {
Packit 3ff832
#endif
Packit 3ff832
        event->length = 1;
Packit 3ff832
        event->string = g_strdup ("\r");
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    if (!event->string) {
Packit 3ff832
        event->length = 0;
Packit 3ff832
        event->string = g_strdup ("");
Packit 3ff832
    }
Packit 3ff832
out:
Packit 3ff832
    return event;
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
_ibus_context_forward_key_event_cb (IBusInputContext  *ibuscontext,
Packit 3ff832
                                    guint              keyval,
Packit 3ff832
                                    guint              keycode,
Packit 3ff832
                                    guint              state,
Packit 3ff832
                                    IBusIMContext     *ibusimcontext)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
    GdkEventKey *event = _create_gdk_event (ibusimcontext, keyval, keycode, state);
Packit 3ff832
    gdk_event_put ((GdkEvent *)event);
Packit 3ff832
    gdk_event_free ((GdkEvent *)event);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
_ibus_context_delete_surrounding_text_cb (IBusInputContext *ibuscontext,
Packit 3ff832
                                          gint              offset_from_cursor,
Packit 3ff832
                                          guint             nchars,
Packit 3ff832
                                          IBusIMContext    *ibusimcontext)
Packit 3ff832
{
Packit 3ff832
    gboolean return_value;
Packit 3ff832
    g_signal_emit (ibusimcontext, _signal_delete_surrounding_id, 0, offset_from_cursor, nchars, &return_value);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
_ibus_context_update_preedit_text_cb (IBusInputContext  *ibuscontext,
Packit 3ff832
                                      IBusText          *text,
Packit 3ff832
                                      gint               cursor_pos,
Packit 3ff832
                                      gboolean           visible,
Packit Service fd1217
                                      guint              mode,
Packit 3ff832
                                      IBusIMContext     *ibusimcontext)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
Packit 3ff832
    const gchar *str;
Packit 3ff832
    gboolean flag;
Packit 3ff832
Packit 3ff832
    if (ibusimcontext->preedit_string) {
Packit 3ff832
        g_free (ibusimcontext->preedit_string);
Packit 3ff832
    }
Packit 3ff832
    if (ibusimcontext->preedit_attrs) {
Packit 3ff832
        pango_attr_list_unref (ibusimcontext->preedit_attrs);
Packit 3ff832
        ibusimcontext->preedit_attrs = NULL;
Packit 3ff832
    }
Packit 3ff832
Packit Service fd1217
#if !GTK_CHECK_VERSION (3, 93, 0)
Packit Service fd1217
    if (!ibusimcontext->use_button_press_event &&
Packit Service fd1217
        mode == IBUS_ENGINE_PREEDIT_COMMIT) {
Packit Service fd1217
        if (ibusimcontext->client_window) {
Packit Service fd1217
            _connect_button_press_event (ibusimcontext, TRUE);
Packit Service fd1217
        }
Packit Service fd1217
    }
Packit Service fd1217
#endif
Packit Service fd1217
Packit 3ff832
    str = text->text;
Packit 3ff832
    ibusimcontext->preedit_string = g_strdup (str);
Packit 3ff832
    if (text->attrs) {
Packit 3ff832
        guint i;
Packit 3ff832
        ibusimcontext->preedit_attrs = pango_attr_list_new ();
Packit 3ff832
        for (i = 0; ; i++) {
Packit 3ff832
            IBusAttribute *attr = ibus_attr_list_get (text->attrs, i);
Packit 3ff832
            if (attr == NULL) {
Packit 3ff832
                break;
Packit 3ff832
            }
Packit 3ff832
Packit 3ff832
            PangoAttribute *pango_attr;
Packit 3ff832
            switch (attr->type) {
Packit 3ff832
            case IBUS_ATTR_TYPE_UNDERLINE:
Packit 3ff832
                pango_attr = pango_attr_underline_new (attr->value);
Packit 3ff832
                break;
Packit 3ff832
            case IBUS_ATTR_TYPE_FOREGROUND:
Packit 3ff832
                pango_attr = pango_attr_foreground_new (
Packit 3ff832
                                        ((attr->value & 0xff0000) >> 8) | 0xff,
Packit 3ff832
                                        ((attr->value & 0x00ff00)) | 0xff,
Packit 3ff832
                                        ((attr->value & 0x0000ff) << 8) | 0xff);
Packit 3ff832
                break;
Packit 3ff832
            case IBUS_ATTR_TYPE_BACKGROUND:
Packit 3ff832
                pango_attr = pango_attr_background_new (
Packit 3ff832
                                        ((attr->value & 0xff0000) >> 8) | 0xff,
Packit 3ff832
                                        ((attr->value & 0x00ff00)) | 0xff,
Packit 3ff832
                                        ((attr->value & 0x0000ff) << 8) | 0xff);
Packit 3ff832
                break;
Packit 3ff832
            default:
Packit 3ff832
                continue;
Packit 3ff832
            }
Packit 3ff832
            pango_attr->start_index = g_utf8_offset_to_pointer (str, attr->start_index) - str;
Packit 3ff832
            pango_attr->end_index = g_utf8_offset_to_pointer (str, attr->end_index) - str;
Packit 3ff832
            pango_attr_list_insert (ibusimcontext->preedit_attrs, pango_attr);
Packit 3ff832
        }
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    ibusimcontext->preedit_cursor_pos = cursor_pos;
Packit 3ff832
Packit 3ff832
    flag = ibusimcontext->preedit_visible != visible;
Packit 3ff832
    ibusimcontext->preedit_visible = visible;
Packit Service fd1217
    ibusimcontext->preedit_mode = mode;
Packit 3ff832
Packit 3ff832
    if (ibusimcontext->preedit_visible) {
Packit 3ff832
        if (flag) {
Packit 3ff832
            /* invisible => visible */
Packit 3ff832
            g_signal_emit (ibusimcontext, _signal_preedit_start_id, 0);
Packit 3ff832
        }
Packit 3ff832
        g_signal_emit (ibusimcontext, _signal_preedit_changed_id, 0);
Packit 3ff832
    }
Packit 3ff832
    else {
Packit 3ff832
        if (flag) {
Packit 3ff832
            /* visible => invisible */
Packit 3ff832
            g_signal_emit (ibusimcontext, _signal_preedit_changed_id, 0);
Packit 3ff832
            g_signal_emit (ibusimcontext, _signal_preedit_end_id, 0);
Packit 3ff832
        }
Packit 3ff832
        else {
Packit 3ff832
            /* still invisible */
Packit 3ff832
            /* do nothing */
Packit 3ff832
        }
Packit 3ff832
    }
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
_ibus_context_show_preedit_text_cb (IBusInputContext   *ibuscontext,
Packit 3ff832
                                    IBusIMContext      *ibusimcontext)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
Packit 3ff832
    if (ibusimcontext->preedit_visible == TRUE)
Packit 3ff832
        return;
Packit 3ff832
Packit 3ff832
    ibusimcontext->preedit_visible = TRUE;
Packit 3ff832
    g_signal_emit (ibusimcontext, _signal_preedit_start_id, 0);
Packit 3ff832
    g_signal_emit (ibusimcontext, _signal_preedit_changed_id, 0);
Packit 3ff832
Packit 3ff832
    _request_surrounding_text (ibusimcontext);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
_ibus_context_hide_preedit_text_cb (IBusInputContext *ibuscontext,
Packit 3ff832
                                    IBusIMContext    *ibusimcontext)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
Packit 3ff832
    if (ibusimcontext->preedit_visible == FALSE)
Packit 3ff832
        return;
Packit 3ff832
Packit 3ff832
    ibusimcontext->preedit_visible = FALSE;
Packit 3ff832
    g_signal_emit (ibusimcontext, _signal_preedit_changed_id, 0);
Packit 3ff832
    g_signal_emit (ibusimcontext, _signal_preedit_end_id, 0);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
_ibus_context_destroy_cb (IBusInputContext *ibuscontext,
Packit 3ff832
                          IBusIMContext    *ibusimcontext)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
    g_assert (ibusimcontext->ibuscontext == ibuscontext);
Packit 3ff832
Packit 3ff832
    g_object_unref (ibusimcontext->ibuscontext);
Packit 3ff832
    ibusimcontext->ibuscontext = NULL;
Packit 3ff832
Packit 3ff832
    /* clear preedit */
Packit 3ff832
    ibusimcontext->preedit_visible = FALSE;
Packit 3ff832
    ibusimcontext->preedit_cursor_pos = 0;
Packit 3ff832
    g_free (ibusimcontext->preedit_string);
Packit 3ff832
    ibusimcontext->preedit_string = NULL;
Packit 3ff832
Packit 3ff832
    g_signal_emit (ibusimcontext, _signal_preedit_changed_id, 0);
Packit 3ff832
    g_signal_emit (ibusimcontext, _signal_preedit_end_id, 0);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
_create_input_context_done (IBusBus       *bus,
Packit 3ff832
                            GAsyncResult  *res,
Packit 3ff832
                            IBusIMContext *ibusimcontext)
Packit 3ff832
{
Packit 3ff832
    GError *error = NULL;
Packit 3ff832
    IBusInputContext *context = ibus_bus_create_input_context_async_finish (
Packit 3ff832
            _bus, res, &error);
Packit 3ff832
Packit 3ff832
    if (ibusimcontext->cancellable != NULL) {
Packit 3ff832
        g_object_unref (ibusimcontext->cancellable);
Packit 3ff832
        ibusimcontext->cancellable = NULL;
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    if (context == NULL) {
Packit 3ff832
        g_warning ("Create input context failed: %s.", error->message);
Packit 3ff832
        g_error_free (error);
Packit 3ff832
    }
Packit 3ff832
    else {
Packit Service fd1217
        ibus_input_context_set_client_commit_preedit (context, TRUE);
Packit 3ff832
        ibusimcontext->ibuscontext = context;
Packit 3ff832
Packit 3ff832
        g_signal_connect (ibusimcontext->ibuscontext,
Packit 3ff832
                          "commit-text",
Packit 3ff832
                          G_CALLBACK (_ibus_context_commit_text_cb),
Packit 3ff832
                          ibusimcontext);
Packit 3ff832
        g_signal_connect (ibusimcontext->ibuscontext,
Packit 3ff832
                          "forward-key-event",
Packit 3ff832
                          G_CALLBACK (_ibus_context_forward_key_event_cb),
Packit 3ff832
                          ibusimcontext);
Packit 3ff832
        g_signal_connect (ibusimcontext->ibuscontext,
Packit 3ff832
                          "delete-surrounding-text",
Packit 3ff832
                          G_CALLBACK (_ibus_context_delete_surrounding_text_cb),
Packit 3ff832
                          ibusimcontext);
Packit 3ff832
        g_signal_connect (ibusimcontext->ibuscontext,
Packit Service fd1217
                          "update-preedit-text-with-mode",
Packit 3ff832
                          G_CALLBACK (_ibus_context_update_preedit_text_cb),
Packit 3ff832
                          ibusimcontext);
Packit 3ff832
        g_signal_connect (ibusimcontext->ibuscontext,
Packit 3ff832
                          "show-preedit-text",
Packit 3ff832
                          G_CALLBACK (_ibus_context_show_preedit_text_cb),
Packit 3ff832
                          ibusimcontext);
Packit 3ff832
        g_signal_connect (ibusimcontext->ibuscontext,
Packit 3ff832
                          "hide-preedit-text",
Packit 3ff832
                          G_CALLBACK (_ibus_context_hide_preedit_text_cb),
Packit 3ff832
                          ibusimcontext);
Packit 3ff832
        g_signal_connect (ibusimcontext->ibuscontext, "destroy",
Packit 3ff832
                          G_CALLBACK (_ibus_context_destroy_cb),
Packit 3ff832
                          ibusimcontext);
Packit 3ff832
Packit 3ff832
        ibus_input_context_set_capabilities (ibusimcontext->ibuscontext, ibusimcontext->caps);
Packit 3ff832
Packit 3ff832
        if (ibusimcontext->has_focus) {
Packit 3ff832
            /* The time order is _create_input_context() ->
Packit 3ff832
             * ibus_im_context_notify() -> ibus_im_context_focus_in() ->
Packit 3ff832
             * _create_input_context_done()
Packit 3ff832
             * so _set_content_type() is called at the beginning here
Packit 3ff832
             * because ibusimcontext->ibuscontext == NULL before. */
Packit 3ff832
            _set_content_type (ibusimcontext);
Packit 3ff832
Packit 3ff832
            ibus_input_context_focus_in (ibusimcontext->ibuscontext);
Packit 3ff832
            _set_cursor_location_internal (ibusimcontext);
Packit 3ff832
        }
Packit 3ff832
Packit 3ff832
        if (!g_queue_is_empty (ibusimcontext->events_queue)) {
Packit 3ff832
            GdkEventKey *event;
Packit 3ff832
            while ((event = g_queue_pop_head (ibusimcontext->events_queue))) {
Packit 3ff832
                _process_key_event (context, event);
Packit 3ff832
                gdk_event_free ((GdkEvent *)event);
Packit 3ff832
            }
Packit 3ff832
        }
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    g_object_unref (ibusimcontext);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
_create_input_context (IBusIMContext *ibusimcontext)
Packit 3ff832
{
Packit 3ff832
    IDEBUG ("%s", __FUNCTION__);
Packit 3ff832
Packit 3ff832
    g_assert (ibusimcontext->ibuscontext == NULL);
Packit 3ff832
Packit 3ff832
    g_return_if_fail (ibusimcontext->cancellable == NULL);
Packit 3ff832
Packit 3ff832
    ibusimcontext->cancellable = g_cancellable_new ();
Packit 3ff832
Packit 3ff832
    ibus_bus_create_input_context_async (_bus,
Packit 3ff832
            "gtk-im", -1,
Packit 3ff832
            ibusimcontext->cancellable,
Packit 3ff832
            (GAsyncReadyCallback)_create_input_context_done,
Packit 3ff832
            g_object_ref (ibusimcontext));
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
/* Callback functions for slave context */
Packit 3ff832
static void
Packit 3ff832
_slave_commit_cb (GtkIMContext  *slave,
Packit 3ff832
                  gchar         *string,
Packit 3ff832
                  IBusIMContext *ibusimcontext)
Packit 3ff832
{
Packit 3ff832
    g_signal_emit (ibusimcontext, _signal_commit_id, 0, string);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
_slave_preedit_changed_cb (GtkIMContext  *slave,
Packit 3ff832
                           IBusIMContext *ibusimcontext)
Packit 3ff832
{
Packit 3ff832
    if (ibusimcontext->ibuscontext) {
Packit 3ff832
        return;
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    g_signal_emit (ibusimcontext, _signal_preedit_changed_id, 0);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
_slave_preedit_start_cb (GtkIMContext  *slave,
Packit 3ff832
                         IBusIMContext *ibusimcontext)
Packit 3ff832
{
Packit 3ff832
    if (ibusimcontext->ibuscontext) {
Packit 3ff832
        return;
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    g_signal_emit (ibusimcontext, _signal_preedit_start_id, 0);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
_slave_preedit_end_cb (GtkIMContext  *slave,
Packit 3ff832
                       IBusIMContext *ibusimcontext)
Packit 3ff832
{
Packit 3ff832
    if (ibusimcontext->ibuscontext) {
Packit 3ff832
        return;
Packit 3ff832
    }
Packit 3ff832
    g_signal_emit (ibusimcontext, _signal_preedit_end_id, 0);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static gboolean
Packit 3ff832
_slave_retrieve_surrounding_cb (GtkIMContext  *slave,
Packit 3ff832
                                IBusIMContext *ibusimcontext)
Packit 3ff832
{
Packit 3ff832
    gboolean return_value;
Packit 3ff832
Packit 3ff832
    if (ibusimcontext->ibuscontext) {
Packit 3ff832
        return FALSE;
Packit 3ff832
    }
Packit 3ff832
    g_signal_emit (ibusimcontext, _signal_retrieve_surrounding_id, 0,
Packit 3ff832
                   &return_value);
Packit 3ff832
    return return_value;
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static gboolean
Packit 3ff832
_slave_delete_surrounding_cb (GtkIMContext  *slave,
Packit 3ff832
                              gint           offset_from_cursor,
Packit 3ff832
                              guint          nchars,
Packit 3ff832
                              IBusIMContext *ibusimcontext)
Packit 3ff832
{
Packit 3ff832
    gboolean return_value;
Packit 3ff832
Packit 3ff832
    if (ibusimcontext->ibuscontext) {
Packit 3ff832
        return FALSE;
Packit 3ff832
    }
Packit 3ff832
    g_signal_emit (ibusimcontext, _signal_delete_surrounding_id, 0, offset_from_cursor, nchars, &return_value);
Packit 3ff832
    return return_value;
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
#ifdef OS_CHROMEOS
Packit 3ff832
static void
Packit 3ff832
_ibus_fake_context_destroy_cb (IBusInputContext *ibuscontext,
Packit 3ff832
                               gpointer          user_data)
Packit 3ff832
{
Packit 3ff832
    /* The fack IC may be destroyed when the connection is lost.
Packit 3ff832
     * Should release it. */
Packit 3ff832
    g_assert (ibuscontext == _fake_context);
Packit 3ff832
    g_object_unref (_fake_context);
Packit 3ff832
    _fake_context = NULL;
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static GCancellable     *_fake_cancellable = NULL;
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
_create_fake_input_context_done (IBusBus       *bus,
Packit 3ff832
                                 GAsyncResult  *res,
Packit 3ff832
                                 IBusIMContext *ibusimcontext)
Packit 3ff832
{
Packit 3ff832
    GError *error = NULL;
Packit 3ff832
    IBusInputContext *context = ibus_bus_create_input_context_async_finish (
Packit 3ff832
            _bus, res, &error);
Packit 3ff832
Packit 3ff832
    if (_fake_cancellable != NULL) {
Packit 3ff832
        g_object_unref (_fake_cancellable);
Packit 3ff832
        _fake_cancellable = NULL;
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    if (context == NULL) {
Packit 3ff832
        g_warning ("Create fake input context failed: %s.", error->message);
Packit 3ff832
        g_error_free (error);
Packit 3ff832
        return;
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    _fake_context = context;
Packit 3ff832
Packit 3ff832
    g_signal_connect (_fake_context, "forward-key-event",
Packit 3ff832
                      G_CALLBACK (_ibus_context_forward_key_event_cb),
Packit 3ff832
                      NULL);
Packit 3ff832
    g_signal_connect (_fake_context, "destroy",
Packit 3ff832
                      G_CALLBACK (_ibus_fake_context_destroy_cb),
Packit 3ff832
                      NULL);
Packit 3ff832
Packit 3ff832
    guint32 caps = IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_FOCUS | IBUS_CAP_SURROUNDING_TEXT;
Packit 3ff832
    ibus_input_context_set_capabilities (_fake_context, caps);
Packit 3ff832
Packit 3ff832
    /* focus in/out the fake context */
Packit 3ff832
    if (_focus_im_context == NULL)
Packit 3ff832
        ibus_input_context_focus_in (_fake_context);
Packit 3ff832
    else
Packit 3ff832
        ibus_input_context_focus_out (_fake_context);
Packit 3ff832
}
Packit 3ff832
Packit 3ff832
static void
Packit 3ff832
_create_fake_input_context (void)
Packit 3ff832
{
Packit 3ff832
    g_return_if_fail (_fake_context == NULL);
Packit 3ff832
Packit 3ff832
     /* Global engine is always enabled in Chrome OS,
Packit 3ff832
      * so create fake IC, and set focus if no other IC has focus.
Packit 3ff832
     */
Packit 3ff832
Packit 3ff832
    if (_fake_cancellable != NULL) {
Packit 3ff832
        g_cancellable_cancel (_fake_cancellable);
Packit 3ff832
        g_object_unref (_fake_cancellable);
Packit 3ff832
        _fake_cancellable = NULL;
Packit 3ff832
    }
Packit 3ff832
Packit 3ff832
    _fake_cancellable = g_cancellable_new ();
Packit 3ff832
Packit 3ff832
    ibus_bus_create_input_context_async (_bus,
Packit 3ff832
            "fake-gtk-im", -1,
Packit 3ff832
            _fake_cancellable,
Packit 3ff832
            (GAsyncReadyCallback)_create_fake_input_context_done,
Packit 3ff832
            NULL);
Packit 3ff832
Packit 3ff832
}
Packit 3ff832
#else
Packit 3ff832
static void
Packit 3ff832
_create_fake_input_context (void)
Packit 3ff832
{
Packit 3ff832
    /* For Linux desktop, do not use fake IC. */
Packit 3ff832
}
Packit 3ff832
#endif