upload tizen2.0 source
[framework/uifw/xorg/lib/libx11.git] / modules / im / ximcp / imThaiFlt.c
1 /***********************************************************
2
3 Copyright 1993, 1998  The Open Group
4
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
9 documentation.
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
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.
20
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.
24
25
26 Copyright 1993 by Digital Equipment Corporation, Maynard, Massachusetts.
27
28                         All Rights Reserved
29
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.
37
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
44 SOFTWARE.
45
46 ******************************************************************/
47
48 /*
49 **++
50 **  FACILITY:
51 **
52 **      Xlib
53 **
54 **  ABSTRACT:
55 **
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.
60 **
61 **  MODIFICATION HISTORY:
62 **
63 **/
64
65 #ifdef HAVE_CONFIG_H
66 #include <config.h>
67 #endif
68 #include <stdio.h>
69 #include <X11/Xlib.h>
70 #include <X11/Xmd.h>
71 #include <X11/keysym.h>
72 #include <X11/Xutil.h>
73 #include "Xlibint.h"
74 #include "Xlcint.h"
75 #include "Ximint.h"
76 #include "XimThai.h"
77 #include "XlcPubI.h"
78
79
80 #define SPACE   32
81
82 /* character classification table */
83 #define TACTIS_CHARS 256
84 Private
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 */
118 };
119
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 */
127
128 #define CH_CLASSES      17  /* 17 classes of chars */
129
130 Private
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*/
152 };
153
154 Private
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*/
176 };
177
178 Private
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*/
200 };
201
202 Private
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*/
224 };
225
226
227 /* returns classification of a char */
228 Private int
229 THAI_chtype (unsigned char      ch)
230 {
231     return tactis_chtype[ch];
232 }
233
234 #ifdef UNUSED
235 /* returns the display level */
236 Private int
237 THAI_chlevel (unsigned char     ch)
238 {
239     int     chlevel;
240
241     switch (tactis_chtype[ch])
242     {
243         case CTRL:
244             chlevel = NON;
245             break;
246         case BV1:
247         case BV2:
248         case BD:
249             chlevel = BELOW;
250             break;
251         case TONE:
252         case AD1:
253         case AD2:
254             chlevel = TOP;
255             break;
256         case AV1:
257         case AV2:
258         case AV3:
259         case AD3:
260             chlevel = ABOVE;
261             break;
262         case NON:
263         case CONS:
264         case LV:
265         case FV1:
266         case FV2:
267         case FV3:
268         default: /* if tactis_chtype is invalid */
269             chlevel = BASE;
270             break;
271     }
272     return chlevel;
273 }
274
275
276 /* return True if char is non-spacing */
277 Private Bool
278 THAI_isdead (unsigned char      ch)
279 {
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));
286 }
287
288
289 /* return True if char is consonant */
290 Private Bool
291 THAI_iscons (unsigned char      ch)
292 {
293     return (tactis_chtype[ch] == CONS);
294 }
295
296
297 /* return True if char is vowel */
298 Private Bool
299 THAI_isvowel (unsigned char     ch)
300 {
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));
306 }
307
308
309 /* return True if char is tonemark */
310 Private Bool
311 THAI_istone (unsigned char      ch)
312 {
313     return (tactis_chtype[ch] == TONE);
314 }
315 #endif
316
317 Private Bool
318 THAI_iscomposible (
319     unsigned char       follow_ch,
320     unsigned char       lead_ch)
321 {/* "Can follow_ch be put in the same display cell as lead_ch?" */
322
323     return (write_rules_lookup[THAI_chtype(lead_ch)][THAI_chtype(follow_ch)]
324             == CP);
325 }
326
327 Private Bool
328 THAI_isaccepted (
329     unsigned char       follow_ch,
330     unsigned char       lead_ch,
331     unsigned char       mode)
332 {
333     Bool iskeyvalid; /*  means "Can follow_ch be keyed in after lead_ch?" */
334
335     switch (mode)
336     {
337         case WTT_ISC1:
338             iskeyvalid =
339           (wtt_isc1_lookup[THAI_chtype(lead_ch)][THAI_chtype(follow_ch)] != RJ);
340             break;
341         case WTT_ISC2:
342             iskeyvalid =
343           (wtt_isc2_lookup[THAI_chtype(lead_ch)][THAI_chtype(follow_ch)] != RJ);
344             break;
345         case THAICAT_ISC:
346             iskeyvalid =
347        (thaicat_isc_lookup[THAI_chtype(lead_ch)][THAI_chtype(follow_ch)] != RJ);
348             break;
349         default:
350             iskeyvalid = True;
351             break;
352     }
353
354     return iskeyvalid;
355 }
356
357 #ifdef UNUSED
358 Private void
359 THAI_apply_write_rules(
360     unsigned char       *instr,
361     unsigned char       *outstr,
362     unsigned char       insert_ch,
363     int                 *num_insert_ch)
364 {
365 /*
366 Input parameters:
367     instr - input string
368     insert_ch specify what char to be added when invalid composition is found
369 Output parameters:
370     outstr - output string after input string has been applied the rules
371     num_insert_ch - number of insert_ch added to outstr.
372 */
373     unsigned char   *lead_ch = NULL, *follow_ch = NULL, *out_ch = NULL;
374
375     *num_insert_ch = 0;
376     lead_ch = follow_ch = instr;
377     out_ch = outstr;
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 */
382         follow_ch++;
383         if (THAI_isdead(*lead_ch)) { /* is first char non-spacing? */
384             *out_ch++ = SPACE;
385             (*num_insert_ch)++;
386         }
387         *out_ch++ = *lead_ch;
388         while (*follow_ch != '\0')  /* more char in string to check */
389         {
390             if (THAI_isdead(*follow_ch) &&
391                  !THAI_iscomposible(*follow_ch,*lead_ch))
392             {
393                 *out_ch++ = SPACE;
394                 (*num_insert_ch)++;
395             }
396             *out_ch++ = *follow_ch;
397             lead_ch = follow_ch;
398             follow_ch++;
399         }
400         *out_ch = '\0';
401     }
402 }
403
404 Private int
405 THAI_find_chtype (
406     unsigned char       *instr,
407     int         chtype)
408 {
409 /*
410 Input parameters:
411     instr - input string
412     chtype - type of character to look for
413 Output parameters:
414     function returns first position of character with matched chtype
415     function returns -1 if it does not find.
416 */
417     int i = 0, position = -1;
418
419     switch (chtype)
420     {
421         case DEAD:
422             for (i = 0; *instr != '\0' && THAI_isdead(*instr); i++, instr++)
423                 ;
424             if (*instr != '\0') position = i;
425             break;
426         default:
427             break;
428     }
429     return position;
430 }
431
432
433 Private int
434 THAI_apply_scm(
435     unsigned char       *instr,
436     unsigned char       *outstr,
437     unsigned char       spec_ch,
438     int         num_sp,
439     unsigned char       insert_ch)
440 {
441     unsigned char   *scan, *outch;
442     int             i, dead_count, found_count;
443     Bool            isconsecutive;
444
445     scan = instr;
446     outch = outstr;
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)
453             if (!isconsecutive)
454                 found_count++;      /* count number consecutive spec char found */
455         *outch++ = *scan++;
456         if (found_count == num_sp) {
457             for (i = 0; i < dead_count; i++)
458                 *outch++ = insert_ch;
459             dead_count = found_count = 0;
460         }
461     }
462     /* what to return? */
463     return 0; /* probably not right but better than returning garbage */
464 }
465
466
467 /* The following functions are copied from XKeyBind.c */
468
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();
473
474
475 /* The following functions are specific to this module */
476
477 Private int XThaiTranslateKey();
478 Private int XThaiTranslateKeySym();
479
480
481 Private KeySym HexIMNormalKey(
482     XicThaiPart *thai_part,
483     KeySym symbol,
484     XKeyEvent *event);
485 Private KeySym HexIMFirstComposeKey(
486     XicThaiPart *thai_part,
487     KeySym symbol,
488     XKeyEvent *event);
489 Private KeySym HexIMSecondComposeKey(
490     XicThaiPart *thai_part,
491     KeySym symbol
492     XKeyEvent *event);
493 Private KeySym HexIMComposeSequence(KeySym ks1, KeySym ks2);
494 Private void InitIscMode(Xic ic);
495 Private Bool ThaiComposeConvert(
496     Display *dpy,
497     KeySym insym,
498     KeySym *outsym, KeySym *lower, KeySym *upper);
499 #endif
500
501 /*
502  * Definitions
503  */
504
505 #define BellVolume              0
506
507 #define ucs2tis(wc)  \
508  (unsigned char) ( \
509    (0<=(wc)&&(wc)<=0x7F) ? \
510      (wc) : \
511      ((0x0E01<=(wc)&&(wc)<=0x0E5F) ? ((wc)-0x0E00+0xA0) : 0))
512 /* "c" is an unsigned char */
513 #define tis2ucs(c)  \
514   ( \
515    ((c)<=0x7F) ? \
516      (wchar_t)(c) : \
517      ((0x0A1<=(c)) ? ((wchar_t)(c)-0xA0+0x0E00) : 0))
518
519 /*
520  * Macros to save and recall last input character in XIC
521  */
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))
532
533 Private unsigned char
534 IC_RealGetPreviousChar(Xic ic, unsigned short pos)
535 {
536     XICCallback* cb = &ic->core.string_conversion_callback;
537     DefTreeBase *b = &ic->private.local.base;
538
539     if (cb && cb->callback) {
540         XIMStringConversionCallbackStruct screc;
541         unsigned char c;
542
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
545          */
546         screc.position = 0;
547         screc.direction = XIMBackwardChar;
548         screc.operation = XIMStringConversionRetrieval;
549         screc.factor = pos;
550         screc.text = 0;
551
552         (cb->callback)((XIC)ic, cb->client_data, (XPointer)&screc);
553         if (!screc.text)
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)
558         {
559             c = 0;
560         } else {
561             Xim     im;
562             XlcConv conv;
563             int     from_left;
564             int     to_left;
565             char   *from_buf;
566             char   *to_buf;
567
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);
574             } else {
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;
579             }
580             to_buf = (char *)&c;
581             to_left = 1;
582
583             _XlcResetConverter(conv);
584             if (_XlcConvert(conv, (XPointer *)&from_buf, &from_left,
585                             (XPointer *)&to_buf, &to_left, NULL, 0) < 0)
586             {
587                 c = (unsigned char) b->mb[b->tree[(ic)->private.local.context].mb];
588             }
589             _XlcCloseConverter(conv);
590
591             XFree(screc.text->string.mbs);
592         }
593         XFree(screc.text);
594         return c;
595     } else {
596         return (unsigned char) b->mb[b->tree[(ic)->private.local.context].mb];
597     }
598 }
599
600 Private unsigned char
601 IC_RealDeletePreviousChar(Xic ic)
602 {
603     XICCallback* cb = &ic->core.string_conversion_callback;
604
605     if (cb && cb->callback) {
606         XIMStringConversionCallbackStruct screc;
607         unsigned char c;
608
609         screc.position = 0;
610         screc.direction = XIMBackwardChar;
611         screc.operation = XIMStringConversionSubstitution;
612         screc.factor = 1;
613         screc.text = 0;
614
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)
620         {
621             c = 0;
622         } else {
623             if (screc.text->encoding_is_wchar) {
624                 c = ucs2tis(screc.text->string.wcs[0]);
625                 XFree(screc.text->string.wcs);
626             } else {
627                 c = screc.text->string.mbs[0];
628                 XFree(screc.text->string.mbs);
629             }
630         }
631         XFree(screc.text);
632         return c;
633     } else {
634         return 0;
635     }
636 }
637 /*
638  * Input sequence check mode in XIC
639  */
640 #define IC_IscMode(ic)          ((ic)->private.local.thai.input_mode)
641
642 /*
643  * Max. size of string handled by the two String Lookup functions.
644  */
645 #define STR_LKUP_BUF_SIZE       256
646
647 /*
648  * Size of buffer to contain previous locale name.
649  */
650 #define SAV_LOCALE_NAME_SIZE    256
651
652 /*
653  * Size of buffer to contain the IM modifier.
654  */
655 #define MAXTHAIIMMODLEN 20
656
657 #define AllMods (ShiftMask|LockMask|ControlMask| \
658                  Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)
659
660
661 #define IsISOControlKey(ks) ((ks) >= XK_2 && (ks) <= XK_8)
662
663 #define IsValidControlKey(ks)   (((((ks)>=XK_A && (ks)<=XK_asciitilde) || \
664                 (ks)==XK_space || (ks)==XK_Delete) && \
665                 ((ks)!=0)))
666
667 #define COMPOSE_LED 2
668
669 #ifdef UNUSED
670 typedef KeySym (*StateProc)(
671     XicThaiPart *thai_part,
672     KeySym symbol,
673     XKeyEvent *event);
674
675
676 /*
677  * macros to classify XKeyEvent state field
678  */
679
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)
688
689 /*
690  * key starts Thai compose sequence (Hex input method) if :
691  */
692
693 #define IsComposeKey(ks, event)  \
694         (( ks==XK_Alt_L &&      \
695            IsControl((event)->state) && \
696            !IsShift((event)->state))    \
697          ? True : False)
698
699
700 /*
701  *  State handler to implement the Thai hex input method.
702  */
703
704 Private int const nstate_handlers = 3;
705 Private StateProc state_handler[] = {
706         HexIMNormalKey,
707         HexIMFirstComposeKey,
708         HexIMSecondComposeKey
709 };
710
711
712 /*
713  *  Table for 'Thai Compose' character input.
714  *  The current implementation uses latin-1 keysyms.
715  */
716 struct _XMapThaiKey {
717         KeySym from;
718         KeySym to;
719 };
720
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 }
730 };
731
732 struct _XKeytrans {
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 */
740 };
741
742
743 /* Convert keysym to 'Thai Compose' keysym */
744 /* The current implementation use latin-1 keysyms */
745 Private Bool
746 ThaiComposeConvert(
747     Display *dpy,
748     KeySym insym,
749     KeySym *outsym, KeySym *lower, KeySym *upper)
750 {
751     struct _XMapThaiKey const *table_entry = ThaiComposeTable;
752
753     while (table_entry->from != XK_VoidSymbol) {
754         if (table_entry->from == insym) {
755             *outsym = table_entry->to;
756             *lower = *outsym;
757             *upper = *outsym;
758             return True;
759         }
760         table_entry++;
761     }
762     return False;
763 }
764
765 Private int
766 XThaiTranslateKey(
767     register Display *dpy,
768     KeyCode keycode,
769     register unsigned int modifiers,
770     unsigned int *modifiers_return,
771     KeySym *keysym_return,
772     KeySym *lsym_return,
773     KeySym *usym_return)
774 {
775     int per;
776     register KeySym *syms;
777     KeySym sym = 0, lsym = 0, usym = 0;
778
779     if ((! dpy->keysyms) && (! _XKeyInitialize(dpy)))
780         return 0;
781     *modifiers_return = (ShiftMask|LockMask) | dpy->mode_switch;
782     if (((int)keycode < dpy->min_keycode) || ((int)keycode > dpy->max_keycode))
783     {
784         *keysym_return = NoSymbol;
785         return 1;
786     }
787     per = dpy->keysyms_per_keycode;
788     syms = &dpy->keysyms[(keycode - dpy->min_keycode) * per];
789     while ((per > 2) && (syms[per - 1] == NoSymbol))
790         per--;
791     if ((per > 2) && (modifiers & dpy->mode_switch)) {
792         syms += 2;
793         per -= 2;
794     }
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);
799         else {
800             XConvertCase(syms[0], &lsym, &usym);
801             *keysym_return = syms[0];
802         }
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;
808     } else {
809         if ((per == 1) || ((sym = syms[1]) == NoSymbol))
810             sym = syms[0];
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;
816     }
817     /*
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
821      * 4 of the keymap.
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
825      * this mapping.
826      * When the control key is also hold, this mapping has no effect.
827      */
828     if ((modifiers & Mod1Mask) &&
829         (modifiers & ShiftMask) &&
830         !(modifiers & ControlMask)) {
831         if (ThaiComposeConvert(dpy, syms[0], &sym, &lsym, &usym))
832             *keysym_return = sym;
833     }
834
835     if (*keysym_return == XK_VoidSymbol)
836         *keysym_return = NoSymbol;
837     *lsym_return = lsym;
838     *usym_return = usym;
839     return 1;
840 }
841
842 /*
843  * XThaiTranslateKeySym
844  *
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
848  * standard.
849  */
850 Private int
851 XThaiTranslateKeySym(
852     Display *dpy,
853     register KeySym symbol,
854     register KeySym lsym,
855     register KeySym usym,
856     unsigned int modifiers,
857     unsigned char *buffer,
858     int nbytes)
859 {
860     KeySym ckey = 0;
861     register struct _XKeytrans *p;
862     int length;
863     unsigned long hiBytes;
864     register unsigned char c;
865
866     /*
867      * initialize length = 1 ;
868      */
869     length = 1;
870
871     if (!symbol)
872         return 0;
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)) {
876             length = p->len;
877             if (length > nbytes) length = nbytes;
878             memcpy (buffer, p->string, length);
879             return length;
880         }
881     }
882     /* try to convert to TACTIS, handling control */
883     hiBytes = symbol >> 8;
884     if (!(nbytes &&
885           ((hiBytes == 0) ||
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) ||
898 #endif
899              (symbol == NoSymbol) ||
900              (symbol == XK_Delete))))))
901         return 0;
902
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 */
906 /* not for Thai
907     else if (symbol == XK_hyphen)
908         c = XK_minus & 0xFF; */ /* map to equiv character */
909     else if (hiBytes == 0xFF)
910         c = symbol & 0x7F;
911     else
912         c = symbol & 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;
922         else length = 0;
923
924         if (length != 0) {
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);
933         }
934     }
935     }
936     /*
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
940      *  0xd1 0xe9.
941      */
942     if (c == XK_thorn) {
943         buffer[0] = 0xd1;
944         buffer[1] = 0xe9;
945         buffer[2] = '\0';
946         return 2;
947     }
948     else {
949         /* Normal case */
950         buffer[0] = c;
951         buffer[1] = '\0';
952         return 1;
953     }
954 }
955
956 /*
957  * given a KeySym, returns the first keycode containing it, if any.
958  */
959 Private CARD8
960 FindKeyCode(
961     register Display *dpy,
962     register KeySym code)
963 {
964
965     register KeySym *kmax = dpy->keysyms +
966         (dpy->max_keycode - dpy->min_keycode + 1) * dpy->keysyms_per_keycode;
967     register KeySym *k = dpy->keysyms;
968     while (k < kmax) {
969         if (*k == code)
970             return (((k - dpy->keysyms) / dpy->keysyms_per_keycode) +
971                     dpy->min_keycode);
972         k += 1;
973         }
974     return 0;
975 }
976
977 /*
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.
982  */
983 Private void
984 ComputeMaskFromKeytrans(
985     Display *dpy,
986     register struct _XKeytrans *p)
987 {
988     register int i;
989     register CARD8 code;
990     register XModifierKeymap *m = dpy->modifiermap;
991
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)
996                 return;
997         /* code is now the keycode for the modifier you want */
998         {
999             register int j = m->max_keypermod<<3;
1000
1001             while ((--j >= 0) && (code != m->modifiermap[j]))
1002                 ;
1003             if (j < 0)
1004                 return;
1005             p->state |= (1<<(j/m->max_keypermod));
1006         }
1007     }
1008     p->state &= AllMods;
1009 }
1010
1011 /************************************************************************
1012  *
1013  *
1014  * Compose handling routines - compose handlers 0,1,2
1015  *
1016  *
1017  ************************************************************************/
1018
1019 #define NORMAL_KEY_STATE 0
1020 #define FIRST_COMPOSE_KEY_STATE 1
1021 #define SECOND_COMPOSE_KEY_STATE 2
1022
1023 Private
1024 KeySym HexIMNormalKey(
1025     XicThaiPart *thai_part,
1026     KeySym symbol,
1027     XKeyEvent *event)
1028 {
1029     if (IsComposeKey (symbol, event))   /* start compose sequence       */
1030         {
1031         SetLed (event->display,COMPOSE_LED, LedModeOn);
1032         thai_part->comp_state = FIRST_COMPOSE_KEY_STATE;
1033         return NoSymbol;
1034         }
1035     return symbol;
1036 }
1037
1038
1039 Private
1040 KeySym HexIMFirstComposeKey(
1041     XicThaiPart *thai_part,
1042     KeySym symbol,
1043     XKeyEvent *event)
1044 {
1045     if (IsModifierKey (symbol)) return symbol; /* ignore shift etc. */
1046     if (IsCancelComposeKey (&symbol, event))    /* cancel sequence */
1047         {
1048         SetLed (event->display,COMPOSE_LED, LedModeOff);
1049         thai_part->comp_state = NORMAL_KEY_STATE;
1050         return symbol;
1051         }
1052     if (IsComposeKey (symbol, event))           /* restart sequence ?? */
1053         {
1054         return NoSymbol;                        /* no state change necessary */
1055         }
1056
1057     thai_part->keysym = symbol;         /* save key pressed */
1058     thai_part->comp_state = SECOND_COMPOSE_KEY_STATE;
1059     return NoSymbol;
1060 }
1061
1062 Private
1063 KeySym HexIMSecondComposeKey(
1064     XicThaiPart *thai_part,
1065     KeySym symbol,
1066     XKeyEvent *event)
1067 {
1068     if (IsModifierKey (symbol)) return symbol;  /* ignore shift etc. */
1069     if (IsComposeKey (symbol, event))           /* restart sequence ? */
1070         {
1071         thai_part->comp_state =FIRST_COMPOSE_KEY_STATE;
1072         return NoSymbol;
1073         }
1074     SetLed (event->display,COMPOSE_LED, LedModeOff);
1075     if (IsCancelComposeKey (&symbol, event))    /* cancel sequence ? */
1076         {
1077         thai_part->comp_state = NORMAL_KEY_STATE;
1078         return symbol;
1079         }
1080
1081     if ((symbol = HexIMComposeSequence (thai_part->keysym, symbol))
1082                                                                 ==NoSymbol)
1083         { /* invalid compose sequence */
1084         XBell(event->display, BellVolume);
1085         }
1086     thai_part->comp_state = NORMAL_KEY_STATE; /* reset to normal state */
1087     return symbol;
1088 }
1089
1090
1091 /*
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.
1095  */
1096
1097 Private
1098 KeySym HexIMComposeSequence(KeySym ks1, KeySym ks2)
1099 {
1100 int     hi_digit;
1101 int     lo_digit;
1102 int     tactis_code;
1103
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 */
1111         return NoSymbol;
1112
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 */
1120         return NoSymbol;
1121
1122     tactis_code = hi_digit * 0x10 + lo_digit ;
1123
1124     return (KeySym)tactis_code;
1125
1126 }
1127
1128 /*
1129  * routine determines
1130  *      1) whether key event should cancel a compose sequence
1131  *      2) whether cancelling key event should be processed or ignored
1132  */
1133
1134 Private
1135 int IsCancelComposeKey(
1136     KeySym *symbol,
1137     XKeyEvent *event)
1138 {
1139     if (*symbol==XK_Delete && !IsControl(event->state) &&
1140                                                 !IsMod1(event->state)) {
1141         *symbol=NoSymbol;  /* cancel compose sequence, and ignore key */
1142         return True;
1143     }
1144     if (IsComposeKey(*symbol, event)) return False;
1145     return (
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 ||
1153 #endif
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           */
1159 }
1160
1161
1162 /*
1163  *      set specified keyboard LED on or off
1164  */
1165
1166 Private
1167 void SetLed(
1168     Display *dpy,
1169     int num,
1170     int state)
1171 {
1172     XKeyboardControl led_control;
1173
1174     led_control.led_mode = state;
1175     led_control.led = num;
1176     XChangeKeyboardControl (dpy, KBLed | KBLedMode,     &led_control);
1177 }
1178 #endif
1179
1180 /*
1181  * Initialize ISC mode from im modifier
1182  */
1183 Private void InitIscMode(Xic ic)
1184 {
1185     Xim im;
1186     char *im_modifier_name;
1187
1188     /* If already defined, just return */
1189
1190     if (IC_IscMode(ic)) return;
1191
1192     /* Get IM modifier */
1193
1194     im = (Xim) XIMOfIC((XIC)ic);
1195     im_modifier_name = im->core.im_name;
1196
1197     /* Match with predefined value, default is Basic Check */
1198
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;
1207     else
1208         IC_IscMode(ic) = WTT_ISC1;
1209
1210     return;
1211 }
1212
1213 /*
1214  * Helper functions for _XimThaiFilter()
1215  */
1216 Private Bool
1217 ThaiFltAcceptInput(Xic ic, unsigned char new_char, KeySym symbol)
1218 {
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';
1222
1223     if ((new_char <= 0x1f) || (new_char == 0x7f))
1224         b->tree[ic->private.local.composed].keysym = symbol;
1225     else
1226         b->tree[ic->private.local.composed].keysym = NoSymbol;
1227
1228     return True;
1229 }
1230
1231 Private Bool
1232 ThaiFltReorderInput(Xic ic, unsigned char previous_char, unsigned char new_char)
1233 {
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';
1239
1240     b->tree[ic->private.local.composed].keysym = NoSymbol;
1241
1242     return True;
1243 }
1244
1245 Private Bool
1246 ThaiFltReplaceInput(Xic ic, unsigned char new_char, KeySym symbol)
1247 {
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';
1252
1253     if ((new_char <= 0x1f) || (new_char == 0x7f))
1254         b->tree[ic->private.local.composed].keysym = symbol;
1255     else
1256         b->tree[ic->private.local.composed].keysym = NoSymbol;
1257
1258     return True;
1259 }
1260
1261 Private unsigned
1262 NumLockMask(Display *d)
1263 {
1264     int i;
1265     XModifierKeymap *map;
1266     KeyCode numlock_keycode = XKeysymToKeycode (d, XK_Num_Lock);
1267     if (numlock_keycode == NoSymbol)
1268         return 0;
1269
1270     map = XGetModifierMapping (d);
1271     if (!map)
1272         return 0;
1273
1274     for (i = 0; i < 8; i++) {
1275         if (map->modifiermap[map->max_keypermod * i] == numlock_keycode) {
1276             XFreeModifiermap(map);
1277             return 1 << i;
1278         }
1279     }
1280     XFreeModifiermap(map);
1281     return 0;
1282 }
1283
1284 /*
1285  * Filter function for TACTIS
1286  */
1287 Bool
1288 _XimThaiFilter(Display *d, Window w, XEvent *ev, XPointer client_data)
1289 {
1290     Xic             ic = (Xic)client_data;
1291     KeySym          symbol;
1292     int             isc_mode; /* Thai Input Sequence Check mode */
1293     unsigned char   previous_char; /* Last inputted Thai char */
1294     unsigned char   new_char;
1295 #ifdef UNUSED
1296     unsigned int    modifiers;
1297     KeySym          lsym,usym;
1298     int             state;
1299     XicThaiPart     *thai_part;
1300     char            buf[10];
1301 #endif
1302     wchar_t         wbuf[10];
1303     Bool            isReject;
1304     DefTreeBase    *b = &ic->private.local.base;
1305
1306     if ((ev->type != KeyPress)
1307         || (ev->xkey.keycode == 0))
1308         return False;
1309
1310     if (!IC_IscMode(ic)) InitIscMode(ic);
1311
1312     XwcLookupString((XIC)ic, &ev->xkey, wbuf, sizeof(wbuf) / sizeof(wbuf[0]),
1313                     &symbol, NULL);
1314
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))))
1328         {
1329             IC_ClearPreviousChar(ic);
1330             return False;
1331         }
1332     if (((symbol >> 8 == 0xFF) &&
1333          IsModifierKey(symbol)) ||
1334 #ifdef XK_XKB_KEYS
1335         ((symbol >> 8 == 0xFE) &&
1336          (XK_ISO_Lock <= symbol && symbol <= XK_ISO_Last_Group_Lock)) ||
1337 #endif
1338         (symbol == NoSymbol))
1339     {
1340         return False;
1341     }
1342 #ifdef UNUSED
1343     if (! XThaiTranslateKey(ev->xkey.display, ev->xkey.keycode, ev->xkey.state,
1344                         &modifiers, &symbol, &lsym, &usym))
1345         return False;
1346
1347     /*
1348      *  Hex input method processing
1349      */
1350
1351     thai_part = &ic->private.local.thai;
1352     state = thai_part->comp_state;
1353     if (state >= 0 && state < nstate_handlers) /* call handler for state */
1354     {
1355         symbol = (* state_handler[state])(thai_part, symbol, (XKeyEvent *)ev);
1356     }
1357
1358     /*
1359      *  Translate KeySym into mb.
1360      */
1361     count = XThaiTranslateKeySym(ev->xkey.display, symbol, lsym,
1362                                 usym, ev->xkey.state, buf, 10);
1363
1364     if (!symbol && !count)
1365         return True;
1366
1367     /* Return symbol if cannot convert to character */
1368     if (!count)
1369         return False;
1370 #endif
1371
1372     /*
1373      *  Thai Input sequence check
1374      */
1375     isc_mode = IC_IscMode(ic);
1376     if (!(previous_char = IC_GetPreviousChar(ic))) previous_char = ' ';
1377     new_char = ucs2tis(wbuf[0]);
1378     isReject = True;
1379     if (THAI_isaccepted(new_char, previous_char, isc_mode)) {
1380         ThaiFltAcceptInput(ic, new_char, symbol);
1381         isReject = False;
1382     } else {
1383         unsigned char context_char;
1384
1385         context_char = IC_GetContextChar(ic);
1386         if (context_char) {
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);
1395                 }
1396             } else if (THAI_isaccepted(new_char, context_char, isc_mode)) {
1397                 isReject = !ThaiFltReplaceInput(ic, new_char, symbol);
1398             }
1399         }
1400     }
1401     if (isReject) {
1402         /* reject character */
1403         XBell(ev->xkey.display, BellVolume);
1404         return True;
1405     }
1406
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);
1409
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);
1412
1413     /* Remember the last character inputted
1414      * (as fallback in case StringConversionCallback is not provided)
1415      */
1416     IC_SavePreviousChar(ic, new_char);
1417
1418     ev->xkey.keycode = 0;
1419     XPutBackEvent(d, ev);
1420     return True;
1421 }