imThaiFlt.c revision 61b2299d
1/* $Xorg: imThaiFlt.c,v 1.5 2001/02/09 02:03:39 xorgcvs Exp $ */
2/***********************************************************
3
4Copyright 1993, 1998  The Open Group
5
6Permission to use, copy, modify, distribute, and sell this software and its
7documentation for any purpose is hereby granted without fee, provided that
8the above copyright notice appear in all copies and that both that
9copyright notice and this permission notice appear in supporting
10documentation.
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall not be
23used in advertising or otherwise to promote the sale, use or other dealings
24in this Software without prior written authorization from The Open Group.
25
26
27Copyright 1993 by Digital Equipment Corporation, Maynard, Massachusetts.
28
29                        All Rights Reserved
30
31Permission to use, copy, modify, and distribute this software and its
32documentation for any purpose and without fee is hereby granted,
33provided that the above copyright notice appear in all copies and that
34both that copyright notice and this permission notice appear in
35supporting documentation, and that the name of Digital not be
36used in advertising or publicity pertaining to distribution of the
37software without specific, written prior permission.
38
39DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
40ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
41DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
42ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
43WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
44ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
45SOFTWARE.
46
47******************************************************************/
48/* $XFree86: xc/lib/X11/imThaiFlt.c,v 3.22tsi Exp $ */
49
50/*
51**++
52**  FACILITY:
53**
54**      Xlib
55**
56**  ABSTRACT:
57**
58**	Thai specific functions.
59**	Handles character classifications, composibility checking,
60**	Input sequence check and other Thai specific requirements
61**	according to WTT specification and DEC extensions.
62**
63**  MODIFICATION HISTORY:
64**
65**/
66
67#ifdef HAVE_CONFIG_H
68#include <config.h>
69#endif
70#include <stdio.h>
71#include <X11/Xlib.h>
72#include <X11/Xmd.h>
73#include <X11/keysym.h>
74#include <X11/Xutil.h>
75#include "Xlibint.h"
76#include "Xlcint.h"
77#include "Ximint.h"
78#include "XimThai.h"
79#include "XlcPubI.h"
80
81
82#define SPACE   32
83
84/* character classification table */
85#define TACTIS_CHARS 256
86Private
87char const tactis_chtype[TACTIS_CHARS] = {
88    CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL,  /*  0 -  7 */
89    CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL,  /*  8 - 15 */
90    CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL,  /* 16 - 23 */
91    CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL,  /* 24 - 31 */
92    NON,  NON,  NON,  NON,  NON,  NON,  NON,  NON,   /* 32 - 39 */
93    NON,  NON,  NON,  NON,  NON,  NON,  NON,  NON,   /* 40 - 47 */
94    NON,  NON,  NON,  NON,  NON,  NON,  NON,  NON,   /* 48 - 55 */
95    NON,  NON,  NON,  NON,  NON,  NON,  NON,  NON,   /* 56 - 63 */
96    NON,  NON,  NON,  NON,  NON,  NON,  NON,  NON,   /* 64 - 71 */
97    NON,  NON,  NON,  NON,  NON,  NON,  NON,  NON,   /* 72 - 79 */
98    NON,  NON,  NON,  NON,  NON,  NON,  NON,  NON,   /* 80 - 87 */
99    NON,  NON,  NON,  NON,  NON,  NON,  NON,  NON,   /* 88 - 95 */
100    NON,  NON,  NON,  NON,  NON,  NON,  NON,  NON,   /* 96 - 103 */
101    NON,  NON,  NON,  NON,  NON,  NON,  NON,  NON,   /* 104 - 111 */
102    NON,  NON,  NON,  NON,  NON,  NON,  NON,  NON,   /* 112 - 119 */
103    NON,  NON,  NON,  NON,  NON,  NON,  NON,  CTRL,  /* 120 - 127 */
104    CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL,  /* 128 - 135 */
105    CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL,  /* 136 - 143 */
106    CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL,  /* 144 - 151 */
107    CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, CTRL,  /* 152 - 159 */
108    NON,  CONS, CONS, CONS, CONS, CONS, CONS, CONS,  /* 160 - 167 */
109    CONS, CONS, CONS, CONS, CONS, CONS, CONS, CONS,  /* 168 - 175 */
110    CONS, CONS, CONS, CONS, CONS, CONS, CONS, CONS,  /* 176 - 183 */
111    CONS, CONS, CONS, CONS, CONS, CONS, CONS, CONS,  /* 184 - 191 */
112    CONS, CONS, CONS, CONS,  FV3, CONS,  FV3, CONS,  /* 192 - 199 */
113    CONS, CONS, CONS, CONS, CONS, CONS, CONS, NON,   /* 200 - 207 */
114    FV1,  AV2,  FV1,  FV1,  AV1,  AV3,  AV2,  AV3,   /* 208 - 215 */
115    BV1,  BV2,  BD,   NON,  NON,  NON,  NON,  NON,   /* 216 - 223 */
116    LV,   LV,   LV,   LV,   LV,   FV2,  NON,  AD2,   /* 224 - 231 */
117    TONE, TONE, TONE, TONE, AD1,  AD1,  AD3,  NON,   /* 232 - 239 */
118    NON,  NON,  NON,  NON,  NON,  NON,  NON,  NON,   /* 240 - 247 */
119    NON,  NON,  NON,  NON,  NON,  NON,  NON,  CTRL   /* 248 - 255 */
120};
121
122/* Composibility checking tables */
123#define NC  0   /* NOT COMPOSIBLE - following char displays in next cell */
124#define CP  1   /* COMPOSIBLE - following char is displayed in the same cell
125                                as leading char, also implies ACCEPT */
126#define XC  3   /* Non-display */
127#define AC  4   /* ACCEPT - display the following char in the next cell */
128#define RJ  5   /* REJECT - discard that following char, ignore it */
129
130#define CH_CLASSES      17  /* 17 classes of chars */
131
132Private
133char const write_rules_lookup[CH_CLASSES][CH_CLASSES] = {
134        /* Table 0: writing/outputing rules */
135        /* row: leading char,  column: following char */
136/* CTRL NON CONS LV FV1 FV2 FV3 BV1 BV2 BD TONE AD1 AD2 AD3 AV1 AV2 AV3 */
137   {XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*CTRL*/
138  ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*NON*/
139  ,{XC, NC, NC, NC, NC, NC, NC, CP, CP, CP, CP, CP, CP, CP, CP, CP, CP}/*CONS*/
140  ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*LV*/
141  ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*FV1*/
142  ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*FV2*/
143  ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*FV3*/
144  ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, CP, CP, NC, NC, NC, NC, NC}/*BV1*/
145  ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, CP, NC, NC, NC, NC, NC, NC}/*BV2*/
146  ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*BD*/
147  ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*TONE*/
148  ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*AD1*/
149  ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*AD2*/
150  ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC}/*AD3*/
151  ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, CP, CP, NC, NC, NC, NC, NC}/*AV1*/
152  ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, CP, NC, NC, NC, NC, NC, NC}/*AV2*/
153  ,{XC, NC, NC, NC, NC, NC, NC, NC, NC, NC, CP, NC, CP, NC, NC, NC, NC}/*AV3*/
154};
155
156Private
157char const wtt_isc1_lookup[CH_CLASSES][CH_CLASSES] = {
158      /* Table 1: WTT default input sequence check rules */
159      /* row: leading char,  column: following char */
160/* CTRL NON CONS LV FV1 FV2 FV3 BV1 BV2 BD TONE AD1 AD2 AD3 AV1 AV2 AV3 */
161   {XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*CTRL*/
162  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*NON*/
163  ,{XC, AC, AC, AC, AC, AC, AC, CP, CP, CP, CP, CP, CP, CP, CP, CP, CP}/*CONS*/
164  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*LV*/
165  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*FV1*/
166  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*FV2*/
167  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*FV3*/
168  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, CP, RJ, RJ, RJ, RJ, RJ}/*BV1*/
169  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, RJ, RJ, RJ, RJ, RJ, RJ}/*BV2*/
170  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*BD*/
171  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*TONE*/
172  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*AD1*/
173  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*AD2*/
174  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*AD3*/
175  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, CP, RJ, RJ, RJ, RJ, RJ}/*AV1*/
176  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, RJ, RJ, RJ, RJ, RJ, RJ}/*AV2*/
177  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, RJ, CP, RJ, RJ, RJ, RJ}/*AV3*/
178};
179
180Private
181char const wtt_isc2_lookup[CH_CLASSES][CH_CLASSES] = {
182      /* Table 2: WTT strict input sequence check rules */
183      /* row: leading char,  column: following char */
184/* CTRL NON CONS LV FV1 FV2 FV3 BV1 BV2 BD TONE AD1 AD2 AD3 AV1 AV2 AV3 */
185   {XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*CTRL*/
186  ,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*NON*/
187  ,{XC, AC, AC, AC, AC, RJ, AC, CP, CP, CP, CP, CP, CP, CP, CP, CP, CP}/*CONS*/
188  ,{XC, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*LV*/
189  ,{XC, AC, AC, AC, AC, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*FV1*/
190  ,{XC, AC, AC, AC, AC, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*FV2*/
191  ,{XC, AC, AC, AC, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*FV3*/
192  ,{XC, AC, AC, AC, AC, RJ, AC, RJ, RJ, RJ, CP, CP, RJ, RJ, RJ, RJ, RJ}/*BV1*/
193  ,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, CP, RJ, RJ, RJ, RJ, RJ, RJ}/*BV2*/
194  ,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*BD*/
195  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*TONE*/
196  ,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*AD1*/
197  ,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*AD2*/
198  ,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*AD3*/
199  ,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, CP, CP, RJ, RJ, RJ, RJ, RJ}/*AV1*/
200  ,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, CP, RJ, RJ, RJ, RJ, RJ, RJ}/*AV2*/
201  ,{XC, AC, AC, AC, RJ, RJ, AC, RJ, RJ, RJ, CP, RJ, CP, RJ, RJ, RJ, RJ}/*AV3*/
202};
203
204Private
205char const thaicat_isc_lookup[CH_CLASSES][CH_CLASSES] = {
206      /* Table 3: Thaicat input sequence check rules */
207      /* row: leading char,  column: following char */
208/* CTRL NON CONS LV FV1 FV2 FV3 BV1 BV2 BD TONE AD1 AD2 AD3 AV1 AV2 AV3 */
209   {XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*CTRL*/
210  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*NON*/
211  ,{XC, AC, AC, AC, AC, AC, AC, CP, CP, CP, CP, CP, CP, CP, CP, CP, CP}/*CONS*/
212  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*LV*/
213  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*FV1*/
214  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*FV2*/
215  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ} /*FV3*/
216  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, CP, RJ, RJ, RJ, RJ, RJ}/*BV1*/
217  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, RJ, RJ, RJ, RJ, RJ, RJ}/*BV2*/
218  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*BD*/
219  ,{XC, AC, AC, AC, AC, AC, AC, CP, CP, RJ, RJ, RJ, RJ, RJ, CP, CP, CP}/*TONE*/
220  ,{XC, AC, AC, AC, AC, AC, AC, CP, RJ, RJ, RJ, RJ, RJ, RJ, CP, RJ, RJ}/*AD1*/
221  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, CP}/*AD2*/
222  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ, RJ}/*AD3*/
223  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, CP, RJ, RJ, RJ, RJ, RJ}/*AV1*/
224  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, RJ, RJ, RJ, RJ, RJ, RJ}/*AV2*/
225  ,{XC, AC, AC, AC, AC, AC, AC, RJ, RJ, RJ, CP, RJ, CP, RJ, RJ, RJ, RJ}/*AV3*/
226};
227
228
229/* returns classification of a char */
230Private int
231THAI_chtype (unsigned char	ch)
232{
233    return tactis_chtype[ch];
234}
235
236#ifdef UNUSED
237/* returns the display level */
238Private int
239THAI_chlevel (unsigned char	ch)
240{
241    int     chlevel;
242
243    switch (tactis_chtype[ch])
244    {
245        case CTRL:
246            chlevel = NON;
247            break;
248        case BV1:
249        case BV2:
250        case BD:
251            chlevel = BELOW;
252            break;
253        case TONE:
254        case AD1:
255        case AD2:
256            chlevel = TOP;
257            break;
258        case AV1:
259        case AV2:
260        case AV3:
261        case AD3:
262            chlevel = ABOVE;
263            break;
264        case NON:
265        case CONS:
266        case LV:
267        case FV1:
268        case FV2:
269        case FV3:
270        default: /* if tactis_chtype is invalid */
271            chlevel = BASE;
272            break;
273    }
274    return chlevel;
275}
276
277
278/* return True if char is non-spacing */
279Private Bool
280THAI_isdead (unsigned char	ch)
281{
282    return ((tactis_chtype[ch] == CTRL) || (tactis_chtype[ch] == BV1) ||
283            (tactis_chtype[ch] == BV2)  || (tactis_chtype[ch] == BD)  ||
284            (tactis_chtype[ch] == TONE) || (tactis_chtype[ch] == AD1) ||
285            (tactis_chtype[ch] == AD2)  || (tactis_chtype[ch] == AD3) ||
286            (tactis_chtype[ch] == AV1)  || (tactis_chtype[ch] == AV2) ||
287            (tactis_chtype[ch] == AV3));
288}
289
290
291/* return True if char is consonant */
292Private Bool
293THAI_iscons (unsigned char	ch)
294{
295    return (tactis_chtype[ch] == CONS);
296}
297
298
299/* return True if char is vowel */
300Private Bool
301THAI_isvowel (unsigned char	ch)
302{
303    return ((tactis_chtype[ch] == LV)  || (tactis_chtype[ch] == FV1) ||
304            (tactis_chtype[ch] == FV2) || (tactis_chtype[ch] == FV3) ||
305            (tactis_chtype[ch] == BV1) || (tactis_chtype[ch] == BV2) ||
306            (tactis_chtype[ch] == AV1) || (tactis_chtype[ch] == AV2) ||
307            (tactis_chtype[ch] == AV3));
308}
309
310
311/* return True if char is tonemark */
312Private Bool
313THAI_istone (unsigned char	ch)
314{
315    return (tactis_chtype[ch] == TONE);
316}
317#endif
318
319Private Bool
320THAI_iscomposible (
321    unsigned char	follow_ch,
322    unsigned char	lead_ch)
323{/* "Can follow_ch be put in the same display cell as lead_ch?" */
324
325    return (write_rules_lookup[THAI_chtype(lead_ch)][THAI_chtype(follow_ch)]
326            == CP);
327}
328
329Private Bool
330THAI_isaccepted (
331    unsigned char	follow_ch,
332    unsigned char	lead_ch,
333    unsigned char	mode)
334{
335    Bool iskeyvalid; /*  means "Can follow_ch be keyed in after lead_ch?" */
336
337    switch (mode)
338    {
339        case WTT_ISC1:
340            iskeyvalid =
341          (wtt_isc1_lookup[THAI_chtype(lead_ch)][THAI_chtype(follow_ch)] != RJ);
342            break;
343        case WTT_ISC2:
344            iskeyvalid =
345          (wtt_isc2_lookup[THAI_chtype(lead_ch)][THAI_chtype(follow_ch)] != RJ);
346            break;
347        case THAICAT_ISC:
348            iskeyvalid =
349       (thaicat_isc_lookup[THAI_chtype(lead_ch)][THAI_chtype(follow_ch)] != RJ);
350            break;
351        default:
352            iskeyvalid = True;
353            break;
354    }
355
356    return iskeyvalid;
357}
358
359#ifdef UNUSED
360Private void
361THAI_apply_write_rules(
362    unsigned char	*instr,
363    unsigned char	*outstr,
364    unsigned char	insert_ch,
365    int 		*num_insert_ch)
366{
367/*
368Input parameters:
369    instr - input string
370    insert_ch specify what char to be added when invalid composition is found
371Output parameters:
372    outstr - output string after input string has been applied the rules
373    num_insert_ch - number of insert_ch added to outstr.
374*/
375    unsigned char   *lead_ch = NULL, *follow_ch = NULL, *out_ch = NULL;
376
377    *num_insert_ch = 0;
378    lead_ch = follow_ch = instr;
379    out_ch = outstr;
380    if ((*lead_ch == '\0') || !(THAI_find_chtype(instr,DEAD)))
381    {   /* Empty string or can't find any non-spacing char*/
382        strcpy((char *)outstr, (char *)instr);
383    } else { /* String of length >= 1, keep looking */
384        follow_ch++;
385        if (THAI_isdead(*lead_ch)) { /* is first char non-spacing? */
386            *out_ch++ = SPACE;
387            (*num_insert_ch)++;
388        }
389        *out_ch++ = *lead_ch;
390        while (*follow_ch != '\0')  /* more char in string to check */
391        {
392            if (THAI_isdead(*follow_ch) &&
393                 !THAI_iscomposible(*follow_ch,*lead_ch))
394            {
395                *out_ch++ = SPACE;
396                (*num_insert_ch)++;
397            }
398            *out_ch++ = *follow_ch;
399            lead_ch = follow_ch;
400            follow_ch++;
401        }
402        *out_ch = '\0';
403    }
404}
405
406Private int
407THAI_find_chtype (
408    unsigned char	*instr,
409    int		chtype)
410{
411/*
412Input parameters:
413    instr - input string
414    chtype - type of character to look for
415Output parameters:
416    function returns first position of character with matched chtype
417    function returns -1 if it does not find.
418*/
419    int i = 0, position = -1;
420
421    switch (chtype)
422    {
423        case DEAD:
424            for (i = 0; *instr != '\0' && THAI_isdead(*instr); i++, instr++)
425		;
426            if (*instr != '\0') position = i;
427            break;
428        default:
429            break;
430    }
431    return position;
432}
433
434
435Private int
436THAI_apply_scm(
437    unsigned char	*instr,
438    unsigned char	*outstr,
439    unsigned char	spec_ch,
440    int		num_sp,
441    unsigned char	insert_ch)
442{
443    unsigned char   *scan, *outch;
444    int             i, dead_count, found_count;
445    Bool            isconsecutive;
446
447    scan = instr;
448    outch = outstr;
449    dead_count = found_count = 0;
450    isconsecutive = False;
451    while (*scan != '\0') {
452        if (THAI_isdead(*scan))
453            dead_count++;       /* count number of non-spacing char */
454        if (*scan == spec_ch)
455            if (!isconsecutive)
456                found_count++;      /* count number consecutive spec char found */
457        *outch++ = *scan++;
458        if (found_count == num_sp) {
459            for (i = 0; i < dead_count; i++)
460                *outch++ = insert_ch;
461            dead_count = found_count = 0;
462        }
463    }
464    /* what to return? */
465    return 0; /* probably not right but better than returning garbage */
466}
467
468
469/* The following functions are copied from XKeyBind.c */
470
471Private void ComputeMaskFromKeytrans();
472Private int IsCancelComposeKey(KeySym *symbol, XKeyEvent *event);
473Private void SetLed(Display *dpy, int num, int state);
474Private CARD8 FindKeyCode();
475
476
477/* The following functions are specific to this module */
478
479Private int XThaiTranslateKey();
480Private int XThaiTranslateKeySym();
481
482
483Private KeySym HexIMNormalKey(
484    XicThaiPart *thai_part,
485    KeySym symbol,
486    XKeyEvent *event);
487Private KeySym HexIMFirstComposeKey(
488    XicThaiPart *thai_part,
489    KeySym symbol,
490    XKeyEvent *event);
491Private KeySym HexIMSecondComposeKey(
492    XicThaiPart *thai_part,
493    KeySym symbol
494    XKeyEvent *event);
495Private KeySym HexIMComposeSequence(KeySym ks1, KeySym ks2);
496Private void InitIscMode(Xic ic);
497Private Bool ThaiComposeConvert(
498    Display *dpy,
499    KeySym insym,
500    KeySym *outsym, KeySym *lower, KeySym *upper);
501#endif
502
503/*
504 * Definitions
505 */
506
507#define BellVolume 		0
508
509#define ucs2tis(wc)  \
510 (unsigned char) ( \
511   (0<=(wc)&&(wc)<=0x7F) ? \
512     (wc) : \
513     ((0x0E01<=(wc)&&(wc)<=0x0E5F) ? ((wc)-0x0E00+0xA0) : 0))
514/* "c" is an unsigned char */
515#define tis2ucs(c)  \
516  ( \
517   ((c)<=0x7F) ? \
518     (wchar_t)(c) : \
519     ((0x0A1<=(c)) ? ((wchar_t)(c)-0xA0+0x0E00) : 0))
520
521/*
522 * Macros to save and recall last input character in XIC
523 */
524#define IC_SavePreviousChar(ic,ch) \
525                ((ic)->private.local.base.mb[(ic)->private.local.base.tree[(ic)->private.local.context].mb] = (char) (ch))
526#define IC_ClearPreviousChar(ic) \
527                ((ic)->private.local.base.mb[(ic)->private.local.base.tree[(ic)->private.local.context].mb] = 0)
528#define IC_GetPreviousChar(ic) \
529		(IC_RealGetPreviousChar(ic,1))
530#define IC_GetContextChar(ic) \
531		(IC_RealGetPreviousChar(ic,2))
532#define IC_DeletePreviousChar(ic) \
533		(IC_RealDeletePreviousChar(ic))
534
535Private unsigned char
536IC_RealGetPreviousChar(Xic ic, unsigned short pos)
537{
538    XICCallback* cb = &ic->core.string_conversion_callback;
539    DefTreeBase *b = &ic->private.local.base;
540
541    if (cb && cb->callback) {
542        XIMStringConversionCallbackStruct screc;
543        unsigned char c;
544
545        /* Use a safe value of position = 0 and stretch the range to desired
546         * place, as XIM protocol is unclear here whether it could be negative
547         */
548        screc.position = 0;
549        screc.direction = XIMBackwardChar;
550        screc.operation = XIMStringConversionRetrieval;
551        screc.factor = pos;
552        screc.text = 0;
553
554        (cb->callback)((XIC)ic, cb->client_data, (XPointer)&screc);
555        if (!screc.text)
556            return (unsigned char) b->mb[b->tree[(ic)->private.local.context].mb];
557        if ((screc.text->feedback &&
558             *screc.text->feedback == XIMStringConversionLeftEdge) ||
559            screc.text->length < 1)
560        {
561            c = 0;
562        } else {
563            if (screc.text->encoding_is_wchar) {
564                c = ucs2tis(screc.text->string.wcs[0]);
565                XFree(screc.text->string.wcs);
566            } else {
567                c = screc.text->string.mbs[0];
568                XFree(screc.text->string.mbs);
569            }
570        }
571        XFree(screc.text);
572        return c;
573    } else {
574        return (unsigned char) b->mb[b->tree[(ic)->private.local.context].mb];
575    }
576}
577
578Private unsigned char
579IC_RealDeletePreviousChar(Xic ic)
580{
581    XICCallback* cb = &ic->core.string_conversion_callback;
582
583    if (cb && cb->callback) {
584        XIMStringConversionCallbackStruct screc;
585        unsigned char c;
586
587        screc.position = 0;
588        screc.direction = XIMBackwardChar;
589        screc.operation = XIMStringConversionSubstitution;
590        screc.factor = 1;
591        screc.text = 0;
592
593        (cb->callback)((XIC)ic, cb->client_data, (XPointer)&screc);
594        if (!screc.text) { return 0; }
595        if ((screc.text->feedback &&
596             *screc.text->feedback == XIMStringConversionLeftEdge) ||
597            screc.text->length < 1)
598        {
599            c = 0;
600        } else {
601            if (screc.text->encoding_is_wchar) {
602                c = ucs2tis(screc.text->string.wcs[0]);
603                XFree(screc.text->string.wcs);
604            } else {
605                c = screc.text->string.mbs[0];
606                XFree(screc.text->string.mbs);
607            }
608        }
609        XFree(screc.text);
610        return c;
611    } else {
612        return 0;
613    }
614}
615/*
616 * Input sequence check mode in XIC
617 */
618#define IC_IscMode(ic)		((ic)->private.local.thai.input_mode)
619
620/*
621 * Max. size of string handled by the two String Lookup functions.
622 */
623#define STR_LKUP_BUF_SIZE	256
624
625/*
626 * Size of buffer to contain previous locale name.
627 */
628#define SAV_LOCALE_NAME_SIZE	256
629
630/*
631 * Size of buffer to contain the IM modifier.
632 */
633#define MAXTHAIIMMODLEN 20
634
635#define AllMods (ShiftMask|LockMask|ControlMask| \
636		 Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)
637
638
639#define IsISOControlKey(ks) ((ks) >= XK_2 && (ks) <= XK_8)
640
641#define IsValidControlKey(ks)   (((((ks)>=XK_A && (ks)<=XK_asciitilde) || \
642                (ks)==XK_space || (ks)==XK_Delete) && \
643                ((ks)!=0)))
644
645#define COMPOSE_LED 2
646
647#ifdef UNUSED
648typedef KeySym (*StateProc)(
649    XicThaiPart *thai_part,
650    KeySym symbol,
651    XKeyEvent *event);
652
653
654/*
655 * macros to classify XKeyEvent state field
656 */
657
658#define IsShift(state) (((state) & ShiftMask) != 0)
659#define IsLock(state) (((state) & LockMask) != 0)
660#define IsControl(state) (((state) & ControlMask) != 0)
661#define IsMod1(state) (((state) & Mod1Mask) != 0)
662#define IsMod2(state) (((state) & Mod2Mask) != 0)
663#define IsMod3(state) (((state) & Mod3Mask) != 0)
664#define IsMod4(state) (((state) & Mod4Mask) != 0)
665#define IsMod5(state) (((state) & Mod5Mask) != 0)
666
667/*
668 * key starts Thai compose sequence (Hex input method) if :
669 */
670
671#define IsComposeKey(ks, event)  \
672	(( ks==XK_Alt_L && 	\
673	   IsControl((event)->state) &&	\
674	   !IsShift((event)->state))	\
675	 ? True : False)
676
677
678/*
679 *  State handler to implement the Thai hex input method.
680 */
681
682Private int const nstate_handlers = 3;
683Private StateProc state_handler[] = {
684	HexIMNormalKey,
685	HexIMFirstComposeKey,
686	HexIMSecondComposeKey
687};
688
689
690/*
691 *  Table for 'Thai Compose' character input.
692 *  The current implementation uses latin-1 keysyms.
693 */
694struct _XMapThaiKey {
695	KeySym from;
696	KeySym to;
697};
698
699Private struct _XMapThaiKey const ThaiComposeTable[] = {
700	{ /* 0xa4 */ XK_currency,	/* 0xa5 */ XK_yen },
701	{ /* 0xa2 */ XK_cent,		/* 0xa3 */ XK_sterling },
702	{ /* 0xe6 */ XK_ae,		/* 0xef */ XK_idiaeresis },
703	{ /* 0xd3 */ XK_Oacute,		/* 0xee */ XK_icircumflex },
704	{ /* 0xb9 */ XK_onesuperior,	/* 0xfa */ XK_uacute },
705	{ /* 0xd2 */ XK_Ograve,		/* 0xe5 */ XK_aring },
706	{ /* 0xbc */ XK_onequarter,	/* 0xfb */ XK_ucircumflex },
707	{	     XK_VoidSymbol,		   XK_VoidSymbol }
708};
709
710struct _XKeytrans {
711	struct _XKeytrans *next;/* next on list */
712	char *string;		/* string to return when the time comes */
713	int len;		/* length of string (since NULL is legit)*/
714	KeySym key;		/* keysym rebound */
715	unsigned int state;	/* modifier state */
716	KeySym *modifiers;	/* modifier keysyms you want */
717	int mlen;		/* length of modifier list */
718};
719
720
721/* Convert keysym to 'Thai Compose' keysym */
722/* The current implementation use latin-1 keysyms */
723Private Bool
724ThaiComposeConvert(
725    Display *dpy,
726    KeySym insym,
727    KeySym *outsym, KeySym *lower, KeySym *upper)
728{
729    struct _XMapThaiKey const *table_entry = ThaiComposeTable;
730
731    while (table_entry->from != XK_VoidSymbol) {
732	if (table_entry->from == insym) {
733	    *outsym = table_entry->to;
734	    *lower = *outsym;
735	    *upper = *outsym;
736	    return True;
737	}
738	table_entry++;
739    }
740    return False;
741}
742
743Private int
744XThaiTranslateKey(
745    register Display *dpy,
746    KeyCode keycode,
747    register unsigned int modifiers,
748    unsigned int *modifiers_return,
749    KeySym *keysym_return,
750    KeySym *lsym_return,
751    KeySym *usym_return)
752{
753    int per;
754    register KeySym *syms;
755    KeySym sym = 0, lsym = 0, usym = 0;
756
757    if ((! dpy->keysyms) && (! _XKeyInitialize(dpy)))
758	return 0;
759    *modifiers_return = (ShiftMask|LockMask) | dpy->mode_switch;
760    if (((int)keycode < dpy->min_keycode) || ((int)keycode > dpy->max_keycode))
761    {
762	*keysym_return = NoSymbol;
763	return 1;
764    }
765    per = dpy->keysyms_per_keycode;
766    syms = &dpy->keysyms[(keycode - dpy->min_keycode) * per];
767    while ((per > 2) && (syms[per - 1] == NoSymbol))
768	per--;
769    if ((per > 2) && (modifiers & dpy->mode_switch)) {
770	syms += 2;
771	per -= 2;
772    }
773    if (!(modifiers & ShiftMask) &&
774	(!(modifiers & LockMask) || (dpy->lock_meaning == NoSymbol))) {
775	if ((per == 1) || (syms[1] == NoSymbol))
776	    XConvertCase(syms[0], keysym_return, &usym);
777	else {
778	    XConvertCase(syms[0], &lsym, &usym);
779	    *keysym_return = syms[0];
780	}
781    } else if (!(modifiers & LockMask) ||
782	       (dpy->lock_meaning != XK_Caps_Lock)) {
783	if ((per == 1) || ((usym = syms[1]) == NoSymbol))
784	    XConvertCase(syms[0], &lsym, &usym);
785	*keysym_return = usym;
786    } else {
787	if ((per == 1) || ((sym = syms[1]) == NoSymbol))
788	    sym = syms[0];
789	XConvertCase(sym, &lsym, &usym);
790	if (!(modifiers & ShiftMask) && (sym != syms[0]) &&
791	    ((sym != usym) || (lsym == usym)))
792	    XConvertCase(syms[0], &lsym, &usym);
793	*keysym_return = usym;
794    }
795    /*
796     * ThaiCat keyboard support :
797     * When the Shift and Thai keys are hold for some keys a 'Thai Compose'
798     * character code is generated which is different from column 3 and
799     * 4 of the keymap.
800     * Since we don't know whether ThaiCat keyboard or WTT keyboard is
801     * in use, the same mapping is done for all Thai input.
802     * We just arbitary choose to use column 3 keysyms as the indices of
803     * this mapping.
804     * When the control key is also hold, this mapping has no effect.
805     */
806    if ((modifiers & Mod1Mask) &&
807	(modifiers & ShiftMask) &&
808	!(modifiers & ControlMask)) {
809	if (ThaiComposeConvert(dpy, syms[0], &sym, &lsym, &usym))
810	    *keysym_return = sym;
811    }
812
813    if (*keysym_return == XK_VoidSymbol)
814	*keysym_return = NoSymbol;
815    *lsym_return = lsym;
816    *usym_return = usym;
817    return 1;
818}
819
820/*
821 * XThaiTranslateKeySym
822 *
823 * Translate KeySym to TACTIS code output.
824 * The current implementation uses ISO latin-1 keysym.
825 * Should be changed to TACTIS keysyms when they are defined by the
826 * standard.
827 */
828Private int
829XThaiTranslateKeySym(
830    Display *dpy,
831    register KeySym symbol,
832    register KeySym lsym,
833    register KeySym usym,
834    unsigned int modifiers,
835    unsigned char *buffer,
836    int nbytes)
837{
838    KeySym ckey = 0;
839    register struct _XKeytrans *p;
840    int length;
841    unsigned long hiBytes;
842    register unsigned char c;
843
844    /*
845     * initialize length = 1 ;
846     */
847    length = 1;
848
849    if (!symbol)
850	return 0;
851    /* see if symbol rebound, if so, return that string. */
852    for (p = dpy->key_bindings; p; p = p->next) {
853	if (((modifiers & AllMods) == p->state) && (symbol == p->key)) {
854	    length = p->len;
855	    if (length > nbytes) length = nbytes;
856	    memcpy (buffer, p->string, length);
857	    return length;
858	}
859    }
860    /* try to convert to TACTIS, handling control */
861    hiBytes = symbol >> 8;
862    if (!(nbytes &&
863	  ((hiBytes == 0) ||
864	   ((hiBytes == 0xFF) &&
865	    (((symbol >= XK_BackSpace) && (symbol <= XK_Clear)) ||
866	     (symbol == XK_Return) ||
867	     (symbol == XK_Escape) ||
868	     (symbol == XK_KP_Space) ||
869	     (symbol == XK_KP_Tab) ||
870	     (symbol == XK_KP_Enter) ||
871	     ((symbol >= XK_KP_Multiply) && (symbol <= XK_KP_9)) ||
872	     (symbol == XK_KP_Equal) ||
873             (symbol == XK_Scroll_Lock) ||
874#ifdef DXK_PRIVATE /* DEC private keysyms */
875             (symbol == DXK_Remove) ||
876#endif
877             (symbol == NoSymbol) ||
878	     (symbol == XK_Delete))))))
879	return 0;
880
881    /* if X keysym, convert to ascii by grabbing low 7 bits */
882    if (symbol == XK_KP_Space)
883	c = XK_space & 0x7F; /* patch encoding botch */
884/* not for Thai
885    else if (symbol == XK_hyphen)
886	c = XK_minus & 0xFF; */ /* map to equiv character */
887    else if (hiBytes == 0xFF)
888	c = symbol & 0x7F;
889    else
890	c = symbol & 0xFF;
891    /* only apply Control key if it makes sense, else ignore it */
892    if (modifiers & ControlMask) {
893    if (!(IsKeypadKey(lsym) || lsym==XK_Return || lsym==XK_Tab)) {
894        if (IsISOControlKey(lsym)) ckey = lsym;
895        else if (IsISOControlKey(usym)) ckey = usym;
896        else if (lsym == XK_question) ckey = lsym;
897        else if (usym == XK_question) ckey = usym;
898        else if (IsValidControlKey(lsym)) ckey = lsym;
899        else if (IsValidControlKey(usym)) ckey = usym;
900        else length = 0;
901
902        if (length != 0) {
903        if (ckey == XK_2) c = '\000';
904        else if (ckey >= XK_3 && ckey <= XK_7)
905            c = (char)(ckey-('3'-'\033'));
906        else if (ckey == XK_8) c = '\177';
907        else if (ckey == XK_Delete) c = '\030';
908        else if (ckey == XK_question) c = '\037';
909        else if (ckey == XK_quoteleft) c = '\036';  /* KLee 1/24/91 */
910        else c = (char)(ckey & 0x1f);
911        }
912    }
913    }
914    /*
915     *  ThaiCat has a key that generates two TACTIS codes D1 & E9.
916     *  It is represented by the latin-1 keysym XK_thorn (0xfe).
917     *  If c is XK_thorn, this key is pressed and it is converted to
918     *  0xd1 0xe9.
919     */
920    if (c == XK_thorn) {
921	buffer[0] = 0xd1;
922	buffer[1] = 0xe9;
923	buffer[2] = '\0';
924	return 2;
925    }
926    else {
927	/* Normal case */
928        buffer[0] = c;
929	buffer[1] = '\0';
930        return 1;
931    }
932}
933
934/*
935 * given a KeySym, returns the first keycode containing it, if any.
936 */
937Private CARD8
938FindKeyCode(
939    register Display *dpy,
940    register KeySym code)
941{
942
943    register KeySym *kmax = dpy->keysyms +
944	(dpy->max_keycode - dpy->min_keycode + 1) * dpy->keysyms_per_keycode;
945    register KeySym *k = dpy->keysyms;
946    while (k < kmax) {
947	if (*k == code)
948	    return (((k - dpy->keysyms) / dpy->keysyms_per_keycode) +
949		    dpy->min_keycode);
950	k += 1;
951	}
952    return 0;
953}
954
955/*
956 * given a list of modifiers, computes the mask necessary for later matching.
957 * This routine must lookup the key in the Keymap and then search to see
958 * what modifier it is bound to, if any.  Sets the AnyModifier bit if it
959 * can't map some keysym to a modifier.
960 */
961Private void
962ComputeMaskFromKeytrans(
963    Display *dpy,
964    register struct _XKeytrans *p)
965{
966    register int i;
967    register CARD8 code;
968    register XModifierKeymap *m = dpy->modifiermap;
969
970    p->state = AnyModifier;
971    for (i = 0; i < p->mlen; i++) {
972	/* if not found, then not on current keyboard */
973	if ((code = FindKeyCode(dpy, p->modifiers[i])) == 0)
974		return;
975	/* code is now the keycode for the modifier you want */
976	{
977	    register int j = m->max_keypermod<<3;
978
979	    while ((--j >= 0) && (code != m->modifiermap[j]))
980		;
981	    if (j < 0)
982		return;
983	    p->state |= (1<<(j/m->max_keypermod));
984	}
985    }
986    p->state &= AllMods;
987}
988
989/************************************************************************
990 *
991 *
992 * Compose handling routines - compose handlers 0,1,2
993 *
994 *
995 ************************************************************************/
996
997#define NORMAL_KEY_STATE 0
998#define FIRST_COMPOSE_KEY_STATE 1
999#define SECOND_COMPOSE_KEY_STATE 2
1000
1001Private
1002KeySym HexIMNormalKey(
1003    XicThaiPart *thai_part,
1004    KeySym symbol,
1005    XKeyEvent *event)
1006{
1007    if (IsComposeKey (symbol, event))	/* start compose sequence	*/
1008	{
1009	SetLed (event->display,COMPOSE_LED, LedModeOn);
1010	thai_part->comp_state = FIRST_COMPOSE_KEY_STATE;
1011	return NoSymbol;
1012	}
1013    return symbol;
1014}
1015
1016
1017Private
1018KeySym HexIMFirstComposeKey(
1019    XicThaiPart *thai_part,
1020    KeySym symbol,
1021    XKeyEvent *event)
1022{
1023    if (IsModifierKey (symbol)) return symbol; /* ignore shift etc. */
1024    if (IsCancelComposeKey (&symbol, event))	/* cancel sequence */
1025	{
1026	SetLed (event->display,COMPOSE_LED, LedModeOff);
1027	thai_part->comp_state = NORMAL_KEY_STATE;
1028	return symbol;
1029	}
1030    if (IsComposeKey (symbol, event))		/* restart sequence ?? */
1031	{
1032	return NoSymbol;			/* no state change necessary */
1033	}
1034
1035    thai_part->keysym = symbol;		/* save key pressed */
1036    thai_part->comp_state = SECOND_COMPOSE_KEY_STATE;
1037    return NoSymbol;
1038}
1039
1040Private
1041KeySym HexIMSecondComposeKey(
1042    XicThaiPart *thai_part,
1043    KeySym symbol,
1044    XKeyEvent *event)
1045{
1046    if (IsModifierKey (symbol)) return symbol;	/* ignore shift etc. */
1047    if (IsComposeKey (symbol, event))		/* restart sequence ? */
1048	{
1049	thai_part->comp_state =FIRST_COMPOSE_KEY_STATE;
1050	return NoSymbol;
1051	}
1052    SetLed (event->display,COMPOSE_LED, LedModeOff);
1053    if (IsCancelComposeKey (&symbol, event))	/* cancel sequence ? */
1054	{
1055	thai_part->comp_state = NORMAL_KEY_STATE;
1056	return symbol;
1057	}
1058
1059    if ((symbol = HexIMComposeSequence (thai_part->keysym, symbol))
1060								==NoSymbol)
1061	{ /* invalid compose sequence */
1062	XBell(event->display, BellVolume);
1063	}
1064    thai_part->comp_state = NORMAL_KEY_STATE; /* reset to normal state */
1065    return symbol;
1066}
1067
1068
1069/*
1070 * Interprets two keysyms entered as hex digits and return the Thai keysym
1071 * correspond to the TACTIS code formed.
1072 * The current implementation of this routine returns ISO Latin Keysyms.
1073 */
1074
1075Private
1076KeySym HexIMComposeSequence(KeySym ks1, KeySym ks2)
1077{
1078int	hi_digit;
1079int	lo_digit;
1080int	tactis_code;
1081
1082    if ((ks1 >= XK_0) && (ks1 <= XK_9))
1083	hi_digit = ks1 - XK_0;
1084    else if ((ks1 >= XK_A) && (ks1 <= XK_F))
1085	hi_digit = ks1 - XK_A + 10;
1086    else if ((ks1 >= XK_a) && (ks1 <= XK_f))
1087	hi_digit = ks1 - XK_a + 10;
1088    else	/* out of range */
1089	return NoSymbol;
1090
1091    if ((ks2 >= XK_0) && (ks2 <= XK_9))
1092	lo_digit = ks2 - XK_0;
1093    else if ((ks2 >= XK_A) && (ks2 <= XK_F))
1094	lo_digit = ks2 - XK_A + 10;
1095    else if ((ks2 >= XK_a) && (ks2 <= XK_f))
1096	lo_digit = ks2 - XK_a + 10;
1097    else	/* out of range */
1098	return NoSymbol;
1099
1100    tactis_code = hi_digit * 0x10 + lo_digit ;
1101
1102    return (KeySym)tactis_code;
1103
1104}
1105
1106/*
1107 * routine determines
1108 *	1) whether key event should cancel a compose sequence
1109 *	2) whether cancelling key event should be processed or ignored
1110 */
1111
1112Private
1113int IsCancelComposeKey(
1114    KeySym *symbol,
1115    XKeyEvent *event)
1116{
1117    if (*symbol==XK_Delete && !IsControl(event->state) &&
1118						!IsMod1(event->state)) {
1119	*symbol=NoSymbol;  /* cancel compose sequence, and ignore key */
1120	return True;
1121    }
1122    if (IsComposeKey(*symbol, event)) return False;
1123    return (
1124	IsControl (event->state) ||
1125	IsMod1(event->state) ||
1126	IsKeypadKey (*symbol) ||
1127	IsFunctionKey (*symbol) ||
1128	IsMiscFunctionKey (*symbol) ||
1129#ifdef DXK_PRIVATE /* DEC private keysyms */
1130	*symbol == DXK_Remove ||
1131#endif
1132	IsPFKey (*symbol) ||
1133	IsCursorKey (*symbol) ||
1134	(*symbol >= XK_Tab && *symbol < XK_Multi_key)
1135		? True : False);	/* cancel compose sequence and pass */
1136					/* cancelling key through	    */
1137}
1138
1139
1140/*
1141 *	set specified keyboard LED on or off
1142 */
1143
1144Private
1145void SetLed(
1146    Display *dpy,
1147    int num,
1148    int state)
1149{
1150    XKeyboardControl led_control;
1151
1152    led_control.led_mode = state;
1153    led_control.led = num;
1154    XChangeKeyboardControl (dpy, KBLed | KBLedMode,	&led_control);
1155}
1156#endif
1157
1158/*
1159 * Initialize ISC mode from im modifier
1160 */
1161Private void InitIscMode(Xic ic)
1162{
1163    Xim im;
1164    char *im_modifier_name;
1165
1166    /* If already defined, just return */
1167
1168    if (IC_IscMode(ic)) return;
1169
1170    /* Get IM modifier */
1171
1172    im = (Xim) XIMOfIC((XIC)ic);
1173    im_modifier_name = im->core.im_name;
1174
1175    /* Match with predefined value, default is Basic Check */
1176
1177    if (!strncmp(im_modifier_name,"BasicCheck",MAXTHAIIMMODLEN+1))
1178	IC_IscMode(ic) = WTT_ISC1;
1179    else if (!strncmp(im_modifier_name,"Strict",MAXTHAIIMMODLEN+1))
1180	IC_IscMode(ic) = WTT_ISC2;
1181    else if (!strncmp(im_modifier_name,"Thaicat",MAXTHAIIMMODLEN+1))
1182	IC_IscMode(ic) = THAICAT_ISC;
1183    else if (!strncmp(im_modifier_name,"Passthrough",MAXTHAIIMMODLEN+1))
1184	IC_IscMode(ic) = NOISC;
1185    else
1186	IC_IscMode(ic) = WTT_ISC1;
1187
1188    return;
1189}
1190
1191/*
1192 * Helper functions for _XimThaiFilter()
1193 */
1194Private Bool
1195ThaiFltAcceptInput(Xic ic, unsigned char new_char, KeySym symbol)
1196{
1197    DefTreeBase *b = &ic->private.local.base;
1198    b->wc[b->tree[ic->private.local.composed].wc+0] = tis2ucs(new_char);
1199    b->wc[b->tree[ic->private.local.composed].wc+1] = '\0';
1200
1201    if ((new_char <= 0x1f) || (new_char == 0x7f))
1202        b->tree[ic->private.local.composed].keysym = symbol;
1203    else
1204        b->tree[ic->private.local.composed].keysym = NoSymbol;
1205
1206    return True;
1207}
1208
1209Private Bool
1210ThaiFltReorderInput(Xic ic, unsigned char previous_char, unsigned char new_char)
1211{
1212    DefTreeBase *b = &ic->private.local.base;
1213    if (!IC_DeletePreviousChar(ic)) return False;
1214    b->wc[b->tree[ic->private.local.composed].wc+0] = tis2ucs(new_char);
1215    b->wc[b->tree[ic->private.local.composed].wc+1] = tis2ucs(previous_char);
1216    b->wc[b->tree[ic->private.local.composed].wc+2] = '\0';
1217
1218    b->tree[ic->private.local.composed].keysym = NoSymbol;
1219
1220    return True;
1221}
1222
1223Private Bool
1224ThaiFltReplaceInput(Xic ic, unsigned char new_char, KeySym symbol)
1225{
1226    DefTreeBase *b = &ic->private.local.base;
1227    if (!IC_DeletePreviousChar(ic)) return False;
1228    b->wc[b->tree[ic->private.local.composed].wc+0] = tis2ucs(new_char);
1229    b->wc[b->tree[ic->private.local.composed].wc+1] = '\0';
1230
1231    if ((new_char <= 0x1f) || (new_char == 0x7f))
1232        b->tree[ic->private.local.composed].keysym = symbol;
1233    else
1234        b->tree[ic->private.local.composed].keysym = NoSymbol;
1235
1236    return True;
1237}
1238
1239/*
1240 * Filter function for TACTIS
1241 */
1242Bool
1243_XimThaiFilter(Display *d, Window w, XEvent *ev, XPointer client_data)
1244{
1245    Xic		    ic = (Xic)client_data;
1246    KeySym 	    symbol;
1247    int 	    isc_mode; /* Thai Input Sequence Check mode */
1248    unsigned char   previous_char; /* Last inputted Thai char */
1249    unsigned char   new_char;
1250#ifdef UNUSED
1251    unsigned int    modifiers;
1252    KeySym	    lsym,usym;
1253    int		    state;
1254    XicThaiPart     *thai_part;
1255    char	    buf[10];
1256#endif
1257    wchar_t	    wbuf[10];
1258    Bool            isReject;
1259    DefTreeBase    *b = &ic->private.local.base;
1260
1261    if ((ev->type != KeyPress)
1262        || (ev->xkey.keycode == 0))
1263        return False;
1264
1265    if (!IC_IscMode(ic)) InitIscMode(ic);
1266
1267    XwcLookupString((XIC)ic, &ev->xkey, wbuf, sizeof(wbuf) / sizeof(wbuf[0]),
1268		    &symbol, NULL);
1269
1270    if ((ev->xkey.state & (AllMods & ~ShiftMask)) ||
1271         ((symbol >> 8 == 0xFF) &&
1272         ((XK_BackSpace <= symbol && symbol <= XK_Clear) ||
1273           (symbol == XK_Return) ||
1274           (symbol == XK_Pause) ||
1275           (symbol == XK_Scroll_Lock) ||
1276           (symbol == XK_Sys_Req) ||
1277           (symbol == XK_Escape) ||
1278           (symbol == XK_Delete) ||
1279           IsCursorKey(symbol) ||
1280           IsKeypadKey(symbol) ||
1281           IsMiscFunctionKey(symbol) ||
1282           IsFunctionKey(symbol))))
1283        {
1284            IC_ClearPreviousChar(ic);
1285            return False;
1286        }
1287    if (((symbol >> 8 == 0xFF) &&
1288         IsModifierKey(symbol)) ||
1289#ifdef XK_XKB_KEYS
1290        ((symbol >> 8 == 0xFE) &&
1291         (XK_ISO_Lock <= symbol && symbol <= XK_ISO_Last_Group_Lock)) ||
1292#endif
1293        (symbol == NoSymbol))
1294    {
1295        return False;
1296    }
1297#ifdef UNUSED
1298    if (! XThaiTranslateKey(ev->xkey.display, ev->xkey.keycode, ev->xkey.state,
1299	 		&modifiers, &symbol, &lsym, &usym))
1300	return False;
1301
1302    /*
1303     *  Hex input method processing
1304     */
1305
1306    thai_part = &ic->private.local.thai;
1307    state = thai_part->comp_state;
1308    if (state >= 0 && state < nstate_handlers) /* call handler for state */
1309    {
1310        symbol = (* state_handler[state])(thai_part, symbol, (XKeyEvent *)ev);
1311    }
1312
1313    /*
1314     *  Translate KeySym into mb.
1315     */
1316    count = XThaiTranslateKeySym(ev->xkey.display, symbol, lsym,
1317				usym, ev->xkey.state, buf, 10);
1318
1319    if (!symbol && !count)
1320	return True;
1321
1322    /* Return symbol if cannot convert to character */
1323    if (!count)
1324	return False;
1325#endif
1326
1327    /*
1328     *  Thai Input sequence check
1329     */
1330    isc_mode = IC_IscMode(ic);
1331    if (!(previous_char = IC_GetPreviousChar(ic))) previous_char = ' ';
1332    new_char = ucs2tis(wbuf[0]);
1333    isReject = True;
1334    if (THAI_isaccepted(new_char, previous_char, isc_mode)) {
1335        ThaiFltAcceptInput(ic, new_char, symbol);
1336        isReject = False;
1337    } else {
1338        unsigned char context_char;
1339
1340        context_char = IC_GetContextChar(ic);
1341        if (context_char) {
1342            if (THAI_iscomposible(new_char, context_char)) {
1343                if (THAI_iscomposible(previous_char, new_char)) {
1344                    isReject = !ThaiFltReorderInput(ic, previous_char, new_char);
1345                } else if (THAI_iscomposible(previous_char, context_char)) {
1346                    isReject = !ThaiFltReplaceInput(ic, new_char, symbol);
1347                } else if (THAI_chtype(previous_char) == FV1
1348                           && THAI_chtype(new_char) == TONE) {
1349                    isReject = !ThaiFltReorderInput(ic, previous_char, new_char);
1350                }
1351            } else if (THAI_isaccepted(new_char, context_char, isc_mode)) {
1352                isReject = !ThaiFltReplaceInput(ic, new_char, symbol);
1353            }
1354        }
1355    }
1356    if (isReject) {
1357        /* reject character */
1358        XBell(ev->xkey.display, BellVolume);
1359        return True;
1360    }
1361
1362    _Xlcwcstombs(ic->core.im->core.lcd, &b->mb[b->tree[ic->private.local.composed].mb],
1363		 &b->wc[b->tree[ic->private.local.composed].wc], 10);
1364
1365    _Xlcmbstoutf8(ic->core.im->core.lcd, &b->utf8[b->tree[ic->private.local.composed].utf8],
1366		  &b->mb[b->tree[ic->private.local.composed].mb], 10);
1367
1368    /* Remember the last character inputted
1369     * (as fallback in case StringConversionCallback is not provided)
1370     */
1371    IC_SavePreviousChar(ic, new_char);
1372
1373    ev->xkey.keycode = 0;
1374    XPutBackEvent(d, ev);
1375    return True;
1376}
1377