imThaiFlt.c revision 9c019ec5
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/* 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 */
959static 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 */
983static 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
1023static
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
1039static
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
1062static
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
1097static
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
1134static
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
1166static
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 */
1183static 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 */
1216static 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
1231static 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
1245static 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
1261static unsigned
1262NumLockMask(Display *d)
1263{
1264    int i;
1265    XModifierKeymap *map;
1266    KeyCode numlock_keycode = XKeysymToKeycode (d, XK_Num_Lock);
1267    if (numlock_keycode == NoSymbol)
1268        return 0;
1269
1270    map = XGetModifierMapping (d);
1271    if (!map)
1272        return 0;
1273
1274    for (i = 0; i < 8; i++) {
1275        if (map->modifiermap[map->max_keypermod * i] == numlock_keycode) {
1276            XFreeModifiermap(map);
1277            return 1 << i;
1278        }
1279    }
1280    XFreeModifiermap(map);
1281    return 0;
1282}
1283
1284/*
1285 * Filter function for TACTIS
1286 */
1287Bool
1288_XimThaiFilter(Display *d, Window w, XEvent *ev, XPointer client_data)
1289{
1290    Xic		    ic = (Xic)client_data;
1291    KeySym 	    symbol;
1292    int 	    isc_mode; /* Thai Input Sequence Check mode */
1293    unsigned char   previous_char; /* Last inputted Thai char */
1294    unsigned char   new_char;
1295#ifdef UNUSED
1296    unsigned int    modifiers;
1297    KeySym	    lsym,usym;
1298    int		    state;
1299    XicThaiPart     *thai_part;
1300    char	    buf[10];
1301#endif
1302    wchar_t	    wbuf[10];
1303    Bool            isReject;
1304    DefTreeBase    *b = &ic->private.local.base;
1305
1306    if ((ev->type != KeyPress)
1307        || (ev->xkey.keycode == 0))
1308        return False;
1309
1310    if (!IC_IscMode(ic)) InitIscMode(ic);
1311
1312    XwcLookupString((XIC)ic, &ev->xkey, wbuf, sizeof(wbuf) / sizeof(wbuf[0]),
1313		    &symbol, NULL);
1314
1315    if ((ev->xkey.state & (AllMods & ~(ShiftMask|LockMask|NumLockMask(d)))) ||
1316         ((symbol >> 8 == 0xFF) &&
1317         ((XK_BackSpace <= symbol && symbol <= XK_Clear) ||
1318           (symbol == XK_Return) ||
1319           (symbol == XK_Pause) ||
1320           (symbol == XK_Scroll_Lock) ||
1321           (symbol == XK_Sys_Req) ||
1322           (symbol == XK_Escape) ||
1323           (symbol == XK_Delete) ||
1324           IsCursorKey(symbol) ||
1325           IsKeypadKey(symbol) ||
1326           IsMiscFunctionKey(symbol) ||
1327           IsFunctionKey(symbol))))
1328        {
1329            IC_ClearPreviousChar(ic);
1330            return False;
1331        }
1332    if (((symbol >> 8 == 0xFF) &&
1333         IsModifierKey(symbol)) ||
1334#ifdef XK_XKB_KEYS
1335        ((symbol >> 8 == 0xFE) &&
1336         (XK_ISO_Lock <= symbol && symbol <= XK_ISO_Last_Group_Lock)) ||
1337#endif
1338        (symbol == NoSymbol))
1339    {
1340        return False;
1341    }
1342#ifdef UNUSED
1343    if (! XThaiTranslateKey(ev->xkey.display, ev->xkey.keycode, ev->xkey.state,
1344	 		&modifiers, &symbol, &lsym, &usym))
1345	return False;
1346
1347    /*
1348     *  Hex input method processing
1349     */
1350
1351    thai_part = &ic->private.local.thai;
1352    state = thai_part->comp_state;
1353    if (state >= 0 && state < nstate_handlers) /* call handler for state */
1354    {
1355        symbol = (* state_handler[state])(thai_part, symbol, (XKeyEvent *)ev);
1356    }
1357
1358    /*
1359     *  Translate KeySym into mb.
1360     */
1361    count = XThaiTranslateKeySym(ev->xkey.display, symbol, lsym,
1362				usym, ev->xkey.state, buf, 10);
1363
1364    if (!symbol && !count)
1365	return True;
1366
1367    /* Return symbol if cannot convert to character */
1368    if (!count)
1369	return False;
1370#endif
1371
1372    /*
1373     *  Thai Input sequence check
1374     */
1375    isc_mode = IC_IscMode(ic);
1376    if (!(previous_char = IC_GetPreviousChar(ic))) previous_char = ' ';
1377    new_char = ucs2tis(wbuf[0]);
1378    isReject = True;
1379    if (THAI_isaccepted(new_char, previous_char, isc_mode)) {
1380        ThaiFltAcceptInput(ic, new_char, symbol);
1381        isReject = False;
1382    } else {
1383        unsigned char context_char;
1384
1385        context_char = IC_GetContextChar(ic);
1386        if (context_char) {
1387            if (THAI_iscomposible(new_char, context_char)) {
1388                if (THAI_iscomposible(previous_char, new_char)) {
1389                    isReject = !ThaiFltReorderInput(ic, previous_char, new_char);
1390                } else if (THAI_iscomposible(previous_char, context_char)) {
1391                    isReject = !ThaiFltReplaceInput(ic, new_char, symbol);
1392                } else if (THAI_chtype(previous_char) == FV1
1393                           && THAI_chtype(new_char) == TONE) {
1394                    isReject = !ThaiFltReorderInput(ic, previous_char, new_char);
1395                }
1396            } else if (THAI_isaccepted(new_char, context_char, isc_mode)) {
1397                isReject = !ThaiFltReplaceInput(ic, new_char, symbol);
1398            }
1399        }
1400    }
1401    if (isReject) {
1402        /* reject character */
1403        XBell(ev->xkey.display, BellVolume);
1404        return True;
1405    }
1406
1407    _Xlcwcstombs(ic->core.im->core.lcd, &b->mb[b->tree[ic->private.local.composed].mb],
1408		 &b->wc[b->tree[ic->private.local.composed].wc], 10);
1409
1410    _Xlcmbstoutf8(ic->core.im->core.lcd, &b->utf8[b->tree[ic->private.local.composed].utf8],
1411		  &b->mb[b->tree[ic->private.local.composed].mb], 10);
1412
1413    /* Remember the last character inputted
1414     * (as fallback in case StringConversionCallback is not provided)
1415     */
1416    IC_SavePreviousChar(ic, new_char);
1417
1418    ev->xkey.keycode = 0;
1419    XPutBackEvent(d, ev);
1420    return True;
1421}
1422