imThaiFlt.c revision b4ee4795
1/***********************************************************
2
3Copyright 1993, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25
26Copyright 1993 by Digital Equipment Corporation, Maynard, Massachusetts.
27
28                        All Rights Reserved
29
30Permission to use, copy, modify, and distribute this software and its
31documentation for any purpose and without fee is hereby granted,
32provided that the above copyright notice appear in all copies and that
33both that copyright notice and this permission notice appear in
34supporting documentation, and that the name of Digital not be
35used in advertising or publicity pertaining to distribution of the
36software without specific, written prior permission.
37
38DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
39ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
40DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
41ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
42WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
43ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
44SOFTWARE.
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
84Private
85char 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
130Private
131char 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
154Private
155char 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
178Private
179char 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
202Private
203char 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 */
228Private int
229THAI_chtype (unsigned char	ch)
230{
231    return tactis_chtype[ch];
232}
233
234#ifdef UNUSED
235/* returns the display level */
236Private int
237THAI_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 */
277Private Bool
278THAI_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 */
290Private Bool
291THAI_iscons (unsigned char	ch)
292{
293    return (tactis_chtype[ch] == CONS);
294}
295
296
297/* return True if char is vowel */
298Private Bool
299THAI_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 */
310Private Bool
311THAI_istone (unsigned char	ch)
312{
313    return (tactis_chtype[ch] == TONE);
314}
315#endif
316
317Private Bool
318THAI_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
327Private Bool
328THAI_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
358Private void
359THAI_apply_write_rules(
360    unsigned char	*instr,
361    unsigned char	*outstr,
362    unsigned char	insert_ch,
363    int 		*num_insert_ch)
364{
365/*
366Input parameters:
367    instr - input string
368    insert_ch specify what char to be added when invalid composition is found
369Output 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
404Private int
405THAI_find_chtype (
406    unsigned char	*instr,
407    int		chtype)
408{
409/*
410Input parameters:
411    instr - input string
412    chtype - type of character to look for
413Output 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
433Private int
434THAI_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
469Private void ComputeMaskFromKeytrans();
470Private int IsCancelComposeKey(KeySym *symbol, XKeyEvent *event);
471Private void SetLed(Display *dpy, int num, int state);
472Private CARD8 FindKeyCode();
473
474
475/* The following functions are specific to this module */
476
477Private int XThaiTranslateKey();
478Private int XThaiTranslateKeySym();
479
480
481Private KeySym HexIMNormalKey(
482    XicThaiPart *thai_part,
483    KeySym symbol,
484    XKeyEvent *event);
485Private KeySym HexIMFirstComposeKey(
486    XicThaiPart *thai_part,
487    KeySym symbol,
488    XKeyEvent *event);
489Private KeySym HexIMSecondComposeKey(
490    XicThaiPart *thai_part,
491    KeySym symbol
492    XKeyEvent *event);
493Private KeySym HexIMComposeSequence(KeySym ks1, KeySym ks2);
494Private void InitIscMode(Xic ic);
495Private 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
533Private unsigned char
534IC_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
600Private unsigned char
601IC_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
670typedef 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
704Private int const nstate_handlers = 3;
705Private 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 */
716struct _XMapThaiKey {
717	KeySym from;
718	KeySym to;
719};
720
721Private 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
732struct _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 */
745Private Bool
746ThaiComposeConvert(
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
765Private int
766XThaiTranslateKey(
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 */
850Private int
851XThaiTranslateKeySym(
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 */
959Private CARD8
960FindKeyCode(
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 */
983Private void
984ComputeMaskFromKeytrans(
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
1023Private
1024KeySym 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
1039Private
1040KeySym 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
1062Private
1063KeySym 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
1097Private
1098KeySym HexIMComposeSequence(KeySym ks1, KeySym ks2)
1099{
1100int	hi_digit;
1101int	lo_digit;
1102int	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
1134Private
1135int 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
1166Private
1167void 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 */
1183Private 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 */
1216Private Bool
1217ThaiFltAcceptInput(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
1231Private Bool
1232ThaiFltReorderInput(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
1245Private Bool
1246ThaiFltReplaceInput(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
1261Private unsigned
1262NumLockMask(Display *d)
1263{
1264    int i;
1265    XModifierKeymap *map = XGetModifierMapping (d);
1266    KeyCode numlock_keycode = XKeysymToKeycode (d, XK_Num_Lock);
1267    if (numlock_keycode == NoSymbol)
1268        return 0;
1269
1270    for (i = 0; i < 8; i++) {
1271        if (map->modifiermap[map->max_keypermod * i] == numlock_keycode)
1272            return 1 << i;
1273    }
1274    return 0;
1275}
1276
1277/*
1278 * Filter function for TACTIS
1279 */
1280Bool
1281_XimThaiFilter(Display *d, Window w, XEvent *ev, XPointer client_data)
1282{
1283    Xic		    ic = (Xic)client_data;
1284    KeySym 	    symbol;
1285    int 	    isc_mode; /* Thai Input Sequence Check mode */
1286    unsigned char   previous_char; /* Last inputted Thai char */
1287    unsigned char   new_char;
1288#ifdef UNUSED
1289    unsigned int    modifiers;
1290    KeySym	    lsym,usym;
1291    int		    state;
1292    XicThaiPart     *thai_part;
1293    char	    buf[10];
1294#endif
1295    wchar_t	    wbuf[10];
1296    Bool            isReject;
1297    DefTreeBase    *b = &ic->private.local.base;
1298
1299    if ((ev->type != KeyPress)
1300        || (ev->xkey.keycode == 0))
1301        return False;
1302
1303    if (!IC_IscMode(ic)) InitIscMode(ic);
1304
1305    XwcLookupString((XIC)ic, &ev->xkey, wbuf, sizeof(wbuf) / sizeof(wbuf[0]),
1306		    &symbol, NULL);
1307
1308    if ((ev->xkey.state & (AllMods & ~(ShiftMask|LockMask|NumLockMask(d)))) ||
1309         ((symbol >> 8 == 0xFF) &&
1310         ((XK_BackSpace <= symbol && symbol <= XK_Clear) ||
1311           (symbol == XK_Return) ||
1312           (symbol == XK_Pause) ||
1313           (symbol == XK_Scroll_Lock) ||
1314           (symbol == XK_Sys_Req) ||
1315           (symbol == XK_Escape) ||
1316           (symbol == XK_Delete) ||
1317           IsCursorKey(symbol) ||
1318           IsKeypadKey(symbol) ||
1319           IsMiscFunctionKey(symbol) ||
1320           IsFunctionKey(symbol))))
1321        {
1322            IC_ClearPreviousChar(ic);
1323            return False;
1324        }
1325    if (((symbol >> 8 == 0xFF) &&
1326         IsModifierKey(symbol)) ||
1327#ifdef XK_XKB_KEYS
1328        ((symbol >> 8 == 0xFE) &&
1329         (XK_ISO_Lock <= symbol && symbol <= XK_ISO_Last_Group_Lock)) ||
1330#endif
1331        (symbol == NoSymbol))
1332    {
1333        return False;
1334    }
1335#ifdef UNUSED
1336    if (! XThaiTranslateKey(ev->xkey.display, ev->xkey.keycode, ev->xkey.state,
1337	 		&modifiers, &symbol, &lsym, &usym))
1338	return False;
1339
1340    /*
1341     *  Hex input method processing
1342     */
1343
1344    thai_part = &ic->private.local.thai;
1345    state = thai_part->comp_state;
1346    if (state >= 0 && state < nstate_handlers) /* call handler for state */
1347    {
1348        symbol = (* state_handler[state])(thai_part, symbol, (XKeyEvent *)ev);
1349    }
1350
1351    /*
1352     *  Translate KeySym into mb.
1353     */
1354    count = XThaiTranslateKeySym(ev->xkey.display, symbol, lsym,
1355				usym, ev->xkey.state, buf, 10);
1356
1357    if (!symbol && !count)
1358	return True;
1359
1360    /* Return symbol if cannot convert to character */
1361    if (!count)
1362	return False;
1363#endif
1364
1365    /*
1366     *  Thai Input sequence check
1367     */
1368    isc_mode = IC_IscMode(ic);
1369    if (!(previous_char = IC_GetPreviousChar(ic))) previous_char = ' ';
1370    new_char = ucs2tis(wbuf[0]);
1371    isReject = True;
1372    if (THAI_isaccepted(new_char, previous_char, isc_mode)) {
1373        ThaiFltAcceptInput(ic, new_char, symbol);
1374        isReject = False;
1375    } else {
1376        unsigned char context_char;
1377
1378        context_char = IC_GetContextChar(ic);
1379        if (context_char) {
1380            if (THAI_iscomposible(new_char, context_char)) {
1381                if (THAI_iscomposible(previous_char, new_char)) {
1382                    isReject = !ThaiFltReorderInput(ic, previous_char, new_char);
1383                } else if (THAI_iscomposible(previous_char, context_char)) {
1384                    isReject = !ThaiFltReplaceInput(ic, new_char, symbol);
1385                } else if (THAI_chtype(previous_char) == FV1
1386                           && THAI_chtype(new_char) == TONE) {
1387                    isReject = !ThaiFltReorderInput(ic, previous_char, new_char);
1388                }
1389            } else if (THAI_isaccepted(new_char, context_char, isc_mode)) {
1390                isReject = !ThaiFltReplaceInput(ic, new_char, symbol);
1391            }
1392        }
1393    }
1394    if (isReject) {
1395        /* reject character */
1396        XBell(ev->xkey.display, BellVolume);
1397        return True;
1398    }
1399
1400    _Xlcwcstombs(ic->core.im->core.lcd, &b->mb[b->tree[ic->private.local.composed].mb],
1401		 &b->wc[b->tree[ic->private.local.composed].wc], 10);
1402
1403    _Xlcmbstoutf8(ic->core.im->core.lcd, &b->utf8[b->tree[ic->private.local.composed].utf8],
1404		  &b->mb[b->tree[ic->private.local.composed].mb], 10);
1405
1406    /* Remember the last character inputted
1407     * (as fallback in case StringConversionCallback is not provided)
1408     */
1409    IC_SavePreviousChar(ic, new_char);
1410
1411    ev->xkey.keycode = 0;
1412    XPutBackEvent(d, ev);
1413    return True;
1414}
1415