#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#define Uses_SCIM_DEBUG
#define Uses_SCIM_BACKEND
#define Uses_SCIM_IMENGINE_MODULE
#define Uses_SCIM_HOTKEY
#define Uses_SCIM_PANEL_CLIENT

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/times.h>
#include <unistd.h>
#include <pthread.h>
#include <Ecore_Evas.h>
#include <Ecore_X.h>
#include <Ecore.h>
#include <Evas.h>

#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/Xutil.h>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#include <scim.h>
#pragma GCC diagnostic pop

#include "scim_imcontext.h"

using namespace scim;

struct _EcoreIMFContextISFImpl
{
    EcoreIMFContextISF      *parent;
    IMEngineInstancePointer  si;
    Ecore_X_Window           client_window;
    Evas                    *client_canvas;
    Ecore_IMF_Input_Mode     input_mode;
    WideString               preedit_string;
    AttributeList            preedit_attrlist;
    Ecore_IMF_Autocapital_Type autocapital_type;
    int                      preedit_caret;
    int                      cursor_x;
    int                      cursor_y;
    int                      cursor_pos;
    bool                     use_preedit;
    bool                     is_on;
    bool                     shared_si;
    bool                     preedit_started;
    bool                     preedit_updating;
    bool                     prediction_allow;

    EcoreIMFContextISFImpl  *next;
};

/* Input Context handling functions. */
static EcoreIMFContextISFImpl *new_ic_impl              (EcoreIMFContextISF     *parent);
static void                    delete_ic_impl           (EcoreIMFContextISFImpl *impl);
static void                    delete_all_ic_impl       (void);

static EcoreIMFContextISF     *find_ic                  (int                     id);


/* private functions */
static void     panel_slot_reload_config                (int                     context);
static void     panel_slot_exit                         (int                     context);
static void     panel_slot_update_lookup_table_page_size(int                     context,
                                                         int                     page_size);
static void     panel_slot_lookup_table_page_up         (int                     context);
static void     panel_slot_lookup_table_page_down       (int                     context);
static void     panel_slot_trigger_property             (int                     context,
                                                         const String           &property);
static void     panel_slot_process_helper_event         (int                     context,
                                                         const String           &target_uuid,
                                                         const String           &helper_uuid,
                                                         const Transaction      &trans);
static void     panel_slot_move_preedit_caret           (int                     context,
                                                         int                     caret_pos);
static void     panel_slot_select_candidate             (int                     context,
                                                         int                     cand_index);
static void     panel_slot_process_key_event            (int                     context,
                                                         const KeyEvent         &key);
static void     panel_slot_commit_string                (int                     context,
                                                         const WideString       &wstr);
static void     panel_slot_forward_key_event            (int                     context,
                                                         const KeyEvent         &key);
static void     panel_slot_request_help                 (int                     context);
static void     panel_slot_request_factory_menu         (int                     context);
static void     panel_slot_change_factory               (int                     context,
                                                         const String           &uuid);

static void     panel_req_focus_in                      (EcoreIMFContextISF     *ic);
static void     panel_req_update_factory_info           (EcoreIMFContextISF     *ic);
static void     panel_req_update_spot_location          (EcoreIMFContextISF     *ic);
static void     panel_req_show_help                     (EcoreIMFContextISF     *ic);
static void     panel_req_show_factory_menu             (EcoreIMFContextISF     *ic);

/* Panel iochannel handler*/
static bool     panel_initialize                        (void);
static void     panel_finalize                          (void);
static Eina_Bool panel_iochannel_handler                (void                   *data,
                                                         Ecore_Fd_Handler       *fd_handler);

/* utility functions */
static bool     filter_hotkeys                          (EcoreIMFContextISF     *ic,
                                                         const KeyEvent         &key);
static void     turn_on_ic                              (EcoreIMFContextISF     *ic);
static void     turn_off_ic                             (EcoreIMFContextISF     *ic);
static void     set_ic_capabilities                     (EcoreIMFContextISF     *ic);

static void     initialize                              (void);
static void     finalize                                (void);

static void     open_next_factory                       (EcoreIMFContextISF     *ic);
static void     open_previous_factory                   (EcoreIMFContextISF     *ic);
static void     open_specific_factory                   (EcoreIMFContextISF     *ic,
                                                         const String           &uuid);
static void     initialize_modifier_bits                (Display *display);
static unsigned int scim_x11_keymask_scim_to_x11        (Display *display, uint16 scimkeymask);
static XKeyEvent createKeyEvent                         (Display *display, Window &win,
                                                         Window &winRoot, bool press,
                                                         int keysym, int modifiers);
static void     _x_send_key_event                       (const KeyEvent &key);

static void     attach_instance                         (const IMEngineInstancePointer &si);

/* slot functions */
static void     slot_show_preedit_string                (IMEngineInstanceBase   *si);
static void     slot_show_aux_string                    (IMEngineInstanceBase   *si);
static void     slot_show_lookup_table                  (IMEngineInstanceBase   *si);

static void     slot_hide_preedit_string                (IMEngineInstanceBase   *si);
static void     slot_hide_aux_string                    (IMEngineInstanceBase   *si);
static void     slot_hide_lookup_table                  (IMEngineInstanceBase   *si);

static void     slot_update_preedit_caret               (IMEngineInstanceBase   *si,
                                                         int                     caret);
static void     slot_update_preedit_string              (IMEngineInstanceBase   *si,
                                                         const WideString       &str,
                                                         const AttributeList    &attrs);
static void     slot_update_aux_string                  (IMEngineInstanceBase   *si,
                                                         const WideString       &str,
                                                         const AttributeList    &attrs);
static void     slot_commit_string                      (IMEngineInstanceBase   *si,
                                                         const WideString       &str);
static void     slot_forward_key_event                  (IMEngineInstanceBase   *si,
                                                         const KeyEvent         &key);
static void     slot_update_lookup_table                (IMEngineInstanceBase   *si,
                                                         const LookupTable      &table);

static void     slot_register_properties                (IMEngineInstanceBase   *si,
                                                         const PropertyList     &properties);
static void     slot_update_property                    (IMEngineInstanceBase   *si,
                                                         const Property         &property);
static void     slot_beep                               (IMEngineInstanceBase   *si);
static void     slot_start_helper                       (IMEngineInstanceBase   *si,
                                                         const String           &helper_uuid);
static void     slot_stop_helper                        (IMEngineInstanceBase   *si,
                                                         const String           &helper_uuid);
static void     slot_send_helper_event                  (IMEngineInstanceBase   *si,
                                                         const String           &helper_uuid,
                                                         const Transaction      &trans);
static bool     slot_get_surrounding_text               (IMEngineInstanceBase   *si,
                                                         WideString             &text,
                                                         int                    &cursor,
                                                         int                     maxlen_before,
                                                         int                     maxlen_after);
static bool     slot_delete_surrounding_text            (IMEngineInstanceBase   *si,
                                                         int                     offset,
                                                         int                     len);

static void     reload_config_callback                  (const ConfigPointer    &config);

static void     fallback_commit_string_cb               (IMEngineInstanceBase   *si,
                                                         const WideString       &str);

/* Local variables declaration */
static String                                           _language;
static EcoreIMFContextISFImpl                          *_used_ic_impl_list          = 0;
static EcoreIMFContextISFImpl                          *_free_ic_impl_list          = 0;
static EcoreIMFContextISF                              *_ic_list                    = 0;

static KeyboardLayout                                   _keyboard_layout            = SCIM_KEYBOARD_Default;
static int                                              _valid_key_mask             = SCIM_KEY_AllMasks;

static FrontEndHotkeyMatcher                            _frontend_hotkey_matcher;
static IMEngineHotkeyMatcher                            _imengine_hotkey_matcher;

static IMEngineInstancePointer                          _default_instance;

static ConfigModule                                    *_config_module              = 0;
static ConfigPointer                                    _config;
static BackEndPointer                                   _backend;

static EcoreIMFContextISF                              *_focused_ic                 = 0;

static bool                                             _scim_initialized           = false;

static int                                              _instance_count             = 0;
static int                                              _context_count              = 0;

static IMEngineFactoryPointer                           _fallback_factory;
static IMEngineInstancePointer                          _fallback_instance;
static PanelClient                                      _panel_client;

static Ecore_Fd_Handler                                *_panel_iochannel_read_handler = 0;
static Ecore_Fd_Handler                                *_panel_iochannel_err_handler  = 0;

static Ecore_X_Window                                  _client_window               = 0;

static bool                                             _on_the_spot                = true;
static bool                                             _shared_input_method        = false;

static Display *__current_display      = 0;
static int      __current_alt_mask     = Mod1Mask;
static int      __current_meta_mask    = 0;
static int      __current_super_mask   = 0;
static int      __current_hyper_mask   = 0;
static int      __current_numlock_mask = Mod2Mask;

// A hack to shutdown the immodule cleanly even if im_module_exit() is not called when exiting.
class FinalizeHandler
{
public:
   FinalizeHandler()
     {
        SCIM_DEBUG_FRONTEND(1) << "FinalizeHandler::FinalizeHandler()\n";
     }
   ~FinalizeHandler()
     {
        SCIM_DEBUG_FRONTEND(1) << "FinalizeHandler::~FinalizeHandler()\n";
        isf_imf_context_shutdown();
     }
};

static FinalizeHandler                                  _finalize_handler;

static unsigned int
utf8_offset_to_index(const char *str, int offset)
{
   int index = 0;
   int i;
   for (i = 0; i < offset; i++)
     {
        eina_unicode_utf8_next_get(str, &index);
     }

   return index;
}

static unsigned int
get_time(void)
{
   unsigned int tint;
   struct timeval tv;
   struct timezone tz;           /* is not used since ages */
   gettimeofday(&tv, &tz);
   tint = tv.tv_sec * 1000;
   tint = tint / 1000 * 1000;
   tint = tint + tv.tv_usec / 1000;
   return tint;
}

/* Function Implementations */
static EcoreIMFContextISFImpl *
new_ic_impl(EcoreIMFContextISF *parent)
{
   EcoreIMFContextISFImpl *impl = NULL;

   if (_free_ic_impl_list != NULL)
     {
        impl = _free_ic_impl_list;
        _free_ic_impl_list = _free_ic_impl_list->next;
     }
   else
     {
        impl = new EcoreIMFContextISFImpl;
        if (impl == NULL)
          return NULL;
     }

   impl->autocapital_type = ECORE_IMF_AUTOCAPITAL_TYPE_NONE;
   impl->next = _used_ic_impl_list;
   _used_ic_impl_list = impl;

   impl->parent = parent;

   return impl;
}

static void
delete_ic_impl(EcoreIMFContextISFImpl *impl)
{
   EcoreIMFContextISFImpl *rec = _used_ic_impl_list, *last = 0;

   for (; rec != 0; last = rec, rec = rec->next)
     {
        if (rec == impl)
          {
             if (last != 0)
               last->next = rec->next;
             else
               _used_ic_impl_list = rec->next;

             rec->next = _free_ic_impl_list;
             _free_ic_impl_list = rec;

             rec->parent = 0;
             rec->si.reset();
             rec->client_window = 0;
             rec->preedit_string = WideString();
             rec->preedit_attrlist.clear();

             return;
          }
     }
}

