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
84static
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
130static
131char const write_rules_lookup[CH_CLASSES][CH_CLASSES] = {
132        /* Table 0: writing/outputting 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
154static
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
178static
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
202static
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 */
228static int
229THAI_chtype (unsigned char	ch)
230{
231    return tactis_chtype[ch];
232}
233
234#ifdef UNUSED
235/* returns the display level */
236static 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 */
277static 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 */
290static Bool
291THAI_iscons (unsigned char	ch)
292{
293    return (tactis_chtype[ch] == CONS);
294}
295
296
297/* return True if char is vowel */
298static 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 */
310static Bool
311THAI_istone (unsigned char	ch)
312{
313    return (tactis_chtype[ch] == TONE);
314}
315#endif
316
317static 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
327static 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
358static 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
404static 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
433static 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
469static void ComputeMaskFromKeytrans();
470static int IsCancelComposeKey(KeySym *symbol, XKeyEvent *event);
471static void SetLed(Display *dpy, int num, int state);
472static CARD8 FindKeyCode();
473
474
475/* The following functions are specific to this module */
476
477static int XThaiTranslateKey();
478static int XThaiTranslateKeySym();
479
480
481static KeySym HexIMNormalKey(
482    XicThaiPart *thai_part,
483    KeySym symbol,
484    XKeyEvent *event);
485static KeySym HexIMFirstComposeKey(
486    XicThaiPart *thai_part,
487    KeySym symbol,
488    XKeyEvent *event);
489static KeySym HexIMSecondComposeKey(
490    XicThaiPart *thai_part,
491    KeySym symbol
492    XKeyEvent *event);
493static KeySym HexIMComposeSequence(KeySym ks1, KeySym ks2);
494static void InitIscMode(Xic ic);
495static 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
533static 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
600static 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
704static int const nstate_handlers = 3;
705static 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
721static 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 */
745static 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
765static 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 arbitrarily 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 */
850static 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    else if (hiBytes == 0xFF)
907	c = symbol & 0x7F;
908    else
909	c = symbol & 0xFF;
910
911    /* only apply Control key if it makes sense, else ignore it */
912    if (modifiers & ControlMask) {
913    if (!(IsKeypadKey(lsym) || lsym==XK_Return || lsym==XK_Tab)) {
914        if (IsISOControlKey(lsym)) ckey = lsym;
915        else if (IsISOControlKey(usym)) ckey = usym;
916        else if (lsym == XK_question) ckey = lsym;
917        else if (usym == XK_question) ckey = usym;
918        else if (IsValidControlKey(lsym)) ckey = lsym;
919        else if (IsValidControlKey(usym)) ckey = usym;
920        else length = 0;
921
922        if (length != 0) {
923        if (ckey == XK_2) c = '\000';
924        else if (ckey >= XK_3 && ckey <= XK_7)
925            c = (char)(ckey-('3'-'\033'));
926        else if (ckey == XK_8) c = '\177';
927        else if (ckey == XK_Delete) c = '\030';
928        else if (ckey == XK_question) c = '\037';
929        else if (ckey == XK_quoteleft) c = '\036';  /* KLee 1/24/91 */
930        else c = (char)(ckey & 0x1f);
931        }
932    }
933    }
934    /*
935     *  ThaiCat has a key that generates two TACTIS codes D1 & E9.
936     *  It is represented by the latin-1 keysym XK_thorn (0xfe).
937     *  If c is XK_thorn, this key is pressed and it is converted to
938     *  0xd1 0xe9.
939     */
940    if (c == XK_thorn) {
941	buffer[0] = 0xd1;
942	buffer[1] = 0xe9;
943	buffer[2] = '\0';
944	return 2;
945    }
946    else {
947	/* Normal case */
948        buffer[0] = c;
949	buffer[1] = '\0';
950        return 1;
951    }
952}
953
954/*
955 * given a KeySym, returns the first keycode containing it, if any.
956 */
957static CARD8
958FindKeyCode(
959    register Display *dpy,
960    register KeySym code)
961{
962
963    register KeySym *kmax = dpy->keysyms +
964	(dpy->max_keycode - dpy->min_keycode + 1) * dpy->keysyms_per_keycode;
965    register KeySym *k = dpy->keysyms;
966    while (k < kmax) {
967	if (*k == code)
968	    return (((k - dpy->keysyms) / dpy->keysyms_per_keycode) +
969		    dpy->min_keycode);
970	k += 1;
971	}
972    return 0;
973}
974
975/*
976 * given a list of modifiers, computes the mask necessary for later matching.
977 * This routine must lookup the key in the Keymap and then search to see
978 * what modifier it is bound to, if any.  Sets the AnyModifier bit if it
979 * can't map some keysym to a modifier.
980 */
981static void
982ComputeMaskFromKeytrans(
983    Display *dpy,
984    register struct _XKeytrans *p)
985{
986    register int i;
987    register CARD8 code;
988    register XModifierKeymap *m = dpy->modifiermap;
989
990    p->state = AnyModifier;
991    for (i = 0; i < p->mlen; i++) {
992	/* if not found, then not on current keyboard */
993	if ((code = FindKeyCode(dpy, p->modifiers[i])) == 0)
994		return;
995	/* code is now the keycode for the modifier you want */
996	{
997	    register int j = m->max_keypermod<<3;
998
999	    while ((--j >= 0) && (code != m->modifiermap[j]))
1000		;
1001	    if (j < 0)
1002		return;
1003	    p->state |= (1<<(j/m->max_keypermod));
1004	}
1005    }
1006    p->state &= AllMods;
1007}
1008
1009/************************************************************************
1010 *
1011 *
1012 * Compose handling routines - compose handlers 0,1,2
1013 *
1014 *
1015 ************************************************************************/
1016
1017#define NORMAL_KEY_STATE 0
1018#define FIRST_COMPOSE_KEY_STATE 1
1019#define SECOND_COMPOSE_KEY_STATE 2
1020
1021static
1022KeySym HexIMNormalKey(
1023    XicThaiPart *thai_part,
1024    KeySym symbol,
1025    XKeyEvent *event)
1026{
1027    if (IsComposeKey (symbol, event))	/* start compose sequence	*/
1028	{
1029	SetLed (event->display,COMPOSE_LED, LedModeOn);
1030	thai_part->comp_state = FIRST_COMPOSE_KEY_STATE;
1031	return NoSymbol;
1032	}
1033    return symbol;
1034}
1035
1036
1037static
1038KeySym HexIMFirstComposeKey(
1039    XicThaiPart *thai_part,
1040    KeySym symbol,
1041    XKeyEvent *event)
1042{
1043    if (IsModifierKey (symbol)) return symbol; /* ignore shift etc. */
1044    if (IsCancelComposeKey (&symbol, event))	/* cancel sequence */
1045	{
1046	SetLed (event->display,COMPOSE_LED, LedModeOff);
1047	thai_part->comp_state = NORMAL_KEY_STATE;
1048	return symbol;
1049	}
1050    if (IsComposeKey (symbol, event))		/* restart sequence ?? */
1051	{
1052	return NoSymbol;			/* no state change necessary */
1053	}
1054
1055    thai_part->keysym = symbol;		/* save key pressed */
1056    thai_part->comp_state = SECOND_COMPOSE_KEY_STATE;
1057    return NoSymbol;
1058}
1059
1060static
1061KeySym HexIMSecondComposeKey(
1062    XicThaiPart *thai_part,
1063    KeySym symbol,
1064    XKeyEvent *event)
1065{
1066    if (IsModifierKey (symbol)) return symbol;	/* ignore shift etc. */
1067    if (IsComposeKey (symbol, event))		/* restart sequence ? */
1068	{
1069	thai_part->comp_state =FIRST_COMPOSE_KEY_STATE;
1070	return NoSymbol;
1071	}
1072    SetLed (event->display,COMPOSE_LED, LedModeOff);
1073    if (IsCancelComposeKey (&symbol, event))	/* cancel sequence ? */
1074	{
1075	thai_part->comp_state = NORMAL_KEY_STATE;
1076	return symbol;
1077	}
1078
1079    if ((symbol = HexIMComposeSequence (thai_part->keysym, symbol))
1080								==NoSymbol)
1081	{ /* invalid compose sequence */
1082	XBell(event->display, BellVolume);
1083	}
1084    thai_part->comp_state = NORMAL_KEY_STATE; /* reset to normal state */
1085    return symbol;
1086}
1087
1088
1089/*
1090 * Interprets two keysyms entered as hex digits and return the Thai keysym
1091 * correspond to the TACTIS code formed.
1092 * The current implementation of this routine returns ISO Latin Keysyms.
1093 */
1094
1095static
1096KeySym HexIMComposeSequence(KeySym ks1, KeySym ks2)
1097{
1098int	hi_digit;
1099int	lo_digit;
1100int	tactis_code;
1101
1102    if ((ks1 >= XK_0) && (ks1 <= XK_9))
1103	hi_digit = ks1 - XK_0;
1104    else if ((ks1 >= XK_A) && (ks1 <= XK_F))
1105	hi_digit = ks1 - XK_A + 10;
1106    else if ((ks1 >= XK_a) && (ks1 <= XK_f))
1107	hi_digit = ks1 - XK_a + 10;
1108    else	/* out of range */
1109	return NoSymbol;
1110
1111    if ((ks2 >= XK_0) && (ks2 <= XK_9))
1112	lo_digit = ks2 - XK_0;
1113    else if ((ks2 >= XK_A) && (ks2 <= XK_F))
1114	lo_digit = ks2 - XK_A + 10;
1115    else if ((ks2 >= XK_a) && (ks2 <= XK_f))
1116	lo_digit = ks2 - XK_a + 10;
1117    else	/* out of range */
1118	return NoSymbol;
1119
1120    tactis_code = hi_digit * 0x10 + lo_digit ;
1121
1122    return (KeySym)tactis_code;
1123
1124}
1125
1126/*
1127 * routine determines
1128 *	1) whether key event should cancel a compose sequence
1129 *	2) whether cancelling key event should be processed or ignored
1130 */
1131
1132static
1133int IsCancelComposeKey(
1134    KeySym *symbol,
1135    XKeyEvent *event)
1136{
1137    if (*symbol==XK_Delete && !IsControl(event->state) &&
1138						!IsMod1(event->state)) {
1139	*symbol=NoSymbol;  /* cancel compose sequence, and ignore key */
1140	return True;
1141    }
1142    if (IsComposeKey(*symbol, event)) return False;
1143    return (
1144	IsControl (event->state) ||
1145	IsMod1(event->state) ||
1146	IsKeypadKey (*symbol) ||
1147	IsFunctionKey (*symbol) ||
1148	IsMiscFunctionKey (*symbol) ||
1149#ifdef DXK_PRIVATE /* DEC private keysyms */
1150	*symbol == DXK_Remove ||
1151#endif
1152	IsPFKey (*symbol) ||
1153	IsCursorKey (*symbol) ||
1154	(*symbol >= XK_Tab && *symbol < XK_Multi_key)
1155		? True : False);	/* cancel compose sequence and pass */
1156					/* cancelling key through	    */
1157}
1158
1159
1160/*
1161 *	set specified keyboard LED on or off
1162 */
1163
1164static
1165void SetLed(
1166    Display *dpy,
1167    int num,
1168    int state)
1169{
1170    XKeyboardControl led_control;
1171
1172    led_control.led_mode = state;
1173    led_control.led = num;
1174    XChangeKeyboardControl (dpy, KBLed | KBLedMode,	&led_control);
1175}
1176#endif
1177
1178/*
1179 * Initialize ISC mode from im modifier
1180 */
1181static void InitIscMode(Xic ic)
1182{
1183    Xim im;
1184    char *im_modifier_name;
1185
1186    /* If already defined, just return */
1187
1188    if (IC_IscMode(ic)) return;
1189
1190    /* Get IM modifier */
1191
1192    im = (Xim) XIMOfIC((XIC)ic);
1193    im_modifier_name = im->core.im_name;
1194
1195    /* Match with predefined value, default is Basic Check */
1196
1197    if (!strncmp(im_modifier_name,"BasicCheck",MAXTHAIIMMODLEN+1))
1198	IC_IscMode(ic) = WTT_ISC1;
1199    else if (!strncmp(im_modifier_name,"Strict",MAXTHAIIMMODLEN+1))
1200	IC_IscMode(ic) = WTT_ISC2;
1201    else if (!strncmp(im_modifier_name,"Thaicat",MAXTHAIIMMODLEN+1))
1202	IC_IscMode(ic) = THAICAT_ISC;
1203    else if (!strncmp(im_modifier_name,"Passthrough",MAXTHAIIMMODLEN+1))
1204	IC_IscMode(ic) = NOISC;
1205    else
1206	IC_IscMode(ic) = WTT_ISC1;
1207
1208    return;
1209}
1210
1211/*
1212 * Helper functions for _XimThaiFilter()
1213 */
1214static Bool
1215ThaiFltAcceptInput(Xic ic, unsigned char new_char, KeySym symbol)
1216{
1217    DefTreeBase *b = &ic->private.local.base;
1218    b->wc[b->tree[ic->private.local.composed].wc+0] = tis2ucs(new_char);
1219    b->wc[b->tree[ic->private.local.composed].wc+1] = '\0';
1220
1221    if ((new_char <= 0x1f) || (new_char == 0x7f))
1222        b->tree[ic->private.local.composed].keysym = symbol;
1223    else
1224        b->tree[ic->private.local.composed].keysym = NoSymbol;
1225
1226    return True;
1227}
1228
1229static Bool
1230ThaiFltReorderInput(Xic ic, unsigned char previous_char, unsigned char new_char)
1231{
1232    DefTreeBase *b = &ic->private.local.base;
1233    if (!IC_DeletePreviousChar(ic)) return False;
1234    b->wc[b->tree[ic->private.local.composed].wc+0] = tis2ucs(new_char);
1235    b->wc[b->tree[ic->private.local.composed].wc+1] = tis2ucs(previous_char);
1236    b->wc[b->tree[ic->private.local.composed].wc+2] = '\0';
1237
1238    b->tree[ic->private.local.composed].keysym = NoSymbol;
1239
1240    return True;
1241}
1242
1243static Bool
1244ThaiFltReplaceInput(Xic ic, unsigned char new_char, KeySym symbol)
1245{
1246    DefTreeBase *b = &ic->private.local.base;
1247    if (!IC_DeletePreviousChar(ic)) return False;
1248    b->wc[b->tree[ic->private.local.composed].wc+0] = tis2ucs(new_char);
1249    b->wc[b->tree[ic->private.local.composed].wc+1] = '\0';
1250
1251    if ((new_char <= 0x1f) || (new_char == 0x7f))
1252        b->tree[ic->private.local.composed].keysym = symbol;
1253    else
1254        b->tree[ic->private.local.composed].keysym = NoSymbol;
1255
1256    return True;
1257}
1258
1259static unsigned
1260NumLockMask(Display *d)
1261{
1262    int i;
1263    XModifierKeymap *map;
1264    KeyCode numlock_keycode = XKeysymToKeycode (d, XK_Num_Lock);
1265    if (numlock_keycode == NoSymbol)
1266        return 0;
1267
1268    map = XGetModifierMapping (d);
1269    if (!map)
1270        return 0;
1271
1272    for (i = 0; i < 8; i++) {
1273        if (map->modifiermap[map->max_keypermod * i] == numlock_keycode) {
1274            XFreeModifiermap(map);
1275            return 1 << i;
1276        }
1277    }
1278    XFreeModifiermap(map);
1279    return 0;
1280}
1281
1282/*
1283 * Filter function for TACTIS
1284 */
1285Bool
1286_XimThaiFilter(Display *d, Window w, XEvent *ev, XPointer client_data)
1287{
1288    Xic		    ic = (Xic)client_data;
1289    KeySym 	    symbol;
1290    int 	    isc_mode; /* Thai Input Sequence Check mode */
1291    unsigned char   previous_char; /* Last inputted Thai char */
1292    unsigned char   new_char;
1293#ifdef UNUSED
1294    unsigned int    modifiers;
1295    KeySym	    lsym,usym;
1296    int		    state;
1297    XicThaiPart     *thai_part;
1298    char	    buf[10];
1299#endif
1300    wchar_t	    wbuf[10];
1301    Bool            isReject;
1302    DefTreeBase    *b = &ic->private.local.base;
1303
1304    if ((ev->type != KeyPress)
1305        || (ev->xkey.keycode == 0))
1306        return False;
1307
1308    if (!IC_IscMode(ic)) InitIscMode(ic);
1309
1310    XwcLookupString((XIC)ic, &ev->xkey, wbuf, sizeof(wbuf) / sizeof(wbuf[0]),
1311		    &symbol, NULL);
1312
1313    if ((ev->xkey.state & (AllMods & ~(ShiftMask|LockMask|NumLockMask(d)))) ||
1314         ((symbol >> 8 == 0xFF) &&
1315         ((XK_BackSpace <= symbol && symbol <= XK_Clear) ||
1316           (symbol == XK_Return) ||
1317           (symbol == XK_Pause) ||
1318           (symbol == XK_Scroll_Lock) ||
1319           (symbol == XK_Sys_Req) ||
1320           (symbol == XK_Escape) ||
1321           (symbol == XK_Delete) ||
1322           IsCursorKey(symbol) ||
1323           IsKeypadKey(symbol) ||
1324           IsMiscFunctionKey(symbol) ||
1325           IsFunctionKey(symbol))))
1326        {
1327            IC_ClearPreviousChar(ic);
1328            return False;
1329        }
1330    if (((symbol >> 8 == 0xFF) &&
1331         IsModifierKey(symbol)) ||
1332#ifdef XK_XKB_KEYS
1333        ((symbol >> 8 == 0xFE) &&
1334         (XK_ISO_Lock <= symbol && symbol <= XK_ISO_Last_Group_Lock)) ||
1335#endif
1336        (symbol == NoSymbol))
1337    {
1338        return False;
1339    }
1340#ifdef UNUSED
1341    if (! XThaiTranslateKey(ev->xkey.display, ev->xkey.keycode, ev->xkey.state,
1342	 		&modifiers, &symbol, &lsym, &usym))
1343	return False;
1344
1345    /*
1346     *  Hex input method processing
1347     */
1348
1349    thai_part = &ic->private.local.thai;
1350    state = thai_part->comp_state;
1351    if (state >= 0 && state < nstate_handlers) /* call handler for state */
1352    {
1353        symbol = (* state_handler[state])(thai_part, symbol, (XKeyEvent *)ev);
1354    }
1355
1356    /*
1357     *  Translate KeySym into mb.
1358     */
1359    count = XThaiTranslateKeySym(ev->xkey.display, symbol, lsym,
1360				usym, ev->xkey.state, buf, 10);
1361
1362    if (!symbol && !count)
1363	return True;
1364
1365    /* Return symbol if cannot convert to character */
1366    if (!count)
1367	return False;
1368#endif
1369
1370    /*
1371     *  Thai Input sequence check
1372     */
1373    isc_mode = IC_IscMode(ic);
1374    if (!(previous_char = IC_GetPreviousChar(ic))) previous_char = ' ';
1375    new_char = ucs2tis(wbuf[0]);
1376    isReject = True;
1377    if (THAI_isaccepted(new_char, previous_char, isc_mode)) {
1378        ThaiFltAcceptInput(ic, new_char, symbol);
1379        isReject = False;
1380    } else {
1381        unsigned char context_char;
1382
1383        context_char = IC_GetContextChar(ic);
1384        if (context_char) {
1385            if (THAI_iscomposible(new_char, context_char)) {
1386                if (THAI_iscomposible(previous_char, new_char)) {
1387                    isReject = !ThaiFltReorderInput(ic, previous_char, new_char);
1388                } else if (THAI_iscomposible(previous_char, context_char)) {
1389                    isReject = !ThaiFltReplaceInput(ic, new_char, symbol);
1390                } else if (THAI_chtype(previous_char) == FV1
1391                           && THAI_chtype(new_char) == TONE) {
1392                    isReject = !ThaiFltReorderInput(ic, previous_char, new_char);
1393                }
1394            } else if (THAI_isaccepted(new_char, context_char, isc_mode)) {
1395                isReject = !ThaiFltReplaceInput(ic, new_char, symbol);
1396            }
1397        }
1398    }
1399    if (isReject) {
1400        /* reject character */
1401        XBell(ev->xkey.display, BellVolume);
1402        return True;
1403    }
1404
1405    _Xlcwcstombs(ic->core.im->core.lcd, &b->mb[b->tree[ic->private.local.composed].mb],
1406		 &b->wc[b->tree[ic->private.local.composed].wc], 10);
1407
1408    _Xlcmbstoutf8(ic->core.im->core.lcd, &b->utf8[b->tree[ic->private.local.composed].utf8],
1409		  &b->mb[b->tree[ic->private.local.composed].mb], 10);
1410
1411    /* Remember the last character inputted
1412     * (as fallback in case StringConversionCallback is not provided)
1413     */
1414    IC_SavePreviousChar(ic, new_char);
1415
1416    ev->xkey.keycode = 0;
1417    XPutBackEvent(d, ev);
1418    return True;
1419}
1420