Permalink
Fetching contributors…
Cannot retrieve contributors at this time
1552 lines (1348 sloc) 37.9 KB
/*
Copyright (c) 2003-2012 uim Project http://code.google.com/p/uim/
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of authors nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#define UIM_XIM_USE_JAPANESE_KANA_KEYBOARD_HACK 1
#include <cctype>
#include <clocale>
#include <cstdio>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <X11/XKBlib.h>
/* workaround for pre X11R6.7 */
#ifndef XK_KOREAN
#define XK_KOREAN
#endif
#ifndef XK_KATAKANA
#define XK_KATAKANA
#endif
#include <X11/keysymdef.h>
#include "xim.h"
#include "convdisp.h"
#include "canddisp.h"
#include "ximserver.h"
#include "util.h"
#include "helper.h"
#include "uim/uim-helper.h"
#include "uim/uim-im-switcher.h"
#include "uim/uim-scm.h"
#if UIM_XIM_USE_JAPANESE_KANA_KEYBOARD_HACK
#include "uim/uim-x-util.h"
#endif
#ifndef XK_dead_horn
#define XK_dead_horn 0xfe62
#endif
#ifndef __GNUC__
# ifdef HAVE_ALLOCA_H
# include <alloca.h>
# endif
#endif
extern int lib_uim_fd;
extern Atom xim_servers;
InputContext *InputContext::mFocusedContext = NULL;
static int check_modifier(std::list<KeySym> list);
static int gMod1Mask, gMod2Mask, gMod3Mask, gMod4Mask, gMod5Mask;
static int gXNumLockMask;
CandWinPosType XimServer::gCandWinPosType;
CandWinStyle XimServer::gCandWinStyle;
bool XimServer::gCandWinStyleUpdated;
void print_ustring(uString *s)
{
uString::iterator i;
printf("length=%d : ", (int)s->size());
uchar ch;
char utf8[6];
int nbyte;
for (i = s->begin(); i != s->end(); ++i) {
ch = *i;
nbyte = utf8_wctomb((unsigned char *)utf8, ch);
utf8[nbyte] = '\0';
printf("%s", utf8);
}
printf("\n");
}
void erase_ustring(uString *s)
{
s->erase(s->begin(), s->end());
}
void append_ustring(uString *d, uString *s)
{
uString::iterator i;
for (i = s->begin(); i !=s->end(); ++i) {
d->push_back(*i);
}
}
XimServer::XimServer(const char *name, const char *lang)
{
mIMName = strdup(name);
mIMLang = lang;
}
InputContext *XimServer::createContext(XimIC *xic, const char *engine)
{
InputContext *ic = new InputContext(this, xic, engine);
ic_list.push_back(ic);
return ic;
}
void XimServer::deleteContext(InputContext *ic)
{
std::list<InputContext *>::iterator it;
for (it = ic_list.begin(); it != ic_list.end(); ++it) {
if (*it == ic) {
ic_list.erase(it);
break;
}
}
}
void XimServer::changeContext(const char *engine) {
set_im(engine);
std::list<InputContext *>::iterator it;
for (it = ic_list.begin(); it != ic_list.end(); ++it) {
(*it)->changeContext(engine);
}
// make sure to use appropriate locale for the focused context
InputContext *focusedContext = InputContext::focusedContext();
if (focusedContext)
focusedContext->focusIn();
}
void XimServer::customContext(const char *custom, const char *val) {
std::list<InputContext *>::iterator it;
for (it = ic_list.begin(); it != ic_list.end(); ++it) {
(*it)->customContext(custom, val);
break;
}
// Updated global IM of XimServer
if (!strcmp(custom, "custom-preserved-default-im-name") &&
uim_scm_symbol_value_bool("custom-activate-default-im-name?"))
set_im(++val);
#if HAVE_XFT_UTF8_STRING
if (!strcmp(custom, "uim-xim-xft-font-name"))
update_default_xftfont();
#endif
if (!strcmp(custom, "bridge-show-input-state?") &&
!uim_scm_symbol_value_bool("bridge-show-input-state?")) {
Canddisp *disp = canddisp_singleton();
disp->hide_caret_state();
}
if (!strcmp(custom, "candidate-window-position"))
check_candwin_pos_type();
if (!strcmp(custom, "candidate-window-style"))
check_candwin_style();
}
void XimServer::reloadConfigs() {
#if 1
uim_prop_reload_configs();
reload_uim(0);
#else
reload_uim(1);
#endif
// Updated global IM of XimServer
char *im = uim_scm_symbol_value_str("default-im-name");
if (im)
set_im(im);
free(im);
#if HAVE_XFT_UTF8_STRING
update_default_xftfont();
#endif
if (!uim_scm_symbol_value_bool("bridge-show-input-state?")) {
Canddisp *disp = canddisp_singleton();
disp->hide_caret_state();
}
check_candwin_style();
check_candwin_pos_type();
}
bool
XimServer::setupConnection(bool useDefaultIM)
{
const char *buf;
if (!useDefaultIM) {
return false;
} else {
buf = "@server=uim";
}
mServerAtom = XInternAtom(XimServer::gDpy, buf, 0);
Window owner = XGetSelectionOwner(XimServer::gDpy, mServerAtom);
if (owner != None) {
if (!useDefaultIM)
printf("Another instance exists (uim-%s).\n", mIMName);
else
printf("Another instance exists (uim).\n");
return false;
}
mSelectionWin = XCreateSimpleWindow(XimServer::gDpy,
DefaultRootWindow(XimServer::gDpy),
0, 0, 1, 1,
1, 0, 0);
XSetSelectionOwner(XimServer::gDpy, mServerAtom, mSelectionWin, CurrentTime);
XSelectInput(XimServer::gDpy, DefaultRootWindow(XimServer::gDpy), 0);
XSync(XimServer::gDpy, False);
Atom type;
int format;
unsigned long nr_prop, nr_bytes;
Atom *prop;
int mode = PropModePrepend;
int valuechange = 1;
XGetWindowProperty(XimServer::gDpy, DefaultRootWindow(XimServer::gDpy),
xim_servers, 0, 8192 ,False,
XA_ATOM, &type, &format,
&nr_prop, &nr_bytes, (unsigned char **)(uintptr_t)&prop);
int i;
if (type != XA_ATOM || format != 32)
mode = PropModeReplace;
else {
for (i = 0; i < (int)nr_prop; i++) {
if (prop[i] == mServerAtom) {
mode = PropModeAppend;
valuechange = 0;
break;
}
}
}
if (nr_prop)
XFree(prop);
XChangeProperty(XimServer::gDpy, DefaultRootWindow(XimServer::gDpy),
xim_servers,
XA_ATOM, 32,
mode, (unsigned char *)&mServerAtom,
valuechange ? 1 : 0);
std::pair<Window, XimServer *> p(mSelectionWin, this);
gServerMap.insert(p);
return true;
}
void
XimServer::strToUstring(uString *d, const char *s)
{
int len;
int l = 0, nbyte = 0;
uchar ch;
len = static_cast<int>(strlen(s));
while (l < len && *s != 0 &&
(nbyte = utf8_mbtowc(&ch, (const unsigned char *)s, len - l)) > 0) {
d->push_back(ch);
s += nbyte;
l += nbyte;
}
}
XimServer *XimServer::findServer(Window w)
{
std::map<Window, XimServer *>::iterator it;
it = gServerMap.find(w);
if (it == gServerMap.end())
return NULL;
return it->second;
}
const char *XimServer::getIMName()
{
return mIMName;
}
const char *XimServer::getIMLang()
{
return mIMLang;
}
void XimServer::set_im(const char *engine)
{
free(mIMName);
mIMName = strdup(engine);
mIMLang = get_im_lang_from_engine(engine);
}
const char *get_im_lang_from_engine(const char *engine)
{
std::list<UIMInfo>::iterator it;
for (it = uim_info.begin(); it != uim_info.end(); ++it) {
if (!strcmp(it->name, engine))
return it->lang;
}
return "en"; // For safety...
}
//
// Methods for InputContext
InputContext::InputContext(XimServer *svr, XimIC *ic, const char *engine)
{
mXic = ic;
m_pe = new pe_stat(this);
mConvdisp = NULL;
mServer = svr;
mEngineName = NULL;
mLocaleName = NULL;
mFocusedContext = this;
createUimContext(engine);
mCandwinActive = false;
#if UIM_XIM_USE_NEW_PAGE_HANDLING
mNumCandidates = 0;
#endif
mNumPage = 1;
mDisplayLimit = 0;
mCaretStateShown = false;
}
InputContext::~InputContext()
{
#if UIM_XIM_USE_DELAY
timer_cancel();
#endif
if (mFocusedContext == this)
mFocusedContext = NULL;
if (mConvdisp)
mConvdisp->set_pe(NULL);
delete m_pe;
uim_release_context(mUc);
mServer->deleteContext(this);
free(mEngineName);
free(mLocaleName);
}
void
InputContext::createUimContext(const char *engine)
{
char *locale;
const char *client_locale, *engine_locales;
const char *encoding;
const char *real_im;
encoding = mXic->get_encoding();
client_locale = mXic->get_lang_region();
engine_locales = compose_localenames_from_im_lang(get_im_lang_from_engine(engine));
if (!strcmp(encoding, "UTF-8")) {
real_im = engine;
if (is_locale_included(engine_locales, client_locale))
locale = strdup(client_locale);
else {
locale = get_prefered_locale(engine_locales);
}
} else {
// Use default engine for corresponding encoding of the client
// unless encoding matches with selected engine.
if (!is_locale_included(engine_locales, client_locale)) {
const char *test_im = uim_get_default_im_name(client_locale);
const char *test_im_lang = get_im_lang_from_engine(test_im);
const char *test_im_locales = compose_localenames_from_im_lang(test_im_lang);
if (is_locale_included(test_im_locales, client_locale))
real_im = test_im;
else
real_im = uim_get_im_name_for_locale(client_locale);
} else
real_im = engine;
locale = strdup(client_locale);
}
locale = (char *)realloc(locale, strlen(locale) + strlen(encoding) + 2);
strcat(locale, ".");
strcat(locale, encoding);
setlocale(LC_CTYPE, locale);
free(mLocaleName);
mLocaleName = locale;
if (mEngineName != real_im) {
free(mEngineName);
mEngineName = strdup(real_im);
}
uim_context uc = uim_create_context((void *) this, "UTF-8",
NULL, real_im, NULL,
InputContext::commit_cb);
if (uc) {
uim_set_preedit_cb(uc,
InputContext::clear_cb,
InputContext::pushback_cb,
InputContext::update_cb);
uim_set_candidate_selector_cb(uc,
InputContext::candidate_activate_cb,
InputContext::candidate_select_cb,
InputContext::candidate_shift_page_cb,
InputContext::candidate_deactivate_cb);
uim_set_prop_list_update_cb(uc,
InputContext::update_prop_list_cb);
#if 0
uim_set_prop_label_update_cb(uc,
InputContext::update_prop_label_cb);
#endif
uim_set_configuration_changed_cb(uc,
InputContext::configuration_changed_cb);
uim_set_im_switch_request_cb(uc,
InputContext::switch_app_global_im_cb,
InputContext::switch_system_global_im_cb);
#if UIM_XIM_USE_DELAY
uim_set_delay_candidate_selector_cb(uc,
InputContext::candidate_activate_with_delay_cb);
#endif
if (mFocusedContext == this)
uim_prop_list_update(uc);
}
mUc = uc;
}
void
InputContext::changeContext(const char *engine)
{
const char *encoding, *im_lang;
if (!strcmp(mEngineName, engine))
return;
encoding = mXic->get_encoding();
im_lang = get_im_lang_from_engine(engine);
// Don't change im unless encoding matches for clients with legacy locales.
if (strcmp(encoding, "UTF-8")) {
const char *client_locale = mXic->get_lang_region();
const char *engine_locales = compose_localenames_from_im_lang(im_lang);
if (!is_locale_included(engine_locales, client_locale))
return;
}
clear();
uim_release_context(mUc);
createUimContext(engine); // mUc, mEngineName, and locale will be set here.
if (mConvdisp) {
mConvdisp->set_im_lang(get_im_lang_from_engine(mEngineName));
mConvdisp->set_locale_name(mLocaleName);
}
}
void InputContext::configuration_changed()
{
const char *engine = uim_get_current_im_name(mUc);
review_im(engine);
InputContext *focusedContext = InputContext::focusedContext();
if (this == focusedContext)
send_im_list();
}
void InputContext::switch_app_global_im(const char *name)
{
get_im_by_id(this->get_ic()->get_imid())->changeContext(name);
}
void InputContext::switch_system_global_im(const char *name)
{
char *msg;
std::map<Window, XimServer *>::iterator it;
for (it = XimServer::gServerMap.begin(); it != XimServer::gServerMap.end(); ++it)
(*it).second->changeContext(name);
if (asprintf(&msg, "im_change_whole_desktop\n%s\n", name) == -1) {
free(msg);
return;
}
uim_helper_send_message(lib_uim_fd, msg);
free(msg);
}
void InputContext::review_im(const char *engine)
{
char *locale, *prev_engine;
const char *client_locale, *engine_locales;
const char *encoding;
prev_engine = mEngineName;
mEngineName = strdup(engine);
encoding = mXic->get_encoding();
client_locale = mXic->get_lang_region();
engine_locales = compose_localenames_from_im_lang(get_im_lang_from_engine(engine));
if (!strcmp(encoding, "UTF-8")) {
if (is_locale_included(engine_locales, client_locale))
locale = strdup(client_locale);
else
locale = get_prefered_locale(engine_locales);
locale = (char *)realloc(locale, strlen(locale) + strlen(".UTF-8") + 1);
strcat(locale, ".UTF-8");
setlocale(LC_CTYPE, locale);
free(mLocaleName);
mLocaleName = locale;
} else {
if (!is_locale_included(engine_locales, client_locale)) {
clear();
uim_switch_im(mUc, prev_engine);
free(mEngineName);
mEngineName = strdup(prev_engine);
}
}
free(prev_engine);
}
void
InputContext::customContext(const char *custom, const char *val)
{
uim_prop_update_custom(mUc, custom, val);
}
InputContext *
InputContext::focusedContext()
{
return mFocusedContext;
}
void
InputContext::deletefocusedContext()
{
mFocusedContext = NULL;
}
void
InputContext::focusIn()
{
setlocale(LC_CTYPE, mLocaleName);
check_helper_connection();
uim_helper_client_focus_in(mUc);
mFocusedContext = this;
if (mConvdisp) {
mConvdisp->unset_focus();
mConvdisp->move_candwin();
mConvdisp->update_caret_state();
}
uim_prop_list_update(mUc);
uim_prop_label_update(mUc);
if (hasActiveCandwin())
candidate_update();
uim_focus_in_context(mUc);
}
void
InputContext::focusOut()
{
uim_focus_out_context(mUc);
uim_helper_client_focus_out(mUc);
if (mFocusedContext == this) {
Canddisp *disp = canddisp_singleton();
if (isCaretStateShown())
disp->hide_caret_state();
if (hasActiveCandwin())
disp->hide();
}
}
XimServer *
InputContext::getServer()
{
return mServer;
}
uim_context
InputContext::getUC()
{
return mUc;
}
void
InputContext::commit_cb(void *ptr, const char *str)
{
InputContext *ic = (InputContext *)ptr;
XimIC *xic = ic->get_ic();
ic->clear_pe_stat();
ic->update_preedit();
xic->commit_string(str);
}
void InputContext::clear_cb(void *ptr)
{
InputContext *ic = (InputContext *)ptr;
ic->clear_pe_stat();
}
void InputContext::pushback_cb(void *ptr, int attr, const char *str)
{
InputContext *ic = (InputContext *)ptr;
ic->pushback_preedit_string(attr, str);
}
void InputContext::update_cb(void *ptr)
{
InputContext *ic = (InputContext *)ptr;
ic->update_preedit();
}
void InputContext::candidate_activate_cb(void *ptr, int nr, int display_limit)
{
InputContext *ic = (InputContext *)ptr;
ic->candidate_activate(nr, display_limit);
}
#if UIM_XIM_USE_DELAY
void InputContext::candidate_activate_with_delay_cb(void *ptr, int delay)
{
InputContext *ic = (InputContext *)ptr;
ic->candidate_activate_with_delay(delay);
}
void InputContext::candidate_activate_timeout_cb(void *ptr)
{
InputContext *ic = (InputContext *)ptr;
ic->candidate_activate_timeout();
}
#endif
void InputContext::candidate_select_cb(void *ptr, int index)
{
InputContext *ic = (InputContext *)ptr;
ic->set_need_hilite_selected_cand(true);
ic->candidate_select(index);
}
void InputContext::candidate_shift_page_cb(void *ptr, int direction)
{
InputContext *ic = (InputContext *)ptr;
ic->candidate_shift_page(direction);
}
void InputContext::candidate_deactivate_cb(void *ptr)
{
InputContext *ic = (InputContext *)ptr;
ic->candidate_deactivate();
}
void InputContext::update_prop_list_cb(void *ptr, const char *str)
{
InputContext *ic = (InputContext *)ptr;
InputContext *focusedContext = InputContext::focusedContext();
if (ic == focusedContext)
ic->update_prop_list(str);
}
void InputContext::update_prop_label_cb(void *ptr, const char *str)
{
InputContext *ic = (InputContext *)ptr;
InputContext *focusedContext = InputContext::focusedContext();
if (ic == focusedContext)
ic->update_prop_label(str);
}
void InputContext::configuration_changed_cb(void *ptr)
{
InputContext *ic = (InputContext *)ptr;
ic->configuration_changed();
}
void InputContext::switch_app_global_im_cb(void *ptr, const char *name)
{
InputContext *ic = (InputContext *)ptr;
ic->switch_app_global_im(name);
}
void InputContext::switch_system_global_im_cb(void *ptr, const char *name)
{
InputContext *ic = (InputContext *)ptr;
ic->switch_system_global_im(name);
}
void InputContext::clear_pe_stat()
{
m_pe->clear();
}
void InputContext::clear_preedit()
{
clear_pe_stat();
if (mConvdisp)
mConvdisp->clear_preedit();
}
uString InputContext::get_preedit_string()
{
uString str;
if (mConvdisp)
str = mConvdisp->get_pe();
return str;
}
void InputContext::pushback_preedit_string(int attr, const char *str)
{
if (str == NULL) {
fprintf(stderr, "Warning: str is NULL in pushback_cb\n");
return;
}
// Need to check caret pos at first.
if (attr & UPreeditAttr_Cursor)
m_pe->caret_pos = m_pe->get_char_count();
if (!strlen(str))
return;
int p = 0;
if (attr & UPreeditAttr_UnderLine)
p |= PE_UNDERLINE;
if (attr & UPreeditAttr_Reverse)
p |= PE_REVERSE;
m_pe->new_segment(p);
uString js;
mServer->strToUstring(&js, str);
uString::iterator it;
for (it = js.begin(); it != js.end(); ++it) {
m_pe->push_uchar(*it);
}
}
void InputContext::update_preedit()
{
if (mConvdisp)
mConvdisp->update_preedit();
}
int InputContext::pushKey(keyState *k)
{
int key = k->key();
int rv = 1;
if (key != UKey_Other) {
if (k->is_push()) {
rv = uim_press_key(mUc, key, k->modifier());
if (!(g_option_mask & OPT_ON_DEMAND_SYNC)) {
// Call uim_release_key here since we don't filter key
// release event with full-synchronous-method for now.
uim_release_key(mUc, key, k->modifier());
}
}
else
rv = uim_release_key(mUc, key, k->modifier());
}
if (rv) {
if (k->check_compose())
return UPDATE_MODE;
else
return COMMIT_RAW;
} else {
return UPDATE_MODE;
}
}
bool InputContext::hasActiveCandwin()
{
return mCandwinActive;
}
// reset
void InputContext::clear()
{
clear_preedit();
candidate_deactivate();
uim_reset_context(mUc);
}
void InputContext::setConvdisp(Convdisp *c)
{
mConvdisp = c;
if (mConvdisp)
mConvdisp->set_pe(m_pe);
}
void InputContext::commit_string(char *s)
{
mXic->commit_string(s);
}
void InputContext::extra_input(char *s)
{
mXic->extra_input(s);
}
XimIC *InputContext::get_ic()
{
return mXic;
}
void InputContext::candidate_activate(int nr, int display_limit)
{
int i;
#if !UIM_XIM_USE_NEW_PAGE_HANDLING
const char *cand_str;
const char *heading_label;
const char *annotation_str;
char *str;
#else
std::vector<CandList>::iterator slot_it;
#endif
std::vector<const char *> candidates;
std::vector<const char *>::iterator it;
#if UIM_XIM_USE_DELAY
timer_cancel();
#endif
Canddisp *disp = canddisp_singleton();
mDisplayLimit = display_limit;
if (display_limit)
mNumPage = (nr - 1) / display_limit + 1;
#if !UIM_XIM_USE_NEW_PAGE_HANDLING
/* remove old data */
if (!active_candidates.empty()) {
for (it = active_candidates.begin();
it != active_candidates.end();
++it)
free((char *)*it);
}
active_candidates.clear();
for (i = 0; i < nr; i++) {
uim_candidate cand;
cand = uim_get_candidate(mUc, i,
display_limit ? i % display_limit : i);
cand_str = uim_candidate_get_cand_str(cand);
heading_label = uim_candidate_get_heading_label(cand);
annotation_str = uim_candidate_get_annotation(cand);
if (cand_str && heading_label && annotation_str) {
str = (char *)malloc(strlen(cand_str) + strlen(heading_label) + strlen(annotation_str) + 3);
sprintf(str, "%s\a%s\a%s", heading_label, cand_str, annotation_str);
candidates.push_back((const char *)str);
}
else {
fprintf(stderr, "Warning: cand_str at %d is NULL\n", i);
candidates.push_back((const char *)strdup("\a\a"));
}
uim_candidate_free(cand);
}
disp->activate(candidates, display_limit);
active_candidates = candidates;
#else /* !UIM_XIM_USE_NEW_PAGE_HANDLING */
mNumCandidates = nr;
/* remove old data */
for (slot_it = mCandidateSlot.begin();
slot_it != mCandidateSlot.end();
++slot_it) {
if (*slot_it != (CandList)0) {
for (it = (*slot_it).begin(); it != (*slot_it).end(); ++it)
free((char *)*it);
}
}
mCandidateSlot.clear();
/* setup dummy data */
for (i = 0; i < mNumPage; i++)
mCandidateSlot.push_back((CandList)0);
prepare_page_candidates(0);
disp->set_nr_candidates(nr, display_limit);
disp->set_page_candidates(0, mCandidateSlot[0]);
disp->show_page(0);
#endif /* !UIM_XIM_USE_NEW_PAGE_HANDLING */
mCandwinActive = true;
current_cand_selection = 0;
current_page = 0;
need_hilite_selected_cand = false;
}
#if UIM_XIM_USE_DELAY
void InputContext::candidate_activate_with_delay(int delay)
{
timer_cancel();
if (delay > 0) {
timer_set(delay, InputContext::candidate_activate_timeout_cb, this);
} else {
candidate_activate_timeout();
}
}
void InputContext::candidate_activate_timeout()
{
int nr = -1, display_limit = -1, selected_index = -1;
uim_delay_activating(mUc, &nr, &display_limit, &selected_index);
if (nr > 0) {
candidate_activate(nr, display_limit);
if (selected_index >= 0) {
candidate_select(selected_index);
}
}
}
#endif
void InputContext::candidate_update()
{
Canddisp *disp = canddisp_singleton();
#if !UIM_XIM_USE_NEW_PAGE_HANDLING
disp->activate(active_candidates, mDisplayLimit);
#else
prepare_page_candidates(current_page);
disp->set_nr_candidates(mNumCandidates, mDisplayLimit);
disp->set_page_candidates(current_page, mCandidateSlot[current_page]);
disp->show_page(current_page);
#endif
disp->select(current_cand_selection, need_hilite_selected_cand);
disp->show();
}
#if UIM_XIM_USE_NEW_PAGE_HANDLING
void InputContext::prepare_page_candidates(int page)
{
int i;
int page_nr, start;
const char *cand_str;
const char *heading_label;
const char *annotation_str;
char *str;
CandList candidates;
if (page < 0)
return;
if (mCandidateSlot[page] != (CandList)0)
return;
start = page * mDisplayLimit;
if (mDisplayLimit && (mNumCandidates - start) > mDisplayLimit)
page_nr = mDisplayLimit;
else
page_nr = mNumCandidates - start;
for (i = 0; i < page_nr; i++) {
uim_candidate cand;
cand = uim_get_candidate(mUc, (i + start),
mDisplayLimit ? (i + start) % mDisplayLimit :
(i + start));
cand_str = uim_candidate_get_cand_str(cand);
heading_label = uim_candidate_get_heading_label(cand);
annotation_str = uim_candidate_get_annotation_str(cand);
if (cand_str && heading_label && annotation_str) {
str = (char *)malloc(strlen(cand_str) + strlen(heading_label) + strlen(annotation_str) + 3);
sprintf(str, "%s\a%s\a%s", heading_label, cand_str, annotation_str);
candidates.push_back((const char *)str);
}
else {
fprintf(stderr, "Warning: cand_str at %d is NULL\n", i);
candidates.push_back((const char *)strdup("\a\a"));
}
uim_candidate_free(cand);
}
mCandidateSlot[page] = candidates;
}
int InputContext::prepare_page_candidates_by_index(int index)
{
int page;
page = mDisplayLimit ? index / mDisplayLimit : 0;
prepare_page_candidates(page);
return page;
}
#endif
void InputContext::candidate_select(int index)
{
Canddisp *disp = canddisp_singleton();
#if UIM_XIM_USE_NEW_PAGE_HANDLING
int new_page = prepare_page_candidates_by_index(index);
if (new_page < 0)
return; // shouldn't happen
if (current_page != new_page)
disp->set_page_candidates(new_page, mCandidateSlot[new_page]);
#endif
disp->select(index, need_hilite_selected_cand);
current_cand_selection = index;
if (mDisplayLimit)
current_page = current_cand_selection / mDisplayLimit;
}
void InputContext::candidate_shift_page(int direction)
{
int new_page;
int new_index;
if (mDisplayLimit) {
if (direction)
new_page = current_page + 1;
else
new_page = current_page - 1;
if (new_page < 0)
current_page = mNumPage - 1;
else if (new_page >= mNumPage)
current_page = 0;
else
current_page = new_page;
new_index = (current_page * mDisplayLimit) + (current_cand_selection % mDisplayLimit);
#if !UIM_XIM_USE_NEW_PAGE_HANDLING
if (new_index >= active_candidates.size())
current_cand_selection = active_candidates.size() - 1;
#else
if (new_index >= mNumCandidates)
current_cand_selection = mNumCandidates - 1;
#endif
else
current_cand_selection = new_index;
#if UIM_XIM_USE_NEW_PAGE_HANDLING
Canddisp *disp = canddisp_singleton();
prepare_page_candidates(current_page);
disp->set_page_candidates(current_page, mCandidateSlot[current_page]);
#endif
}
candidate_select(current_cand_selection);
if (need_hilite_selected_cand)
uim_set_candidate_index(mUc, current_cand_selection);
}
void InputContext::candidate_deactivate()
{
#if UIM_XIM_USE_DELAY
timer_cancel();
#endif
if (mCandwinActive) {
std::vector<const char *>::iterator i;
Canddisp *disp = canddisp_singleton();
disp->deactivate();
#if !UIM_XIM_USE_NEW_PAGE_HANDLING
for (i = active_candidates.begin(); i != active_candidates.end(); ++i) {
free((char *)*i);
}
active_candidates.clear();
#else
int j;
for (j = 0; j < mNumPage; j++) {
if ((CandList)mCandidateSlot[j] != (CandList)0) {
for (i = mCandidateSlot[j].begin();
i != mCandidateSlot[j].end();
++i) {
free((char *)*i);
}
}
}
mCandidateSlot.clear();
#endif
mCandwinActive = false;
current_cand_selection = 0;
}
}
void InputContext::set_need_hilite_selected_cand(bool set)
{
need_hilite_selected_cand = set;
}
char *InputContext::get_caret_state_label_from_prop_list(const char *str)
{
const char *p, *q;
char *state_label = NULL;
char label[10];
int len, state_label_len = 0;
p = str;
while ((p = strstr(p, "branch\t"))) {
p = strchr(p + 7, '\t');
if (p) {
p++;
q = strchr(p, '\t');
len = static_cast<int>(q - p);
if (q && len < 10) {
strlcpy(label, p, len + 1);
if (!state_label) {
state_label_len = len;
state_label = strdup(label);
} else {
state_label_len += (len + 1);
state_label = (char *)realloc(state_label,
state_label_len + 1);
if (state_label) {
strcat(state_label, "\t");
strcat(state_label, label);
state_label[state_label_len] = '\0';
}
}
}
}
}
return state_label;
}
void InputContext::update_prop_list(const char *str)
{
char *buf;
if (asprintf(&buf, "prop_list_update\ncharset=UTF-8\n%s", str) == -1) {
free(buf);
return;
}
uim_helper_send_message(lib_uim_fd, buf);
free(buf);
#if 1
// Show caret state indicator with this function instead of
// InputContext::update_prop_label() to workaround the label
// mismatch during IM switch caused from context-update-widgets.
uim_bool show_caret_state =
uim_scm_symbol_value_bool("bridge-show-input-state?");
char *show_caret_with =
uim_scm_c_symbol(uim_scm_symbol_value("bridge-show-with?"));
uim_bool show_caret_mode = (strcmp(show_caret_with, "mode") == 0);
uim_bool show_caret_mode_on = uim_scm_symbol_value_bool("bridge-show-input-state-mode-on?");
if (show_caret_state == UIM_TRUE && !(show_caret_mode && !show_caret_mode_on)) {
char *label;
int timeout;
Canddisp *disp = canddisp_singleton();
if (strcmp(show_caret_with, "time") == 0)
timeout = static_cast<int>(uim_scm_symbol_value_int(
"bridge-show-input-state-time-length"));
else
timeout = 0;
label = get_caret_state_label_from_prop_list(str);
disp->show_caret_state(label, timeout);
free(label);
mCaretStateShown = true;
} else if (show_caret_mode && !show_caret_mode_on) {
Canddisp *disp = canddisp_singleton();
disp->hide_caret_state();
}
free(show_caret_with);
#endif
}
void InputContext::update_prop_label(const char *str)
{
char *buf;
if (asprintf(&buf, "prop_label_update\ncharset=UTF-8\n%s", str) == -1) {
free(buf);
return;
}
uim_helper_send_message(lib_uim_fd, buf);
free(buf);
#if 0
uim_bool show_caret_state = uim_scm_symbol_value_bool("bridge-show-input-state?");
if (show_caret_state == UIM_TRUE) {
int timeout = uim_scm_symbol_value_int("bridge-show-input-state-time-length");
Canddisp *disp = canddisp_singleton();
disp->show_caret_state(str, timeout);
mCaretStateShown = true;
}
#endif
}
const char *InputContext::get_engine_name()
{
return mEngineName;
}
const char *InputContext::get_locale_name()
{
return mLocaleName;
}
bool InputContext::isCaretStateShown()
{
return mCaretStateShown;
}
keyState::keyState(XimIC *ic)
{
XimIM *im;
DefTree *top;
mModState = 0;
mIc = ic;
im = get_im_by_id(mIc->get_imid());
top = im->get_compose_tree();
mCompose = new Compose(top, mIc);
}
keyState::~keyState()
{
delete mCompose;
}
void keyState::check_key(keyEventX *x)
{
mModifier = 0;
mXKeySym = x->key_sym;
mXKeyState = x->state;
mPreModState = mModState;
if (x->press) {
m_bPush = true;
if (!(x->state) || x->state == LockMask || x->state == gXNumLockMask)
mModState = mPreModState = 0;
mPreModState = mModState;
switch (x->key_sym) {
case XK_Alt_L:
case XK_Alt_R:
mModState |= UMod_Alt;
break;
case XK_Meta_L:
case XK_Meta_R:
mModState |= UMod_Meta;
break;
case XK_Super_L:
case XK_Super_R:
mModState |= UMod_Super;
break;
case XK_Hyper_L:
case XK_Hyper_R:
mModState |= UMod_Hyper;
break;
default:
break;
}
} else {
m_bPush = false;
switch (x->key_sym) {
case XK_Alt_L:
case XK_Alt_R:
mModState &= ~UMod_Alt;
break;
case XK_Meta_L:
case XK_Meta_R:
mModState &= ~UMod_Meta;
break;
case XK_Super_L:
case XK_Super_R:
mModState &= ~UMod_Super;
break;
case XK_Hyper_L:
case XK_Hyper_R:
mModState &= ~UMod_Hyper;
break;
default:
break;
}
}
if (x->state & ShiftMask)
mModifier |= UMod_Shift;
if (x->state & ControlMask)
mModifier |= UMod_Control;
if (x->state & Mod1Mask)
mModifier |= (gMod1Mask & mPreModState);
if (x->state & Mod2Mask)
mModifier |= (gMod2Mask & mPreModState);
if (x->state & Mod3Mask)
mModifier |= (gMod3Mask & mPreModState);
if (x->state & Mod4Mask)
mModifier |= (gMod4Mask & mPreModState);
if (x->state & Mod5Mask)
mModifier |= (gMod5Mask & mPreModState);
if (x->key_sym < 128 && x->key_sym >= 32)
mKey = static_cast<int>(x->key_sym);
else if (x->key_sym >= XK_F1 && x->key_sym <= XK_F35)
mKey = static_cast<int>(x->key_sym - XK_F1 + UKey_F1);
// GTK+ and Qt don't support dead_stroke yet
else if (x->key_sym >= XK_dead_grave && x->key_sym <= XK_dead_horn)
mKey = static_cast<int>(x->key_sym - XK_dead_grave + UKey_Dead_Grave);
else if (x->key_sym >= XK_Kanji && x->key_sym <= XK_Eisu_toggle)
mKey = static_cast<int>(x->key_sym - XK_Kanji + UKey_Kanji);
else if (x->key_sym >= XK_Hangul && x->key_sym <= XK_Hangul_Special)
mKey = static_cast<int>(x->key_sym - XK_Hangul + UKey_Hangul);
else if (x->key_sym >= XK_kana_fullstop && x->key_sym <= XK_semivoicedsound)
mKey = static_cast<int>(
x->key_sym - XK_kana_fullstop + UKey_Kana_Fullstop);
else {
switch (x->key_sym) {
case XK_yen: mKey = UKey_Yen; break;
case XK_BackSpace: mKey = UKey_Backspace; break;
case XK_Delete: mKey = UKey_Delete; break;
case XK_Insert: mKey = UKey_Insert; break;
case XK_Escape: mKey = UKey_Escape; break;
case XK_Tab:
case XK_ISO_Left_Tab: mKey = UKey_Tab; break;
case XK_Return: mKey = UKey_Return; break;
case XK_Left: mKey = UKey_Left; break;
case XK_Up: mKey = UKey_Up; break;
case XK_Right: mKey = UKey_Right; break;
case XK_Down: mKey = UKey_Down; break;
case XK_Prior: mKey = UKey_Prior; break;
case XK_Next: mKey = UKey_Next; break;
case XK_Home: mKey = UKey_Home; break;
case XK_End: mKey = UKey_End; break;
case XK_Multi_key: mKey = UKey_Multi_key; break;
case XK_Codeinput: mKey = UKey_Codeinput; break;
case XK_SingleCandidate: mKey = UKey_SingleCandidate; break;
case XK_MultipleCandidate: mKey = UKey_MultipleCandidate; break;
case XK_PreviousCandidate: mKey = UKey_PreviousCandidate; break;
case XK_Mode_switch: mKey = UKey_Mode_switch; break;
case XK_Shift_L: mKey = UKey_Shift_key; break;
case XK_Shift_R: mKey = UKey_Shift_key; break;
case XK_Control_L: mKey = UKey_Control_key; break;
case XK_Control_R: mKey = UKey_Control_key; break;
case XK_Alt_L: mKey = UKey_Alt_key; break;
case XK_Alt_R: mKey = UKey_Alt_key; break;
case XK_Meta_L: mKey = UKey_Meta_key; break;
case XK_Meta_R: mKey = UKey_Meta_key; break;
case XK_Super_L: mKey = UKey_Super_key; break;
case XK_Super_R: mKey = UKey_Super_key; break;
case XK_Hyper_L: mKey = UKey_Hyper_key; break;
case XK_Hyper_R: mKey = UKey_Hyper_key; break;
case XK_Caps_Lock: mKey = UKey_Caps_Lock; break;
case XK_Num_Lock: mKey = UKey_Num_Lock; break;
case XK_Scroll_Lock: mKey = UKey_Scroll_Lock; break;
default:
mKey = UKey_Other;
}
}
#if UIM_XIM_USE_JAPANESE_KANA_KEYBOARD_HACK
mKey = uim_x_kana_input_hack_translate_key(mKey,
(KeyCode)x->ev.xkey.keycode);
#endif
}
bool keyState::check_compose()
{
return mCompose->handleKey(mXKeySym, mXKeyState, m_bPush);
}
int keyState::key()
{
return mKey;
}
int keyState::modifier()
{
return mModifier;
}
bool keyState::is_push()
{
return m_bPush;
}
KeySym keyState::xkeysym()
{
return mXKeySym;
}
int keyState::xkeystate()
{
return mXKeyState;
}
void keyState::reset()
{
mModState = 0;
mCompose->reset();
}
void keyState::print()
{
printf("key code=%x,modifier=%d.\n",
mKey, mModifier);
}
static int check_modifier(std::list<KeySym> keysym_list)
{
int ret = 0;
std::list<KeySym>::iterator i;
for (i = keysym_list.begin(); i != keysym_list.end(); ++i) {
switch (*i) {
case XK_Alt_L:
case XK_Alt_R:
ret |= UMod_Alt;
break;
case XK_Meta_L:
case XK_Meta_R:
ret |= UMod_Meta;
break;
case XK_Super_L:
case XK_Super_R:
ret |= UMod_Super;
break;
case XK_Hyper_L:
case XK_Hyper_R:
ret |= UMod_Hyper;
break;
default:
break;
}
}
return ret;
}
void init_modifier_keys() {
int i, k = 0;
int min_keycode, max_keycode, keysyms_per_keycode = 0;
std::list<KeySym> Mod1MaskSyms, Mod2MaskSyms, Mod3MaskSyms,
Mod4MaskSyms, Mod5MaskSyms;
gXNumLockMask = 0;
XModifierKeymap *map = XGetModifierMapping(XimServer::gDpy);
XDisplayKeycodes(XimServer::gDpy, &min_keycode, &max_keycode);
KeySym *sym = XGetKeyboardMapping(XimServer::gDpy,
static_cast<KeyCode>(min_keycode),
(max_keycode - min_keycode + 1), &keysyms_per_keycode);
for (i = 0; i < 8; i++) {
int j;
for (j = 0; j < map->max_keypermod; j++) {
if (map->modifiermap[k]) {
KeySym ks;
int index = 0;
do {
ks = XkbKeycodeToKeysym(XimServer::gDpy,
map->modifiermap[k], 0, index);
index++;
} while (!ks && index < keysyms_per_keycode);
switch (i) {
case ShiftMapIndex: break;
case LockMapIndex: break;
case ControlMapIndex: break;
case Mod1MapIndex: Mod1MaskSyms.push_back(ks); break;
case Mod2MapIndex: Mod2MaskSyms.push_back(ks); break;
case Mod3MapIndex: Mod3MaskSyms.push_back(ks); break;
case Mod4MapIndex: Mod4MaskSyms.push_back(ks); break;
case Mod5MapIndex: Mod5MaskSyms.push_back(ks); break;
default: break;
}
// Check NumLock key
if (ks == XK_Num_Lock)
gXNumLockMask |= (1 << i);
}
k++;
}
}
XFreeModifiermap(map);
XFree(sym);
gMod1Mask = check_modifier(Mod1MaskSyms);
gMod2Mask = check_modifier(Mod2MaskSyms);
gMod3Mask = check_modifier(Mod3MaskSyms);
gMod4Mask = check_modifier(Mod4MaskSyms);
gMod5Mask = check_modifier(Mod5MaskSyms);
#if UIM_XIM_USE_JAPANESE_KANA_KEYBOARD_HACK
// Init at here to sync with proper update timing although not a modifier.
uim_x_kana_input_hack_init(XimServer::gDpy);
#endif
}
void
check_candwin_style()
{
char *style = uim_scm_symbol_value_str("candidate-window-style");
CandWinStyle PrevStyle = XimServer::gCandWinStyle;
if (style && !strcmp(style, "table"))
XimServer::gCandWinStyle = Table;
else if (style && !strcmp(style, "horizontal"))
XimServer::gCandWinStyle = Horizontal;
else
XimServer::gCandWinStyle = Vertical;
XimServer::gCandWinStyleUpdated =
(PrevStyle != XimServer::gCandWinStyle) ? true : false;
free(style);
}
void
check_candwin_pos_type()
{
char *candwin_pos_type = uim_scm_symbol_value_str("candidate-window-position");
if (candwin_pos_type && !strcmp(candwin_pos_type, "left"))
XimServer::gCandWinPosType = Left;
else if (candwin_pos_type && !strcmp(candwin_pos_type, "right"))
XimServer::gCandWinPosType = Right;
else
XimServer::gCandWinPosType = Caret;
free(candwin_pos_type);
}
/*
* Local variables:
* c-indent-level: 4
* c-basic-offset: 4
* End:
*/