static void
delete_all_ic_impl(void)
{
   EcoreIMFContextISFImpl *it = _used_ic_impl_list;

   while (it != 0)
     {
        _used_ic_impl_list = it->next;
        delete it;
        it = _used_ic_impl_list;
     }

   it = _free_ic_impl_list;
   while (it != 0)
     {
        _free_ic_impl_list = it->next;
        delete it;
        it = _free_ic_impl_list;
     }
}

static EcoreIMFContextISF *
find_ic(int id)
{
   EcoreIMFContextISFImpl *rec = _used_ic_impl_list;

   while (rec != 0)
     {
        if (rec->parent && rec->parent->id == id)
          return rec->parent;
        rec = rec->next;
     }

   return 0;
}

static void
feed_key_event(Evas *evas, const char *str, Eina_Bool fake)
{
   char key_string[128] = { 0 };
   unsigned int timestamp = 0;

   if (!fake)
     timestamp = get_time();

   if (strncmp(str, "KeyRelease+", 11) == 0)
     {
        if ((strlen(str) - 11 + 1) > sizeof(key_string))
          {
             fprintf(stderr, "Key string too long: '%s'", str);
             return;
          }
        strcpy(key_string, str + 11);
        evas_event_feed_key_up(evas, key_string, key_string, NULL, NULL,
                               timestamp, NULL);
        SCIM_DEBUG_FRONTEND(1) << "    evas_event_feed_key_up()...\n";
     }
   else
     {
        if ((strlen(str) + 1) > sizeof(key_string))
          {
             fprintf(stderr, "Key string too long: '%s'", str);
             return;
          }
        strcpy(key_string, str);
        evas_event_feed_key_down(evas, key_string, key_string, NULL, NULL,
                                 timestamp, NULL);
        SCIM_DEBUG_FRONTEND(1) << "    evas_event_feed_key_down()...\n";
     }
}

static void
window_to_screen_geometry_get(Ecore_X_Window client_win, int *x, int *y)
{
   Ecore_X_Window root_window, win;
   int win_x, win_y;
   int sum_x = 0, sum_y = 0;

   if (!ecore_x_display_get()) goto end;

   root_window = ecore_x_window_root_get(client_win);
   win = client_win;

   while (root_window != win)
     {
        ecore_x_window_geometry_get(win, &win_x, &win_y, NULL, NULL);
        sum_x += win_x;
        sum_y += win_y;
        win = ecore_x_window_parent_get(win);
     }

end:
   if (x)
     *x = sum_x;
   if (y)
     *y = sum_y;
}

static unsigned int
_ecore_imf_modifier_to_scim_mask(unsigned int modifiers)
{
   unsigned int mask = 0;

   /**< "Control" is pressed */
   if (modifiers & ECORE_IMF_KEYBOARD_MODIFIER_CTRL)
     mask |= SCIM_KEY_ControlMask;

   /**< "Alt" is pressed */
   if (modifiers & ECORE_IMF_KEYBOARD_MODIFIER_ALT)
     mask |= SCIM_KEY_AltMask;

   /**< "Shift" is pressed */
   if (modifiers & ECORE_IMF_KEYBOARD_MODIFIER_SHIFT)
     mask |= SCIM_KEY_ShiftMask;

   /**< "Win" (between "Ctrl" and "Alt") is pressed */
   if (modifiers & ECORE_IMF_KEYBOARD_MODIFIER_WIN)
     mask |= SCIM_KEY_SuperMask;

   /**< "AltGr" is pressed */
   if (modifiers & ECORE_IMF_KEYBOARD_MODIFIER_ALTGR)
     mask |= SCIM_KEY_Mod5Mask;

   return mask;
}

static unsigned int
_ecore_imf_lock_to_scim_mask(unsigned int locks)
{
   unsigned int mask = 0;

   if (locks & ECORE_IMF_KEYBOARD_LOCK_CAPS)
     mask |= SCIM_KEY_CapsLockMask;

   if (locks & ECORE_IMF_KEYBOARD_LOCK_NUM)
     mask |= SCIM_KEY_NumLockMask;

   return mask;
}

/* Public functions */
/**
 * isf_imf_context_new
 *
 * This function will be called by Ecore IMF.
 * Create a instance of type EcoreIMFContextISF.
 *
 * Return value: A pointer to the newly created EcoreIMFContextISF instance
 */
EcoreIMFContextISF *
isf_imf_context_new(void)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *context_scim = new EcoreIMFContextISF;
   if (context_scim == NULL)
     {
        std::cerr << "memory allocation failed in " << __func__ << "\n";
        return NULL;
     }

   context_scim->id = _context_count++;

   if (!_scim_initialized)
     {
        initialize();
        _scim_initialized = true;
     }

   return context_scim;
}

/**
 * isf_imf_context_shutdown
 *
 * It will be called when the scim im module is unloaded by ecore. It will do some
 * cleanup job.
 */
void
isf_imf_context_shutdown(void)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   if (_scim_initialized)
     {
        _scim_initialized = false;
        finalize();
     }
}

void
isf_imf_context_add(Ecore_IMF_Context *ctx)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *context_scim = (EcoreIMFContextISF*)ecore_imf_context_data_get(ctx);
   EINA_SAFETY_ON_NULL_RETURN(context_scim);

   context_scim->impl = NULL;

   if (_backend.null())
     return;

   IMEngineInstancePointer si;

   // Use the default instance if "shared input method" mode is enabled.
   if (_shared_input_method && !_default_instance.null())
     {
        si = _default_instance;
        SCIM_DEBUG_FRONTEND(2) << "use default instance: " << si->get_id() << " " << si->get_factory_uuid() << "\n";
     }

   // Not in "shared input method" mode, or no default instance, create an instance.
   if (si.null())
     {
        IMEngineFactoryPointer factory = _backend->get_default_factory(_language, "UTF-8");
        if (factory.null()) return;
        si = factory->create_instance("UTF-8", _instance_count++);
        if (si.null()) return;
        attach_instance(si);
        SCIM_DEBUG_FRONTEND(2) << "create new instance: " << si->get_id() << " " << si->get_factory_uuid() << "\n";
     }

   // If "shared input method" mode is enabled, and there is no default instance,
   // then store this instance as default one.
   if (_shared_input_method && _default_instance.null())
     {
        SCIM_DEBUG_FRONTEND(2) << "update default instance.\n";
        _default_instance = si;
     }

   context_scim->ctx                       = ctx;
   context_scim->impl                      = new_ic_impl(context_scim);
   if (context_scim->impl == NULL)
     {
        std::cerr << "memory allocation failed in " << __func__ << "\n";
        return;
     }

   context_scim->impl->si                  = si;
   context_scim->impl->client_window       = 0;
   context_scim->impl->client_canvas       = NULL;
   context_scim->impl->preedit_caret       = 0;
   context_scim->impl->cursor_x            = 0;
   context_scim->impl->cursor_y            = 0;
   context_scim->impl->cursor_pos          = -1;
   context_scim->impl->is_on               = false;
   context_scim->impl->shared_si           = _shared_input_method;
   context_scim->impl->use_preedit         = _on_the_spot;
   context_scim->impl->preedit_started     = false;
   context_scim->impl->preedit_updating    = false;
   context_scim->impl->prediction_allow    = false;

   if (!_ic_list)
     context_scim->next = NULL;
   else
     context_scim->next = _ic_list;
   _ic_list = context_scim;

   if (_shared_input_method)
     context_scim->impl->is_on = _config->read(String(SCIM_CONFIG_FRONTEND_IM_OPENED_BY_DEFAULT), context_scim->impl->is_on);

   _panel_client.prepare(context_scim->id);
   _panel_client.register_input_context(context_scim->id, si->get_factory_uuid());
   set_ic_capabilities(context_scim);
   _panel_client.send();

   SCIM_DEBUG_FRONTEND(2) << "input context created: id = " << context_scim->id << "\n";
}

void
isf_imf_context_del(Ecore_IMF_Context *ctx)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   if (!_ic_list) return;

   EcoreIMFContextISF *context_scim = (EcoreIMFContextISF*)ecore_imf_context_data_get(ctx);
   EINA_SAFETY_ON_NULL_RETURN(context_scim);
   EINA_SAFETY_ON_NULL_RETURN(context_scim->impl);

   if (context_scim->id != _ic_list->id)
     {
        EcoreIMFContextISF *pre = _ic_list;
        EcoreIMFContextISF *cur = _ic_list->next;
        while (cur != NULL)
          {
             if (cur->id == context_scim->id)
               {
                  pre->next = cur->next;
                  break;
               }
             pre = cur;
             cur = cur->next;
          }
     }
   else
     _ic_list = _ic_list->next;

   _panel_client.prepare(context_scim->id);

   if (context_scim == _focused_ic)
     context_scim->impl->si->focus_out();

   // Delete the instance.
   EcoreIMFContextISF *old_focused = _focused_ic;
   _focused_ic = context_scim;
   context_scim->impl->si.reset();
   _focused_ic = old_focused;

   if (context_scim == _focused_ic)
     {
        _panel_client.turn_off(context_scim->id);
        _panel_client.focus_out(context_scim->id);
     }

   _panel_client.remove_input_context(context_scim->id);
   _panel_client.send();

   if (context_scim->impl->client_window)
     isf_imf_context_client_window_set(ctx, NULL);

   delete_ic_impl(context_scim->impl);
   context_scim->impl = 0;

   if (context_scim == _focused_ic)
     _focused_ic = 0;

   delete context_scim;
   context_scim = 0;
}

/**
 * isf_imf_context_client_canvas_set
 * @ctx: a #Ecore_IMF_Context
 * @canvas: the client canvas
 *
 * This function will be called by Ecore IMF.
 *
 * Set the client canvas for the Input Method Context; this is the canvas
 * in which the input appears.
 *
 * The canvas type can be determined by using the context canvas type.
 * Actually only canvas with type "evas" (Evas *) is supported. This canvas
 * may be used in order to correctly position status windows, and may also
 * be used for purposes internal to the Input Method Context.
 */
void
isf_imf_context_client_canvas_set(Ecore_IMF_Context *ctx, void *canvas)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *context_scim = (EcoreIMFContextISF *)ecore_imf_context_data_get(ctx);
   EINA_SAFETY_ON_NULL_RETURN(context_scim);
   EINA_SAFETY_ON_NULL_RETURN(context_scim->impl);

   if (context_scim->impl->client_canvas != (Evas*) canvas)
     context_scim->impl->client_canvas = (Evas*)canvas;
}

/**
 * isf_imf_context_client_window_set
 * @ctx: a #Ecore_IMF_Context
 * @window: the client window
 *
 * This function will be called by Ecore IMF.
 *
 * Set the client window for the Input Method Context; this is the Ecore_X_Window
 * when using X11, Ecore_Win32_Window when using Win32, etc.
 *
 * This window is used in order to correctly position status windows,
 * and may also be used for purposes internal to the Input Method Context.
 */
void
isf_imf_context_client_window_set(Ecore_IMF_Context *ctx, void *window)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *context_scim = (EcoreIMFContextISF *)ecore_imf_context_data_get(ctx);
   EINA_SAFETY_ON_NULL_RETURN(context_scim);
   EINA_SAFETY_ON_NULL_RETURN(context_scim->impl);

   if (context_scim->impl->client_window != (Ecore_X_Window)((Ecore_Window)window))
     {
        context_scim->impl->client_window = (Ecore_X_Window)((Ecore_Window)window);

        if ((context_scim->impl->client_window != 0) &&
            (context_scim->impl->client_window != _client_window))
          _client_window = context_scim->impl->client_window;
     }
}

