/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
/* vim:set et sts=4: */
/* ibus - The Input Bus
* Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
* Copyright (C) 2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
* Copyright (C) 2008-2018 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include <stdarg.h>
#include <string.h>
#include "ibusaccelgroup.h"
#include "ibusengine.h"
#include "ibuskeysyms.h"
#include "ibusmarshalers.h"
#include "ibusinternal.h"
#include "ibusshare.h"
#include "ibusxevent.h"
#define IBUS_ENGINE_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_ENGINE, IBusEnginePrivate))
enum {
PROCESS_KEY_EVENT,
FOCUS_IN,
FOCUS_OUT,
RESET,
ENABLE,
DISABLE,
SET_CURSOR_LOCATION,
SET_CAPABILITIES,
PAGE_UP,
PAGE_DOWN,
CURSOR_UP,
CURSOR_DOWN,
PROPERTY_ACTIVATE,
PROPERTY_SHOW,
PROPERTY_HIDE,
CANDIDATE_CLICKED,
SET_SURROUNDING_TEXT,
PROCESS_HAND_WRITING_EVENT,
CANCEL_HAND_WRITING,
SET_CONTENT_TYPE,
LAST_SIGNAL,
};
enum {
PROP_0,
PROP_ENGINE_NAME,
};
/* IBusEnginePriv */
struct _IBusEnginePrivate {
gchar *engine_name;
GDBusConnection *connection;
/* cached surrounding text (see also IBusInputContextPrivate and
BusEngineProxy) */
IBusText *surrounding_text;
guint surrounding_cursor_pos;
guint selection_anchor_pos;
/* cached content-type */
guint content_purpose;
guint content_hints;
GHashTable *extension_keybindings;
gboolean enable_extension;
gchar *current_extension_name;
};
static guint engine_signals[LAST_SIGNAL] = { 0 };
static IBusText *text_empty;
/* functions prototype */
static void ibus_engine_destroy (IBusEngine *engine);
static void ibus_engine_set_property (IBusEngine *engine,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void ibus_engine_get_property (IBusEngine *engine,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void ibus_engine_service_method_call
(IBusService *service,
GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation
*invocation);
static GVariant *ibus_engine_service_get_property
(IBusService *service,
GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GError **error);
static gboolean ibus_engine_service_set_property
(IBusService *service,
GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GVariant *value,
GError **error);
static gboolean ibus_engine_process_key_event
(IBusEngine *engine,
guint keyval,
guint keycode,
guint state);
static void ibus_engine_focus_in (IBusEngine *engine);
static void ibus_engine_focus_out (IBusEngine *engine);
static void ibus_engine_reset (IBusEngine *engine);
static void ibus_engine_enable (IBusEngine *engine);
static void ibus_engine_disable (IBusEngine *engine);
static void ibus_engine_set_cursor_location
(IBusEngine *engine,
gint x,
gint y,
gint w,
gint h);
static void ibus_engine_set_capabilities
(IBusEngine *engine,
guint caps);
static void ibus_engine_page_up (IBusEngine *engine);
static void ibus_engine_page_down (IBusEngine *engine);
static void ibus_engine_cursor_up (IBusEngine *engine);
static void ibus_engine_cursor_down (IBusEngine *engine);
static void ibus_engine_candidate_clicked
(IBusEngine *engine,
guint index,
guint button,
guint state);
static void ibus_engine_property_activate
(IBusEngine *engine,
const gchar *prop_name,
guint prop_state);
static void ibus_engine_property_show (IBusEngine *engine,
const gchar *prop_name);
static void ibus_engine_property_hide (IBusEngine *engine,
const gchar *prop_name);
static void ibus_engine_set_surrounding_text
(IBusEngine *engine,
IBusText *text,
guint cursor_pos,
guint anchor_pos);
static void ibus_engine_process_hand_writing_event
(IBusEngine *engine,
const gdouble *coordinates,
guint coordinates_len);
static void ibus_engine_cancel_hand_writing
(IBusEngine *engine,
guint n_strokes);
static void ibus_engine_set_content_type
(IBusEngine *engine,
guint purpose,
guint hints);
static void ibus_engine_emit_signal (IBusEngine *engine,
const gchar *signal_name,
GVariant *parameters);
static void ibus_engine_dbus_property_changed
(IBusEngine *engine,
const gchar *property_name,
GVariant *value);
G_DEFINE_TYPE (IBusEngine, ibus_engine, IBUS_TYPE_SERVICE)
static const gchar introspection_xml[] =
"<node>"
" <interface name='org.freedesktop.IBus.Engine'>"
/* FIXME methods */
" <method name='ProcessKeyEvent'>"
" <arg direction='in' type='u' name='keyval' />"
" <arg direction='in' type='u' name='keycode' />"
" <arg direction='in' type='u' name='state' />"
" <arg direction='out' type='b' />"
" </method>"
" <method name='SetCursorLocation'>"
" <arg direction='in' type='i' name='x' />"
" <arg direction='in' type='i' name='y' />"
" <arg direction='in' type='i' name='w' />"
" <arg direction='in' type='i' name='h' />"
" </method>"
" <method name='ProcessHandWritingEvent'>"
" <arg direction='in' type='ad' name='coordinates' />"
" </method>"
" <method name='CancelHandWriting'>"
" <arg direction='in' type='u' name='n_strokes' />"
" </method>"
" <method name='SetCapabilities'>"
" <arg direction='in' type='u' name='caps' />"
" </method>"
" <method name='PropertyActivate'>"
" <arg direction='in' type='s' name='name' />"
" <arg direction='in' type='u' name='state' />"
" </method>"
" <method name='PropertyShow'>"
" <arg direction='in' type='s' name='name' />"
" </method>"
" <method name='PropertyHide'>"
" <arg direction='in' type='s' name='name' />"
" </method>"
" <method name='CandidateClicked'>"
" <arg direction='in' type='u' name='index' />"
" <arg direction='in' type='u' name='button' />"
" <arg direction='in' type='u' name='state' />"
" </method>"
" <method name='FocusIn' />"
" <method name='FocusOut' />"
" <method name='Reset' />"
" <method name='Enable' />"
" <method name='Disable' />"
" <method name='PageUp' />"
" <method name='PageDown' />"
" <method name='CursorUp' />"
" <method name='CursorDown' />"
" <method name='SetSurroundingText'>"
" <arg direction='in' type='v' name='text' />"
" <arg direction='in' type='u' name='cursor_pos' />"
" <arg direction='in' type='u' name='anchor_pos' />"
" </method>"
" <method name='PanelExtensionReceived'>"
" <arg direction='in' type='v' name='event' />"
" </method>"
" <method name='PanelExtensionRegisterKeys'>"
" <arg direction='in' type='v' name='data' />"
" </method>"
/* FIXME signals */
" <signal name='CommitText'>"
" <arg type='v' name='text' />"
" </signal>"
" <signal name='UpdatePreeditText'>"
" <arg type='v' name='text' />"
" <arg type='u' name='cursor_pos' />"
" <arg type='b' name='visible' />"
" <arg type='u' name='mode' />"
" </signal>"
" <signal name='UpdateAuxiliaryText'>"
" <arg type='v' name='text' />"
" <arg type='b' name='visible' />"
" </signal>"
" <signal name='UpdateLookupTable'>"
" <arg type='v' name='table' />"
" <arg type='b' name='visible' />"
" </signal>"
" <signal name='RegisterProperties'>"
" <arg type='v' name='props' />"
" </signal>"
" <signal name='UpdateProperty'>"
" <arg type='v' name='prop' />"
" </signal>"
" <signal name='ForwardKeyEvent'>"
" <arg type='u' name='keyval' />"
" <arg type='u' name='keycode' />"
" <arg type='u' name='state' />"
" </signal>"
" <signal name='PanelExtension'>"
" <arg type='v' name='data' />"
" </signal>"
/* FIXME properties */
" <property name='ContentType' type='(uu)' access='write' />"
" </interface>"
"</node>";
static const guint IBUS_MODIFIER_FILTER =
IBUS_MODIFIER_MASK & ~(
IBUS_LOCK_MASK | /* Caps Lock */
IBUS_MOD2_MASK | /* Num Lock */
IBUS_BUTTON1_MASK |
IBUS_BUTTON2_MASK |
IBUS_BUTTON3_MASK |
IBUS_BUTTON4_MASK |
IBUS_BUTTON5_MASK |
IBUS_SUPER_MASK |
IBUS_HYPER_MASK |
IBUS_META_MASK);
static void
ibus_engine_class_init (IBusEngineClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (class);
gobject_class->set_property =
(GObjectSetPropertyFunc) ibus_engine_set_property;
gobject_class->get_property =
(GObjectGetPropertyFunc) ibus_engine_get_property;
ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_engine_destroy;
IBUS_SERVICE_CLASS (class)->service_method_call =
ibus_engine_service_method_call;
IBUS_SERVICE_CLASS (class)->service_get_property =
ibus_engine_service_get_property;
IBUS_SERVICE_CLASS (class)->service_set_property =
ibus_engine_service_set_property;
ibus_service_class_add_interfaces (IBUS_SERVICE_CLASS (class),
introspection_xml);
class->process_key_event = ibus_engine_process_key_event;
class->focus_in = ibus_engine_focus_in;
class->focus_out = ibus_engine_focus_out;
class->reset = ibus_engine_reset;
class->enable = ibus_engine_enable;
class->disable = ibus_engine_disable;
class->page_up = ibus_engine_page_up;
class->page_down = ibus_engine_page_down;
class->cursor_up = ibus_engine_cursor_up;
class->cursor_down = ibus_engine_cursor_down;
class->candidate_clicked = ibus_engine_candidate_clicked;
class->property_activate = ibus_engine_property_activate;
class->property_show = ibus_engine_property_show;
class->property_hide = ibus_engine_property_hide;
class->set_cursor_location = ibus_engine_set_cursor_location;
class->set_capabilities = ibus_engine_set_capabilities;
class->set_surrounding_text = ibus_engine_set_surrounding_text;
class->process_hand_writing_event
= ibus_engine_process_hand_writing_event;
class->cancel_hand_writing = ibus_engine_cancel_hand_writing;
class->set_content_type = ibus_engine_set_content_type;
/* install properties */
/**
* IBusEngine:name:
*
* Name of this IBusEngine.
*/
g_object_class_install_property (gobject_class,
PROP_ENGINE_NAME,
g_param_spec_string ("engine-name",
"engine name",
"engine name",
"noname",
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
/* install signals */
/**
* IBusEngine::process-key-event:
* @engine: An IBusEngine.
* @keyval: Key symbol of the key press.
* @keycode: KeyCode of the key press.
* @state: Key modifier flags.
*
* Emitted when a key event is received.
* Implement the member function IBusEngineClass::process_key_event
* in extended class to receive this signal.
* Both the key symbol and keycode are passed to the member function.
* See ibus_input_context_process_key_event() for further explanation of
* key symbol, keycode and which to use.
*
* Returns: %TRUE for successfully process the key; %FALSE otherwise.
* See also: ibus_input_context_process_key_event().
*
* <note><para>Argument @user_data is ignored in this function.</para></note>
*/
engine_signals[PROCESS_KEY_EVENT] =
g_signal_new (I_("process-key-event"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusEngineClass, process_key_event),
g_signal_accumulator_true_handled, NULL,
_ibus_marshal_BOOLEAN__UINT_UINT_UINT,
G_TYPE_BOOLEAN,
3,
G_TYPE_UINT,
G_TYPE_UINT,
G_TYPE_UINT);
/**
* IBusEngine::focus-in:
* @engine: An IBusEngine.
*
* Emitted when the client application get the focus.
* Implement the member function IBusEngineClass::focus_in
* in extended class to receive this signal.
*
* See also: ibus_input_context_focus_in()
* <note><para>Argument @user_data is ignored in this function.</para></note>
*/
engine_signals[FOCUS_IN] =
g_signal_new (I_("focus-in"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusEngineClass, focus_in),
NULL, NULL,
_ibus_marshal_VOID__VOID,
G_TYPE_NONE,
0);
/**
* IBusEngine::focus-out:
* @engine: An IBusEngine.
*
* Emitted when the client application lost the focus.
* Implement the member function IBusEngineClass::focus_out
* in extended class to receive this signal.
*
* See also: ibus_input_context_focus_out()
* <note><para>Argument @user_data is ignored in this function.</para></note>
*/
engine_signals[FOCUS_OUT] =
g_signal_new (I_("focus-out"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusEngineClass, focus_out),
NULL, NULL,
_ibus_marshal_VOID__VOID,
G_TYPE_NONE,
0);
/**
* IBusEngine::reset:
* @engine: An IBusEngine.
*
* Emitted when the IME is reset.
* Implement the member function IBusEngineClass::reset
* in extended class to receive this signal.
*
* See also: ibus_input_context_reset().
* <note><para>Argument @user_data is ignored in this function.</para></note>
*/
engine_signals[RESET] =
g_signal_new (I_("reset"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusEngineClass, reset),
NULL, NULL,
_ibus_marshal_VOID__VOID,
G_TYPE_NONE,
0);
/**
* IBusEngine::enable:
* @engine: An IBusEngine.
*
* Emitted when the IME is enabled.
* Implement the member function IBusEngineClass::enable
* in extended class to receive this signal.
*
* See also: ibus_bus_set_global_engine().
* <note><para>Argument @user_data is ignored in this function.</para></note>
*/
engine_signals[ENABLE] =
g_signal_new (I_("enable"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusEngineClass, enable),
NULL, NULL,
_ibus_marshal_VOID__VOID,
G_TYPE_NONE,
0);
/**
* IBusEngine::disable:
* @engine: An IBusEngine.
*
* Emitted when the IME is disabled.
* Implement the member function IBusEngineClass::disable
* in extended class to receive this signal.
*
* See also: ibus_bus_set_global_engine().
* <note><para>Argument @user_data is ignored in this function.</para></note>
*/
engine_signals[DISABLE] =
g_signal_new (I_("disable"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusEngineClass, disable),
NULL, NULL,
_ibus_marshal_VOID__VOID,
G_TYPE_NONE,
0);
/**
* IBusEngine::set-cursor-location:
* @engine: An IBusEngine.
* @x: X coordinate of the cursor.
* @y: Y coordinate of the cursor.
* @w: Width of the cursor.
* @h: Height of the cursor.
*
* Emitted when the location of IME is set.
* Implement the member function IBusEngineClass::set_cursor_location
* in extended class to receive this signal.
*
* See also: ibus_input_context_set_cursor_location().
* <note><para>Argument @user_data is ignored in this function.</para></note>
*/
engine_signals[SET_CURSOR_LOCATION] =
g_signal_new (I_("set-cursor-location"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusEngineClass, set_cursor_location),
NULL, NULL,
_ibus_marshal_VOID__INT_INT_INT_INT,
G_TYPE_NONE,
4,
G_TYPE_INT,
G_TYPE_INT,
G_TYPE_INT,
G_TYPE_INT);
/**
* IBusEngine::set-capabilities:
* @engine: An IBusEngine.
* @caps: Capabilities flags of IBusEngine, see #IBusCapabilite
*
* Emitted when the client application capabilities is set.
* Implement the member function IBusEngineClass::set_capabilities
* in extended class to receive this signal.
*
* See also: ibus_input_context_set_capabilities().
* <note><para>Argument @user_data is ignored in this function.</para></note>
*/
engine_signals[SET_CAPABILITIES] =
g_signal_new (I_("set-capabilities"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusEngineClass, set_capabilities),
NULL, NULL,
_ibus_marshal_VOID__UINT,
G_TYPE_NONE,
1,
G_TYPE_UINT);
/**
* IBusEngine::page-up:
* @engine: An IBusEngine.
*
* Emitted when the page-up button is pressed.
* Implement the member function IBusEngineClass::page_up
* in extended class to receive this signal.
*
* <note><para>Argument @user_data is ignored in this function.</para></note>
*/
engine_signals[PAGE_UP] =
g_signal_new (I_("page-up"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusEngineClass, page_up),
NULL, NULL,
_ibus_marshal_VOID__VOID,
G_TYPE_NONE,
0);
/**
* IBusEngine::page-down:
* @engine: An IBusEngine.
*
* Emitted when the page-down button is pressed.
* Implement the member function IBusEngineClass::page_down
* in extended class to receive this signal.
*
* <note><para>Argument @user_data is ignored in this function.</para></note>
*/
engine_signals[PAGE_DOWN] =
g_signal_new (I_("page-down"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusEngineClass, page_down),
NULL, NULL,
_ibus_marshal_VOID__VOID,
G_TYPE_NONE,
0);
/**
* IBusEngine::cursor-up:
* @engine: An IBusEngine.
*
* Emitted when the up cursor button is pressed.
* Implement the member function IBusEngineClass::cursor_up
* in extended class to receive this signal.
*
* <note><para>Argument @user_data is ignored in this function.</para></note>
*/
engine_signals[CURSOR_UP] =
g_signal_new (I_("cursor-up"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusEngineClass, cursor_up),
NULL, NULL,
_ibus_marshal_VOID__VOID,
G_TYPE_NONE,
0);
/**
* IBusEngine::cursor-down:
* @engine: An IBusEngine.
*
* Emitted when the down cursor button is pressed.
* Implement the member function IBusEngineClass::cursor_down
* in extended class to receive this signal.
*
* <note><para>Argument @user_data is ignored in this function.</para></note>
*/
engine_signals[CURSOR_DOWN] =
g_signal_new (I_("cursor-down"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusEngineClass, cursor_down),
NULL, NULL,
_ibus_marshal_VOID__VOID,
G_TYPE_NONE,
0);
/**
* IBusEngine::candidate-clicked:
* @engine: An IBusEngine.
* @index: Index of candidate be clicked.
* @button: Mouse button.
* @state: Keyboard state.
*
* Emitted when candidate on lookup table is clicked.
* Implement the member function IBusEngineClass::candidate_clicked
* in extended class to receive this signal.
*
* <note><para>Argument @user_data is ignored in this function.</para></note>
*/
engine_signals[CANDIDATE_CLICKED] =
g_signal_new (I_("candidate-clicked"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusEngineClass, candidate_clicked),
NULL, NULL,
_ibus_marshal_VOID__UINT_UINT_UINT,
G_TYPE_NONE,
3,
G_TYPE_UINT,
G_TYPE_UINT,
G_TYPE_UINT);
/**
* IBusEngine::property-activate:
* @engine: An IBusEngine.
* @name: Property name.
* @state: Property state.
*
* Emitted when a property is activated or change changed.
* Implement the member function IBusEngineClass::property_activate
* in extended class to receive this signal.
*
* <note><para>Argument @user_data is ignored in this function.</para></note>
*/
engine_signals[PROPERTY_ACTIVATE] =
g_signal_new (I_("property-activate"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusEngineClass, property_activate),
NULL, NULL,
_ibus_marshal_VOID__STRING_UINT,
G_TYPE_NONE,
2,
G_TYPE_STRING,
G_TYPE_UINT);
/**
* IBusEngine::property-show:
* @engine: An IBusEngine.
* @name: Property name.
*
* Emitted when a property is shown.
* Implement the member function IBusEngineClass::property_side
* in extended class to receive this signal.
*
* <note><para>Argument @user_data is ignored in this function.</para></note>
*/
engine_signals[PROPERTY_SHOW] =
g_signal_new (I_("property-show"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusEngineClass, property_show),
NULL, NULL,
_ibus_marshal_VOID__STRING,
G_TYPE_NONE,
1,
G_TYPE_STRING);
/**
* IBusEngine::property-hide:
* @engine: An IBusEngine.
* @name: Property name.
*
* Emitted when a property is hidden.
* Implement the member function IBusEngineClass::property_hide
* in extended class to receive this signal.
*
* <note><para>Argument @user_data is ignored in this function.</para></note>
*/
engine_signals[PROPERTY_HIDE] =
g_signal_new (I_("property-hide"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusEngineClass, property_hide),
NULL, NULL,
_ibus_marshal_VOID__STRING,
G_TYPE_NONE,
1,
G_TYPE_STRING);
/**
* IBusEngine::process-hand-writing-event:
* @engine: An IBusEngine.
* @coordinates: An array of double (0.0 to 1.0) which represents a stroke (i.e. [x1, y1, x2, y2, x3, y3, ...]).
* @coordinates_len: The number of elements in the array.
*
* Emitted when a hand writing operation is cancelled.
* Implement the member function IBusEngineClass::cancel_hand_writing
* in extended class to receive this signal.
*
* <note><para>Argument @user_data is ignored in this function.</para></note>
*/
engine_signals[PROCESS_HAND_WRITING_EVENT] =
g_signal_new (I_("process-hand-writing-event"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusEngineClass, process_hand_writing_event),
NULL, NULL,
_ibus_marshal_VOID__POINTER_UINT,
G_TYPE_NONE,
2,
G_TYPE_POINTER,
G_TYPE_UINT);
/**
* IBusEngine::cancel-hand-writing:
* @engine: An IBusEngine.
* @n_strokes: The number of strokes to be removed. 0 means "remove all".
*
* Emitted when a hand writing operation is cancelled.
* Implement the member function IBusEngineClass::cancel_hand_writing
* in extended class to receive this signal.
*
* <note><para>Argument @user_data is ignored in this function.</para></note>
*/
engine_signals[CANCEL_HAND_WRITING] =
g_signal_new (I_("cancel-hand-writing"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusEngineClass, cancel_hand_writing),
NULL, NULL,
_ibus_marshal_VOID__UINT,
G_TYPE_NONE,
1,
G_TYPE_UINT);
/**
* IBusEngine::set-surrounding-text:
* @engine: An IBusEngine.
* @text: The surrounding text.
* @cursor_pos: The cursor position on surrounding text.
* @anchor_pos: The anchor position on selection area.
*
* Emitted when a surrounding text is set.
* Implement the member function IBusEngineClass::set_surrounding_text
* in extended class to receive this signal.
* If anchor_pos equals to cursor_pos, it means "there are no selection"
* or "does not support selection retrival".
*
* <note><para>Argument @user_data is ignored in this function.</para></note>
*/
engine_signals[SET_SURROUNDING_TEXT] =
g_signal_new (I_("set-surrounding-text"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusEngineClass, set_surrounding_text),
NULL, NULL,
_ibus_marshal_VOID__OBJECT_UINT_UINT,
G_TYPE_NONE,
3,
G_TYPE_OBJECT,
G_TYPE_UINT,
G_TYPE_UINT);
/**
* IBusEngine::set-content-type:
* @engine: An #IBusEngine.
* @purpose: Primary purpose of the input context, as an #IBusInputPurpose.
* @hints: Hints that augment @purpose, as an #IBusInputHints.
*
* Emitted when the client application content-type (primary
* purpose and hints) is set. The engine could change the
* behavior according to the content-type. Implement the member
* function IBusEngineClass::set_content_type
* in extended class to receive this signal.
*
* For example, if the client application wants to restrict input
* to numbers, this signal will be emitted with @purpose set to
* #IBUS_INPUT_PURPOSE_NUMBER, so the engine can switch the input
* mode to latin.
*
* <note><para>Argument @user_data is ignored in this
* function.</para></note>
*/
engine_signals[SET_CONTENT_TYPE] =
g_signal_new (I_("set-content-type"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusEngineClass, set_content_type),
NULL, NULL,
_ibus_marshal_VOID__UINT_UINT,
G_TYPE_NONE,
2,
G_TYPE_UINT,
G_TYPE_UINT);
g_type_class_add_private (class, sizeof (IBusEnginePrivate));
text_empty = ibus_text_new_from_static_string ("");
g_object_ref_sink (text_empty);
}
static void
ibus_engine_init (IBusEngine *engine)
{
IBusEnginePrivate *priv;
engine->priv = priv = IBUS_ENGINE_GET_PRIVATE (engine);
priv->surrounding_text = g_object_ref_sink (text_empty);
priv->extension_keybindings = g_hash_table_new_full (
g_str_hash,
g_str_equal,
g_free,
g_free);
}
static void
ibus_engine_destroy (IBusEngine *engine)
{
IBusEnginePrivate *priv = engine->priv;
g_clear_pointer (&priv->engine_name, g_free);
g_clear_pointer (&priv->current_extension_name, g_free);
if (priv->surrounding_text)
g_clear_object (&priv->surrounding_text);
if (priv->extension_keybindings)
g_clear_pointer (&priv->extension_keybindings, g_hash_table_destroy);
IBUS_OBJECT_CLASS(ibus_engine_parent_class)->destroy (IBUS_OBJECT (engine));
}
static void
ibus_engine_set_property (IBusEngine *engine,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
switch (prop_id) {
case PROP_ENGINE_NAME:
engine->priv->engine_name = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (engine, prop_id, pspec);
}
}
static void
ibus_engine_get_property (IBusEngine *engine,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
switch (prop_id) {
case PROP_ENGINE_NAME:
g_value_set_string (value, engine->priv->engine_name);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (engine, prop_id, pspec);
}
}
static void
ibus_engine_panel_extension (IBusEngine *engine,
const gchar *name)
{
IBusEnginePrivate *priv;
IBusExtensionEvent *event;
GVariant *data;
g_assert (IBUS_IS_ENGINE (engine));
g_assert (name);
priv = engine->priv;
if (!g_strcmp0 (name, priv->current_extension_name))
priv->enable_extension = !priv->enable_extension;
else
priv->enable_extension = TRUE;
if (priv->enable_extension) {
g_free (priv->current_extension_name);
priv->current_extension_name = g_strdup (name);
}
event = ibus_extension_event_new (
"name", name,
"is-enabled", priv->enable_extension,
NULL);
g_assert (IBUS_IS_EXTENSION_EVENT (event));
data = ibus_serializable_serialize_object (
IBUS_SERIALIZABLE (event));
g_assert (data != NULL);
ibus_engine_emit_signal (engine,
"PanelExtension",
g_variant_new ("(v)", data));
g_object_unref (event);
}
static gboolean
ibus_engine_filter_key_event (IBusEngine *engine,
guint keyval,
guint keycode,
guint state)
{
IBusEnginePrivate *priv;
GList *names, *n;
IBusProcessKeyEventData *keys;
guint modifiers;
if ((state & IBUS_RELEASE_MASK) != 0)
return FALSE;
g_return_val_if_fail (IBUS_IS_ENGINE (engine), FALSE);
priv = engine->priv;
modifiers = state & IBUS_MODIFIER_FILTER;
if (keyval >= IBUS_KEY_A && keyval <= IBUS_KEY_Z &&
(modifiers & IBUS_SHIFT_MASK) != 0) {
keyval = keyval - IBUS_KEY_A + IBUS_KEY_a;
}
names = g_hash_table_get_keys (priv->extension_keybindings);
if (!names)
return FALSE;
for (n = names; n; n = n->next) {
const gchar *name = (const gchar *)n->data;
keys = g_hash_table_lookup (priv->extension_keybindings, name);
for (; keys; keys++) {
if (keys->keyval == 0 && keys->keycode == 0 && keys->state == 0)
break;
if (keys->keyval == keyval &&
keys->state == modifiers &&
(keys->keycode == 0 || keys->keycode == keycode)) {
ibus_engine_panel_extension (engine, name);
return TRUE;
}
}
}
g_list_free (names);
return FALSE;
}
static gboolean
ibus_engine_service_authorized_method (IBusService *service,
GDBusConnection *connection)
{
if (ibus_service_get_connection (service) == connection)
return TRUE;
return FALSE;
}
static void
ibus_engine_service_panel_extension_register_keys (IBusEngine *engine,
GVariant *parameters,
GDBusMethodInvocation
*invocation)
{
IBusEnginePrivate *priv = engine->priv;
GVariant *v1 = NULL;
GVariant *v2 = NULL;
GVariant *v3 = NULL;
GVariant *vkeys = NULL;
GVariantIter *iter1 = NULL;
GVariantIter *iter2 = NULL;
const gchar *name = NULL;
guint failure_id = 0;
g_variant_get (parameters, "(v)", &v1);
if (v1)
g_variant_get (v1, "(v)", &v2);
else
failure_id = 1;
if (v2)
g_variant_get (v2, "a{sv}", &iter1);
else
failure_id = 2;
if (iter1) {
while (g_variant_iter_loop (iter1, "{&sv}", &name, &vkeys)) {
if (vkeys)
g_variant_get (vkeys, "av", &iter2);
if (name && iter2) {
IBusProcessKeyEventData *keys = NULL;
gint num = 0;
while (g_variant_iter_loop (iter2, "v", &v3)) {
if (v3) {
guint keyval = 0;
guint keycode = 0;
guint state = 0;
g_variant_get (v3, "(iii)",
&keyval, &keycode, &state);
if (!keys)
keys = g_new0 (IBusProcessKeyEventData, 2);
else
keys = g_renew (IBusProcessKeyEventData,
keys,
num + 2);
keys[num].keyval = keyval;
keys[num].keycode = keycode;
keys[num].state = state;
keys[num + 1].keyval = 0;
keys[num + 1].keycode = 0;
keys[num + 1].state = 0;
g_clear_pointer (&v3, g_variant_unref);
num++;
} else {
failure_id = 5;
}
}
if (num > 0) {
g_hash_table_replace (priv->extension_keybindings,
g_strdup (name),
keys);
} else {
g_hash_table_remove (priv->extension_keybindings, name);
}
g_clear_pointer (&iter2, g_variant_iter_free);
} else {
failure_id = 4;
}
g_clear_pointer (&vkeys, g_variant_unref);
name = NULL;
}
g_variant_iter_free (iter1);
} else {
failure_id = 3;
}
if (failure_id == 0) {
g_dbus_method_invocation_return_value (invocation, NULL);
} else {
g_dbus_method_invocation_return_error (
invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"PanelExtensionRegisterKeys method gives NULL: %d",
failure_id);
}
if (v2)
g_variant_unref (v2);
if (v1)
g_variant_unref (v1);
}
static void
ibus_engine_service_method_call (IBusService *service,
GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation)
{
IBusEngine *engine = IBUS_ENGINE (service);
IBusEnginePrivate *priv = engine->priv;
if (g_strcmp0 (interface_name, IBUS_INTERFACE_ENGINE) != 0) {
IBUS_SERVICE_CLASS (ibus_engine_parent_class)->
service_method_call (service,
connection,
sender,
object_path,
interface_name,
method_name,
parameters,
invocation);
return;
}
if (!ibus_engine_service_authorized_method (service, connection))
return;
if (g_strcmp0 (method_name, "ProcessKeyEvent") == 0) {
guint keyval, keycode, state;
gboolean retval = FALSE;
g_variant_get (parameters, "(uuu)", &keyval, &keycode, &state);
g_signal_emit (engine,
engine_signals[PROCESS_KEY_EVENT],
0,
keyval,
keycode,
state,
&retval);
if (!retval) {
retval = ibus_engine_filter_key_event (engine,
keyval,
keycode,
state);
}
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", retval));
return;
}
if (g_strcmp0 (method_name, "PanelExtensionReceived") == 0) {
GVariant *arg0 = NULL;
IBusExtensionEvent *event = NULL;
g_variant_get (parameters, "(v)", &arg0);
if (arg0) {
event = (IBusExtensionEvent *)ibus_serializable_deserialize_object (
arg0);
}
if (!event) {
g_dbus_method_invocation_return_error (
invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"PanelExtensionReceived method gives NULL");
return;
}
priv->enable_extension = ibus_extension_event_is_enabled (event);
g_dbus_method_invocation_return_value (invocation, NULL);
return;
}
if (g_strcmp0 (method_name, "PanelExtensionRegisterKeys") == 0) {
ibus_engine_service_panel_extension_register_keys (engine,
parameters,
invocation);
return;
}
static const struct {
gchar *member;
guint signal_id;
} no_arg_methods[] = {
{ "FocusIn", FOCUS_IN },
{ "FocusOut", FOCUS_OUT },
{ "Reset", RESET },
{ "Enable", ENABLE },
{ "Disable", DISABLE },
{ "PageUp", PAGE_UP },
{ "PageDown", PAGE_DOWN },
{ "CursorUp", CURSOR_UP },
{ "CursorDown", CURSOR_DOWN },
};
gint i;
for (i = 0; i < G_N_ELEMENTS (no_arg_methods); i++) {
if (g_strcmp0 (method_name, no_arg_methods[i].member) == 0) {
g_signal_emit (engine, engine_signals[no_arg_methods[i].signal_id], 0);
g_dbus_method_invocation_return_value (invocation, NULL);
return;
}
}
if (g_strcmp0 (method_name, "CandidateClicked") == 0) {
guint index, button, state;
g_variant_get (parameters, "(uuu)", &index, &button, &state);
g_signal_emit (engine,
engine_signals[CANDIDATE_CLICKED],
0,
index,
button,
state);
g_dbus_method_invocation_return_value (invocation, NULL);
return;
}
if (g_strcmp0 (method_name, "PropertyActivate") == 0) {
gchar *name;
guint state;
g_variant_get (parameters, "(&su)", &name, &state);
g_signal_emit (engine,
engine_signals[PROPERTY_ACTIVATE],
0,
name,
state);
g_dbus_method_invocation_return_value (invocation, NULL);
return;
}
if (g_strcmp0 (method_name, "PropertyShow") == 0) {
gchar *name;
g_variant_get (parameters, "(&s)", &name);
g_signal_emit (engine,
engine_signals[PROPERTY_SHOW],
0,
name);
g_dbus_method_invocation_return_value (invocation, NULL);
return;
}
if (g_strcmp0 (method_name, "PropertyHide") == 0) {
gchar *name;
g_variant_get (parameters, "(&s)", &name);
g_signal_emit (engine,
engine_signals[PROPERTY_HIDE],
0,
name);
g_dbus_method_invocation_return_value (invocation, NULL);
return;
}
if (g_strcmp0 (method_name, "SetCursorLocation") == 0) {
gint x, y, w, h;
g_variant_get (parameters, "(iiii)", &x, &y, &w, &h);
engine->cursor_area.x = x;
engine->cursor_area.y = y;
engine->cursor_area.width = w;
engine->cursor_area.height = h;
g_signal_emit (engine,
engine_signals[SET_CURSOR_LOCATION],
0,
x, y, w, h);
g_dbus_method_invocation_return_value (invocation, NULL);
return;
}
if (g_strcmp0 (method_name, "SetCapabilities") == 0) {
guint caps;
g_variant_get (parameters, "(u)", &caps);
engine->client_capabilities = caps;
g_signal_emit (engine, engine_signals[SET_CAPABILITIES], 0, caps);
g_dbus_method_invocation_return_value (invocation, NULL);
return;
}
if (g_strcmp0 (method_name, "SetSurroundingText") == 0) {
GVariant *variant = NULL;
IBusText *text;
guint cursor_pos;
guint anchor_pos;
g_variant_get (parameters,
"(vuu)",
&variant,
&cursor_pos,
&anchor_pos);
text = IBUS_TEXT (ibus_serializable_deserialize (variant));
g_variant_unref (variant);
g_signal_emit (engine, engine_signals[SET_SURROUNDING_TEXT],
0,
text,
cursor_pos,
anchor_pos);
if (g_object_is_floating (text)) {
g_object_unref (text);
}
g_dbus_method_invocation_return_value (invocation, NULL);
return;
}
if (g_strcmp0 (method_name, "ProcessHandWritingEvent") == 0) {
const gdouble *coordinates;
gsize coordinates_len = 0;
coordinates = g_variant_get_fixed_array (g_variant_get_child_value (parameters, 0), &coordinates_len, sizeof (gdouble));
g_return_if_fail (coordinates != NULL);
g_return_if_fail (coordinates_len >= 4); /* The array should contain at least one line. */
g_return_if_fail (coordinates_len <= G_MAXUINT); /* to prevent overflow in the cast in g_signal_emit */
g_return_if_fail ((coordinates_len & 1) == 0);
g_signal_emit (engine, engine_signals[PROCESS_HAND_WRITING_EVENT], 0,
coordinates, (guint) coordinates_len);
g_dbus_method_invocation_return_value (invocation, NULL);
return;
}
if (g_strcmp0 (method_name, "CancelHandWriting") == 0) {
guint n_strokes = 0;
g_variant_get (parameters, "(u)", &n_strokes);
g_signal_emit (engine, engine_signals[CANCEL_HAND_WRITING], 0, n_strokes);
g_dbus_method_invocation_return_value (invocation, NULL);
return;
}
/* should not be reached */
g_return_if_reached ();
}
static GVariant *
ibus_engine_service_get_property (IBusService *service,
GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GError **error)
{
return IBUS_SERVICE_CLASS (ibus_engine_parent_class)->
service_get_property (service,
connection,
sender,
object_path,
interface_name,
property_name,
error);
}
static gboolean
ibus_engine_service_set_property (IBusService *service,
GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GVariant *value,
GError **error)
{
IBusEngine *engine = IBUS_ENGINE (service);
if (g_strcmp0 (interface_name, IBUS_INTERFACE_ENGINE) != 0) {
return IBUS_SERVICE_CLASS (ibus_engine_parent_class)->
service_set_property (service,
connection,
sender,
object_path,
interface_name,
property_name,
value,
error);
}
if (!ibus_engine_service_authorized_method (service, connection))
return FALSE;
if (g_strcmp0 (property_name, "ContentType") == 0) {
guint purpose = 0;
guint hints = 0;
g_variant_get (value, "(uu)", &purpose, &hints);
if (purpose != engine->priv->content_purpose ||
hints != engine->priv->content_hints) {
engine->priv->content_purpose = purpose;
engine->priv->content_hints = hints;
g_signal_emit (engine,
engine_signals[SET_CONTENT_TYPE],
0,
purpose,
hints);
ibus_engine_dbus_property_changed (engine, "ContentType", value);
}
return TRUE;
}
g_return_val_if_reached (FALSE);
}
static gboolean
ibus_engine_process_key_event (IBusEngine *engine,
guint keyval,
guint keycode,
guint state)
{
return FALSE;
}
static void
ibus_engine_focus_in (IBusEngine *engine)
{
// g_debug ("focus-in");
}
static void
ibus_engine_focus_out (IBusEngine *engine)
{
// g_debug ("focus-out");
}
static void
ibus_engine_reset (IBusEngine *engine)
{
// g_debug ("reset");
}
static void
ibus_engine_enable (IBusEngine *engine)
{
// g_debug ("enable");
}
static void
ibus_engine_disable (IBusEngine *engine)
{
// g_debug ("disable");
}
static void
ibus_engine_set_cursor_location (IBusEngine *engine,
gint x,
gint y,
gint w,
gint h)
{
// g_debug ("set-cursor-location (%d, %d, %d, %d)", x, y, w, h);
}
static void
ibus_engine_set_capabilities (IBusEngine *engine,
guint caps)
{
// g_debug ("set-capabilities (0x%04x)", caps);
}
static void
ibus_engine_page_up (IBusEngine *engine)
{
// g_debug ("page-up");
}
static void
ibus_engine_page_down (IBusEngine *engine)
{
// g_debug ("page-down");
}
static void
ibus_engine_cursor_up (IBusEngine *engine)
{
// g_debug ("cursor-up");
}
static void
ibus_engine_cursor_down (IBusEngine *engine)
{
// g_debug ("cursor-down");
}
static void
ibus_engine_candidate_clicked (IBusEngine *engine,
guint index,
guint button,
guint state)
{
// g_debug ("candidate-clicked");
}
static void
ibus_engine_property_activate (IBusEngine *engine,
const gchar *prop_name,
guint prop_state)
{
// g_debug ("property-activate ('%s', %d)", prop_name, prop_state);
}
static void
ibus_engine_property_show (IBusEngine *engine, const gchar *prop_name)
{
// g_debug ("property-show ('%s')", prop_name);
}
static void
ibus_engine_property_hide (IBusEngine *engine, const gchar *prop_name)
{
// g_debug ("property-hide ('%s')", prop_name);
}
static void
ibus_engine_set_surrounding_text (IBusEngine *engine,
IBusText *text,
guint cursor_pos,
guint anchor_pos)
{
g_assert (IBUS_IS_ENGINE (engine));
if (engine->priv->surrounding_text) {
g_object_unref (engine->priv->surrounding_text);
}
engine->priv->surrounding_text = (IBusText *) g_object_ref_sink (text ? text : text_empty);
engine->priv->surrounding_cursor_pos = cursor_pos;
engine->priv->selection_anchor_pos = anchor_pos;
// g_debug ("set-surrounding-text ('%s', %d, %d)", text->text, cursor_pos, anchor_pos);
}
static void
ibus_engine_process_hand_writing_event (IBusEngine *engine,
const gdouble *coordinates,
guint coordinates_len)
{
// guint i;
// g_debug ("process-hand-writing-event (%u)", coordinates_len);
// for (i = 0; i < coordinates_len; i++)
// g_debug (" %lf", coordinates[i]);
}
static void
ibus_engine_cancel_hand_writing (IBusEngine *engine,
guint n_strokes)
{
// g_debug ("cancel-hand-writing (%u)", n_strokes);
}
static void
ibus_engine_set_content_type (IBusEngine *engine,
guint purpose,
guint hints)
{
// g_debug ("set-content-type (%u %u)", purpose, hints);
}
static void
ibus_engine_emit_signal (IBusEngine *engine,
const gchar *signal_name,
GVariant *parameters)
{
ibus_service_emit_signal ((IBusService *)engine,
NULL,
IBUS_INTERFACE_ENGINE,
signal_name,
parameters,
NULL);
}
static void
ibus_engine_dbus_property_changed (IBusEngine *engine,
const gchar *property_name,
GVariant *value)
{
const gchar *object_path;
GDBusConnection *connection;
GDBusMessage *message;
GVariantBuilder *builder;
gboolean retval;
GError *error;
/* we cannot use ibus_service_emit_signal() here, since we need to
set sender of the signal so that GDBusProxy can properly track
the property change. */
object_path = ibus_service_get_object_path ((IBusService *)engine);
message = g_dbus_message_new_signal (object_path,
"org.freedesktop.DBus.Properties",
"PropertiesChanged");
g_dbus_message_set_sender (message, "org.freedesktop.IBus");
builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
g_variant_builder_add (builder, "{sv}", property_name, value);
g_dbus_message_set_body (message,
g_variant_new ("(sa{sv}as)",
IBUS_INTERFACE_ENGINE,
builder,
NULL));
g_variant_builder_unref (builder);
error = NULL;
connection = ibus_service_get_connection ((IBusService *)engine);
retval = g_dbus_connection_send_message (connection,
message,
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
NULL,
&error);
if (!retval) {
g_warning ("Failed to emit PropertiesChanged signal: %s",
error->message);
g_error_free (error);
}
g_object_unref (message);
}
IBusEngine *
ibus_engine_new (const gchar *engine_name,
const gchar *object_path,
GDBusConnection *connection)
{
return ibus_engine_new_with_type (IBUS_TYPE_ENGINE,
engine_name,
object_path,
connection);
}
IBusEngine *
ibus_engine_new_with_type (GType engine_type,
const gchar *engine_name,
const gchar *object_path,
GDBusConnection *connection)
{
g_return_val_if_fail (g_type_is_a (engine_type, IBUS_TYPE_ENGINE), NULL);
g_return_val_if_fail (engine_name != NULL, NULL);
g_return_val_if_fail (object_path != NULL, NULL);
g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
GObject *object = g_object_new (engine_type,
"engine-name", engine_name,
"object-path", object_path,
"connection", connection,
NULL);
return IBUS_ENGINE (object);
}
void
ibus_engine_commit_text (IBusEngine *engine,
IBusText *text)
{
g_return_if_fail (IBUS_IS_ENGINE (engine));
g_return_if_fail (IBUS_IS_TEXT (text));
GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)text);
ibus_engine_emit_signal (engine,
"CommitText",
g_variant_new ("(v)", variant));
if (g_object_is_floating (text)) {
g_object_unref (text);
}
}
void
ibus_engine_update_preedit_text (IBusEngine *engine,
IBusText *text,
guint cursor_pos,
gboolean visible)
{
ibus_engine_update_preedit_text_with_mode (engine,
text, cursor_pos, visible, IBUS_ENGINE_PREEDIT_CLEAR);
}
void
ibus_engine_update_preedit_text_with_mode (IBusEngine *engine,
IBusText *text,
guint cursor_pos,
gboolean visible,
IBusPreeditFocusMode mode)
{
g_return_if_fail (IBUS_IS_ENGINE (engine));
g_return_if_fail (IBUS_IS_TEXT (text));
GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)text);
ibus_engine_emit_signal (engine,
"UpdatePreeditText",
g_variant_new ("(vubu)", variant, cursor_pos, visible, mode));
if (g_object_is_floating (text)) {
g_object_unref (text);
}
}
void ibus_engine_update_auxiliary_text (IBusEngine *engine,
IBusText *text,
gboolean visible)
{
g_return_if_fail (IBUS_IS_ENGINE (engine));
g_return_if_fail (IBUS_IS_TEXT (text));
GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)text);
ibus_engine_emit_signal (engine,
"UpdateAuxiliaryText",
g_variant_new ("(vb)", variant, visible));
if (g_object_is_floating (text)) {
g_object_unref (text);
}
}
void
ibus_engine_update_lookup_table (IBusEngine *engine,
IBusLookupTable *table,
gboolean visible)
{
g_return_if_fail (IBUS_IS_ENGINE (engine));
g_return_if_fail (IBUS_IS_LOOKUP_TABLE (table));
GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)table);
ibus_engine_emit_signal (engine,
"UpdateLookupTable",
g_variant_new ("(vb)", variant, visible));
if (g_object_is_floating (table)) {
g_object_unref (table);
}
}
void
ibus_engine_update_lookup_table_fast (IBusEngine *engine,
IBusLookupTable *table,
gboolean visible)
{
/* Note: gnome shell needs the previous page and next page
to correctly show the page up/down arrows,
send three pages instead of one page. */
g_return_if_fail (IBUS_IS_ENGINE (engine));
g_return_if_fail (IBUS_IS_LOOKUP_TABLE (table));
IBusLookupTable *new_table;
IBusText *text;
gint page_begin;
gint cursor_pos;
gint i;
if (table->candidates->len < table->page_size << 2) {
ibus_engine_update_lookup_table (engine, table, visible);
return;
}
page_begin = (table->cursor_pos / table->page_size) * table->page_size;
cursor_pos = ibus_lookup_table_get_cursor_in_page (table);
if (table->cursor_pos >= table->page_size) {
/* has previous page, adjust the value. */
page_begin -= table->page_size;
cursor_pos += table->page_size;
}
new_table = ibus_lookup_table_new
(table->page_size, 0, table->cursor_visible, table->round);
/* '3' means the previous page, current page and next page. */
for (i = page_begin; i < page_begin + 3 * table->page_size &&
i < table->candidates->len; i++) {
ibus_lookup_table_append_candidate
(new_table, ibus_lookup_table_get_candidate (table, i));
}
for (i = 0; (text = ibus_lookup_table_get_label (table, i)) != NULL; i++) {
ibus_lookup_table_append_label (new_table, text);
}
ibus_lookup_table_set_cursor_pos (new_table, cursor_pos);
ibus_lookup_table_set_orientation
(new_table, ibus_lookup_table_get_orientation (table));
ibus_engine_update_lookup_table (engine, new_table, visible);
if (g_object_is_floating (table)) {
g_object_unref (table);
}
}
void
ibus_engine_forward_key_event (IBusEngine *engine,
guint keyval,
guint keycode,
guint state)
{
g_return_if_fail (IBUS_IS_ENGINE (engine));
ibus_engine_emit_signal (engine,
"ForwardKeyEvent",
g_variant_new ("(uuu)", keyval, keycode, state));
}
void ibus_engine_delete_surrounding_text (IBusEngine *engine,
gint offset_from_cursor,
guint nchars)
{
IBusEnginePrivate *priv;
g_return_if_fail (IBUS_IS_ENGINE (engine));
priv = IBUS_ENGINE_GET_PRIVATE (engine);
/* Update surrounding-text cache. This is necessary since some
engines call ibus_engine_get_surrounding_text() immediately
after ibus_engine_delete_surrounding_text(). */
if (priv->surrounding_text) {
IBusText *text;
glong cursor_pos, len;
cursor_pos = priv->surrounding_cursor_pos + offset_from_cursor;
len = ibus_text_get_length (priv->surrounding_text);
if (cursor_pos >= 0 && len - cursor_pos >= nchars) {
gunichar *ucs;
ucs = g_utf8_to_ucs4_fast (priv->surrounding_text->text,
-1,
NULL);
memmove (&ucs[cursor_pos],
&ucs[cursor_pos + nchars],
sizeof(gunichar) * (len - cursor_pos - nchars));
ucs[len - nchars] = 0;
text = ibus_text_new_from_ucs4 (ucs);
g_free (ucs);
priv->surrounding_cursor_pos = cursor_pos;
} else {
text = text_empty;
priv->surrounding_cursor_pos = 0;
}
g_object_unref (priv->surrounding_text);
priv->surrounding_text = g_object_ref_sink (text);
}
ibus_engine_emit_signal (engine,
"DeleteSurroundingText",
g_variant_new ("(iu)", offset_from_cursor, nchars));
}
void
ibus_engine_get_surrounding_text (IBusEngine *engine,
IBusText **text,
guint *cursor_pos,
guint *anchor_pos)
{
IBusEnginePrivate *priv;
g_return_if_fail (IBUS_IS_ENGINE (engine));
const gboolean signal_only = (text == NULL);
g_return_if_fail (( signal_only && (cursor_pos == NULL)) ||
(!signal_only && (cursor_pos != NULL)));
g_return_if_fail (( signal_only && (anchor_pos == NULL)) ||
(!signal_only && (anchor_pos != NULL)));
priv = IBUS_ENGINE_GET_PRIVATE (engine);
if (!signal_only) {
*text = g_object_ref (priv->surrounding_text);
*cursor_pos = priv->surrounding_cursor_pos;
*anchor_pos = priv->selection_anchor_pos;
}
/* tell the client that this engine will utilize surrounding-text
* feature, which causes periodical update. Note that the client
* should request the initial surrounding-text when the engine is
* enabled (see ibus_im_context_focus_in() and
* _ibus_context_enabled_cb() in client/gtk2/ibusimcontext.c). */
ibus_engine_emit_signal (engine,
"RequireSurroundingText",
NULL);
// g_debug ("get-surrounding-text ('%s', %d, %d)", (*text)->text, *cursor_pos, *anchor_pos);
}
void
ibus_engine_get_content_type (IBusEngine *engine,
guint *purpose,
guint *hints)
{
g_return_if_fail (IBUS_IS_ENGINE (engine));
*purpose = engine->priv->content_purpose;
*hints = engine->priv->content_hints;
}
void
ibus_engine_register_properties (IBusEngine *engine,
IBusPropList *prop_list)
{
g_return_if_fail (IBUS_IS_ENGINE (engine));
g_return_if_fail (IBUS_IS_PROP_LIST (prop_list));
GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)prop_list);
ibus_engine_emit_signal (engine,
"RegisterProperties",
g_variant_new ("(v)", variant));
if (g_object_is_floating (prop_list)) {
g_object_unref (prop_list);
}
}
void
ibus_engine_update_property (IBusEngine *engine,
IBusProperty *prop)
{
g_return_if_fail (IBUS_IS_ENGINE (engine));
g_return_if_fail (IBUS_IS_PROPERTY (prop));
GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)prop);
ibus_engine_emit_signal (engine,
"UpdateProperty",
g_variant_new ("(v)", variant));
if (g_object_is_floating (prop)) {
g_object_unref (prop);
}
}
#define DEFINE_FUNC(name, Name) \
void \
ibus_engine_##name (IBusEngine *engine) \
{ \
g_return_if_fail (IBUS_IS_ENGINE (engine)); \
ibus_engine_emit_signal (engine, \
#Name, \
NULL); \
}
DEFINE_FUNC (show_preedit_text, ShowPreeditText)
DEFINE_FUNC (hide_preedit_text, HidePreeditText)
DEFINE_FUNC (show_auxiliary_text, ShowAuxiliaryText)
DEFINE_FUNC (hide_auxiliary_text, HideAuxiliaryText)
DEFINE_FUNC (show_lookup_table, ShowLookupTable)
DEFINE_FUNC (hide_lookup_table, HideLookupTable)
#undef DEFINE_FUNC
const gchar *
ibus_engine_get_name (IBusEngine *engine)
{
g_return_val_if_fail (IBUS_IS_ENGINE (engine), NULL);
return engine->priv->engine_name;
}