spiegel-keyman/linux/scim_kmfl_imengine/src/scim_kmfl_imengine.cpp

674 lines
20 KiB
C++

/** @file scim_kmfl_imengine.cpp
* implementation of class KmflInstance.
*/
/*
* KMFL Input Method for SCIM (Smart Common Input Method)
*
* Copyright (C) 2005 SIL International
* based on source from SCIM Copyright (c) 2004 James Su <suzhe@tsinghua.org.cn>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 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
* General Public License for more details.
*
* You should have received a copy of the GNU 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 <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <kmfl/kmfl.h>
#include <kmfl/libkmfl.h>
#include "xkbmap.h"
#define Uses_SCIM_IMENGINE
#define Uses_SCIM_ICONV
#define Uses_SCIM_CONFIG_BASE
#define Uses_SCIM_CONFIG_PATH
#include "scim_kmfl_imengine_private.h"
#include <scim.h>
#include <queue>
#include "scim_kmfl_imengine.h"
#define scim_module_init kmfl_LTX_scim_module_init
#define scim_module_exit kmfl_LTX_scim_module_exit
#define scim_imengine_module_init kmfl_LTX_scim_imengine_module_init
#define scim_imengine_module_create_factory kmfl_LTX_scim_imengine_module_create_factory
#ifndef SCIM_KMFL_IMENGINE_MODULE_DATADIR
#define SCIM_KMFL_IMENGINE_MODULE_DATADIR "/usr/share/scim/kmfl"
#endif
#define SCIM_KMFL_MAX_KEYBOARD_NUMBER 64
#define KEY_AltRMask 0x10;
#define COMMIT_KEYCODE 0xFFFE
#define COMMIT_KEYMASK 0x0F
using namespace scim;
static unsigned int _scim_number_of_keyboards = 0;
static Pointer < KmflFactory >
_scim_kmfl_imengine_factories[SCIM_KMFL_MAX_KEYBOARD_NUMBER];
static std::vector < String > _scim_system_keyboard_list;
static std::vector < String > _scim_user_keyboard_list;
static ConfigPointer _scim_config;
static Xkbmap xkbmap;
static const char *_DEFAULT_LOCALES = N_("en_US.UTF-8,"
"en_AU.UTF-8,"
"en_CA.UTF-8,"
"en_GB.UTF-8,"
"my_MM.UTF-8,"
"zh_CN.UTF-8,zh_CN.GB18030,zh_CN.GBK,zh_CN.GB2312,zh_CN,"
"zh_TW.UTF-8,zh_TW.Big5,zh_TW,"
"zh_HK.UTF-8,zh_HK,"
"ja_JP.UTF-8,ja_JP.eucJP,ja_JP.ujis,ja_JP,ja,"
"ko_KR.UTF-8,ko_KR.eucKR,ko_KR");
static String get_dirname(const String & path)
{
size_t dirend = path.find_last_of(SCIM_PATH_DELIM_STRING);
if (dirend > 0) {
return path.substr(0, dirend);
} else {
return String("");
}
}
static void
_get_keyboard_list(std::vector < String > &keyboard_list,
const String & path)
{
keyboard_list.clear();
DIR *dir = opendir(path.c_str());
if (dir != NULL) {
struct dirent *file = readdir(dir);
while (file != NULL) {
struct stat filestat;
String absfn = path + SCIM_PATH_DELIM_STRING + file->d_name;
stat(absfn.c_str(), &filestat);
// Only .kmfl and .kmn extensions are valid keyboard files
if (S_ISREG(filestat.st_mode)
&& ((absfn.substr(absfn.length() - 5, 5) == ".kmfl"
&& kmfl_check_keyboard(absfn.c_str()) == 0)
|| absfn.substr(absfn.length() - 4, 4) == ".kmn")) {
DBGMSG(1, "DAR: kmfl - found keyboard: %s\n",
absfn.c_str());
keyboard_list.push_back(absfn);
}
file = readdir(dir);
}
closedir(dir);
}
}
extern "C" {
void scim_module_init(void)
{
#ifdef DEBUGGING
kmfl_debug = 1;
#endif
DBGMSG(1, "DAR/JD: kmfl - Kmfl Module init!!!\n");
}
void scim_module_exit(void)
{
DBGMSG(1, "DAR: kmfl - Kmfl Module exit\n");
for (UINT i = 0; i < _scim_number_of_keyboards; ++i) {
_scim_kmfl_imengine_factories[i].reset();
}
_scim_config.reset();
}
unsigned int scim_imengine_module_init(const ConfigPointer & config)
{
DBGMSG(1, "DAR: kmfl - Kmfl IMEngine Module init\n");
_scim_config = config;
_get_keyboard_list(_scim_system_keyboard_list,
SCIM_KMFL_IMENGINE_MODULE_DATADIR);
_get_keyboard_list(_scim_user_keyboard_list,
scim_get_home_dir() + SCIM_PATH_DELIM_STRING +
".scim" + SCIM_PATH_DELIM_STRING + "kmfl");
_scim_number_of_keyboards =
_scim_system_keyboard_list.size() +
_scim_user_keyboard_list.size();
if (_scim_number_of_keyboards == 0) {
DBGMSG(1, "DAR: kmfl - No valid keyboards found\n");
}
return _scim_number_of_keyboards; // actually the number of files, may not all be valid
}
IMEngineFactoryPointer scim_imengine_module_create_factory(unsigned int imengine)
{
DBGMSG(1, "DAR: kmfl - Kmfl IMEngine Module Create Factory %d\n",
imengine);
if (imengine >= _scim_number_of_keyboards) {
return 0;
}
if (_scim_kmfl_imengine_factories[imengine].null()) {
_scim_kmfl_imengine_factories[imengine] = new KmflFactory();
if (imengine < _scim_system_keyboard_list.size()) {
if (!_scim_kmfl_imengine_factories[imengine]->
load_keyboard(_scim_system_keyboard_list[imengine],
false))
return 0;
} else {
if (!_scim_kmfl_imengine_factories[imengine]->
load_keyboard(_scim_user_keyboard_list
[imengine -
_scim_system_keyboard_list.size()],
true))
return 0;
}
if (!_scim_kmfl_imengine_factories[imengine]->valid()) {
_scim_kmfl_imengine_factories[imengine].reset();
}
char buf[2];
sprintf(buf, "%c", 21 + imengine);
_scim_kmfl_imengine_factories[imengine]->
set_uuid(String("d1534208-27e5-8ec4-b2cd-df0fb0d2275") +
String(buf));
}
return _scim_kmfl_imengine_factories[imengine];
}
}
// Implementation of Kmfl
KmflFactory::KmflFactory()
{
String current_locale = String (setlocale (LC_CTYPE, 0));
if (current_locale.length() > 0) {
set_locales(String(_(_DEFAULT_LOCALES)) + String(",") +
current_locale);
} else {
set_locales(String(_(_DEFAULT_LOCALES)));
}
}
KmflFactory::KmflFactory(const WideString & name,
const String & locales)
{
if (locales == String("default")) {
String current_locale = String (setlocale (LC_CTYPE, 0));
if (current_locale.length() > 0) {
set_locales(String(_(_DEFAULT_LOCALES)) + String(",") +
current_locale);
} else {
set_locales(String(_(_DEFAULT_LOCALES)));
}
} else {
set_locales(locales);
}
}
KmflFactory::~KmflFactory()
{
kmfl_unload_keyboard(m_keyboard_number);
}
bool KmflFactory::load_keyboard(const String & keyboard_file,
bool user_keyboard)
{
char buf[256];
KMSI * p_kmsi;
m_keyboard_file = keyboard_file;
DBGMSG(1, "DAR/jd: kmfl loading %s\n", keyboard_file.c_str());
if (keyboard_file.length()) {
m_keyboard_number =
kmfl_load_keyboard((char *) keyboard_file.c_str());
if (m_keyboard_number >= 0) {
m_name = WideString(utf8_mbstowcs(kmfl_keyboard_name(m_keyboard_number)));
DBGMSG(1, "DAR/jd: kmfl - Keyboard %s loaded\n",
kmfl_keyboard_name(m_keyboard_number));
p_kmsi = kmfl_make_keyboard_instance(NULL);
if (p_kmsi) {
kmfl_attach_keyboard(p_kmsi, m_keyboard_number);
*buf='\0';
kmfl_get_header(p_kmsi,SS_AUTHOR,buf,sizeof(buf) - 1);
m_Author=String(buf);
*buf='\0';
kmfl_get_header(p_kmsi,SS_COPYRIGHT,buf,sizeof(buf) - 1);
m_Copyright=String(buf);
*buf='\0';
kmfl_get_header(p_kmsi,SS_LANGUAGE,buf,sizeof(buf) - 1);
m_Language=String(buf);
kmfl_detach_keyboard(p_kmsi);
kmfl_delete_keyboard_instance(p_kmsi);
}
if (m_Language.length() != 0)
set_languages(m_Language);
return valid();
}
return false;
}
return false;
}
WideString KmflFactory::get_name() const
{
return m_name;
}
WideString KmflFactory::get_authors() const
{
return utf8_mbstowcs(m_Author);
}
WideString KmflFactory::get_credits() const
{
return utf8_mbstowcs(m_Copyright);
}
String KmflFactory::get_language () const
{
return scim_validate_language(m_Language);
}
WideString KmflFactory::get_help() const
{
return utf8_mbstowcs(String(_("Hot Keys:\n\n"
" Esc:\n"
" reset the input method.\n")));
}
void KmflFactory::set_uuid(const String & suuid)
{
uuid = suuid;
}
String KmflFactory::get_uuid() const
{
return uuid;
}
String KmflFactory::get_icon_file() const
{
String icon_file = kmfl_icon_file(m_keyboard_number);
if (icon_file.length() == 0) {
return String(SCIM_KMFL_IMENGINE_MODULE_DATADIR
SCIM_PATH_DELIM_STRING "icons" SCIM_PATH_DELIM_STRING
"default.png");
} else {
String full_path_to_icon_file =
get_dirname(m_keyboard_file) +
SCIM_PATH_DELIM_STRING "icons" SCIM_PATH_DELIM_STRING +
icon_file;
struct stat filestat;
stat(full_path_to_icon_file.c_str(), &filestat);
if (S_ISREG(filestat.st_mode)) {
return full_path_to_icon_file;
} else {
return String("");
}
}
}
IMEngineInstancePointer
KmflFactory::create_instance(const String & encoding,
int id)
{
return new KmflInstance(this, encoding, id);
}
// Implementation of KmflInstance
KmflInstance::KmflInstance(KmflFactory * factory,
const String & encoding, int id)
: IMEngineInstanceBase(factory, encoding, id), m_factory(factory),
m_forward(false), m_focused(false), m_unicode(false),
m_changelayout(false), m_iconv(encoding), p_kmsi(NULL), m_currentsymbols(""), m_keyboardlayout(""), m_keyboardlayoutactive(false)
{
m_display = XOpenDisplay(NULL);
if (factory) {
p_kmsi = kmfl_make_keyboard_instance(this);
if (p_kmsi) {
char buf[256];
int keyboard_number = factory->get_keyboard_number();
DBGMSG(1, "DAR: Loading keyboard %d\n", keyboard_number);
kmfl_attach_keyboard(p_kmsi, keyboard_number);
*buf='\0';
if (kmfl_get_header(p_kmsi, SS_LAYOUT, buf, sizeof(buf) - 1)== 0) {
m_keyboardlayout= buf;
if (m_keyboardlayout.length() > 0) {
*buf='\0';
if (kmfl_get_header(p_kmsi,SS_MNEMONIC,buf,sizeof(buf) - 1) == 0) {
if (*buf != '1' && *buf != '2') {
m_changelayout= true;
}
} else {
m_changelayout= true;
}
}
}
}
}
if (m_changelayout) {
DBGMSG(1, "DAR: change layout is set, layout is %s\n", m_keyboardlayout.c_str());
} else {
DBGMSG(1, "DAR: change layout is not set\n");
}
}
KmflInstance::~KmflInstance()
{
restore_system_layout();
if (p_kmsi) {
kmfl_detach_keyboard(p_kmsi);
kmfl_delete_keyboard_instance(p_kmsi);
}
p_kmsi = NULL;
XCloseDisplay(m_display);
}
void KmflInstance::activate_keyboard_layout(void)
{
if (!m_keyboardlayoutactive) {
m_currentsymbols=xkbmap.getCurrentSymbols();
DBGMSG(1, "DAR: changing layout from %s to %s\n", m_currentsymbols.c_str(), m_keyboardlayout.c_str());
xkbmap.setLayout(m_keyboardlayout);
m_keyboardlayoutactive= true;
}
}
void KmflInstance::restore_system_layout(void)
{
if (m_keyboardlayoutactive) {
DBGMSG(1, "DAR: changing layout from %s to %s\n", m_keyboardlayout.c_str(), m_currentsymbols.c_str());
xkbmap.setSymbols(m_currentsymbols);
m_keyboardlayoutactive=false;
}
}
int KmflInstance::is_key_pressed(char *key_vec, KeySym keysym)
{
unsigned char keycode;
keycode = XKeysymToKeycode(m_display, keysym);
return key_vec[keycode >> 3] & (1 << (keycode & 7));
}
bool KmflInstance::process_key_event(const KeyEvent & key)
{
int mask;
WideString context;
int cursor;
if (!m_focused) {
return false;
}
DBGMSG(1, "DAR: kmfl - Keyevent, code: %x, mask: %x\n", key.code,
key.mask);
// Ignore key releases
if (key.is_key_release()) {
return true;
}
if (key.code == SCIM_KEY_Sys_Req && (key.mask & SCIM_KEY_ControlMask) && (key.mask & SCIM_KEY_AltMask)){
DBGMSG(1, "DAR: kmfl -Reloading all keyboards\n");
kmfl_reload_all_keyboards();
return true;
}
if (key.code == SCIM_KEY_Print && (key.mask & SCIM_KEY_ControlMask)) {
DBGMSG(1, "DAR: kmfl -Reloading keyboard %s\n", p_kmsi->kbd_name);
kmfl_reload_keyboard(p_kmsi->keyboard_number);
return true;
}
if (!m_forward) {
// If a modifier key is pressed, check to see if it is a right modifier key
// This is rather expensive so only do it if a shift state is active
int right_modifier_mask = 0;
if (key.mask & (SCIM_KEY_ShiftMask | SCIM_KEY_ControlMask | SCIM_KEY_Mod1Mask)) {
char key_vec[32];
XQueryKeymap(m_display, key_vec);
if ((key.mask & SCIM_KEY_Mod1Mask) && is_key_pressed(key_vec, SCIM_KEY_Alt_R)) {
right_modifier_mask |= (SCIM_KEY_Mod1Mask << 8);
}
if ((key.mask & SCIM_KEY_ControlMask) && is_key_pressed(key_vec, SCIM_KEY_Control_R)) {
right_modifier_mask |= (SCIM_KEY_ControlMask << 8);
}
if ((key.mask & SCIM_KEY_ShiftMask) && is_key_pressed(key_vec, SCIM_KEY_Shift_R)) {
right_modifier_mask |= (SCIM_KEY_ShiftMask << 8);
}
}
mask = key.mask | right_modifier_mask;
DBGMSG(1, "DAR: kmfl - keymask %x\n", mask);
// Reset key
if (key.code == SCIM_KEY_Pause) {
reset();
return true;
}
DBGMSG(1, "DAR: kmfl - Checking sequences for %d\n", key.code);
if (!deadkey_in_history(p_kmsi)) {
if (get_surrounding_text (context, cursor, MAX_HISTORY, 0)) {
UINT nItems= context.size ();
ITEM items[MAX_HISTORY];
DBGMSG(1, "DAR: kmfl - get_surround_text: cursor at %d, length = %d, string %s\n", cursor, nItems, utf8_wcstombs(context).c_str());
for (unsigned int i=0; i< nItems; ++i) {
items[nItems - i - 1] = MAKE_ITEM(ITEM_CHAR,context [i]);
}
set_history(p_kmsi, items, nItems);
}
}
if (kmfl_interpret(p_kmsi, key.code, mask) == 1) {
return true;
// Not a modifier key, ie shift, ctrl, alt, etc
} else if (!(key.code >= XK_Shift_L && key.code <= XK_Hyper_R)) {
DBGMSG(1, "DAR: kmfl - key.code causing reset %x\n", key.code);
reset();
}
}
return false;
}
void KmflInstance::reset()
{
DBGMSG(1, "DAR: kmfl - Reset called\n");
// Clear the history for this instance (reset the context)
clear_history(p_kmsi);
m_iconv.set_encoding(get_encoding());
}
void KmflInstance::focus_in()
{
if (m_changelayout && !m_forward) {
activate_keyboard_layout();
}
m_focused = true;
refresh_status_property();
initialize_properties ();
}
void KmflInstance::focus_out()
{
if (m_changelayout) {
restore_system_layout();
}
m_focused = false;
}
void KmflInstance::toggle_input_status()
{
DBGMSG(1, "DAR: kmfl - toggle_input_status\n");
}
void KmflInstance::trigger_property(const String &property)
{
DBGMSG(1, "DAR: kmfl - trigger_property\n");
}
void KmflInstance::initialize_properties ()
{
PropertyList proplist;
proplist.push_back (m_factory->m_status_property);
register_properties (proplist);
refresh_status_property ();
}
void KmflInstance::refresh_status_property()
{
if (m_focused) {
if (m_forward) {
m_factory->m_status_property.set_label(_("En"));
} else if (m_unicode) {
m_factory->m_status_property.set_label(_("Unicode"));
} else {
m_factory->m_status_property.set_label(get_encoding());
}
update_property (m_factory->m_status_property);
}
}
void KmflInstance::forward_keyevent(unsigned int key, unsigned int state)
{
KeyEvent fkey(key, state);
DBGMSG(1, "DAR: kmfl - forward key event key=%x, state=%x\n", key,state);
forward_key_event(fkey);
}
void KmflInstance::erase_char()
{
KeyEvent backspacekey(SCIM_KEY_BackSpace, 0);
WideString text;
int cursor;
bool result;
DBGMSG(1, "DAR: kmfl - backspace\n");
if (get_surrounding_text (text, cursor, 1, 0)) {
if (!delete_surrounding_text(-1, 1)) {
DBGMSG(1, "DAR: delete_surrounding_text failed...forwarding key event\n");
forward_key_event(backspacekey);
DBGMSG(1, "DAR: kmfl - key event forwarded\n");
}
} else {
forward_key_event(backspacekey);
DBGMSG(1, "DAR: kmfl - key event forwarded\n");
}
}
void KmflInstance::output_string(const String & str)
{
if (str.length() > 0) {
DBGMSG(1, "DAR: kmfl - committing string %s\n", str.c_str());
commit_string(utf8_mbstowcs(str));
}
}
void KmflInstance::output_beep()
{
beep();
}
extern "C" {
void output_string(void *contrack, char *ptr) {
if (ptr) {
((KmflInstance *) contrack)->output_string(ptr);
}
}
void erase_char(void *contrack) {
((KmflInstance *) contrack)->erase_char();
}
void output_char(void *contrack, unsigned char byte) {
if (byte == 8) {
erase_char(contrack);
} else {
char s[2];
s[0] = byte;
s[1] = '\0';
output_string(contrack, s);
}
}
void forward_keyevent(void *contrack, unsigned int key, unsigned int state)
{
((KmflInstance *) contrack)->forward_keyevent(key, state);
}
void output_beep(void *contrack) {
DBGMSG(1, "DAR: kmfl - beep\n");
((KmflInstance *) contrack)->output_beep();
}
} /* extern "c" */
/*
vi:ts=4:nowrap:ai:expandtab
*/