/**
 * isf_imf_context_reset
 * @ctx: a #Ecore_IMF_Context
 *
 * This function will be called by Ecore IMF.
 *
 * Notify the Input Method Context that a change such as a change in cursor
 * position has been made. This will typically cause the Input Method Context
 * to clear the preedit state.
 */
void
isf_imf_context_reset(Ecore_IMF_Context *ctx)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *context_scim = (EcoreIMFContextISF *)ecore_imf_context_data_get(ctx);
   EINA_SAFETY_ON_NULL_RETURN(context_scim);
   EINA_SAFETY_ON_NULL_RETURN(context_scim->impl);

   if (context_scim != _focused_ic)
     return;

   WideString wstr = context_scim->impl->preedit_string;

   _panel_client.prepare(context_scim->id);
   context_scim->impl->si->reset();
   _panel_client.send();
}

/**
 * isf_imf_context_focus_in
 * @ctx: a #Ecore_IMF_Context
 *
 * This function will be called by Ecore IMF.
 *
 * Notify the Input Method Context that the widget to which its correspond has gained focus.
 */
void
isf_imf_context_focus_in(Ecore_IMF_Context *ctx)
{
   EcoreIMFContextISF *context_scim = (EcoreIMFContextISF *)ecore_imf_context_data_get(ctx);
   EINA_SAFETY_ON_NULL_RETURN(context_scim);
   EINA_SAFETY_ON_NULL_RETURN(context_scim->impl);

   SCIM_DEBUG_FRONTEND(1) << __func__<< "(" << context_scim->id << ")...\n";

   if (_focused_ic)
     {
        if (_focused_ic == context_scim)
          {
             SCIM_DEBUG_FRONTEND(1) << "It's already focused.\n";
             return;
          }
        SCIM_DEBUG_FRONTEND(1) << "Focus out previous IC first: " << _focused_ic->id << "\n";
        if (_focused_ic->ctx)
          isf_imf_context_focus_out(_focused_ic->ctx);
     }

   bool need_cap   = false;
   bool need_reset = false;
   bool need_reg   = false;

   _focused_ic = context_scim;
   _panel_client.prepare(context_scim->id);

   // Handle the "Shared Input Method" mode.
   if (_shared_input_method)
     {
        SCIM_DEBUG_FRONTEND(2) << "shared input method.\n";
        IMEngineFactoryPointer factory = _backend->get_default_factory(_language, "UTF-8");
        if (!factory.null())
          {
             if (_default_instance.null() || _default_instance->get_factory_uuid() != factory->get_uuid())
               {
                  _default_instance = factory->create_instance("UTF-8", _default_instance.null() ? _instance_count++ : _default_instance->get_id());
                  attach_instance(_default_instance);
                  SCIM_DEBUG_FRONTEND(2) << "create new default instance: " << _default_instance->get_id() << " " << _default_instance->get_factory_uuid() << "\n";
               }

             context_scim->impl->shared_si = true;
             context_scim->impl->si = _default_instance;

             context_scim->impl->is_on = _config->read(String(SCIM_CONFIG_FRONTEND_IM_OPENED_BY_DEFAULT), context_scim->impl->is_on);
             context_scim->impl->preedit_string.clear();
             context_scim->impl->preedit_attrlist.clear();
             context_scim->impl->preedit_caret = 0;
             context_scim->impl->preedit_started = false;
             need_cap = true;
             need_reset = true;
             need_reg = true;
          }
     }
   else if (context_scim->impl->shared_si)
     {
        SCIM_DEBUG_FRONTEND(2) << "exit shared input method.\n";
        IMEngineFactoryPointer factory = _backend->get_default_factory(_language, "UTF-8");
        if (!factory.null())
          {
             context_scim->impl->si = factory->create_instance("UTF-8", _instance_count++);
             context_scim->impl->preedit_string.clear();
             context_scim->impl->preedit_attrlist.clear();
             context_scim->impl->preedit_caret = 0;
             context_scim->impl->preedit_started = false;
             attach_instance(context_scim->impl->si);
             need_cap = true;
             need_reg = true;
             context_scim->impl->shared_si = false;
             SCIM_DEBUG_FRONTEND(2) << "create new instance: " << context_scim->impl->si->get_id() << " " << context_scim->impl->si->get_factory_uuid() << "\n";
          }
     }

   context_scim->impl->si->set_frontend_data(static_cast <void*>(context_scim));

   if (need_reg) _panel_client.register_input_context(context_scim->id, context_scim->impl->si->get_factory_uuid());
   if (need_cap) set_ic_capabilities(context_scim);
   if (need_reset) context_scim->impl->si->reset();

   panel_req_focus_in(context_scim);
   panel_req_update_spot_location(context_scim);
   panel_req_update_factory_info(context_scim);

   if (context_scim->impl->is_on)
     {
        _panel_client.turn_on(context_scim->id);
        _panel_client.hide_preedit_string(context_scim->id);
        _panel_client.hide_aux_string(context_scim->id);
        _panel_client.hide_lookup_table(context_scim->id);
        context_scim->impl->si->focus_in();
     }
   else
     {
        _panel_client.turn_off(context_scim->id);
     }

   _panel_client.send();

   if (ecore_imf_context_input_panel_enabled_get(ctx))
     ecore_imf_context_input_panel_show(ctx);
}

/**
 * isf_imf_context_focus_out
 * @ctx: a #Ecore_IMF_Context
 *
 * This function will be called by Ecore IMF.
 *
 * Notify the Input Method Context that the widget to which its correspond has lost focus.
 */
void
isf_imf_context_focus_out(Ecore_IMF_Context *ctx)
{
   EcoreIMFContextISF *context_scim = (EcoreIMFContextISF *)ecore_imf_context_data_get(ctx);
   EINA_SAFETY_ON_NULL_RETURN(context_scim);
   EINA_SAFETY_ON_NULL_RETURN(context_scim->impl);

   SCIM_DEBUG_FRONTEND(1) << __func__ << "(" << context_scim->id << ")...\n";

   if (context_scim == _focused_ic)
     {
        WideString wstr = context_scim->impl->preedit_string;

        _panel_client.prepare(context_scim->id);
        context_scim->impl->si->focus_out();
        context_scim->impl->si->reset();
        _panel_client.turn_off(context_scim->id);
        _panel_client.focus_out(context_scim->id);
        _panel_client.send();
        _focused_ic = 0;
     }

   if (ecore_imf_context_input_panel_enabled_get(ctx))
     ecore_imf_context_input_panel_hide(ctx);
}

/**
 * isf_imf_context_cursor_location_set
 * @ctx: a #Ecore_IMF_Context
 * @x: x position of New cursor.
 * @y: y position of New cursor.
 * @w: the width of New cursor.
 * @h: the height of New cursor.
 *
 * This function will be called by Ecore IMF.
 *
 * Notify the Input Method Context that a change in the cursor location has been made.
 */
void
isf_imf_context_cursor_location_set(Ecore_IMF_Context *ctx, int cx, int cy, int cw, int ch)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *context_scim = (EcoreIMFContextISF *)ecore_imf_context_data_get(ctx);
   EINA_SAFETY_ON_NULL_RETURN(context_scim);
   EINA_SAFETY_ON_NULL_RETURN(context_scim->impl);

   Ecore_Evas *ee;
   int canvas_x, canvas_y;
   int new_cursor_x, new_cursor_y;
   Ecore_X_Window client_win = 0;

   if (cw == 0 && ch == 0)
     return;

   if (context_scim != _focused_ic)
     return;

   if (context_scim->impl->client_window)
     client_win = context_scim->impl->client_window;
   else
     {
        if (context_scim->impl->client_canvas)
          {
             ee = ecore_evas_ecore_evas_get(context_scim->impl->client_canvas);

             if (ee)
               client_win = (Ecore_X_Window)ecore_evas_window_get(ee);
          }
     }

   window_to_screen_geometry_get(client_win, &canvas_x, &canvas_y);

   new_cursor_x = canvas_x + cx;
   new_cursor_y = canvas_y + cy + ch;

   // Don't update spot location while updating preedit string.
   if (context_scim->impl->preedit_updating && (context_scim->impl->cursor_y == new_cursor_y))
     return;

   if (context_scim->impl->cursor_x != new_cursor_x || context_scim->impl->cursor_y != new_cursor_y)
     {
        context_scim->impl->cursor_x     = new_cursor_x;
        context_scim->impl->cursor_y     = new_cursor_y;
        _panel_client.prepare(context_scim->id);
        panel_req_update_spot_location(context_scim);
        _panel_client.send();
        SCIM_DEBUG_FRONTEND(2) << "new cursor location = " << context_scim->impl->cursor_x << "," << context_scim->impl->cursor_y << "\n";
     }
}

/**
 * isf_imf_context_use_preedit_set
 * @ctx: a #Ecore_IMF_Context
 * @use_preedit: Whether the IM context should use the preedit string.
 *
 * This function will be called by Ecore IMF.
 *
 * Set whether the IM context should use the preedit string to display feedback.
 * If is 0 (default is 1), then the IM context may use some other method to
 * display feedback, such as displaying it in a child of the root window.
 */
void
isf_imf_context_use_preedit_set(Ecore_IMF_Context* ctx, Eina_Bool use_preedit)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << " = " << (use_preedit ? "true" : "false") << "...\n";

   EcoreIMFContextISF *context_scim = (EcoreIMFContextISF*)ecore_imf_context_data_get(ctx);
   EINA_SAFETY_ON_NULL_RETURN(context_scim);
   EINA_SAFETY_ON_NULL_RETURN(context_scim->impl);

   if (!_on_the_spot) return;

   bool old = context_scim->impl->use_preedit;
   context_scim->impl->use_preedit = use_preedit;
   if (context_scim == _focused_ic)
     {
        _panel_client.prepare(context_scim->id);

        if (old != use_preedit)
          set_ic_capabilities(context_scim);

        if (context_scim->impl->preedit_string.length())
          slot_show_preedit_string(context_scim->impl->si);

        _panel_client.send();
     }
}

