1 /***********************************************************
3 Copyright 1993, 1998 The Open Group
5 Permission to use, copy, modify, distribute, and sell this software and its
6 documentation for any purpose is hereby granted without fee, provided that
7 the above copyright notice appear in all copies and that both that
8 copyright notice and this permission notice appear in supporting
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 Except as contained in this notice, the name of The Open Group shall not be
22 used in advertising or otherwise to promote the sale, use or other dealings
23 in this Software without prior written authorization from The Open Group.
26 Copyright 1993 by Digital Equipment Corporation, Maynard, Massachusetts.
30 Permission to use, copy, modify, and distribute this software and its
31 documentation for any purpose and without fee is hereby granted,
32 provided that the above copyright notice appear in all copies and that
33 both that copyright notice and this permission notice appear in
34 supporting documentation, and that the name of Digital not be
35 used in advertising or publicity pertaining to distribution of the
36 software without specific, written prior permission.
38 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
39 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
40 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
41 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
42 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
43 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
46 ******************************************************************/
56 ** Thai specific functions.
57 ** Handles character classifications, composibility checking,
58 ** Input sequence check and other Thai specific requirements
59 ** according to WTT specification and DEC extensions.
61 ** MODIFICATION HISTORY:
71 #include <X11/keysym.h>
72 #include <X11/Xutil.h>
82 /* character classification table */
83 #define TACTIS_CHARS 256
85 char const tactis_chtype[TACTIS_CHARS] = {
86 CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, /* 0 - 7 */
87 CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, /* 8 - 15 */
88 CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, /* 16 - 23 */
89 CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, /* 24 - 31 */
90 NON, NON, NON, NON, NON, NON, NON, NON, /* 32 - 39 */
91 NON, NON, NON, NON, NON, NON, NON, NON, /* 40 - 47 */
92 NON, NON, NON, NON, NON, NON, NON, NON, /* 48 - 55 */
93 NON, NON, NON, NON, NON, NON, NON, NON, /* 56 - 63 */
94 NON, NON, NON, NON, NON, NON, NON, NON, /* 64 - 71 */
95 NON, NON, NON, NON, NON, NON, NON, NON, /* 72 - 79 */
96 NON, NON, NON, NON, NON, NON, NON, NON, /* 80 - 87 */
97 NON, NON, NON, NON, NON, NON, NON, NON, /* 88 - 95 */
98 NON, NON, NON, NON, NON, NON, NON, NON, /* 96 - 103 */
99 NON, NON, NON, NON, NON, NON, NON, NON, /* 104 - 111 */
100 NON, NON, NON, NON, NON, NON, NON, NON, /* 112 - 119 */
101 NON, NON, NON, NON, NON, NON, NON, CTRL, /* 120 - 127 */
102 CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, /* 128 - 135 */
103 CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, /* 136 - 143 */
104 CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, /* 144 - 151 */
105 CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, /* 152 - 159 */
106 NON, CONS, CONS, CONS, CONS, CONS, CONS, CONS, /* 160 - 167 */
107 CONS, CONS, CONS, CONS, CONS, CONS, CONS, CONS, /* 168 - 175 */
108 CONS, CONS, CONS, CONS, CONS, CONS, CONS, CONS, /* 176 - 183 */
109 CONS, CONS, CONS, CONS, CONS, CONS, CONS, CONS, /* 184 - 191 */
110 CONS, CONS, CONS, CONS, FV3, CONS, FV3, CONS, /* 192 - 199 */
111 CONS, CONS, CONS, CONS, CONS, CONS, CONS, NON, /* 200 - 207 */
112 FV1, AV2, FV1, FV1, AV1, AV3, AV2, AV3, /* 208 - 215 */
113 BV1, BV2, BD, NON, NON, NON, NON, NON, /* 216 - 223 */
114 LV, LV, LV, LV, LV, FV2, NON, AD2, /* 224 - 231 */
115 TONE, TONE, TONE, TONE, AD1, AD1, AD3, NON, /* 232 - 239 */
116 NON, NON, NON, NON, NON, NON, NON, NON, /* 240 - 247 */
117 NON, NON, NON, NON, NON, NON, NON, CTRL /* 248 - 255 */
120 /* Composibility checking tables */
121 #define NC 0 /* NOT COMPOSIBLE - following char displays in next cell */
122 #define CP 1 /* COMPOSIBLE - following char is displayed in the same cell
123 as leading char, also implies ACCEPT */
124 #define XC 3 /* Non-display */
125 #define AC 4 /* ACCEPT - display the following char in the next cell */
126 #define RJ 5 /* REJECT - discard that following char, ignore it */
128 #define CH_CLASSES 17 /* 17 classes of chars */
131 char const write_rules_lookup[CH_CLASSES][CH_CLASSES] = {
132 /* Table 0: writing/outputing rules */
133 /* row: leading char, column: following char */
134 /* CTRL NON CONS LV FV1 FV2 FV3 BV1 BV2 BD TONE AD1 AD2 AD3 AV1 AV2 AV3 */
135 {XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*CTRL*/
136 ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*NON*/
137 ,{XC, NC, NC, NC, NC, NC, NC, CP, CP, CP, CP, CP, CP, CP, CP, CP, CP}/*CONS*/
138 ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*LV*/
139 ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*FV1*/
140 ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*FV2*/
141 ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*FV3*/
142 ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, CP, CP, NC, NC, NC, NC, NC}/*BV1*/
143 ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, CP, NC, NC, NC, NC, NC, NC}/*BV2*/
144 ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*BD*/
145 ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*TONE*/
146 ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*AD1*/
147 ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*AD2*/
148 ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*AD3*/
149 ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, CP, CP, NC, NC, NC, NC, NC}/*AV1*/
150 ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, CP, NC, NC, NC, NC, NC, NC}/*AV2*/
151 ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, CP, NC, CP, NC, NC, NC, NC}/*AV3*/
155 char const wtt_isc1_lookup[CH_CLASSES][CH_CLASSES] = {
156 /* Table 1: WTT default input sequence check rules */
157 /* row: leading char, column: following char */
158 /* CTRL NON CONS LV FV1 FV2 FV3 BV1 BV2 BD TONE AD1 AD2 AD3 AV1 AV2 AV3 */
159 {XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*CTRL*/
160 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*NON*/
161 ,{XC, AC, AC, AC, AC, AC, AC, CP, CP, CP, CP, CP, CP, CP, CP, CP, CP}/*CONS*/
162 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*LV*/
163 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*FV1*/
164 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*FV2*/
165 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*FV3*/
166 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, CP, RJ, RJ, RJ, RJ, RJ}/*BV1*/
167 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, RJ, RJ, RJ, RJ, RJ, RJ}/*BV2*/
168 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*BD*/
169 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*TONE*/
170 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*AD1*/
171 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*AD2*/
172 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*AD3*/
173 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, CP, RJ, RJ, RJ, RJ, RJ}/*AV1*/
174 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, RJ, RJ, RJ, RJ, RJ, RJ}/*AV2*/
175 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, RJ, CP, RJ, RJ, RJ, RJ}/*AV3*/
179 char const wtt_isc2_lookup[CH_CLASSES][CH_CLASSES] = {
180 /* Table 2: WTT strict input sequence check rules */
181 /* row: leading char, column: following char */
182 /* CTRL NON CONS LV FV1 FV2 FV3 BV1 BV2 BD TONE AD1 AD2 AD3 AV1 AV2 AV3 */
183 {XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*CTRL*/
184 ,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*NON*/
185 ,{XC, AC, AC, AC, AC, RJ, AC, CP, CP, CP, CP, CP, CP, CP, CP, CP, CP}/*CONS*/
186 ,{XC, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*LV*/
187 ,{XC, AC, AC, AC, AC, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*FV1*/
188 ,{XC, AC, AC, AC, AC, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*FV2*/
189 ,{XC, AC, AC, AC, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*FV3*/
190 ,{XC, AC, AC, AC, AC, RJ, AC, RJ, RJ, RJ, CP, CP, RJ, RJ, RJ, RJ, RJ}/*BV1*/
191 ,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, CP, RJ, RJ, RJ, RJ, RJ, RJ}/*BV2*/
192 ,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*BD*/
193 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*TONE*/
194 ,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*AD1*/
195 ,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*AD2*/
196 ,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*AD3*/
197 ,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, CP, CP, RJ, RJ, RJ, RJ, RJ}/*AV1*/
198 ,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, CP, RJ, RJ, RJ, RJ, RJ, RJ}/*AV2*/
199 ,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, CP, RJ, CP, RJ, RJ, RJ, RJ}/*AV3*/
203 char const thaicat_isc_lookup[CH_CLASSES][CH_CLASSES] = {
204 /* Table 3: Thaicat input sequence check rules */
205 /* row: leading char, column: following char */
206 /* CTRL NON CONS LV FV1 FV2 FV3 BV1 BV2 BD TONE AD1 AD2 AD3 AV1 AV2 AV3 */
207 {XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*CTRL*/
208 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*NON*/
209 ,{XC, AC, AC, AC, AC, AC, AC, CP, CP, CP, CP, CP, CP, CP, CP, CP, CP}/*CONS*/
210 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*LV*/
211 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*FV1*/
212 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*FV2*/
213 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ} /*FV3*/
214 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, CP, RJ, RJ, RJ, RJ, RJ}/*BV1*/
215 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, RJ, RJ, RJ, RJ, RJ, RJ}/*BV2*/
216 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*BD*/
217 ,{XC, AC, AC, AC, AC, AC, AC, CP, CP, RJ, RJ, RJ, RJ, RJ, CP, CP, CP}/*TONE*/
218 ,{XC, AC, AC, AC, AC, AC, AC, CP, RJ, RJ, RJ, RJ, RJ, RJ, CP, RJ, RJ}/*AD1*/
219 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, CP}/*AD2*/
220 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*AD3*/
221 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, CP, RJ, RJ, RJ, RJ, RJ}/*AV1*/
222 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, RJ, RJ, RJ, RJ, RJ, RJ}/*AV2*/
223 ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, RJ, CP, RJ, RJ, RJ, RJ}/*AV3*/
227 /* returns classification of a char */
229 THAI_chtype (unsigned char ch)
231 return tactis_chtype[ch];
235 /* returns the display level */
237 THAI_chlevel (unsigned char ch)
241 switch (tactis_chtype[ch])
268 default: /* if tactis_chtype is invalid */
276 /* return True if char is non-spacing */
278 THAI_isdead (unsigned char ch)
280 return ((tactis_chtype[ch] == CTRL) || (tactis_chtype[ch] == BV1) ||
281 (tactis_chtype[ch] == BV2) || (tactis_chtype[ch] == BD) ||
282 (tactis_chtype[ch] == TONE) || (tactis_chtype[ch] == AD1) ||
283 (tactis_chtype[ch] == AD2) || (tactis_chtype[ch] == AD3) ||
284 (tactis_chtype[ch] == AV1) || (tactis_chtype[ch] == AV2) ||
285 (tactis_chtype[ch] == AV3));
289 /* return True if char is consonant */
291 THAI_iscons (unsigned char ch)
293 return (tactis_chtype[ch] == CONS);
297 /* return True if char is vowel */
299 THAI_isvowel (unsigned char ch)
301 return ((tactis_chtype[ch] == LV) || (tactis_chtype[ch] == FV1) ||
302 (tactis_chtype[ch] == FV2) || (tactis_chtype[ch] == FV3) ||
303 (tactis_chtype[ch] == BV1) || (tactis_chtype[ch] == BV2) ||
304 (tactis_chtype[ch] == AV1) || (tactis_chtype[ch] == AV2) ||
305 (tactis_chtype[ch] == AV3));
309 /* return True if char is tonemark */
311 THAI_istone (unsigned char ch)
313 return (tactis_chtype[ch] == TONE);
319 unsigned char follow_ch,
320 unsigned char lead_ch)
321 {/* "Can follow_ch be put in the same display cell as lead_ch?" */
323 return (write_rules_lookup[THAI_chtype(lead_ch)][THAI_chtype(follow_ch)]
329 unsigned char follow_ch,
330 unsigned char lead_ch,
333 Bool iskeyvalid; /* means "Can follow_ch be keyed in after lead_ch?" */
339 (wtt_isc1_lookup[THAI_chtype(lead_ch)][THAI_chtype(follow_ch)] != RJ);
343 (wtt_isc2_lookup[THAI_chtype(lead_ch)][THAI_chtype(follow_ch)] != RJ);
347 (thaicat_isc_lookup[THAI_chtype(lead_ch)][THAI_chtype(follow_ch)] != RJ);
359 THAI_apply_write_rules(
360 unsigned char *instr,
361 unsigned char *outstr,
362 unsigned char insert_ch,
368 insert_ch specify what char to be added when invalid composition is found
370 outstr - output string after input string has been applied the rules
371 num_insert_ch - number of insert_ch added to outstr.
373 unsigned char *lead_ch = NULL, *follow_ch = NULL, *out_ch = NULL;
376 lead_ch = follow_ch = instr;
378 if ((*lead_ch == '\0') || !(THAI_find_chtype(instr,DEAD)))
379 { /* Empty string or can't find any non-spacing char*/
380 strcpy((char *)outstr, (char *)instr);
381 } else { /* String of length >= 1, keep looking */
383 if (THAI_isdead(*lead_ch)) { /* is first char non-spacing? */
387 *out_ch++ = *lead_ch;
388 while (*follow_ch != '\0') /* more char in string to check */
390 if (THAI_isdead(*follow_ch) &&
391 !THAI_iscomposible(*follow_ch,*lead_ch))
396 *out_ch++ = *follow_ch;
406 unsigned char *instr,
412 chtype - type of character to look for
414 function returns first position of character with matched chtype
415 function returns -1 if it does not find.
417 int i = 0, position = -1;
422 for (i = 0; *instr != '\0' && THAI_isdead(*instr); i++, instr++)
424 if (*instr != '\0') position = i;
435 unsigned char *instr,
436 unsigned char *outstr,
437 unsigned char spec_ch,
439 unsigned char insert_ch)
441 unsigned char *scan, *outch;
442 int i, dead_count, found_count;
447 dead_count = found_count = 0;
448 isconsecutive = False;
449 while (*scan != '\0') {
450 if (THAI_isdead(*scan))
451 dead_count++; /* count number of non-spacing char */
452 if (*scan == spec_ch)
454 found_count++; /* count number consecutive spec char found */
456 if (found_count == num_sp) {
457 for (i = 0; i < dead_count; i++)
458 *outch++ = insert_ch;
459 dead_count = found_count = 0;
462 /* what to return? */
463 return 0; /* probably not right but better than returning garbage */
467 /* The following functions are copied from XKeyBind.c */
469 Private void ComputeMaskFromKeytrans();
470 Private int IsCancelComposeKey(KeySym *symbol, XKeyEvent *event);
471 Private void SetLed(Display *dpy, int num, int state);
472 Private CARD8 FindKeyCode();
475 /* The following functions are specific to this module */
477 Private int XThaiTranslateKey();
478 Private int XThaiTranslateKeySym();
481 Private KeySym HexIMNormalKey(
482 XicThaiPart *thai_part,
485 Private KeySym HexIMFirstComposeKey(
486 XicThaiPart *thai_part,
489 Private KeySym HexIMSecondComposeKey(
490 XicThaiPart *thai_part,
493 Private KeySym HexIMComposeSequence(KeySym ks1, KeySym ks2);
494 Private void InitIscMode(Xic ic);
495 Private Bool ThaiComposeConvert(
498 KeySym *outsym, KeySym *lower, KeySym *upper);
507 #define ucs2tis(wc) \
509 (0<=(wc)&&(wc)<=0x7F) ? \
511 ((0x0E01<=(wc)&&(wc)<=0x0E5F) ? ((wc)-0x0E00+0xA0) : 0))
512 /* "c" is an unsigned char */
517 ((0x0A1<=(c)) ? ((wchar_t)(c)-0xA0+0x0E00) : 0))
520 * Macros to save and recall last input character in XIC
522 #define IC_SavePreviousChar(ic,ch) \
523 ((ic)->private.local.base.mb[(ic)->private.local.base.tree[(ic)->private.local.context].mb] = (char) (ch))
524 #define IC_ClearPreviousChar(ic) \
525 ((ic)->private.local.base.mb[(ic)->private.local.base.tree[(ic)->private.local.context].mb] = 0)
526 #define IC_GetPreviousChar(ic) \
527 (IC_RealGetPreviousChar(ic,1))
528 #define IC_GetContextChar(ic) \
529 (IC_RealGetPreviousChar(ic,2))
530 #define IC_DeletePreviousChar(ic) \
531 (IC_RealDeletePreviousChar(ic))
533 Private unsigned char
534 IC_RealGetPreviousChar(Xic ic, unsigned short pos)
536 XICCallback* cb = &ic->core.string_conversion_callback;
537 DefTreeBase *b = &ic->private.local.base;
539 if (cb && cb->callback) {
540 XIMStringConversionCallbackStruct screc;
543 /* Use a safe value of position = 0 and stretch the range to desired
544 * place, as XIM protocol is unclear here whether it could be negative
547 screc.direction = XIMBackwardChar;
548 screc.operation = XIMStringConversionRetrieval;
552 (cb->callback)((XIC)ic, cb->client_data, (XPointer)&screc);
554 return (unsigned char) b->mb[b->tree[(ic)->private.local.context].mb];
555 if ((screc.text->feedback &&
556 *screc.text->feedback == XIMStringConversionLeftEdge) ||
557 screc.text->length < 1)
568 im = (Xim) XIMOfIC((XIC)ic);
569 if (screc.text->encoding_is_wchar) {
570 conv = _XlcOpenConverter(im->core.lcd, XlcNWideChar,
571 im->core.lcd, XlcNCharSet);
572 from_buf = (char *) screc.text->string.wcs;
573 from_left = screc.text->length * sizeof(wchar_t);
575 conv = _XlcOpenConverter(im->core.lcd, XlcNMultiByte,
576 im->core.lcd, XlcNCharSet);
577 from_buf = screc.text->string.mbs;
578 from_left = screc.text->length;
583 _XlcResetConverter(conv);
584 if (_XlcConvert(conv, (XPointer *)&from_buf, &from_left,
585 (XPointer *)&to_buf, &to_left, NULL, 0) < 0)
587 c = (unsigned char) b->mb[b->tree[(ic)->private.local.context].mb];
589 _XlcCloseConverter(conv);
591 XFree(screc.text->string.mbs);
596 return (unsigned char) b->mb[b->tree[(ic)->private.local.context].mb];
600 Private unsigned char
601 IC_RealDeletePreviousChar(Xic ic)
603 XICCallback* cb = &ic->core.string_conversion_callback;
605 if (cb && cb->callback) {
606 XIMStringConversionCallbackStruct screc;
610 screc.direction = XIMBackwardChar;
611 screc.operation = XIMStringConversionSubstitution;
615 (cb->callback)((XIC)ic, cb->client_data, (XPointer)&screc);
616 if (!screc.text) { return 0; }
617 if ((screc.text->feedback &&
618 *screc.text->feedback == XIMStringConversionLeftEdge) ||
619 screc.text->length < 1)
623 if (screc.text->encoding_is_wchar) {
624 c = ucs2tis(screc.text->string.wcs[0]);
625 XFree(screc.text->string.wcs);
627 c = screc.text->string.mbs[0];
628 XFree(screc.text->string.mbs);
638 * Input sequence check mode in XIC
640 #define IC_IscMode(ic) ((ic)->private.local.thai.input_mode)
643 * Max. size of string handled by the two String Lookup functions.
645 #define STR_LKUP_BUF_SIZE 256
648 * Size of buffer to contain previous locale name.
650 #define SAV_LOCALE_NAME_SIZE 256
653 * Size of buffer to contain the IM modifier.
655 #define MAXTHAIIMMODLEN 20
657 #define AllMods (ShiftMask|LockMask|ControlMask| \
658 Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)
661 #define IsISOControlKey(ks) ((ks) >= XK_2 && (ks) <= XK_8)
663 #define IsValidControlKey(ks) (((((ks)>=XK_A && (ks)<=XK_asciitilde) || \
664 (ks)==XK_space || (ks)==XK_Delete) && \
667 #define COMPOSE_LED 2
670 typedef KeySym (*StateProc)(
671 XicThaiPart *thai_part,
677 * macros to classify XKeyEvent state field
680 #define IsShift(state) (((state) & ShiftMask) != 0)
681 #define IsLock(state) (((state) & LockMask) != 0)
682 #define IsControl(state) (((state) & ControlMask) != 0)
683 #define IsMod1(state) (((state) & Mod1Mask) != 0)
684 #define IsMod2(state) (((state) & Mod2Mask) != 0)
685 #define IsMod3(state) (((state) & Mod3Mask) != 0)
686 #define IsMod4(state) (((state) & Mod4Mask) != 0)
687 #define IsMod5(state) (((state) & Mod5Mask) != 0)
690 * key starts Thai compose sequence (Hex input method) if :
693 #define IsComposeKey(ks, event) \
695 IsControl((event)->state) && \
696 !IsShift((event)->state)) \
701 * State handler to implement the Thai hex input method.
704 Private int const nstate_handlers = 3;
705 Private StateProc state_handler[] = {
707 HexIMFirstComposeKey,
708 HexIMSecondComposeKey
713 * Table for 'Thai Compose' character input.
714 * The current implementation uses latin-1 keysyms.
716 struct _XMapThaiKey {
721 Private struct _XMapThaiKey const ThaiComposeTable[] = {
722 { /* 0xa4 */ XK_currency, /* 0xa5 */ XK_yen },
723 { /* 0xa2 */ XK_cent, /* 0xa3 */ XK_sterling },
724 { /* 0xe6 */ XK_ae, /* 0xef */ XK_idiaeresis },
725 { /* 0xd3 */ XK_Oacute, /* 0xee */ XK_icircumflex },
726 { /* 0xb9 */ XK_onesuperior, /* 0xfa */ XK_uacute },
727 { /* 0xd2 */ XK_Ograve, /* 0xe5 */ XK_aring },
728 { /* 0xbc */ XK_onequarter, /* 0xfb */ XK_ucircumflex },
729 { XK_VoidSymbol, XK_VoidSymbol }
733 struct _XKeytrans *next;/* next on list */
734 char *string; /* string to return when the time comes */
735 int len; /* length of string (since NULL is legit)*/
736 KeySym key; /* keysym rebound */
737 unsigned int state; /* modifier state */
738 KeySym *modifiers; /* modifier keysyms you want */
739 int mlen; /* length of modifier list */
743 /* Convert keysym to 'Thai Compose' keysym */
744 /* The current implementation use latin-1 keysyms */
749 KeySym *outsym, KeySym *lower, KeySym *upper)
751 struct _XMapThaiKey const *table_entry = ThaiComposeTable;
753 while (table_entry->from != XK_VoidSymbol) {
754 if (table_entry->from == insym) {
755 *outsym = table_entry->to;
767 register Display *dpy,
769 register unsigned int modifiers,
770 unsigned int *modifiers_return,
771 KeySym *keysym_return,
776 register KeySym *syms;
777 KeySym sym = 0, lsym = 0, usym = 0;
779 if ((! dpy->keysyms) && (! _XKeyInitialize(dpy)))
781 *modifiers_return = (ShiftMask|LockMask) | dpy->mode_switch;
782 if (((int)keycode < dpy->min_keycode) || ((int)keycode > dpy->max_keycode))
784 *keysym_return = NoSymbol;
787 per = dpy->keysyms_per_keycode;
788 syms = &dpy->keysyms[(keycode - dpy->min_keycode) * per];
789 while ((per > 2) && (syms[per - 1] == NoSymbol))
791 if ((per > 2) && (modifiers & dpy->mode_switch)) {
795 if (!(modifiers & ShiftMask) &&
796 (!(modifiers & LockMask) || (dpy->lock_meaning == NoSymbol))) {
797 if ((per == 1) || (syms[1] == NoSymbol))
798 XConvertCase(syms[0], keysym_return, &usym);
800 XConvertCase(syms[0], &lsym, &usym);
801 *keysym_return = syms[0];
803 } else if (!(modifiers & LockMask) ||
804 (dpy->lock_meaning != XK_Caps_Lock)) {
805 if ((per == 1) || ((usym = syms[1]) == NoSymbol))
806 XConvertCase(syms[0], &lsym, &usym);
807 *keysym_return = usym;
809 if ((per == 1) || ((sym = syms[1]) == NoSymbol))
811 XConvertCase(sym, &lsym, &usym);
812 if (!(modifiers & ShiftMask) && (sym != syms[0]) &&
813 ((sym != usym) || (lsym == usym)))
814 XConvertCase(syms[0], &lsym, &usym);
815 *keysym_return = usym;
818 * ThaiCat keyboard support :
819 * When the Shift and Thai keys are hold for some keys a 'Thai Compose'
820 * character code is generated which is different from column 3 and
822 * Since we don't know whether ThaiCat keyboard or WTT keyboard is
823 * in use, the same mapping is done for all Thai input.
824 * We just arbitary choose to use column 3 keysyms as the indices of
826 * When the control key is also hold, this mapping has no effect.
828 if ((modifiers & Mod1Mask) &&
829 (modifiers & ShiftMask) &&
830 !(modifiers & ControlMask)) {
831 if (ThaiComposeConvert(dpy, syms[0], &sym, &lsym, &usym))
832 *keysym_return = sym;
835 if (*keysym_return == XK_VoidSymbol)
836 *keysym_return = NoSymbol;
843 * XThaiTranslateKeySym
845 * Translate KeySym to TACTIS code output.
846 * The current implementation uses ISO latin-1 keysym.
847 * Should be changed to TACTIS keysyms when they are defined by the
851 XThaiTranslateKeySym(
853 register KeySym symbol,
854 register KeySym lsym,
855 register KeySym usym,
856 unsigned int modifiers,
857 unsigned char *buffer,
861 register struct _XKeytrans *p;
863 unsigned long hiBytes;
864 register unsigned char c;
867 * initialize length = 1 ;
873 /* see if symbol rebound, if so, return that string. */
874 for (p = dpy->key_bindings; p; p = p->next) {
875 if (((modifiers & AllMods) == p->state) && (symbol == p->key)) {
877 if (length > nbytes) length = nbytes;
878 memcpy (buffer, p->string, length);
882 /* try to convert to TACTIS, handling control */
883 hiBytes = symbol >> 8;
886 ((hiBytes == 0xFF) &&
887 (((symbol >= XK_BackSpace) && (symbol <= XK_Clear)) ||
888 (symbol == XK_Return) ||
889 (symbol == XK_Escape) ||
890 (symbol == XK_KP_Space) ||
891 (symbol == XK_KP_Tab) ||
892 (symbol == XK_KP_Enter) ||
893 ((symbol >= XK_KP_Multiply) && (symbol <= XK_KP_9)) ||
894 (symbol == XK_KP_Equal) ||
895 (symbol == XK_Scroll_Lock) ||
896 #ifdef DXK_PRIVATE /* DEC private keysyms */
897 (symbol == DXK_Remove) ||
899 (symbol == NoSymbol) ||
900 (symbol == XK_Delete))))))
903 /* if X keysym, convert to ascii by grabbing low 7 bits */
904 if (symbol == XK_KP_Space)
905 c = XK_space & 0x7F; /* patch encoding botch */
907 else if (symbol == XK_hyphen)
908 c = XK_minus & 0xFF; */ /* map to equiv character */
909 else if (hiBytes == 0xFF)
913 /* only apply Control key if it makes sense, else ignore it */
914 if (modifiers & ControlMask) {
915 if (!(IsKeypadKey(lsym) || lsym==XK_Return || lsym==XK_Tab)) {
916 if (IsISOControlKey(lsym)) ckey = lsym;
917 else if (IsISOControlKey(usym)) ckey = usym;
918 else if (lsym == XK_question) ckey = lsym;
919 else if (usym == XK_question) ckey = usym;
920 else if (IsValidControlKey(lsym)) ckey = lsym;
921 else if (IsValidControlKey(usym)) ckey = usym;
925 if (ckey == XK_2) c = '\000';
926 else if (ckey >= XK_3 && ckey <= XK_7)
927 c = (char)(ckey-('3'-'\033'));
928 else if (ckey == XK_8) c = '\177';
929 else if (ckey == XK_Delete) c = '\030';
930 else if (ckey == XK_question) c = '\037';
931 else if (ckey == XK_quoteleft) c = '\036'; /* KLee 1/24/91 */
932 else c = (char)(ckey & 0x1f);
937 * ThaiCat has a key that generates two TACTIS codes D1 & E9.
938 * It is represented by the latin-1 keysym XK_thorn (0xfe).
939 * If c is XK_thorn, this key is pressed and it is converted to
957 * given a KeySym, returns the first keycode containing it, if any.
961 register Display *dpy,
962 register KeySym code)
965 register KeySym *kmax = dpy->keysyms +
966 (dpy->max_keycode - dpy->min_keycode + 1) * dpy->keysyms_per_keycode;
967 register KeySym *k = dpy->keysyms;
970 return (((k - dpy->keysyms) / dpy->keysyms_per_keycode) +
978 * given a list of modifiers, computes the mask necessary for later matching.
979 * This routine must lookup the key in the Keymap and then search to see
980 * what modifier it is bound to, if any. Sets the AnyModifier bit if it
981 * can't map some keysym to a modifier.
984 ComputeMaskFromKeytrans(
986 register struct _XKeytrans *p)
990 register XModifierKeymap *m = dpy->modifiermap;
992 p->state = AnyModifier;
993 for (i = 0; i < p->mlen; i++) {
994 /* if not found, then not on current keyboard */
995 if ((code = FindKeyCode(dpy, p->modifiers[i])) == 0)
997 /* code is now the keycode for the modifier you want */
999 register int j = m->max_keypermod<<3;
1001 while ((--j >= 0) && (code != m->modifiermap[j]))
1005 p->state |= (1<<(j/m->max_keypermod));
1008 p->state &= AllMods;
1011 /************************************************************************
1014 * Compose handling routines - compose handlers 0,1,2
1017 ************************************************************************/
1019 #define NORMAL_KEY_STATE 0
1020 #define FIRST_COMPOSE_KEY_STATE 1
1021 #define SECOND_COMPOSE_KEY_STATE 2
1024 KeySym HexIMNormalKey(
1025 XicThaiPart *thai_part,
1029 if (IsComposeKey (symbol, event)) /* start compose sequence */
1031 SetLed (event->display,COMPOSE_LED, LedModeOn);
1032 thai_part->comp_state = FIRST_COMPOSE_KEY_STATE;
1040 KeySym HexIMFirstComposeKey(
1041 XicThaiPart *thai_part,
1045 if (IsModifierKey (symbol)) return symbol; /* ignore shift etc. */
1046 if (IsCancelComposeKey (&symbol, event)) /* cancel sequence */
1048 SetLed (event->display,COMPOSE_LED, LedModeOff);
1049 thai_part->comp_state = NORMAL_KEY_STATE;
1052 if (IsComposeKey (symbol, event)) /* restart sequence ?? */
1054 return NoSymbol; /* no state change necessary */
1057 thai_part->keysym = symbol; /* save key pressed */
1058 thai_part->comp_state = SECOND_COMPOSE_KEY_STATE;
1063 KeySym HexIMSecondComposeKey(
1064 XicThaiPart *thai_part,
1068 if (IsModifierKey (symbol)) return symbol; /* ignore shift etc. */
1069 if (IsComposeKey (symbol, event)) /* restart sequence ? */
1071 thai_part->comp_state =FIRST_COMPOSE_KEY_STATE;
1074 SetLed (event->display,COMPOSE_LED, LedModeOff);
1075 if (IsCancelComposeKey (&symbol, event)) /* cancel sequence ? */
1077 thai_part->comp_state = NORMAL_KEY_STATE;
1081 if ((symbol = HexIMComposeSequence (thai_part->keysym, symbol))
1083 { /* invalid compose sequence */
1084 XBell(event->display, BellVolume);
1086 thai_part->comp_state = NORMAL_KEY_STATE; /* reset to normal state */
1092 * Interprets two keysyms entered as hex digits and return the Thai keysym
1093 * correspond to the TACTIS code formed.
1094 * The current implementation of this routine returns ISO Latin Keysyms.
1098 KeySym HexIMComposeSequence(KeySym ks1, KeySym ks2)
1104 if ((ks1 >= XK_0) && (ks1 <= XK_9))
1105 hi_digit = ks1 - XK_0;
1106 else if ((ks1 >= XK_A) && (ks1 <= XK_F))
1107 hi_digit = ks1 - XK_A + 10;
1108 else if ((ks1 >= XK_a) && (ks1 <= XK_f))
1109 hi_digit = ks1 - XK_a + 10;
1110 else /* out of range */
1113 if ((ks2 >= XK_0) && (ks2 <= XK_9))
1114 lo_digit = ks2 - XK_0;
1115 else if ((ks2 >= XK_A) && (ks2 <= XK_F))
1116 lo_digit = ks2 - XK_A + 10;
1117 else if ((ks2 >= XK_a) && (ks2 <= XK_f))
1118 lo_digit = ks2 - XK_a + 10;
1119 else /* out of range */
1122 tactis_code = hi_digit * 0x10 + lo_digit ;
1124 return (KeySym)tactis_code;
1129 * routine determines
1130 * 1) whether key event should cancel a compose sequence
1131 * 2) whether cancelling key event should be processed or ignored
1135 int IsCancelComposeKey(
1139 if (*symbol==XK_Delete && !IsControl(event->state) &&
1140 !IsMod1(event->state)) {
1141 *symbol=NoSymbol; /* cancel compose sequence, and ignore key */
1144 if (IsComposeKey(*symbol, event)) return False;
1146 IsControl (event->state) ||
1147 IsMod1(event->state) ||
1148 IsKeypadKey (*symbol) ||
1149 IsFunctionKey (*symbol) ||
1150 IsMiscFunctionKey (*symbol) ||
1151 #ifdef DXK_PRIVATE /* DEC private keysyms */
1152 *symbol == DXK_Remove ||
1154 IsPFKey (*symbol) ||
1155 IsCursorKey (*symbol) ||
1156 (*symbol >= XK_Tab && *symbol < XK_Multi_key)
1157 ? True : False); /* cancel compose sequence and pass */
1158 /* cancelling key through */
1163 * set specified keyboard LED on or off
1172 XKeyboardControl led_control;
1174 led_control.led_mode = state;
1175 led_control.led = num;
1176 XChangeKeyboardControl (dpy, KBLed | KBLedMode, &led_control);
1181 * Initialize ISC mode from im modifier
1183 Private void InitIscMode(Xic ic)
1186 char *im_modifier_name;
1188 /* If already defined, just return */
1190 if (IC_IscMode(ic)) return;
1192 /* Get IM modifier */
1194 im = (Xim) XIMOfIC((XIC)ic);
1195 im_modifier_name = im->core.im_name;
1197 /* Match with predefined value, default is Basic Check */
1199 if (!strncmp(im_modifier_name,"BasicCheck",MAXTHAIIMMODLEN+1))
1200 IC_IscMode(ic) = WTT_ISC1;
1201 else if (!strncmp(im_modifier_name,"Strict",MAXTHAIIMMODLEN+1))
1202 IC_IscMode(ic) = WTT_ISC2;
1203 else if (!strncmp(im_modifier_name,"Thaicat",MAXTHAIIMMODLEN+1))
1204 IC_IscMode(ic) = THAICAT_ISC;
1205 else if (!strncmp(im_modifier_name,"Passthrough",MAXTHAIIMMODLEN+1))
1206 IC_IscMode(ic) = NOISC;
1208 IC_IscMode(ic) = WTT_ISC1;
1214 * Helper functions for _XimThaiFilter()
1217 ThaiFltAcceptInput(Xic ic, unsigned char new_char, KeySym symbol)
1219 DefTreeBase *b = &ic->private.local.base;
1220 b->wc[b->tree[ic->private.local.composed].wc+0] = tis2ucs(new_char);
1221 b->wc[b->tree[ic->private.local.composed].wc+1] = '\0';
1223 if ((new_char <= 0x1f) || (new_char == 0x7f))
1224 b->tree[ic->private.local.composed].keysym = symbol;
1226 b->tree[ic->private.local.composed].keysym = NoSymbol;
1232 ThaiFltReorderInput(Xic ic, unsigned char previous_char, unsigned char new_char)
1234 DefTreeBase *b = &ic->private.local.base;
1235 if (!IC_DeletePreviousChar(ic)) return False;
1236 b->wc[b->tree[ic->private.local.composed].wc+0] = tis2ucs(new_char);
1237 b->wc[b->tree[ic->private.local.composed].wc+1] = tis2ucs(previous_char);
1238 b->wc[b->tree[ic->private.local.composed].wc+2] = '\0';
1240 b->tree[ic->private.local.composed].keysym = NoSymbol;
1246 ThaiFltReplaceInput(Xic ic, unsigned char new_char, KeySym symbol)
1248 DefTreeBase *b = &ic->private.local.base;
1249 if (!IC_DeletePreviousChar(ic)) return False;
1250 b->wc[b->tree[ic->private.local.composed].wc+0] = tis2ucs(new_char);
1251 b->wc[b->tree[ic->private.local.composed].wc+1] = '\0';
1253 if ((new_char <= 0x1f) || (new_char == 0x7f))
1254 b->tree[ic->private.local.composed].keysym = symbol;
1256 b->tree[ic->private.local.composed].keysym = NoSymbol;
1262 NumLockMask(Display *d)
1265 XModifierKeymap *map;
1266 KeyCode numlock_keycode = XKeysymToKeycode (d, XK_Num_Lock);
1267 if (numlock_keycode == NoSymbol)
1270 map = XGetModifierMapping (d);
1274 for (i = 0; i < 8; i++) {
1275 if (map->modifiermap[map->max_keypermod * i] == numlock_keycode) {
1276 XFreeModifiermap(map);
1280 XFreeModifiermap(map);
1285 * Filter function for TACTIS
1288 _XimThaiFilter(Display *d, Window w, XEvent *ev, XPointer client_data)
1290 Xic ic = (Xic)client_data;
1292 int isc_mode; /* Thai Input Sequence Check mode */
1293 unsigned char previous_char; /* Last inputted Thai char */
1294 unsigned char new_char;
1296 unsigned int modifiers;
1299 XicThaiPart *thai_part;
1304 DefTreeBase *b = &ic->private.local.base;
1306 if ((ev->type != KeyPress)
1307 || (ev->xkey.keycode == 0))
1310 if (!IC_IscMode(ic)) InitIscMode(ic);
1312 XwcLookupString((XIC)ic, &ev->xkey, wbuf, sizeof(wbuf) / sizeof(wbuf[0]),
1315 if ((ev->xkey.state & (AllMods & ~(ShiftMask|LockMask|NumLockMask(d)))) ||
1316 ((symbol >> 8 == 0xFF) &&
1317 ((XK_BackSpace <= symbol && symbol <= XK_Clear) ||
1318 (symbol == XK_Return) ||
1319 (symbol == XK_Pause) ||
1320 (symbol == XK_Scroll_Lock) ||
1321 (symbol == XK_Sys_Req) ||
1322 (symbol == XK_Escape) ||
1323 (symbol == XK_Delete) ||
1324 IsCursorKey(symbol) ||
1325 IsKeypadKey(symbol) ||
1326 IsMiscFunctionKey(symbol) ||
1327 IsFunctionKey(symbol))))
1329 IC_ClearPreviousChar(ic);
1332 if (((symbol >> 8 == 0xFF) &&
1333 IsModifierKey(symbol)) ||
1335 ((symbol >> 8 == 0xFE) &&
1336 (XK_ISO_Lock <= symbol && symbol <= XK_ISO_Last_Group_Lock)) ||
1338 (symbol == NoSymbol))
1343 if (! XThaiTranslateKey(ev->xkey.display, ev->xkey.keycode, ev->xkey.state,
1344 &modifiers, &symbol, &lsym, &usym))
1348 * Hex input method processing
1351 thai_part = &ic->private.local.thai;
1352 state = thai_part->comp_state;
1353 if (state >= 0 && state < nstate_handlers) /* call handler for state */
1355 symbol = (* state_handler[state])(thai_part, symbol, (XKeyEvent *)ev);
1359 * Translate KeySym into mb.
1361 count = XThaiTranslateKeySym(ev->xkey.display, symbol, lsym,
1362 usym, ev->xkey.state, buf, 10);
1364 if (!symbol && !count)
1367 /* Return symbol if cannot convert to character */
1373 * Thai Input sequence check
1375 isc_mode = IC_IscMode(ic);
1376 if (!(previous_char = IC_GetPreviousChar(ic))) previous_char = ' ';
1377 new_char = ucs2tis(wbuf[0]);
1379 if (THAI_isaccepted(new_char, previous_char, isc_mode)) {
1380 ThaiFltAcceptInput(ic, new_char, symbol);
1383 unsigned char context_char;
1385 context_char = IC_GetContextChar(ic);
1387 if (THAI_iscomposible(new_char, context_char)) {
1388 if (THAI_iscomposible(previous_char, new_char)) {
1389 isReject = !ThaiFltReorderInput(ic, previous_char, new_char);
1390 } else if (THAI_iscomposible(previous_char, context_char)) {
1391 isReject = !ThaiFltReplaceInput(ic, new_char, symbol);
1392 } else if (THAI_chtype(previous_char) == FV1
1393 && THAI_chtype(new_char) == TONE) {
1394 isReject = !ThaiFltReorderInput(ic, previous_char, new_char);
1396 } else if (THAI_isaccepted(new_char, context_char, isc_mode)) {
1397 isReject = !ThaiFltReplaceInput(ic, new_char, symbol);
1402 /* reject character */
1403 XBell(ev->xkey.display, BellVolume);
1407 _Xlcwcstombs(ic->core.im->core.lcd, &b->mb[b->tree[ic->private.local.composed].mb],
1408 &b->wc[b->tree[ic->private.local.composed].wc], 10);
1410 _Xlcmbstoutf8(ic->core.im->core.lcd, &b->utf8[b->tree[ic->private.local.composed].utf8],
1411 &b->mb[b->tree[ic->private.local.composed].mb], 10);
1413 /* Remember the last character inputted
1414 * (as fallback in case StringConversionCallback is not provided)
1416 IC_SavePreviousChar(ic, new_char);
1418 ev->xkey.keycode = 0;
1419 XPutBackEvent(d, ev);