#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xmd.h>
#include <X11/keysym.h>
#include <X11/Xutil.h>
#include "Xlibint.h"
#include "Xlcint.h"
#include "Ximint.h"
#include "XimThai.h"
#include "XlcPubI.h"
#define SPACE 32
#define TACTIS_CHARS 256
Private
char const tactis_chtype[TACTIS_CHARS] = {
CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL,
CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL,
CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL,
CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL,
NON, NON, NON, NON, NON, NON, NON, NON,
NON, NON, NON, NON, NON, NON, NON, NON,
NON, NON, NON, NON, NON, NON, NON, NON,
NON, NON, NON, NON, NON, NON, NON, NON,
NON, NON, NON, NON, NON, NON, NON, NON,
NON, NON, NON, NON, NON, NON, NON, NON,
NON, NON, NON, NON, NON, NON, NON, NON,
NON, NON, NON, NON, NON, NON, NON, NON,
NON, NON, NON, NON, NON, NON, NON, NON,
NON, NON, NON, NON, NON, NON, NON, NON,
NON, NON, NON, NON, NON, NON, NON, NON,
NON, NON, NON, NON, NON, NON, NON, CTRL,
CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL,
CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL,
CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL,
CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL,
NON, CONS, CONS, CONS, CONS, CONS, CONS, CONS,
CONS, CONS, CONS, CONS, CONS, CONS, CONS, CONS,
CONS, CONS, CONS, CONS, CONS, CONS, CONS, CONS,
CONS, CONS, CONS, CONS, CONS, CONS, CONS, CONS,
CONS, CONS, CONS, CONS, FV3, CONS, FV3, CONS,
CONS, CONS, CONS, CONS, CONS, CONS, CONS, NON,
FV1, AV2, FV1, FV1, AV1, AV3, AV2, AV3,
BV1, BV2, BD, NON, NON, NON, NON, NON,
LV, LV, LV, LV, LV, FV2, NON, AD2,
TONE, TONE, TONE, TONE, AD1, AD1, AD3, NON,
NON, NON, NON, NON, NON, NON, NON, NON,
NON, NON, NON, NON, NON, NON, NON, CTRL
};
#define NC 0
#define CP 1
#define XC 3
#define AC 4
#define RJ 5
#define CH_CLASSES 17
Private
char const write_rules_lookup[CH_CLASSES][CH_CLASSES] = {
{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}
,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}
,{XC, NC, NC, NC, NC, NC, NC, CP, CP, CP, CP, CP, CP, CP, CP, CP, CP}
,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}
,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}
,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}
,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}
,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, CP, CP, NC, NC, NC, NC, NC}
,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, CP, NC, NC, NC, NC, NC, NC}
,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}
,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}
,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}
,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}
,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}
,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, CP, CP, NC, NC, NC, NC, NC}
,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, CP, NC, NC, NC, NC, NC, NC}
,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, CP, NC, CP, NC, NC, NC, NC}
};
Private
char const wtt_isc1_lookup[CH_CLASSES][CH_CLASSES] = {
{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, CP, CP, CP, CP, CP, CP, CP, CP, CP, CP}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, CP, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, CP, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, RJ, CP, RJ, RJ, RJ, RJ}
};
Private
char const wtt_isc2_lookup[CH_CLASSES][CH_CLASSES] = {
{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, RJ, AC, CP, CP, CP, CP, CP, CP, CP, CP, CP, CP}
,{XC, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, RJ, AC, RJ, RJ, RJ, CP, CP, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, CP, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, CP, CP, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, CP, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, CP, RJ, CP, RJ, RJ, RJ, RJ}
};
Private
char const thaicat_isc_lookup[CH_CLASSES][CH_CLASSES] = {
{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, CP, CP, CP, CP, CP, CP, CP, CP, CP, CP}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, CP, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, CP, CP, RJ, RJ, RJ, RJ, RJ, CP, CP, CP}
,{XC, AC, AC, AC, AC, AC, AC, CP, RJ, RJ, RJ, RJ, RJ, RJ, CP, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, CP}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, CP, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, RJ, RJ, RJ, RJ, RJ, RJ}
,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, RJ, CP, RJ, RJ, RJ, RJ}
};
Private int
THAI_chtype (unsigned char ch)
{
return tactis_chtype[ch];
}
#ifdef UNUSED
Private int
THAI_chlevel (unsigned char ch)
{
int chlevel;
switch (tactis_chtype[ch])
{
case CTRL:
chlevel = NON;
break;
case BV1:
case BV2:
case BD:
chlevel = BELOW;
break;
case TONE:
case AD1:
case AD2:
chlevel = TOP;
break;
case AV1:
case AV2:
case AV3:
case AD3:
chlevel = ABOVE;
break;
case NON:
case CONS:
case LV:
case FV1:
case FV2:
case FV3:
default:
chlevel = BASE;
break;
}
return chlevel;
}
Private Bool
THAI_isdead (unsigned char ch)
{
return ((tactis_chtype[ch] == CTRL) || (tactis_chtype[ch] == BV1) ||
(tactis_chtype[ch] == BV2) || (tactis_chtype[ch] == BD) ||
(tactis_chtype[ch] == TONE) || (tactis_chtype[ch] == AD1) ||
(tactis_chtype[ch] == AD2) || (tactis_chtype[ch] == AD3) ||
(tactis_chtype[ch] == AV1) || (tactis_chtype[ch] == AV2) ||
(tactis_chtype[ch] == AV3));
}
Private Bool
THAI_iscons (unsigned char ch)
{
return (tactis_chtype[ch] == CONS);
}
Private Bool
THAI_isvowel (unsigned char ch)
{
return ((tactis_chtype[ch] == LV) || (tactis_chtype[ch] == FV1) ||
(tactis_chtype[ch] == FV2) || (tactis_chtype[ch] == FV3) ||
(tactis_chtype[ch] == BV1) || (tactis_chtype[ch] == BV2) ||
(tactis_chtype[ch] == AV1) || (tactis_chtype[ch] == AV2) ||
(tactis_chtype[ch] == AV3));
}
Private Bool
THAI_istone (unsigned char ch)
{
return (tactis_chtype[ch] == TONE);
}
#endif
Private Bool
THAI_iscomposible (
unsigned char follow_ch,
unsigned char lead_ch)
{
return (write_rules_lookup[THAI_chtype(lead_ch)][THAI_chtype(follow_ch)]
== CP);
}
Private Bool
THAI_isaccepted (
unsigned char follow_ch,
unsigned char lead_ch,
unsigned char mode)
{
Bool iskeyvalid;
switch (mode)
{
case WTT_ISC1:
iskeyvalid =
(wtt_isc1_lookup[THAI_chtype(lead_ch)][THAI_chtype(follow_ch)] != RJ);
break;
case WTT_ISC2:
iskeyvalid =
(wtt_isc2_lookup[THAI_chtype(lead_ch)][THAI_chtype(follow_ch)] != RJ);
break;
case THAICAT_ISC:
iskeyvalid =
(thaicat_isc_lookup[THAI_chtype(lead_ch)][THAI_chtype(follow_ch)] != RJ);
break;
default:
iskeyvalid = True;
break;
}
return iskeyvalid;
}
#ifdef UNUSED
Private void
THAI_apply_write_rules(
unsigned char *instr,
unsigned char *outstr,
unsigned char insert_ch,
int *num_insert_ch)
{
unsigned char *lead_ch = NULL, *follow_ch = NULL, *out_ch = NULL;
*num_insert_ch = 0;
lead_ch = follow_ch = instr;
out_ch = outstr;
if ((*lead_ch == '\0') || !(THAI_find_chtype(instr,DEAD)))
{
strcpy((char *)outstr, (char *)instr);
} else {
follow_ch++;
if (THAI_isdead(*lead_ch)) {
*out_ch++ = SPACE;
(*num_insert_ch)++;
}
*out_ch++ = *lead_ch;
while (*follow_ch != '\0')
{
if (THAI_isdead(*follow_ch) &&
!THAI_iscomposible(*follow_ch,*lead_ch))
{
*out_ch++ = SPACE;
(*num_insert_ch)++;
}
*out_ch++ = *follow_ch;
lead_ch = follow_ch;
follow_ch++;
}
*out_ch = '\0';
}
}
Private int
THAI_find_chtype (
unsigned char *instr,
int chtype)
{
int i = 0, position = -1;
switch (chtype)
{
case DEAD:
for (i = 0; *instr != '\0' && THAI_isdead(*instr); i++, instr++)
;
if (*instr != '\0') position = i;
break;
default:
break;
}
return position;
}
Private int
THAI_apply_scm(
unsigned char *instr,
unsigned char *outstr,
unsigned char spec_ch,
int num_sp,
unsigned char insert_ch)
{
unsigned char *scan, *outch;
int i, dead_count, found_count;
Bool isconsecutive;
scan = instr;
outch = outstr;
dead_count = found_count = 0;
isconsecutive = False;
while (*scan != '\0') {
if (THAI_isdead(*scan))
dead_count++;
if (*scan == spec_ch)
if (!isconsecutive)
found_count++;
*outch++ = *scan++;
if (found_count == num_sp) {
for (i = 0; i < dead_count; i++)
*outch++ = insert_ch;
dead_count = found_count = 0;
}
}
return 0;
}
Private void ComputeMaskFromKeytrans();
Private int IsCancelComposeKey(KeySym *symbol, XKeyEvent *event);
Private void SetLed(Display *dpy, int num, int state);
Private CARD8 FindKeyCode();
Private int XThaiTranslateKey();
Private int XThaiTranslateKeySym();
Private KeySym HexIMNormalKey(
XicThaiPart *thai_part,
KeySym symbol,
XKeyEvent *event);
Private KeySym HexIMFirstComposeKey(
XicThaiPart *thai_part,
KeySym symbol,
XKeyEvent *event);
Private KeySym HexIMSecondComposeKey(
XicThaiPart *thai_part,
KeySym symbol
XKeyEvent *event);
Private KeySym HexIMComposeSequence(KeySym ks1, KeySym ks2);
Private void InitIscMode(Xic ic);
Private Bool ThaiComposeConvert(
Display *dpy,
KeySym insym,
KeySym *outsym, KeySym *lower, KeySym *upper);
#endif
#define BellVolume 0
#define ucs2tis(wc) \
(unsigned char) ( \
(0<=(wc)&&(wc)<=0x7F) ? \
(wc) : \
((0x0E01<=(wc)&&(wc)<=0x0E5F) ? ((wc)-0x0E00+0xA0) : 0))
#define tis2ucs(c) \
( \
((c)<=0x7F) ? \
(wchar_t)(c) : \
((0x0A1<=(c)) ? ((wchar_t)(c)-0xA0+0x0E00) : 0))
#define IC_SavePreviousChar(ic,ch) \
((ic)->private.local.base.mb[(ic)->private.local.base.tree[(ic)->private.local.context].mb] = (char) (ch))
#define IC_ClearPreviousChar(ic) \
((ic)->private.local.base.mb[(ic)->private.local.base.tree[(ic)->private.local.context].mb] = 0)
#define IC_GetPreviousChar(ic) \
(IC_RealGetPreviousChar(ic,1))
#define IC_GetContextChar(ic) \
(IC_RealGetPreviousChar(ic,2))
#define IC_DeletePreviousChar(ic) \
(IC_RealDeletePreviousChar(ic))
Private unsigned char
IC_RealGetPreviousChar(Xic ic, unsigned short pos)
{
XICCallback* cb = &ic->core.string_conversion_callback;
DefTreeBase *b = &ic->private.local.base;
if (cb && cb->callback) {
XIMStringConversionCallbackStruct screc;
unsigned char c;
screc.position = 0;
screc.direction = XIMBackwardChar;
screc.operation = XIMStringConversionRetrieval;
screc.factor = pos;
screc.text = 0;
(cb->callback)((XIC)ic, cb->client_data, (XPointer)&screc);
if (!screc.text)
return (unsigned char) b->mb[b->tree[(ic)->private.local.context].mb];
if ((screc.text->feedback &&
*screc.text->feedback == XIMStringConversionLeftEdge) ||
screc.text->length < 1)
{
c = 0;
} else {
if (screc.text->encoding_is_wchar) {
c = ucs2tis(screc.text->string.wcs[0]);
XFree(screc.text->string.wcs);
} else {
c = screc.text->string.mbs[0];
XFree(screc.text->string.mbs);
}
}
XFree(screc.text);
return c;
} else {
return (unsigned char) b->mb[b->tree[(ic)->private.local.context].mb];
}
}
Private unsigned char
IC_RealDeletePreviousChar(Xic ic)
{
XICCallback* cb = &ic->core.string_conversion_callback;
if (cb && cb->callback) {
XIMStringConversionCallbackStruct screc;
unsigned char c;
screc.position = 0;
screc.direction = XIMBackwardChar;
screc.operation = XIMStringConversionSubstitution;
screc.factor = 1;
screc.text = 0;
(cb->callback)((XIC)ic, cb->client_data, (XPointer)&screc);
if (!screc.text) { return 0; }
if ((screc.text->feedback &&
*screc.text->feedback == XIMStringConversionLeftEdge) ||
screc.text->length < 1)
{
c = 0;
} else {
if (screc.text->encoding_is_wchar) {
c = ucs2tis(screc.text->string.wcs[0]);
XFree(screc.text->string.wcs);
} else {
c = screc.text->string.mbs[0];
XFree(screc.text->string.mbs);
}
}
XFree(screc.text);
return c;
} else {
return 0;
}
}
#define IC_IscMode(ic) ((ic)->private.local.thai.input_mode)
#define STR_LKUP_BUF_SIZE 256
#define SAV_LOCALE_NAME_SIZE 256
#define MAXTHAIIMMODLEN 20
#define AllMods (ShiftMask|LockMask|ControlMask| \
Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)
#define IsISOControlKey(ks) ((ks) >= XK_2 && (ks) <= XK_8)
#define IsValidControlKey(ks) (((((ks)>=XK_A && (ks)<=XK_asciitilde) || \
(ks)==XK_space || (ks)==XK_Delete) && \
((ks)!=0)))
#define COMPOSE_LED 2
#ifdef UNUSED
typedef KeySym (*StateProc)(
XicThaiPart *thai_part,
KeySym symbol,
XKeyEvent *event);
#define IsShift(state) (((state) & ShiftMask) != 0)
#define IsLock(state) (((state) & LockMask) != 0)
#define IsControl(state) (((state) & ControlMask) != 0)
#define IsMod1(state) (((state) & Mod1Mask) != 0)
#define IsMod2(state) (((state) & Mod2Mask) != 0)
#define IsMod3(state) (((state) & Mod3Mask) != 0)
#define IsMod4(state) (((state) & Mod4Mask) != 0)
#define IsMod5(state) (((state) & Mod5Mask) != 0)
#define IsComposeKey(ks, event) \
(( ks==XK_Alt_L && \
IsControl((event)->state) && \
!IsShift((event)->state)) \
? True : False)
Private int const nstate_handlers = 3;
Private StateProc state_handler[] = {
HexIMNormalKey,
HexIMFirstComposeKey,
HexIMSecondComposeKey
};
struct _XMapThaiKey {
KeySym from;
KeySym to;
};
Private struct _XMapThaiKey const ThaiComposeTable[] = {
{ XK_currency, XK_yen },
{ XK_cent, XK_sterling },
{ XK_ae, XK_idiaeresis },
{ XK_Oacute, XK_icircumflex },
{ XK_onesuperior, XK_uacute },
{ XK_Ograve, XK_aring },
{ XK_onequarter, XK_ucircumflex },
{ XK_VoidSymbol, XK_VoidSymbol }
};
struct _XKeytrans {
struct _XKeytrans *next;
char *string;
int len;
KeySym key;
unsigned int state;
KeySym *modifiers;
int mlen;
};
Private Bool
ThaiComposeConvert(
Display *dpy,
KeySym insym,
KeySym *outsym, KeySym *lower, KeySym *upper)
{
struct _XMapThaiKey const *table_entry = ThaiComposeTable;
while (table_entry->from != XK_VoidSymbol) {
if (table_entry->from == insym) {
*outsym = table_entry->to;
*lower = *outsym;
*upper = *outsym;
return True;
}
table_entry++;
}
return False;
}
Private int
XThaiTranslateKey(
register Display *dpy,
KeyCode keycode,
register unsigned int modifiers,
unsigned int *modifiers_return,
KeySym *keysym_return,
KeySym *lsym_return,
KeySym *usym_return)
{
int per;
register KeySym *syms;
KeySym sym = 0, lsym = 0, usym = 0;
if ((! dpy->keysyms) && (! _XKeyInitialize(dpy)))
return 0;
*modifiers_return = (ShiftMask|LockMask) | dpy->mode_switch;
if (((int)keycode < dpy->min_keycode) || ((int)keycode > dpy->max_keycode))
{
*keysym_return = NoSymbol;
return 1;
}
per = dpy->keysyms_per_keycode;
syms = &dpy->keysyms[(keycode - dpy->min_keycode) * per];
while ((per > 2) && (syms[per - 1] == NoSymbol))
per--;
if ((per > 2) && (modifiers & dpy->mode_switch)) {
syms += 2;
per -= 2;
}
if (!(modifiers & ShiftMask) &&
(!(modifiers & LockMask) || (dpy->lock_meaning == NoSymbol))) {
if ((per == 1) || (syms[1] == NoSymbol))
XConvertCase(syms[0], keysym_return, &usym);
else {
XConvertCase(syms[0], &lsym, &usym);
*keysym_return = syms[0];
}
} else if (!(modifiers & LockMask) ||
(dpy->lock_meaning != XK_Caps_Lock)) {
if ((per == 1) || ((usym = syms[1]) == NoSymbol))
XConvertCase(syms[0], &lsym, &usym);
*keysym_return = usym;
} else {
if ((per == 1) || ((sym = syms[1]) == NoSymbol))
sym = syms[0];
XConvertCase(sym, &lsym, &usym);
if (!(modifiers & ShiftMask) && (sym != syms[0]) &&
((sym != usym) || (lsym == usym)))
XConvertCase(syms[0], &lsym, &usym);
*keysym_return = usym;
}
if ((modifiers & Mod1Mask) &&
(modifiers & ShiftMask) &&
!(modifiers & ControlMask)) {
if (ThaiComposeConvert(dpy, syms[0], &sym, &lsym, &usym))
*keysym_return = sym;
}
if (*keysym_return == XK_VoidSymbol)
*keysym_return = NoSymbol;
*lsym_return = lsym;
*usym_return = usym;
return 1;
}
Private int
XThaiTranslateKeySym(
Display *dpy,
register KeySym symbol,
register KeySym lsym,
register KeySym usym,
unsigned int modifiers,
unsigned char *buffer,
int nbytes)
{
KeySym ckey = 0;
register struct _XKeytrans *p;
int length;
unsigned long hiBytes;
register unsigned char c;
length = 1;
if (!symbol)
return 0;
for (p = dpy->key_bindings; p; p = p->next) {
if (((modifiers & AllMods) == p->state) && (symbol == p->key)) {
length = p->len;
if (length > nbytes) length = nbytes;
memcpy (buffer, p->string, length);
return length;
}
}
hiBytes = symbol >> 8;
if (!(nbytes &&
((hiBytes == 0) ||
((hiBytes == 0xFF) &&
(((symbol >= XK_BackSpace) && (symbol <= XK_Clear)) ||
(symbol == XK_Return) ||
(symbol == XK_Escape) ||
(symbol == XK_KP_Space) ||
(symbol == XK_KP_Tab) ||
(symbol == XK_KP_Enter) ||
((symbol >= XK_KP_Multiply) && (symbol <= XK_KP_9)) ||
(symbol == XK_KP_Equal) ||
(symbol == XK_Scroll_Lock) ||
#ifdef DXK_PRIVATE
(symbol == DXK_Remove) ||
#endif
(symbol == NoSymbol) ||
(symbol == XK_Delete))))))
return 0;
if (symbol == XK_KP_Space)
c = XK_space & 0x7F;
else if (hiBytes == 0xFF)
c = symbol & 0x7F;
else
c = symbol & 0xFF;
if (modifiers & ControlMask) {
if (!(IsKeypadKey(lsym) || lsym==XK_Return || lsym==XK_Tab)) {
if (IsISOControlKey(lsym)) ckey = lsym;
else if (IsISOControlKey(usym)) ckey = usym;
else if (lsym == XK_question) ckey = lsym;
else if (usym == XK_question) ckey = usym;
else if (IsValidControlKey(lsym)) ckey = lsym;
else if (IsValidControlKey(usym)) ckey = usym;
else length = 0;
if (length != 0) {
if (ckey == XK_2) c = '\000';
else if (ckey >= XK_3 && ckey <= XK_7)
c = (char)(ckey-('3'-'\033'));
else if (ckey == XK_8) c = '\177';
else if (ckey == XK_Delete) c = '\030';
else if (ckey == XK_question) c = '\037';
else if (ckey == XK_quoteleft) c = '\036';
else c = (char)(ckey & 0x1f);
}
}
}
if (c == XK_thorn) {
buffer[0] = 0xd1;
buffer[1] = 0xe9;
buffer[2] = '\0';
return 2;
}
else {
buffer[0] = c;
buffer[1] = '\0';
return 1;
}
}
Private CARD8
FindKeyCode(
register Display *dpy,
register KeySym code)
{
register KeySym *kmax = dpy->keysyms +
(dpy->max_keycode - dpy->min_keycode + 1) * dpy->keysyms_per_keycode;
register KeySym *k = dpy->keysyms;
while (k < kmax) {
if (*k == code)
return (((k - dpy->keysyms) / dpy->keysyms_per_keycode) +
dpy->min_keycode);
k += 1;
}
return 0;
}
Private void
ComputeMaskFromKeytrans(
Display *dpy,
register struct _XKeytrans *p)
{
register int i;
register CARD8 code;
register XModifierKeymap *m = dpy->modifiermap;
p->state = AnyModifier;
for (i = 0; i < p->mlen; i++) {
if ((code = FindKeyCode(dpy, p->modifiers[i])) == 0)
return;
{
register int j = m->max_keypermod<<3;
while ((--j >= 0) && (code != m->modifiermap[j]))
;
if (j < 0)
return;
p->state |= (1<<(j/m->max_keypermod));
}
}
p->state &= AllMods;
}
#define NORMAL_KEY_STATE 0
#define FIRST_COMPOSE_KEY_STATE 1
#define SECOND_COMPOSE_KEY_STATE 2
Private
KeySym HexIMNormalKey(
XicThaiPart *thai_part,
KeySym symbol,
XKeyEvent *event)
{
if (IsComposeKey (symbol, event))
{
SetLed (event->display,COMPOSE_LED, LedModeOn);
thai_part->comp_state = FIRST_COMPOSE_KEY_STATE;
return NoSymbol;
}
return symbol;
}
Private
KeySym HexIMFirstComposeKey(
XicThaiPart *thai_part,
KeySym symbol,
XKeyEvent *event)
{
if (IsModifierKey (symbol)) return symbol;
if (IsCancelComposeKey (&symbol, event))
{
SetLed (event->display,COMPOSE_LED, LedModeOff);
thai_part->comp_state = NORMAL_KEY_STATE;
return symbol;
}
if (IsComposeKey (symbol, event))
{
return NoSymbol;
}
thai_part->keysym = symbol;
thai_part->comp_state = SECOND_COMPOSE_KEY_STATE;
return NoSymbol;
}
Private
KeySym HexIMSecondComposeKey(
XicThaiPart *thai_part,
KeySym symbol,
XKeyEvent *event)
{
if (IsModifierKey (symbol)) return symbol;
if (IsComposeKey (symbol, event))
{
thai_part->comp_state =FIRST_COMPOSE_KEY_STATE;
return NoSymbol;
}
SetLed (event->display,COMPOSE_LED, LedModeOff);
if (IsCancelComposeKey (&symbol, event))
{
thai_part->comp_state = NORMAL_KEY_STATE;
return symbol;
}
if ((symbol = HexIMComposeSequence (thai_part->keysym, symbol))
==NoSymbol)
{
XBell(event->display, BellVolume);
}
thai_part->comp_state = NORMAL_KEY_STATE;
return symbol;
}
Private
KeySym HexIMComposeSequence(KeySym ks1, KeySym ks2)
{
int hi_digit;
int lo_digit;
int tactis_code;
if ((ks1 >= XK_0) && (ks1 <= XK_9))
hi_digit = ks1 - XK_0;
else if ((ks1 >= XK_A) && (ks1 <= XK_F))
hi_digit = ks1 - XK_A + 10;
else if ((ks1 >= XK_a) && (ks1 <= XK_f))
hi_digit = ks1 - XK_a + 10;
else
return NoSymbol;
if ((ks2 >= XK_0) && (ks2 <= XK_9))
lo_digit = ks2 - XK_0;
else if ((ks2 >= XK_A) && (ks2 <= XK_F))
lo_digit = ks2 - XK_A + 10;
else if ((ks2 >= XK_a) && (ks2 <= XK_f))
lo_digit = ks2 - XK_a + 10;
else
return NoSymbol;
tactis_code = hi_digit * 0x10 + lo_digit ;
return (KeySym)tactis_code;
}
Private
int IsCancelComposeKey(
KeySym *symbol,
XKeyEvent *event)
{
if (*symbol==XK_Delete && !IsControl(event->state) &&
!IsMod1(event->state)) {
*symbol=NoSymbol;
return True;
}
if (IsComposeKey(*symbol, event)) return False;
return (
IsControl (event->state) ||
IsMod1(event->state) ||
IsKeypadKey (*symbol) ||
IsFunctionKey (*symbol) ||
IsMiscFunctionKey (*symbol) ||
#ifdef DXK_PRIVATE
*symbol == DXK_Remove ||
#endif
IsPFKey (*symbol) ||
IsCursorKey (*symbol) ||
(*symbol >= XK_Tab && *symbol < XK_Multi_key)
? True : False);
}
Private
void SetLed(
Display *dpy,
int num,
int state)
{
XKeyboardControl led_control;
led_control.led_mode = state;
led_control.led = num;
XChangeKeyboardControl (dpy, KBLed | KBLedMode, &led_control);
}
#endif
Private void InitIscMode(Xic ic)
{
Xim im;
char *im_modifier_name;
if (IC_IscMode(ic)) return;
im = (Xim) XIMOfIC((XIC)ic);
im_modifier_name = im->core.im_name;
if (!strncmp(im_modifier_name,"BasicCheck",MAXTHAIIMMODLEN+1))
IC_IscMode(ic) = WTT_ISC1;
else if (!strncmp(im_modifier_name,"Strict",MAXTHAIIMMODLEN+1))
IC_IscMode(ic) = WTT_ISC2;
else if (!strncmp(im_modifier_name,"Thaicat",MAXTHAIIMMODLEN+1))
IC_IscMode(ic) = THAICAT_ISC;
else if (!strncmp(im_modifier_name,"Passthrough",MAXTHAIIMMODLEN+1))
IC_IscMode(ic) = NOISC;
else
IC_IscMode(ic) = WTT_ISC1;
return;
}
Private Bool
ThaiFltAcceptInput(Xic ic, unsigned char new_char, KeySym symbol)
{
DefTreeBase *b = &ic->private.local.base;
b->wc[b->tree[ic->private.local.composed].wc+0] = tis2ucs(new_char);
b->wc[b->tree[ic->private.local.composed].wc+1] = '\0';
if ((new_char <= 0x1f) || (new_char == 0x7f))
b->tree[ic->private.local.composed].keysym = symbol;
else
b->tree[ic->private.local.composed].keysym = NoSymbol;
return True;
}
Private Bool
ThaiFltReorderInput(Xic ic, unsigned char previous_char, unsigned char new_char)
{
DefTreeBase *b = &ic->private.local.base;
if (!IC_DeletePreviousChar(ic)) return False;
b->wc[b->tree[ic->private.local.composed].wc+0] = tis2ucs(new_char);
b->wc[b->tree[ic->private.local.composed].wc+1] = tis2ucs(previous_char);
b->wc[b->tree[ic->private.local.composed].wc+2] = '\0';
b->tree[ic->private.local.composed].keysym = NoSymbol;
return True;
}
Private Bool
ThaiFltReplaceInput(Xic ic, unsigned char new_char, KeySym symbol)
{
DefTreeBase *b = &ic->private.local.base;
if (!IC_DeletePreviousChar(ic)) return False;
b->wc[b->tree[ic->private.local.composed].wc+0] = tis2ucs(new_char);
b->wc[b->tree[ic->private.local.composed].wc+1] = '\0';
if ((new_char <= 0x1f) || (new_char == 0x7f))
b->tree[ic->private.local.composed].keysym = symbol;
else
b->tree[ic->private.local.composed].keysym = NoSymbol;
return True;
}
Bool
_XimThaiFilter(d, w, ev, client_data)
Display *d;
Window w;
XEvent *ev;
XPointer client_data;
{
Xic ic = (Xic)client_data;
KeySym symbol;
int isc_mode;
unsigned char previous_char;
unsigned char new_char;
#ifdef UNUSED
unsigned int modifiers;
KeySym lsym,usym;
int state;
XicThaiPart *thai_part;
char buf[10];
#endif
wchar_t wbuf[10];
Bool isReject;
DefTreeBase *b = &ic->private.local.base;
if ((ev->type != KeyPress)
|| (ev->xkey.keycode == 0))
return False;
if (!IC_IscMode(ic)) InitIscMode(ic);
XwcLookupString((XIC)ic, &ev->xkey, wbuf, sizeof(wbuf) / sizeof(wbuf[0]),
&symbol, NULL);
if ((ev->xkey.state & (AllMods & ~ShiftMask)) ||
((symbol >> 8 == 0xFF) &&
((XK_BackSpace <= symbol && symbol <= XK_Clear) ||
(symbol == XK_Return) ||
(symbol == XK_Pause) ||
(symbol == XK_Scroll_Lock) ||
(symbol == XK_Sys_Req) ||
(symbol == XK_Escape) ||
(symbol == XK_Delete) ||
IsCursorKey(symbol) ||
IsKeypadKey(symbol) ||
IsMiscFunctionKey(symbol) ||
IsFunctionKey(symbol))))
{
IC_ClearPreviousChar(ic);
return False;
}
if (((symbol >> 8 == 0xFF) &&
IsModifierKey(symbol)) ||
#ifdef XK_XKB_KEYS
((symbol >> 8 == 0xFE) &&
(XK_ISO_Lock <= symbol && symbol <= XK_ISO_Last_Group_Lock)) ||
#endif
(symbol == NoSymbol))
{
return False;
}
#ifdef UNUSED
if (! XThaiTranslateKey(ev->xkey.display, ev->xkey.keycode, ev->xkey.state,
&modifiers, &symbol, &lsym, &usym))
return False;
thai_part = &ic->private.local.thai;
state = thai_part->comp_state;
if (state >= 0 && state < nstate_handlers)
{
symbol = (* state_handler[state])(thai_part, symbol, (XKeyEvent *)ev);
}
count = XThaiTranslateKeySym(ev->xkey.display, symbol, lsym,
usym, ev->xkey.state, buf, 10);
if (!symbol && !count)
return True;
if (!count)
return False;
#endif
isc_mode = IC_IscMode(ic);
if (!(previous_char = IC_GetPreviousChar(ic))) previous_char = ' ';
new_char = ucs2tis(wbuf[0]);
isReject = True;
if (THAI_isaccepted(new_char, previous_char, isc_mode)) {
ThaiFltAcceptInput(ic, new_char, symbol);
isReject = False;
} else {
unsigned char context_char;
context_char = IC_GetContextChar(ic);
if (context_char) {
if (THAI_iscomposible(new_char, context_char)) {
if (THAI_iscomposible(previous_char, new_char)) {
isReject = !ThaiFltReorderInput(ic, previous_char, new_char);
} else if (THAI_iscomposible(previous_char, context_char)) {
isReject = !ThaiFltReplaceInput(ic, new_char, symbol);
} else if (THAI_chtype(previous_char) == FV1
&& THAI_chtype(new_char) == TONE) {
isReject = !ThaiFltReorderInput(ic, previous_char, new_char);
}
} else if (THAI_isaccepted(new_char, context_char, isc_mode)) {
isReject = !ThaiFltReplaceInput(ic, new_char, symbol);
}
}
}
if (isReject) {
XBell(ev->xkey.display, BellVolume);
return True;
}
_Xlcwcstombs(ic->core.im->core.lcd, &b->mb[b->tree[ic->private.local.composed].mb],
&b->wc[b->tree[ic->private.local.composed].wc], 10);
_Xlcmbstoutf8(ic->core.im->core.lcd, &b->utf8[b->tree[ic->private.local.composed].utf8],
&b->mb[b->tree[ic->private.local.composed].mb], 10);
IC_SavePreviousChar(ic, new_char);
ev->xkey.keycode = 0;
XPutBackEvent(d, ev);
return True;
}