void
isf_imf_context_preedit_string_with_attributes_get(Ecore_IMF_Context *ctx, char** str, Eina_List **attrs, int *cursor_pos)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *context_scim = (EcoreIMFContextISF*)ecore_imf_context_data_get(ctx);
   EINA_SAFETY_ON_NULL_RETURN(context_scim);
   EINA_SAFETY_ON_NULL_RETURN(context_scim->impl);

   if (context_scim->impl->is_on)
     {
        String mbs = utf8_wcstombs(context_scim->impl->preedit_string);

        if (str)
          {
             if (mbs.length())
               *str = strdup(mbs.c_str());
             else
               *str = strdup("");
          }

        if (cursor_pos)
          {
             *cursor_pos = context_scim->impl->preedit_caret;
          }

        if (attrs)
          {
             if (mbs.length())
               {
                  int start_index, end_index;
                  int wlen = context_scim->impl->preedit_string.length();

                  Ecore_IMF_Preedit_Attr *attr = NULL;
                  AttributeList::const_iterator i;
                  bool *attrs_flag = new bool [mbs.length()];
                  memset(attrs_flag, 0, mbs.length() *sizeof(bool));

                  for (i = context_scim->impl->preedit_attrlist.begin();
                       i != context_scim->impl->preedit_attrlist.end(); ++i)
                    {
                       start_index = i->get_start();
                       end_index = i->get_end();

                       if (end_index <= wlen && start_index < end_index && i->get_type() != SCIM_ATTR_DECORATE_NONE)
                         {
                            start_index = utf8_offset_to_index(mbs.c_str(), i->get_start());
                            end_index = utf8_offset_to_index(mbs.c_str(), i->get_end());

                            if (i->get_type() == SCIM_ATTR_DECORATE)
                              {
                                 attr = (Ecore_IMF_Preedit_Attr *)calloc(1, sizeof(Ecore_IMF_Preedit_Attr));
                                 if (attr == NULL)
                                   continue;
                                 attr->start_index = start_index;
                                 attr->end_index = end_index;

                                 if (i->get_value() == SCIM_ATTR_DECORATE_UNDERLINE)
                                   {
                                      attr->preedit_type = ECORE_IMF_PREEDIT_TYPE_SUB1;
                                      *attrs = eina_list_append(*attrs, (void *)attr);
                                   }
                                 else if (i->get_value() == SCIM_ATTR_DECORATE_REVERSE)
                                   {
                                      attr->preedit_type = ECORE_IMF_PREEDIT_TYPE_SUB2;
                                      *attrs = eina_list_append(*attrs, (void *)attr);
                                   }
                                 else if (i->get_value() == SCIM_ATTR_DECORATE_HIGHLIGHT)
                                   {
                                      attr->preedit_type = ECORE_IMF_PREEDIT_TYPE_SUB3;
                                      *attrs = eina_list_append(*attrs, (void *)attr);
                                   }
                                 else
                                   {
                                      free(attr);
                                   }

                                 switch(i->get_value())
                                   {
                                    case SCIM_ATTR_DECORATE_UNDERLINE:
                                    case SCIM_ATTR_DECORATE_REVERSE:
                                    case SCIM_ATTR_DECORATE_HIGHLIGHT:
                                       // Record which character has attribute.
                                       for (int pos = start_index; pos < end_index; ++pos)
                                         attrs_flag [pos] = 1;
                                       break;
                                    default:
                                       break;
                                   }
                            }
                            else if (i->get_type() == SCIM_ATTR_FOREGROUND)
                              {
                                 SCIM_DEBUG_FRONTEND(4) << "SCIM_ATTR_FOREGROUND\n";
                              }
                            else if (i->get_type() == SCIM_ATTR_BACKGROUND)
                              {
                                 SCIM_DEBUG_FRONTEND(4) << "SCIM_ATTR_BACKGROUND\n";
                              }
                         }
                    }

                  // Add underline for all characters which don't have attribute.
                  for (unsigned int pos = 0; pos < mbs.length(); ++pos)
                    {
                       if (!attrs_flag [pos])
                         {
                            int begin_pos = pos;

                            while (pos < mbs.length() && !attrs_flag[pos])
                              ++pos;

                            // use REVERSE style as default
                            attr = (Ecore_IMF_Preedit_Attr *)calloc(1, sizeof(Ecore_IMF_Preedit_Attr));
                            if (attr == NULL)
                              continue;
                            attr->preedit_type = ECORE_IMF_PREEDIT_TYPE_SUB2;
                            attr->start_index = begin_pos;
                            attr->end_index = pos;
                            *attrs = eina_list_append(*attrs, (void *)attr);
                         }
                    }

                  delete [] attrs_flag;
               }
          }
     }
   else
     {
        if (str)
          *str = strdup("");

        if (cursor_pos)
          *cursor_pos = 0;

        if (attrs)
          *attrs = NULL;
     }
}

/**
 * isf_imf_context_preedit_string_get
 * @ctx: a #Ecore_IMF_Context
 * @str: the preedit string
 * @cursor_pos: the cursor position
 *
 * This function will be called by Ecore IMF.
 *
 * To get the preedit string of the input method.
 */
void
isf_imf_context_preedit_string_get(Ecore_IMF_Context *ctx, char** str, int *cursor_pos)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *context_scim = (EcoreIMFContextISF*)ecore_imf_context_data_get(ctx);
   EINA_SAFETY_ON_NULL_RETURN(context_scim);
   EINA_SAFETY_ON_NULL_RETURN(context_scim->impl);

   if (context_scim->impl->is_on)
     {
        String mbs = utf8_wcstombs(context_scim->impl->preedit_string);

        if (str)
          {
             if (mbs.length())
               *str = strdup(mbs.c_str());
             else
               *str = strdup("");
          }

        if (cursor_pos)
          *cursor_pos = context_scim->impl->preedit_caret;
     }
   else
     {
        if (str)
          *str = strdup("");

        if (cursor_pos)
          *cursor_pos = 0;
     }
}

/**
 * isf_imf_context_cursor_position_set
 * @ctx: a #Ecore_IMF_Context
 * @cursor_pos: New cursor position in characters.
 *
 * This function will be called by Ecore IMF.
 *
 * Notify the Input Method Context that a change in the cursor position has been made.
 */
void
isf_imf_context_cursor_position_set(Ecore_IMF_Context *ctx, int cursor_pos)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *context_scim = (EcoreIMFContextISF *)ecore_imf_context_data_get(ctx);
   EINA_SAFETY_ON_NULL_RETURN(context_scim);
   EINA_SAFETY_ON_NULL_RETURN(context_scim->impl);

   if (context_scim != _focused_ic)
     return;

   // Don't update spot location while updating preedit string.
   if (context_scim->impl->preedit_updating)
     return;

   if (context_scim->impl->cursor_pos != cursor_pos)
     context_scim->impl->cursor_pos = cursor_pos;
}

/**
 * isf_imf_context_input_mode_set
 * @ctx: a #Ecore_IMF_Context
 * @input_mode: the input mode
 *
 * This function will be called by Ecore IMF.
 *
 * To set the input mode of input method. The definition of Ecore_IMF_Input_Mode
 * is in Ecore_IMF.h.
 */
void
isf_imf_context_input_mode_set(Ecore_IMF_Context *ctx, Ecore_IMF_Input_Mode input_mode)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *context_scim = (EcoreIMFContextISF*)ecore_imf_context_data_get(ctx);
   EINA_SAFETY_ON_NULL_RETURN(context_scim);
   EINA_SAFETY_ON_NULL_RETURN(context_scim->impl);

   context_scim->impl->input_mode = input_mode;
}

/**
 * isf_imf_context_prediction_allow_set
 * @ctx: a #Ecore_IMF_Context
 * @use_prediction: Whether the IM context should use the prediction.
 *
 * This function will be called by Ecore IMF.
 *
 * Set whether the IM context should use the prediction.
 */
void
isf_imf_context_prediction_allow_set(Ecore_IMF_Context* ctx, Eina_Bool prediction)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << " = " << (prediction ? "true" : "false") << "...\n";

   EcoreIMFContextISF *context_scim = (EcoreIMFContextISF *)ecore_imf_context_data_get(ctx);
   EINA_SAFETY_ON_NULL_RETURN(context_scim);
   EINA_SAFETY_ON_NULL_RETURN(context_scim->impl);

   if (context_scim->impl->prediction_allow != prediction)
     context_scim->impl->prediction_allow = prediction;
}

void
isf_imf_context_autocapital_type_set(Ecore_IMF_Context* ctx, Ecore_IMF_Autocapital_Type autocapital_type)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << " = " << autocapital_type << "...\n";

   EcoreIMFContextISF *context_scim = (EcoreIMFContextISF *)ecore_imf_context_data_get(ctx);
   EINA_SAFETY_ON_NULL_RETURN(context_scim);
   EINA_SAFETY_ON_NULL_RETURN(context_scim->impl);

   if (context_scim->impl->autocapital_type != autocapital_type)
     context_scim->impl->autocapital_type = autocapital_type;
}

/**
 * isf_imf_context_filter_event
 * @ctx: a #Ecore_IMF_Context
 * @type: The type of event defined by Ecore_IMF_Event_Type.
 * @event: The event itself.
 * Return value: %TRUE if the input method handled the key event.
 *
 * This function will be called by Ecore IMF.
 *
 * Allow an Ecore Input Context to internally handle an event. If this function
 * returns 1, then no further processing should be done for this event. Input
 * methods must be able to accept all types of events (simply returning 0 if
 * the event was not handled), but there is no obligation of any events to be
 * submitted to this function.
 */
Eina_Bool
isf_imf_context_filter_event(Ecore_IMF_Context *ctx, Ecore_IMF_Event_Type type, Ecore_IMF_Event *event)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *ic = (EcoreIMFContextISF*)ecore_imf_context_data_get(ctx);
   EINA_SAFETY_ON_NULL_RETURN_VAL(ic, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(ic->impl, EINA_FALSE);

   Eina_Bool ret = EINA_FALSE;
   KeyEvent key;

   if (type == ECORE_IMF_EVENT_KEY_DOWN)
     {
        Ecore_IMF_Event_Key_Down *ev = (Ecore_IMF_Event_Key_Down *)event;
        scim_string_to_key(key, ev->key);
        key.mask |= _ecore_imf_modifier_to_scim_mask(ev->modifiers);
        key.mask |= _ecore_imf_lock_to_scim_mask(ev->locks);
     }
   else if (type == ECORE_IMF_EVENT_KEY_UP)
     {
        Ecore_IMF_Event_Key_Up *ev = (Ecore_IMF_Event_Key_Up *)event;
        scim_string_to_key(key, ev->key);
        key.mask = SCIM_KEY_ReleaseMask;
        key.mask |= _ecore_imf_modifier_to_scim_mask(ev->modifiers);
        key.mask |= _ecore_imf_lock_to_scim_mask(ev->locks);
     }
   else
     {
        return ret;
     }

   key.mask &= _valid_key_mask;

   _panel_client.prepare(ic->id);

   ret = EINA_TRUE;
   if (!filter_hotkeys(ic, key))
     {
        if (!_focused_ic || !_focused_ic->impl->is_on ||
            !_focused_ic->impl->si->process_key_event(key))
          ret = EINA_FALSE;
     }

   _panel_client.send();

   return ret;
}

void
isf_imf_context_input_panel_show(Ecore_IMF_Context *ctx)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *context_scim = (EcoreIMFContextISF*)ecore_imf_context_data_get(ctx);
   EINA_SAFETY_ON_NULL_RETURN(context_scim);
   EINA_SAFETY_ON_NULL_RETURN(context_scim->impl);

   if (!ecore_x_display_get()) return;
   ecore_x_e_virtual_keyboard_state_set
        (context_scim->impl->client_window, ECORE_X_VIRTUAL_KEYBOARD_STATE_ON);
}

void
isf_imf_context_input_panel_hide(Ecore_IMF_Context *ctx)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *context_scim = (EcoreIMFContextISF*)ecore_imf_context_data_get(ctx);
   EINA_SAFETY_ON_NULL_RETURN(context_scim);
   EINA_SAFETY_ON_NULL_RETURN(context_scim->impl);

   if (!ecore_x_display_get()) return;
   ecore_x_e_virtual_keyboard_state_set
        (context_scim->impl->client_window, ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF);
}

