/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
/* vim:set et sts=4: */
/* bus - The Input Bus
* Copyright (C) 2010 Google 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 <ibus.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "test-client.h"
#ifdef DEBUG
# define IDEBUG g_debug
#else
# define IDEBUG(a...)
#endif
/* functions prototype */
static void bus_test_client_class_init (BusTestClientClass *class);
static void bus_test_client_destroy (IBusObject *object);
/* static methods*/
static void _store_modifier_state (BusTestClient *client,
guint modifier);
static gboolean _is_shift_set (BusTestClient *client);
static gboolean _is_modifier_set (BusTestClient *client,
guint modifier);
static gboolean _is_modifier_key (guint modifier);
static guint _get_modifiers_to_mask (BusTestClient *client);
static gint16 _get_keysym_to_keycode (guint keysym);
static void _bus_disconnected_cb (IBusBus *ibusbus,
BusTestClient *client);
static void _bus_disabled_cb (IBusInputContext *ibuscontext,
BusTestClient *client);
static IBusBus *_bus = NULL;
static Display *_xdisplay = NULL;
G_DEFINE_TYPE (BusTestClient, bus_test_client, IBUS_TYPE_OBJECT)
static void
bus_test_client_class_init (BusTestClientClass *class)
{
IDEBUG ("%s", __FUNCTION__);
IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (class);
ibus_object_class->destroy = bus_test_client_destroy;
/* init display object */
if (_xdisplay == NULL) {
_xdisplay = XOpenDisplay (NULL);
}
/* init bus object */
if (_bus == NULL) {
ibus_set_display (XDisplayString (_xdisplay));
_bus = ibus_bus_new();
}
g_signal_connect (_bus, "disconnected", G_CALLBACK (_bus_disconnected_cb), NULL);
}
static void
bus_test_client_init (BusTestClient *client)
{
IDEBUG ("%s", __FUNCTION__);
client->connected = FALSE;
client->enabled = FALSE;
g_return_if_fail (ibus_bus_is_connected (_bus));
client->connected = TRUE;
client->ibuscontext = ibus_bus_create_input_context (_bus, "test-client");
g_return_if_fail (client->ibuscontext != NULL);
g_signal_connect (client->ibuscontext,
"disabled",
G_CALLBACK (_bus_disabled_cb),
client);
bus_test_client_clear_modifier (client);
client->caps = IBUS_CAP_FOCUS;
ibus_input_context_set_capabilities (client->ibuscontext, client->caps);
ibus_bus_set_global_engine (_bus, "xkb:us::eng");
client->enabled = TRUE;
}
static void
bus_test_client_destroy (IBusObject *object)
{
IDEBUG ("%s", __FUNCTION__);
BusTestClient *client = BUS_TEST_CLIENT (object);
g_object_unref (client->ibuscontext);
}
BusTestClient *
bus_test_client_new (void)
{
IDEBUG ("%s", __FUNCTION__);
BusTestClient *client = BUS_TEST_CLIENT (g_object_new (BUS_TYPE_TEST_CLIENT, NULL));
if (client->connected && client->enabled) {
return client;
} else {
return NULL;
}
}
gboolean
bus_test_client_is_enabled (BusTestClient *client)
{
IDEBUG ("%s", __FUNCTION__);
return client->enabled;
}
gboolean
bus_test_client_is_connected (BusTestClient *client)
{
IDEBUG ("%s", __FUNCTION__);
return client->connected;
}
gboolean
bus_test_client_send_key (BusTestClient *client,
guint keysym)
{
gboolean is_modifier = _is_modifier_key (keysym);
gint16 keycode;
guint state;
if (is_modifier) {
IDEBUG ("key: %d is modifier.", keysym);
gboolean is_modifier_set = _is_modifier_set (client, keysym);
keycode = _get_keysym_to_keycode (keysym);
state = _get_modifiers_to_mask (client);
if (is_modifier_set) {
state |= IBUS_RELEASE_MASK;
}
ibus_input_context_process_key_event (client->ibuscontext,
keysym,
keycode,
state);
_store_modifier_state (client, keysym);
} else {
IDEBUG ("key: %d is not modifier.", keysym);
/* This is an example code. If you use the keysym >= 0x01000000,
* gdk_keyval_is_upper may be useful since
* XConvertCase supports implementation-independent conversions.
*/
KeySym xlower = 0;
KeySym xupper = 0;
gboolean is_upper = FALSE;
if (keysym) {
XConvertCase (keysym, &xlower, &xupper);
is_upper = ((guint) xupper == keysym);
}
gboolean is_shift_set = _is_shift_set (client);
if (is_upper && !is_shift_set) {
_store_modifier_state (client, IBUS_KEY_Shift_L);
}
keycode = _get_keysym_to_keycode (keysym);
state = _get_modifiers_to_mask (client);
ibus_input_context_process_key_event (client->ibuscontext,
keysym,
keycode,
state);
state |= IBUS_RELEASE_MASK;
ibus_input_context_process_key_event (client->ibuscontext,
keysym,
keycode,
state);
if (is_upper && !is_shift_set) {
_store_modifier_state (client, IBUS_KEY_Shift_L);
}
}
return TRUE;
}
void bus_test_client_clear_modifier (BusTestClient *client)
{
int i;
for (i = 0; i < MODIFIER_KEY_NUM; i++) {
(client->modifier)[i] = FALSE;
}
}
static void
_store_modifier_state (BusTestClient *client,
guint modifier)
{
switch(modifier) {
case IBUS_KEY_Shift_L:
case IBUS_KEY_Shift_R:
/* ShiftMask */
client->modifier[0] = !client->modifier[0];
break;
case IBUS_KEY_Shift_Lock:
case IBUS_KEY_Caps_Lock:
/* LockMask */
client->modifier[1] = !client->modifier[1];
break;
case IBUS_KEY_Control_L:
case IBUS_KEY_Control_R:
/* ControlMask */
client->modifier[2] = !client->modifier[2];
break;
case IBUS_KEY_Alt_L:
case IBUS_KEY_Alt_R:
case IBUS_KEY_Meta_L:
/* Mod1Mask */
client->modifier[3] = !client->modifier[3];
break;
case IBUS_KEY_Num_Lock:
/* Mod2Mask */
client->modifier[4] = !client->modifier[4];
break;
case IBUS_KEY_Super_L:
case IBUS_KEY_Hyper_L:
/* Mod4Mask */
client->modifier[5] = !client->modifier[5];
break;
case IBUS_KEY_ISO_Level3_Shift:
case IBUS_KEY_Mode_switch:
/* Mod5Mask */
client->modifier[6] = !client->modifier[6];
break;
default:
break;
}
}
static gint16
_get_keysym_to_keycode (guint keysym)
{
return XKeysymToKeycode (_xdisplay, keysym);
}
static gboolean
_is_shift_set (BusTestClient *client)
{
return client->modifier[0];
}
static gboolean
_is_modifier_set (BusTestClient *client,
guint modifier)
{
switch(modifier) {
case IBUS_KEY_Shift_L:
case IBUS_KEY_Shift_R:
/* ShiftMask */
return client->modifier[0];
case IBUS_KEY_Shift_Lock:
case IBUS_KEY_Caps_Lock:
/* LockMask */
return client->modifier[1];
case IBUS_KEY_Control_L:
case IBUS_KEY_Control_R:
/* ControlMask */
return client->modifier[2];
case IBUS_KEY_Alt_L:
case IBUS_KEY_Alt_R:
case IBUS_KEY_Meta_L:
/* Mod1Mask */
return client->modifier[3];
case IBUS_KEY_Num_Lock:
/* Mod2Mask */
return client->modifier[4];
case IBUS_KEY_Super_L:
case IBUS_KEY_Hyper_L:
/* Mod4Mask */
return client->modifier[5];
case IBUS_KEY_ISO_Level3_Shift:
case IBUS_KEY_Mode_switch:
/* Mod5Mask */
return client->modifier[6];
default:
return FALSE;
}
}
static gboolean
_is_modifier_key (guint modifier)
{
switch(modifier) {
case IBUS_KEY_Shift_L:
case IBUS_KEY_Shift_R:
case IBUS_KEY_Shift_Lock:
case IBUS_KEY_Caps_Lock:
case IBUS_KEY_Control_L:
case IBUS_KEY_Control_R:
case IBUS_KEY_Alt_L:
case IBUS_KEY_Alt_R:
case IBUS_KEY_Meta_L:
case IBUS_KEY_Num_Lock:
case IBUS_KEY_Super_L:
case IBUS_KEY_Hyper_L:
case IBUS_KEY_ISO_Level3_Shift:
case IBUS_KEY_Mode_switch:
return TRUE;
default:
return FALSE;
}
}
static guint
_get_modifiers_to_mask (BusTestClient *client)
{
guint retval = 0;
if(client->modifier[0])
retval |= IBUS_SHIFT_MASK;
if(client->modifier[1])
retval |= IBUS_LOCK_MASK;
if(client->modifier[2])
retval |= IBUS_CONTROL_MASK;
if(client->modifier[3])
retval |= IBUS_MOD1_MASK;
if(client->modifier[4])
retval |= IBUS_MOD2_MASK;
if(client->modifier[5])
retval |= IBUS_MOD4_MASK;
if(client->modifier[6])
retval |= IBUS_MOD5_MASK;
return retval;
}
static void
_bus_disconnected_cb (IBusBus *ibusbus,
BusTestClient *client)
{
g_assert (IBUS_IS_BUS (ibusbus));
g_assert (BUS_IS_TEST_CLIENT (client));
IDEBUG ("%s", __FUNCTION__);
client->connected = FALSE;
IDEBUG ("Disconnected ibus daemon");
}
static void
_bus_disabled_cb (IBusInputContext *ibuscontext,
BusTestClient *client)
{
g_assert (IBUS_IS_INPUT_CONTEXT (ibuscontext));
g_assert (BUS_IS_TEST_CLIENT (client));
IDEBUG ("%s", __FUNCTION__);
client->enabled = FALSE;
IDEBUG ("Disabled ibus engine");
}