/* Panel Slot functions */
static void
panel_slot_reload_config(int context EINA_UNUSED)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";
   _config->reload();
}

static void
panel_slot_exit(int /* context */)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   finalize();
}

static void
panel_slot_update_lookup_table_page_size(int context, int page_size)
{
   EcoreIMFContextISF *ic = find_ic(context);
   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   SCIM_DEBUG_FRONTEND(1) << __func__ << " context=" << context << " page_size=" << page_size << " ic=" << ic << "\n";

   _panel_client.prepare(ic->id);
   ic->impl->si->update_lookup_table_page_size(page_size);
   _panel_client.send();
}

static void
panel_slot_lookup_table_page_up(int context)
{
   EcoreIMFContextISF *ic = find_ic(context);
   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   SCIM_DEBUG_FRONTEND(1) << __func__ << " context=" << context << " ic=" << ic << "\n";

   _panel_client.prepare(ic->id);
   ic->impl->si->lookup_table_page_up();
   _panel_client.send();
}

static void
panel_slot_lookup_table_page_down(int context)
{
   EcoreIMFContextISF *ic = find_ic(context);
   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   SCIM_DEBUG_FRONTEND(1) << __func__ << " context=" << context << " ic=" << ic << "\n";

   _panel_client.prepare(ic->id);
   ic->impl->si->lookup_table_page_down();
   _panel_client.send();
}

static void
panel_slot_trigger_property(int context, const String &property)
{
   EcoreIMFContextISF *ic = find_ic(context);
   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   SCIM_DEBUG_FRONTEND(1) << __func__ << " context=" << context << " property=" << property << " ic=" << ic << "\n";

   _panel_client.prepare(ic->id);
   ic->impl->si->trigger_property(property);
   _panel_client.send();
}

static void
panel_slot_process_helper_event(int context, const String &target_uuid, const String &helper_uuid, const Transaction &trans)
{
   EcoreIMFContextISF *ic = find_ic(context);
   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   SCIM_DEBUG_FRONTEND(1) << __func__ << " context=" << context << " target=" << target_uuid
      << " helper=" << helper_uuid << " ic=" << ic << " ic->impl=" << ic->impl << " ic-uuid="
      << (ic->impl ? ic->impl->si->get_factory_uuid() : "" ) << "\n";

   if (ic->impl->si->get_factory_uuid() == target_uuid)
     {
        _panel_client.prepare(ic->id);
        SCIM_DEBUG_FRONTEND(2) << "call process_helper_event\n";
        ic->impl->si->process_helper_event(helper_uuid, trans);
        _panel_client.send();
     }
}

static void
panel_slot_move_preedit_caret(int context, int caret_pos)
{
   EcoreIMFContextISF *ic = find_ic(context);
   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   SCIM_DEBUG_FRONTEND(1) << __func__ << " context=" << context << " caret=" << caret_pos << " ic=" << ic << "\n";

   _panel_client.prepare(ic->id);
   ic->impl->si->move_preedit_caret(caret_pos);
   _panel_client.send();
}

static void
panel_slot_select_candidate(int context, int cand_index)
{
   EcoreIMFContextISF *ic = find_ic(context);
   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   SCIM_DEBUG_FRONTEND(1) << __func__ << " context=" << context << " candidate=" << cand_index << " ic=" << ic << "\n";

   _panel_client.prepare(ic->id);
   ic->impl->si->select_candidate(cand_index);
   _panel_client.send();
}

static void
panel_slot_process_key_event(int context, const KeyEvent &key)
{
   EcoreIMFContextISF *ic = find_ic(context);
   SCIM_DEBUG_FRONTEND(1) << __func__ << " context=" << context << " key=" << key.get_key_string() << " ic=" << ic << "\n";

   if (!ecore_x_display_get()) return;
   if (key.is_key_press())
     ecore_x_test_fake_key_press(key.get_key_string().c_str());
}

static void
panel_slot_commit_string(int context, const WideString &wstr)
{
   EcoreIMFContextISF *ic = find_ic(context);
   EINA_SAFETY_ON_NULL_RETURN(ic);

   SCIM_DEBUG_FRONTEND(1) << __func__ << " context=" << context << " str=" << utf8_wcstombs(wstr) << " ic=" << ic << "\n";

   if (_focused_ic != ic)
     return;

   ecore_imf_context_event_callback_call(ic->ctx, ECORE_IMF_CALLBACK_COMMIT, (void *)utf8_wcstombs(wstr).c_str());
}

static void
panel_slot_forward_key_event(int context, const KeyEvent &key)
{
   EcoreIMFContextISF *ic = find_ic(context);
   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   SCIM_DEBUG_FRONTEND(1) << __func__ << " context=" << context << " key=" << key.get_key_string() << " ic=" << ic << "\n";

   if (ic->impl->client_canvas)
     feed_key_event(ic->impl->client_canvas, key.get_key_string().c_str(), EINA_TRUE);
}

static void
panel_slot_request_help(int context)
{
   EcoreIMFContextISF *ic = find_ic(context);
   EINA_SAFETY_ON_NULL_RETURN(ic);

   SCIM_DEBUG_FRONTEND(1) << __func__ << " context=" << context << " ic=" << ic << "\n";

   _panel_client.prepare(ic->id);
   panel_req_show_help(ic);
   _panel_client.send();
}

static void
panel_slot_request_factory_menu(int context)
{
   EcoreIMFContextISF *ic = find_ic(context);
   EINA_SAFETY_ON_NULL_RETURN(ic);

   SCIM_DEBUG_FRONTEND(1) << __func__ << " context=" << context << " ic=" << ic << "\n";

   _panel_client.prepare(ic->id);
   panel_req_show_factory_menu(ic);
   _panel_client.send();
}

static void
panel_slot_change_factory(int context, const String &uuid)
{
   EcoreIMFContextISF *ic = find_ic(context);
   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   SCIM_DEBUG_FRONTEND(1) << __func__ << " context=" << context << " factory=" << uuid << " ic=" << ic << "\n";

   ic->impl->si->reset();
   _panel_client.prepare(ic->id);
   open_specific_factory(ic, uuid);
   _panel_client.send();
}

/* Panel Requestion functions. */
static void
panel_req_show_help(EcoreIMFContextISF *ic)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   String help;

   help =  String("Smart Common Input Method platform ") +
      //String(SCIM_VERSION) +
      String("\n(C) 2002-2005 James Su <suzhe@tsinghua.org.cn>\n\n");

   IMEngineFactoryPointer sf = _backend->get_factory(ic->impl->si->get_factory_uuid());
   if (sf)
     {
        help += utf8_wcstombs(sf->get_name());
        help += String(":\n\n");

        help += utf8_wcstombs(sf->get_help());
        help += String("\n\n");

        help += utf8_wcstombs(sf->get_credits());
     }
   _panel_client.show_help(ic->id, help);
}

static void
panel_req_show_factory_menu(EcoreIMFContextISF *ic)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   std::vector<IMEngineFactoryPointer> factories;
   std::vector <PanelFactoryInfo> menu;

   _backend->get_factories_for_encoding(factories, "UTF-8");

   for (size_t i = 0; i < factories.size(); ++ i)
     {
        menu.push_back(PanelFactoryInfo(
              factories [i]->get_uuid(),
              utf8_wcstombs(factories [i]->get_name()),
              factories [i]->get_language(),
              factories [i]->get_icon_file()));
     }

   if (menu.size())
     _panel_client.show_factory_menu(ic->id, menu);
}

static void
panel_req_update_factory_info(EcoreIMFContextISF *ic)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   if (ic != _focused_ic)
     return;

   PanelFactoryInfo info;
   if (ic->impl->is_on)
     {
        IMEngineFactoryPointer sf = _backend->get_factory(ic->impl->si->get_factory_uuid());
        if (sf)
          info = PanelFactoryInfo(sf->get_uuid(), utf8_wcstombs(sf->get_name()), sf->get_language(), sf->get_icon_file());
     }
   else
     {
        info = PanelFactoryInfo(String(""), String("English/Keyboard"), String("C"), "");
     }
   _panel_client.update_factory_info(ic->id, info);
}

static void
panel_req_focus_in(EcoreIMFContextISF *ic)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   _panel_client.focus_in(ic->id, ic->impl->si->get_factory_uuid());
}

static void
panel_req_update_spot_location(EcoreIMFContextISF *ic)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   _panel_client.update_spot_location(ic->id, ic->impl->cursor_x, ic->impl->cursor_y);
}

static bool
filter_hotkeys(EcoreIMFContextISF *ic, const KeyEvent &key)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   bool ret = false;

   _frontend_hotkey_matcher.push_key_event(key);
   _imengine_hotkey_matcher.push_key_event(key);

   FrontEndHotkeyAction hotkey_action = _frontend_hotkey_matcher.get_match_result();

   if (hotkey_action == SCIM_FRONTEND_HOTKEY_TRIGGER)
     {
        if (!ic->impl->is_on)
          turn_on_ic(ic);
        else
          turn_off_ic(ic);
        ret = true;
     }
   else if (hotkey_action == SCIM_FRONTEND_HOTKEY_ON)
     {
        if (!ic->impl->is_on)
          turn_on_ic(ic);
        ret = true;
     }
   else if (hotkey_action == SCIM_FRONTEND_HOTKEY_OFF)
     {
        if (ic->impl->is_on)
          turn_off_ic(ic);
        ret = true;
     }
   else if (hotkey_action == SCIM_FRONTEND_HOTKEY_NEXT_FACTORY)
     {
        open_next_factory(ic);
        ret = true;
     }
   else if (hotkey_action == SCIM_FRONTEND_HOTKEY_PREVIOUS_FACTORY)
     {
        open_previous_factory(ic);
        ret = true;
     }
   else if (hotkey_action == SCIM_FRONTEND_HOTKEY_SHOW_FACTORY_MENU)
     {
        panel_req_show_factory_menu(ic);
        ret = true;
     }
   else if (_imengine_hotkey_matcher.is_matched())
     {
        String sfid = _imengine_hotkey_matcher.get_match_result();
        open_specific_factory(ic, sfid);
        ret = true;
     }
   return ret;
}

static bool
panel_initialize(void)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   String display_name;
     {
        const char *p = getenv("DISPLAY");
        if (p) display_name = String(p);
     }

   if (_panel_client.open_connection(_config->get_name(), display_name) >= 0)
     {
        int fd = _panel_client.get_connection_number();

        _panel_iochannel_read_handler = ecore_main_fd_handler_add(fd, ECORE_FD_READ, panel_iochannel_handler, NULL, NULL, NULL);

        SCIM_DEBUG_FRONTEND(2) << " Panel FD= " << fd << "\n";

        return true;
     }
   std::cerr << "panel_initialize() failed!!!\n";
   return false;
}

static void
panel_finalize(void)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   _panel_client.close_connection();

   if (_panel_iochannel_read_handler)
     {
        ecore_main_fd_handler_del(_panel_iochannel_read_handler);
        _panel_iochannel_read_handler = 0;
     }

   if (_panel_iochannel_err_handler)
     {
        ecore_main_fd_handler_del(_panel_iochannel_err_handler);
        _panel_iochannel_err_handler = 0;
     }
}

static Eina_Bool
panel_iochannel_handler(void *data EINA_UNUSED, Ecore_Fd_Handler *fd_handler)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   if (fd_handler == _panel_iochannel_read_handler)
     {
        if (!_panel_client.filter_event())
          {
             panel_finalize();
             panel_initialize();
             return ECORE_CALLBACK_CANCEL;
          }
     }
   else if (fd_handler == _panel_iochannel_err_handler)
     {
        panel_finalize();
        panel_initialize();
        return ECORE_CALLBACK_CANCEL;
     }
   return ECORE_CALLBACK_RENEW;
}

static void
turn_on_ic(EcoreIMFContextISF *ic)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   if (!ic->impl->is_on)
     {
        ic->impl->is_on = true;

        if (ic == _focused_ic)
          {
             panel_req_focus_in(ic);
             panel_req_update_spot_location(ic);
             panel_req_update_factory_info(ic);
             _panel_client.turn_on(ic->id);
             _panel_client.hide_preedit_string(ic->id);
             _panel_client.hide_aux_string(ic->id);
             _panel_client.hide_lookup_table(ic->id);
             ic->impl->si->focus_in();
          }

        //Record the IC on/off status
        if (_shared_input_method)
          _config->write(String(SCIM_CONFIG_FRONTEND_IM_OPENED_BY_DEFAULT), true);

        if (ic->impl->use_preedit && ic->impl->preedit_string.length())
          {
             ecore_imf_context_event_callback_call(ic->ctx, ECORE_IMF_CALLBACK_PREEDIT_START, NULL);
             ecore_imf_context_event_callback_call(ic->ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
             ic->impl->preedit_started = true;
          }
     }
}

static void
turn_off_ic(EcoreIMFContextISF *ic)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   if (ic->impl->is_on)
     {
        ic->impl->is_on = false;

        if (ic == _focused_ic)
          {
             ic->impl->si->focus_out();

             panel_req_update_factory_info(ic);
             _panel_client.turn_off(ic->id);
          }

        //Record the IC on/off status
        if (_shared_input_method)
          _config->write(String(SCIM_CONFIG_FRONTEND_IM_OPENED_BY_DEFAULT), false);

        if (ic->impl->use_preedit && ic->impl->preedit_string.length())
          {
             ecore_imf_context_event_callback_call(ic->ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
             ecore_imf_context_event_callback_call(ic->ctx, ECORE_IMF_CALLBACK_PREEDIT_END, NULL);
             ic->impl->preedit_started = false;
          }
     }
}

static void
set_ic_capabilities(EcoreIMFContextISF *ic)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   unsigned int cap = SCIM_CLIENT_CAP_ALL_CAPABILITIES;

   if (!_on_the_spot || !ic->impl->use_preedit)
     cap -= SCIM_CLIENT_CAP_ONTHESPOT_PREEDIT;

   ic->impl->si->update_client_capabilities(cap);
}

static bool
check_socket_frontend(void)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   SocketAddress address;
   SocketClient client;

   uint32 magic;

   address.set_address(scim_get_default_socket_frontend_address());

   if (!client.connect(address))
     return false;

   if (!scim_socket_open_connection(magic,
                                    String("ConnectionTester"),
                                    String("SocketFrontEnd"),
                                    client,
                                    1000))
        return false;

   return true;
}

void
initialize(void)
{
   std::vector<String>     config_list;
   std::vector<String>     engine_list;
   std::vector<String>     load_engine_list;

   std::vector<String>::iterator it;

   bool                    manual = false;

   bool                    socket = true;

   String                  config_module_name = "simple";

   SCIM_DEBUG_FRONTEND(1) << "Initializing Ecore SCIM IMModule...\n";

   // Get system language.
   _language = scim_get_locale_language(scim_get_current_locale());

   if (socket)
     {
        // If no Socket FrontEnd is running, then launch one.
        // And set manual to false.
        bool check_result = check_socket_frontend();
        if (!check_result)
          {
             std::cerr << "Launching a SCIM daemon with Socket FrontEnd...\n";
             //get modules list
             scim_get_imengine_module_list(engine_list);

             for (it = engine_list.begin(); it != engine_list.end(); it++)
               {
                  if (*it != "socket")
                    load_engine_list.push_back(*it);
               }

             const char *new_argv [] = { "--no-stay", 0 };
             scim_launch(true,
                         config_module_name,
                         (load_engine_list.size() ? scim_combine_string_list(load_engine_list, ',') : "none"),
                         "socket",
                         (char **)new_argv);
             manual = false;
          }

        // If there is one Socket FrontEnd running and it's not manual mode,
        // then just use this Socket Frontend.
        if (!manual)
          {
             for (int i = 0; i < 200; ++i)
               {
                  if (check_result)
                    {
                       config_module_name = "socket";
                       load_engine_list.clear();
                       load_engine_list.push_back("socket");
                       break;
                    }
                  scim_usleep(50000);
                  check_result = check_socket_frontend();
               }
          }
     }

   if (config_module_name != "dummy")
     {
        //load config module
        SCIM_DEBUG_FRONTEND(1) << "Loading Config module: " << config_module_name << "...\n";
        _config_module = new ConfigModule(config_module_name);

        //create config instance
        if (_config_module != NULL && _config_module->valid())
          _config = _config_module->create_config();
     }

   if (_config.null())
     {
        SCIM_DEBUG_FRONTEND(1) << "Config module cannot be loaded, using dummy Config.\n";

        if (_config_module) delete _config_module;
        _config_module = NULL;

        _config = new DummyConfig();
        config_module_name = "dummy";
     }

   reload_config_callback(_config);
   _config->signal_connect_reload(slot(reload_config_callback));

   // create backend
   _backend = new CommonBackEnd(_config, load_engine_list.size() ? load_engine_list : engine_list);

   if (_backend.null())
     std::cerr << "Cannot create BackEnd Object!\n";
   else
     _fallback_factory = _backend->get_factory(SCIM_COMPOSE_KEY_FACTORY_UUID);

   if (_fallback_factory.null())
     _fallback_factory = new DummyIMEngineFactory();

   _fallback_instance = _fallback_factory->create_instance(String("UTF-8"), 0);
   _fallback_instance->signal_connect_commit_string(slot(fallback_commit_string_cb));

   // Attach Panel Client signal.
   _panel_client.signal_connect_reload_config                (slot(panel_slot_reload_config));
   _panel_client.signal_connect_exit                         (slot(panel_slot_exit));
   _panel_client.signal_connect_update_lookup_table_page_size(slot(panel_slot_update_lookup_table_page_size));
   _panel_client.signal_connect_lookup_table_page_up         (slot(panel_slot_lookup_table_page_up));
   _panel_client.signal_connect_lookup_table_page_down       (slot(panel_slot_lookup_table_page_down));
   _panel_client.signal_connect_trigger_property             (slot(panel_slot_trigger_property));
   _panel_client.signal_connect_process_helper_event         (slot(panel_slot_process_helper_event));
   _panel_client.signal_connect_move_preedit_caret           (slot(panel_slot_move_preedit_caret));
   _panel_client.signal_connect_select_candidate             (slot(panel_slot_select_candidate));
   _panel_client.signal_connect_process_key_event            (slot(panel_slot_process_key_event));
   _panel_client.signal_connect_commit_string                (slot(panel_slot_commit_string));
   _panel_client.signal_connect_forward_key_event            (slot(panel_slot_forward_key_event));
   _panel_client.signal_connect_request_help                 (slot(panel_slot_request_help));
   _panel_client.signal_connect_request_factory_menu         (slot(panel_slot_request_factory_menu));
   _panel_client.signal_connect_change_factory               (slot(panel_slot_change_factory));

   if (!panel_initialize())
     std::cerr << "Ecore IM Module: Cannot connect to Panel!\n";
}

static void
finalize(void)
{
   SCIM_DEBUG_FRONTEND(1) << "Finalizing Ecore ISF IMModule...\n";

   // Reset this first so that the shared instance could be released correctly afterwards.
   _default_instance.reset();

   SCIM_DEBUG_FRONTEND(2) << "Finalize all IC partially.\n";
   while (_used_ic_impl_list)
     {
        // In case in "shared input method" mode,
        // all contexts share only one instance,
        // so we need point the reference pointer correctly before finalizing.
        _used_ic_impl_list->si->set_frontend_data(static_cast <void*>(_used_ic_impl_list->parent));
        isf_imf_context_del(_used_ic_impl_list->parent->ctx);
     }

   delete_all_ic_impl();

   _fallback_instance.reset();
   _fallback_factory.reset();

   SCIM_DEBUG_FRONTEND(2) << " Releasing BackEnd...\n";
   _backend.reset();

   SCIM_DEBUG_FRONTEND(2) << " Releasing Config...\n";
   _config.reset();

   if (_config_module)
     {
        SCIM_DEBUG_FRONTEND(2) << " Deleting _config_module...\n";
        delete _config_module;
        _config_module = 0;
     }

   _focused_ic = NULL;
   _ic_list = NULL;

   _scim_initialized = false;

   panel_finalize();
}

static void
open_next_factory(EcoreIMFContextISF *ic)
{
   SCIM_DEBUG_FRONTEND(2) << __func__ << " context=" << ic->id << "\n";
   IMEngineFactoryPointer sf = _backend->get_next_factory("", "UTF-8", ic->impl->si->get_factory_uuid());

   if (!sf.null())
     {
        turn_off_ic(ic);
        ic->impl->si = sf->create_instance("UTF-8", ic->impl->si->get_id());
        ic->impl->si->set_frontend_data(static_cast <void*>(ic));
        ic->impl->preedit_string = WideString();
        ic->impl->preedit_caret = 0;
        attach_instance(ic->impl->si);
        _backend->set_default_factory(_language, sf->get_uuid());
        _panel_client.register_input_context(ic->id, sf->get_uuid());
        set_ic_capabilities(ic);
        turn_on_ic(ic);

        if (_shared_input_method)
          {
             _default_instance = ic->impl->si;
             ic->impl->shared_si = true;
          }
     }
}

static void
open_previous_factory(EcoreIMFContextISF *ic)
{
   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   SCIM_DEBUG_FRONTEND(2) << __func__ << " context=" << ic->id << "\n";
   IMEngineFactoryPointer sf = _backend->get_previous_factory("", "UTF-8", ic->impl->si->get_factory_uuid());

   if (!sf.null())
     {
        turn_off_ic(ic);
        ic->impl->si = sf->create_instance("UTF-8", ic->impl->si->get_id());
        ic->impl->si->set_frontend_data(static_cast <void*>(ic));
        ic->impl->preedit_string = WideString();
        ic->impl->preedit_caret = 0;
        attach_instance(ic->impl->si);
        _backend->set_default_factory(_language, sf->get_uuid());
        _panel_client.register_input_context(ic->id, sf->get_uuid());
        set_ic_capabilities(ic);
        turn_on_ic(ic);

        if (_shared_input_method)
          {
             _default_instance = ic->impl->si;
             ic->impl->shared_si = true;
          }
     }
}

static void
open_specific_factory(EcoreIMFContextISF *ic,
                       const String     &uuid)
{
   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   SCIM_DEBUG_FRONTEND(2) << __func__ << " context=" << ic->id << "\n";

   // The same input method is selected, just turn on the IC.
   if (ic->impl->si->get_factory_uuid() == uuid)
     {
        turn_on_ic(ic);
        return;
     }

   IMEngineFactoryPointer sf = _backend->get_factory(uuid);

   if (uuid.length() && !sf.null())
     {
        turn_off_ic(ic);
        ic->impl->si = sf->create_instance("UTF-8", ic->impl->si->get_id());
        ic->impl->si->set_frontend_data(static_cast <void*>(ic));
        ic->impl->preedit_string = WideString();
        ic->impl->preedit_caret = 0;
        attach_instance(ic->impl->si);
        _backend->set_default_factory(_language, sf->get_uuid());
        _panel_client.register_input_context(ic->id, sf->get_uuid());
        set_ic_capabilities(ic);
        turn_on_ic(ic);

        if (_shared_input_method)
          {
             _default_instance = ic->impl->si;
             ic->impl->shared_si = true;
          }
     }
   else
     {
        // turn_off_ic comment out panel_req_update_factory_info()
        turn_off_ic(ic);
        if (ic->impl->is_on)
          {
             ic->impl->is_on = false;

             if (ic == _focused_ic)
               {
                  ic->impl->si->focus_out();

                  panel_req_update_factory_info(ic);
                  _panel_client.turn_off(ic->id);
               }

             //Record the IC on/off status
             if (_shared_input_method)
               _config->write(String(SCIM_CONFIG_FRONTEND_IM_OPENED_BY_DEFAULT), false);

             if (ic->impl->use_preedit && ic->impl->preedit_string.length())
               {
                  ecore_imf_context_event_callback_call(ic->ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
                  ecore_imf_context_event_callback_call(ic->ctx, ECORE_IMF_CALLBACK_PREEDIT_END, NULL);
                  ic->impl->preedit_started = false;
               }
          }
     }
}

static void initialize_modifier_bits(Display *display)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   if (__current_display == display)
     return;

   __current_display = display;

   if (display == 0)
     {
        __current_alt_mask     = Mod1Mask;
        __current_meta_mask    = ShiftMask | Mod1Mask;
        __current_super_mask   = 0;
        __current_hyper_mask   = 0;
        __current_numlock_mask = Mod2Mask;
        return;
     }

   XModifierKeymap *mods = NULL;

   ::KeyCode ctrl_l  = XKeysymToKeycode(display, XK_Control_L);
   ::KeyCode ctrl_r  = XKeysymToKeycode(display, XK_Control_R);
   ::KeyCode meta_l  = XKeysymToKeycode(display, XK_Meta_L);
   ::KeyCode meta_r  = XKeysymToKeycode(display, XK_Meta_R);
   ::KeyCode alt_l   = XKeysymToKeycode(display, XK_Alt_L);
   ::KeyCode alt_r   = XKeysymToKeycode(display, XK_Alt_R);
   ::KeyCode super_l = XKeysymToKeycode(display, XK_Super_L);
   ::KeyCode super_r = XKeysymToKeycode(display, XK_Super_R);
   ::KeyCode hyper_l = XKeysymToKeycode(display, XK_Hyper_L);
   ::KeyCode hyper_r = XKeysymToKeycode(display, XK_Hyper_R);
   ::KeyCode numlock = XKeysymToKeycode(display, XK_Num_Lock);

   int i, j;

   mods = XGetModifierMapping(display);
   if (mods == NULL)
     return;

   __current_alt_mask     = 0;
   __current_meta_mask    = 0;
   __current_super_mask   = 0;
   __current_hyper_mask   = 0;
   __current_numlock_mask = 0;

   /* We skip the first three sets for Shift, Lock, and Control.  The
      remaining sets are for Mod1, Mod2, Mod3, Mod4, and Mod5.  */
   for (i = 3; i < 8; i++)
     {
        for (j = 0; j < mods->max_keypermod; j++)
          {
             ::KeyCode code = mods->modifiermap [i * mods->max_keypermod + j];
             if (! code) continue;
             if (code == alt_l || code == alt_r)
               __current_alt_mask |= (1 << i);
             else if (code == meta_l || code == meta_r)
               __current_meta_mask |= (1 << i);
             else if (code == super_l || code == super_r)
               __current_super_mask |= (1 << i);
             else if (code == hyper_l || code == hyper_r)
               __current_hyper_mask |= (1 << i);
             else if (code == numlock)
               __current_numlock_mask |= (1 << i);
          }
     }

   /* Check whether there is a combine keys mapped to Meta */
   if (__current_meta_mask == 0)
     {
        char buf [32];
        XKeyEvent xkey;
        KeySym keysym_l, keysym_r;

        xkey.type = KeyPress;
        xkey.display = display;
        xkey.serial = 0L;
        xkey.send_event = False;
        xkey.x = xkey.y = xkey.x_root = xkey.y_root = 0;
        xkey.time = 0;
        xkey.same_screen = False;
        xkey.subwindow = None;
        xkey.window = None;
        xkey.root = DefaultRootWindow(display);
        xkey.state = ShiftMask;

        xkey.keycode = meta_l;
        XLookupString(&xkey, buf, 32, &keysym_l, 0);
        xkey.keycode = meta_r;
        XLookupString(&xkey, buf, 32, &keysym_r, 0);

        if ((meta_l == alt_l && keysym_l == XK_Meta_L) || (meta_r == alt_r && keysym_r == XK_Meta_R))
          __current_meta_mask = ShiftMask + __current_alt_mask;
        else if ((meta_l == ctrl_l && keysym_l == XK_Meta_L) || (meta_r == ctrl_r && keysym_r == XK_Meta_R))
          __current_meta_mask = ShiftMask + ControlMask;
     }

   XFreeModifiermap(mods);
}

static unsigned int scim_x11_keymask_scim_to_x11(Display *display, uint16 scimkeymask)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   unsigned int state = 0;

   initialize_modifier_bits(display);

   if (scimkeymask & SCIM_KEY_ShiftMask)    state |= ShiftMask;
   if (scimkeymask & SCIM_KEY_CapsLockMask) state |= LockMask;
   if (scimkeymask & SCIM_KEY_ControlMask)  state |= ControlMask;
   if (scimkeymask & SCIM_KEY_AltMask)      state |= __current_alt_mask;
   if (scimkeymask & SCIM_KEY_MetaMask)     state |= __current_meta_mask;
   if (scimkeymask & SCIM_KEY_SuperMask)    state |= __current_super_mask;
   if (scimkeymask & SCIM_KEY_HyperMask)    state |= __current_hyper_mask;
   if (scimkeymask & SCIM_KEY_NumLockMask)  state |= __current_numlock_mask;

   return state;
}

static XKeyEvent createKeyEvent(Display *display, Window &win,
                                Window &winRoot, bool press,
                                int keysym, int modifiers)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   XKeyEvent event;

   event.display     = display;
   event.window      = win;
   event.root        = winRoot;
   event.subwindow   = None;
   event.time        = CurrentTime;
   event.x           = 1;
   event.y           = 1;
   event.x_root      = 1;
   event.y_root      = 1;
   event.same_screen = EINA_TRUE;
   event.state       = modifiers;
   event.keycode     = XKeysymToKeycode(display, keysym);
   if (press)
     event.type = KeyPress;
   else
     event.type = KeyRelease;
   event.send_event  = EINA_FALSE;
   event.serial = 0;

   return event;
}

static void _x_send_key_event(const KeyEvent &key)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   // Obtain the X11 display.
   Display *display = (Display *)ecore_x_display_get();
   EINA_SAFETY_ON_NULL_RETURN(display);

   // Get the root window for the current display.
   Window winRoot = 0;

   // Find the window which has the current keyboard focus.
   Window winFocus = 0;
   int revert = RevertToParent;

   XGetInputFocus(display, &winFocus, &revert);

   unsigned int modifier = scim_x11_keymask_scim_to_x11(display, key.mask);
   XKeyEvent event;
   if (key.is_key_press())
     {
        event = createKeyEvent(display, winFocus, winRoot, true, key.code, modifier);
        XSendEvent(event.display, event.window, True, KeyPressMask, (XEvent *)&event);
     }
   else
     {
        event = createKeyEvent(display, winFocus, winRoot, false, key.code, modifier);
        XSendEvent(event.display, event.window, True, KeyReleaseMask, (XEvent *)&event);
     }
}

static void
attach_instance(const IMEngineInstancePointer &si)
{
   si->signal_connect_show_preedit_string(
      slot(slot_show_preedit_string));
   si->signal_connect_show_aux_string(
      slot(slot_show_aux_string));
   si->signal_connect_show_lookup_table(
      slot(slot_show_lookup_table));

   si->signal_connect_hide_preedit_string(
      slot(slot_hide_preedit_string));
   si->signal_connect_hide_aux_string(
      slot(slot_hide_aux_string));
   si->signal_connect_hide_lookup_table(
      slot(slot_hide_lookup_table));

   si->signal_connect_update_preedit_caret(
      slot(slot_update_preedit_caret));
   si->signal_connect_update_preedit_string(
      slot(slot_update_preedit_string));
   si->signal_connect_update_aux_string(
      slot(slot_update_aux_string));
   si->signal_connect_update_lookup_table(
      slot(slot_update_lookup_table));

   si->signal_connect_commit_string(
      slot(slot_commit_string));

   si->signal_connect_forward_key_event(
      slot(slot_forward_key_event));

   si->signal_connect_register_properties(
      slot(slot_register_properties));

   si->signal_connect_update_property(
      slot(slot_update_property));

   si->signal_connect_beep(
      slot(slot_beep));

   si->signal_connect_start_helper(
      slot(slot_start_helper));

   si->signal_connect_stop_helper(
      slot(slot_stop_helper));

   si->signal_connect_send_helper_event(
      slot(slot_send_helper_event));

   si->signal_connect_get_surrounding_text(
      slot(slot_get_surrounding_text));

   si->signal_connect_delete_surrounding_text(
      slot(slot_delete_surrounding_text));
}

// Implementation of slot functions
static void
slot_show_preedit_string(IMEngineInstanceBase *si)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *ic = static_cast<EcoreIMFContextISF *>(si->get_frontend_data());
   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   if (_focused_ic != ic)
     return;

   if (ic->impl->use_preedit)
     {
        if (!ic->impl->preedit_started)
          {
             ecore_imf_context_event_callback_call(_focused_ic->ctx, ECORE_IMF_CALLBACK_PREEDIT_START, NULL);
             ic->impl->preedit_started = true;
          }
     }
   else
     _panel_client.show_preedit_string(ic->id);
}

static void
slot_show_aux_string(IMEngineInstanceBase *si)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *ic = static_cast<EcoreIMFContextISF *>(si->get_frontend_data());
   EINA_SAFETY_ON_NULL_RETURN(ic);

   if (_focused_ic == ic)
     _panel_client.show_aux_string(ic->id);
}

static void
slot_show_lookup_table(IMEngineInstanceBase *si)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *ic = static_cast<EcoreIMFContextISF *>(si->get_frontend_data());
   EINA_SAFETY_ON_NULL_RETURN(ic);

   if (_focused_ic == ic)
     _panel_client.show_lookup_table(ic->id);
}

static void
slot_hide_preedit_string(IMEngineInstanceBase *si)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *ic = static_cast<EcoreIMFContextISF *>(si->get_frontend_data());
   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   if (_focused_ic != ic)
     return;

   bool emit = false;
   if (ic->impl->preedit_string.length())
     {
        ic->impl->preedit_string = WideString();
        ic->impl->preedit_caret = 0;
        ic->impl->preedit_attrlist.clear();
        emit = true;
     }
   if (ic->impl->use_preedit)
     {
        if (emit)
          {
             ecore_imf_context_event_callback_call(ic->ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
          }
        if (ic->impl->preedit_started)
          {
             ecore_imf_context_event_callback_call(ic->ctx, ECORE_IMF_CALLBACK_PREEDIT_END, NULL);
             ic->impl->preedit_started = false;
          }
     }
   else
     _panel_client.hide_preedit_string(ic->id);
}

static void
slot_hide_aux_string(IMEngineInstanceBase *si)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *ic = static_cast<EcoreIMFContextISF *>(si->get_frontend_data());
   EINA_SAFETY_ON_NULL_RETURN(ic);

   if (_focused_ic == ic)
     _panel_client.hide_aux_string(ic->id);
}

static void
slot_hide_lookup_table(IMEngineInstanceBase *si)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *ic = static_cast<EcoreIMFContextISF *>(si->get_frontend_data());
   EINA_SAFETY_ON_NULL_RETURN(ic);

   if (_focused_ic == ic)
     _panel_client.hide_lookup_table(ic->id);
}

static void
slot_update_preedit_caret(IMEngineInstanceBase *si, int caret)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *ic = static_cast<EcoreIMFContextISF *>(si->get_frontend_data());
   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   if (_focused_ic == ic && ic->impl->preedit_caret != caret)
     {
        ic->impl->preedit_caret = caret;
        if (ic->impl->use_preedit)
          {
             if (!ic->impl->preedit_started)
               {
                  ecore_imf_context_event_callback_call(ic->ctx, ECORE_IMF_CALLBACK_PREEDIT_START, NULL);
                  ic->impl->preedit_started = true;
               }
             ecore_imf_context_event_callback_call(ic->ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
          }
        else
          _panel_client.update_preedit_caret(ic->id, caret);
     }
}

static void
slot_update_preedit_string(IMEngineInstanceBase *si,
                            const WideString & str,
                            const AttributeList & attrs)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *ic = static_cast<EcoreIMFContextISF *>(si->get_frontend_data());
   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   if (_focused_ic == ic && (ic->impl->preedit_string != str || str.length()))
     {
        ic->impl->preedit_string   = str;
        ic->impl->preedit_attrlist = attrs;
        if (ic->impl->use_preedit)
          {
             if (!ic->impl->preedit_started)
               {
                  ecore_imf_context_event_callback_call(_focused_ic->ctx, ECORE_IMF_CALLBACK_PREEDIT_START, NULL);
                  ic->impl->preedit_started = true;
               }
             ic->impl->preedit_caret    = str.length();
             ic->impl->preedit_updating = true;
             ecore_imf_context_event_callback_call(ic->ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
             ic->impl->preedit_updating = false;
          }
        else
          {
             _panel_client.update_preedit_string(ic->id, str, attrs);
          }
     }
}

static void
slot_update_aux_string(IMEngineInstanceBase *si,
                        const WideString & str,
                        const AttributeList & attrs)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *ic = static_cast<EcoreIMFContextISF *>(si->get_frontend_data());
   EINA_SAFETY_ON_NULL_RETURN(ic);

   if (_focused_ic == ic)
     _panel_client.update_aux_string(ic->id, str, attrs);
}

static void
slot_commit_string(IMEngineInstanceBase *si,
                    const WideString & str)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *ic = static_cast<EcoreIMFContextISF *>(si->get_frontend_data());
   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->ctx);

   ecore_imf_context_event_callback_call(ic->ctx, ECORE_IMF_CALLBACK_COMMIT, (void *)utf8_wcstombs(str).c_str());
}

static void
slot_forward_key_event(IMEngineInstanceBase *si,
                        const KeyEvent & key)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *ic = static_cast<EcoreIMFContextISF *>(si->get_frontend_data());
   EINA_SAFETY_ON_NULL_RETURN(ic);

   if (_focused_ic == ic)
     {
        if (!_fallback_instance->process_key_event(key))
          _x_send_key_event(key);
     }
}

static void
slot_update_lookup_table(IMEngineInstanceBase *si,
                          const LookupTable & table)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *ic = static_cast<EcoreIMFContextISF *>(si->get_frontend_data());
   EINA_SAFETY_ON_NULL_RETURN(ic);

   if (_focused_ic == ic)
     _panel_client.update_lookup_table(ic->id, table);
}

static void
slot_register_properties(IMEngineInstanceBase *si,
                         const PropertyList & properties)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *ic = static_cast<EcoreIMFContextISF *>(si->get_frontend_data());
   EINA_SAFETY_ON_NULL_RETURN(ic);

   if (_focused_ic == ic)
     _panel_client.register_properties(ic->id, properties);
}

static void
slot_update_property(IMEngineInstanceBase *si,
                     const Property & property)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *ic = static_cast<EcoreIMFContextISF *>(si->get_frontend_data());
   EINA_SAFETY_ON_NULL_RETURN(ic);

   if (_focused_ic == ic)
     _panel_client.update_property(ic->id, property);
}

static void
slot_beep(IMEngineInstanceBase *si)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *ic = static_cast<EcoreIMFContextISF *>(si->get_frontend_data());
   EINA_SAFETY_ON_NULL_RETURN(ic);

   if (!ecore_x_display_get()) return;
   if (_focused_ic == ic)
     ecore_x_bell(0);
}

static void
slot_start_helper(IMEngineInstanceBase *si,
                  const String &helper_uuid)
{
   EcoreIMFContextISF *ic = static_cast<EcoreIMFContextISF *>(si->get_frontend_data());
   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   SCIM_DEBUG_FRONTEND(1) << __func__ << " helper= " << helper_uuid << " context="
      << ic->id << " ic=" << ic
      << " ic-uuid=" << ic->impl->si->get_factory_uuid() << "...\n";

   _panel_client.start_helper(ic->id, helper_uuid);
}

static void
slot_stop_helper(IMEngineInstanceBase *si,
                 const String &helper_uuid)
{
   EcoreIMFContextISF *ic = static_cast<EcoreIMFContextISF *>(si->get_frontend_data());
   EINA_SAFETY_ON_NULL_RETURN(ic);

   SCIM_DEBUG_FRONTEND(1) << __func__ << " helper= " << helper_uuid << " context=" << ic->id << " ic=" << ic << "...\n";

   _panel_client.stop_helper(ic->id, helper_uuid);
}

static void
slot_send_helper_event(IMEngineInstanceBase *si,
                       const String      &helper_uuid,
                       const Transaction &trans)
{
   EcoreIMFContextISF *ic = static_cast<EcoreIMFContextISF *>(si->get_frontend_data());
   EINA_SAFETY_ON_NULL_RETURN(ic);
   EINA_SAFETY_ON_NULL_RETURN(ic->impl);

   SCIM_DEBUG_FRONTEND(1) << __func__ << " helper= " << helper_uuid << " context="
      << ic->id << " ic=" << ic
      << " ic-uuid=" << ic->impl->si->get_factory_uuid() << "...\n";

   _panel_client.send_helper_event(ic->id, helper_uuid, trans);
}

static bool
slot_get_surrounding_text(IMEngineInstanceBase *si,
                          WideString            &text,
                          int                   &cursor,
                          int                    maxlen_before,
                          int                    maxlen_after)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *ic = static_cast<EcoreIMFContextISF *>(si->get_frontend_data());
   EINA_SAFETY_ON_NULL_RETURN_VAL(ic, false);
   EINA_SAFETY_ON_NULL_RETURN_VAL(ic->impl, false);

   if (_focused_ic != ic)
     return false;

   char *surrounding = NULL;
   int   cursor_index;
   if (ecore_imf_context_surrounding_get(_focused_ic->ctx, &surrounding, &cursor_index))
     {
        SCIM_DEBUG_FRONTEND(2) << "Surrounding text: " << surrounding <<"\n";
        SCIM_DEBUG_FRONTEND(2) << "Cursor Index    : " << cursor_index <<"\n";
        if (!surrounding)
          return false;

        if (cursor_index < 0)
          {
             free(surrounding);
             surrounding = NULL;
             return false;
          }

        WideString before(utf8_mbstowcs(String(surrounding, surrounding + cursor_index)));
        WideString after(utf8_mbstowcs(String(surrounding + cursor_index)));

        free(surrounding);
        surrounding = NULL;

        if (maxlen_before > 0 && ((unsigned int)maxlen_before) < before.length())
          before = WideString(before.begin() + (before.length() - maxlen_before), before.end());
        else if (maxlen_before == 0) before = WideString();
        if (maxlen_after > 0 && ((unsigned int)maxlen_after) < after.length())
          after = WideString(after.begin(), after.begin() + maxlen_after);
        else if (maxlen_after == 0) after = WideString();
        text = before + after;
        cursor = before.length();
        return true;
     }

   return false;
}

static bool
slot_delete_surrounding_text(IMEngineInstanceBase *si,
                             int                   offset,
                             int                   len)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   EcoreIMFContextISF *ic = static_cast<EcoreIMFContextISF *>(si->get_frontend_data());
   EINA_SAFETY_ON_NULL_RETURN_VAL(ic, false);

   if (_focused_ic != ic)
     return false;

   Ecore_IMF_Event_Delete_Surrounding ev;
   ev.ctx = _focused_ic->ctx;
   ev.n_chars = len;
   ev.offset = offset;
   ecore_imf_context_event_callback_call(_focused_ic->ctx, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, &ev);

   return true;
}

static void
reload_config_callback(const ConfigPointer &config)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   _frontend_hotkey_matcher.load_hotkeys(config);
   _imengine_hotkey_matcher.load_hotkeys(config);

   KeyEvent key;

   scim_string_to_key(key,
                      config->read(String(SCIM_CONFIG_HOTKEYS_FRONTEND_VALID_KEY_MASK),
                                     String("Shift+Control+Alt+Lock")));

   _valid_key_mask = (key.mask > 0)?(key.mask):0xFFFF;
   _valid_key_mask |= SCIM_KEY_ReleaseMask;
   // Special treatment for two backslash keys on jp106 keyboard.
   _valid_key_mask |= SCIM_KEY_QuirkKanaRoMask;

   _on_the_spot = config->read(String(SCIM_CONFIG_FRONTEND_ON_THE_SPOT), _on_the_spot);
   _shared_input_method = config->read(String(SCIM_CONFIG_FRONTEND_SHARED_INPUT_METHOD), _shared_input_method);

   // Get keyboard layout setting
   // Flush the global config first, in order to load the new configs from disk.
   scim_global_config_flush();

   _keyboard_layout = scim_get_default_keyboard_layout();
}

static void
fallback_commit_string_cb(IMEngineInstanceBase  *si EINA_UNUSED,
                          const WideString      &str)
{
   SCIM_DEBUG_FRONTEND(1) << __func__ << "...\n";

   if (_focused_ic)
     {
        ecore_imf_context_event_callback_call(_focused_ic->ctx, ECORE_IMF_CALLBACK_COMMIT, (void *)utf8_wcstombs(str).c_str());
     }
}

