imThaiFlt.c revision 2e9c7c8c
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            Xim     im;
564            XlcConv conv;
565            int     from_left;
566            int     to_left;
567            char   *from_buf;
568            char   *to_buf;
569
570            im = (Xim) XIMOfIC((XIC)ic);
571            if (screc.text->encoding_is_wchar) {
572                conv = _XlcOpenConverter(im->core.lcd, XlcNWideChar,
573                                         im->core.lcd, XlcNCharSet);
574                from_buf = (char *) screc.text->string.wcs;
575                from_left = screc.text->length * sizeof(wchar_t);
576            } else {
577                conv = _XlcOpenConverter(im->core.lcd, XlcNMultiByte,
578                                         im->core.lcd, XlcNCharSet);
579                from_buf = screc.text->string.mbs;
580                from_left = screc.text->length;
581            }
582            to_buf = (char *)&c;
583            to_left = 1;
584
585            _XlcResetConverter(conv);
586            if (_XlcConvert(conv, (XPointer *)&from_buf, &from_left,
587                            (XPointer *)&to_buf, &to_left, NULL, 0) < 0)
588            {
589                c = (unsigned char) b->mb[b->tree[(ic)->private.local.context].mb];
590            }
591            _XlcCloseConverter(conv);
592
593            XFree(screc.text->string.mbs);
594        }
595        XFree(screc.text);
596        return c;
597    } else {
598        return (unsigned char) b->mb[b->tree[(ic)->private.local.context].mb];
599    }
600}
601
602Private unsigned char
603IC_RealDeletePreviousChar(Xic ic)
604{
605    XICCallback* cb = &ic->core.string_conversion_callback;
606
607    if (cb && cb->callback) {
608        XIMStringConversionCallbackStruct screc;
609        unsigned char c;
610
611        screc.position = 0;
612        screc.direction = XIMBackwardChar;
613        screc.operation = XIMStringConversionSubstitution;
614        screc.factor = 1;
615        screc.text = 0;
616
617        (cb->callback)((XIC)ic, cb->client_data, (XPointer)&screc);
618        if (!screc.text) { return 0; }
619        if ((screc.text->feedback &&
620             *screc.text->feedback == XIMStringConversionLeftEdge) ||
621            screc.text->length < 1)
622        {
623            c = 0;
624        } else {
625            if (screc.text->encoding_is_wchar) {
626                c = ucs2tis(screc.text->string.wcs[0]);
627                XFree(screc.text->string.wcs);
628            } else {
629                c = screc.text->string.mbs[0];
630                XFree(screc.text->string.mbs);
631            }
632        }
633        XFree(screc.text);
634        return c;
635    } else {
636        return 0;
637    }
638}
639/*
640 * Input sequence check mode in XIC
641 */
642#define IC_IscMode(ic)		((ic)->private.local.thai.input_mode)
643
644/*
645 * Max. size of string handled by the two String Lookup functions.
646 */
647#define STR_LKUP_BUF_SIZE	256
648
649/*
650 * Size of buffer to contain previous locale name.
651 */
652#define SAV_LOCALE_NAME_SIZE	256
653
654/*
655 * Size of buffer to contain the IM modifier.
656 */
657#define MAXTHAIIMMODLEN 20
658
659#define AllMods (ShiftMask|LockMask|ControlMask| \
660		 Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)
661
662
663#define IsISOControlKey(ks) ((ks) >= XK_2 && (ks) <= XK_8)
664
665#define IsValidControlKey(ks)   (((((ks)>=XK_A && (ks)<=XK_asciitilde) || \
666                (ks)==XK_space || (ks)==XK_Delete) && \
667                ((ks)!=0)))
668
669#define COMPOSE_LED 2
670
671#ifdef UNUSED
672typedef KeySym (*StateProc)(
673    XicThaiPart *thai_part,
674    KeySym symbol,
675    XKeyEvent *event);
676
677
678/*
679 * macros to classify XKeyEvent state field
680 */
681
682#define IsShift(state) (((state) & ShiftMask) != 0)
683#define IsLock(state) (((state) & LockMask) != 0)
684#define IsControl(state) (((state) & ControlMask) != 0)
685#define IsMod1(state) (((state) & Mod1Mask) != 0)
686#define IsMod2(state) (((state) & Mod2Mask) != 0)
687#define IsMod3(state) (((state) & Mod3Mask) != 0)
688#define IsMod4(state) (((state) & Mod4Mask) != 0)
689#define IsMod5(state) (((state) & Mod5Mask) != 0)
690
691/*
692 * key starts Thai compose sequence (Hex input method) if :
693 */
694
695#define IsComposeKey(ks, event)  \
696	(( ks==XK_Alt_L && 	\
697	   IsControl((event)->state) &&	\
698	   !IsShift((event)->state))	\
699	 ? True : False)
700
701
702/*
703 *  State handler to implement the Thai hex input method.
704 */
705
706Private int const nstate_handlers = 3;
707Private StateProc state_handler[] = {
708	HexIMNormalKey,
709	HexIMFirstComposeKey,
710	HexIMSecondComposeKey
711};
712
713
714/*
715 *  Table for 'Thai Compose' character input.
716 *  The current implementation uses latin-1 keysyms.
717 */
718struct _XMapThaiKey {
719	KeySym from;
720	KeySym to;
721};
722
723Private struct _XMapThaiKey const ThaiComposeTable[] = {
724	{ /* 0xa4 */ XK_currency,	/* 0xa5 */ XK_yen },
725	{ /* 0xa2 */ XK_cent,		/* 0xa3 */ XK_sterling },
726	{ /* 0xe6 */ XK_ae,		/* 0xef */ XK_idiaeresis },
727	{ /* 0xd3 */ XK_Oacute,		/* 0xee */ XK_icircumflex },
728	{ /* 0xb9 */ XK_onesuperior,	/* 0xfa */ XK_uacute },
729	{ /* 0xd2 */ XK_Ograve,		/* 0xe5 */ XK_aring },
730	{ /* 0xbc */ XK_onequarter,	/* 0xfb */ XK_ucircumflex },
731	{	     XK_VoidSymbol,		   XK_VoidSymbol }
732};
733
734struct _XKeytrans {
735	struct _XKeytrans *next;/* next on list */
736	char *string;		/* string to return when the time comes */
737	int len;		/* length of string (since NULL is legit)*/
738	KeySym key;		/* keysym rebound */
739	unsigned int state;	/* modifier state */
740	KeySym *modifiers;	/* modifier keysyms you want */
741	int mlen;		/* length of modifier list */
742};
743
744
745/* Convert keysym to 'Thai Compose' keysym */
746/* The current implementation use latin-1 keysyms */
747Private Bool
748ThaiComposeConvert(
749    Display *dpy,
750    KeySym insym,
751    KeySym *outsym, KeySym *lower, KeySym *upper)
752{
753    struct _XMapThaiKey const *table_entry = ThaiComposeTable;
754
755    while (table_entry->from != XK_VoidSymbol) {
756	if (table_entry->from == insym) {
757	    *outsym = table_entry->to;
758	    *lower = *outsym;
759	    *upper = *outsym;
760	    return True;
761	}
762	table_entry++;
763    }
764    return False;
765}
766
767Private int
768XThaiTranslateKey(
769    register Display *dpy,
770    KeyCode keycode,
771    register unsigned int modifiers,
772    unsigned int *modifiers_return,
773    KeySym *keysym_return,
774    KeySym *lsym_return,
775    KeySym *usym_return)
776{
777    int per;
778    register KeySym *syms;
779    KeySym sym = 0, lsym = 0, usym = 0;
780
781    if ((! dpy->keysyms) && (! _XKeyInitialize(dpy)))
782	return 0;
783    *modifiers_return = (ShiftMask|LockMask) | dpy->mode_switch;
784    if (((int)keycode < dpy->min_keycode) || ((int)keycode > dpy->max_keycode))
785    {
786	*keysym_return = NoSymbol;
787	return 1;
788    }
789    per = dpy->keysyms_per_keycode;
790    syms = &dpy->keysyms[(keycode - dpy->min_keycode) * per];
791    while ((per > 2) && (syms[per - 1] == NoSymbol))
792	per--;
793    if ((per > 2) && (modifiers & dpy->mode_switch)) {
794	syms += 2;
795	per -= 2;
796    }
797    if (!(modifiers & ShiftMask) &&
798	(!(modifiers & LockMask) || (dpy->lock_meaning == NoSymbol))) {
799	if ((per == 1) || (syms[1] == NoSymbol))
800	    XConvertCase(syms[0], keysym_return, &usym);
801	else {
802	    XConvertCase(syms[0], &lsym, &usym);
803	    *keysym_return = syms[0];
804	}
805    } else if (!(modifiers & LockMask) ||
806	       (dpy->lock_meaning != XK_Caps_Lock)) {
807	if ((per == 1) || ((usym = syms[1]) == NoSymbol))
808	    XConvertCase(syms[0], &lsym, &usym);
809	*keysym_return = usym;
810    } else {
811	if ((per == 1) || ((sym = syms[1]) == NoSymbol))
812	    sym = syms[0];
813	XConvertCase(sym, &lsym, &usym);
814	if (!(modifiers & ShiftMask) && (sym != syms[0]) &&
815	    ((sym != usym) || (lsym == usym)))
816	    XConvertCase(syms[0], &lsym, &usym);
817	*keysym_return = usym;
818    }
819    /*
820     * ThaiCat keyboard support :
821     * When the Shift and Thai keys are hold for some keys a 'Thai Compose'
822     * character code is generated which is different from column 3 and
823     * 4 of the keymap.
824     * Since we don't know whether ThaiCat keyboard or WTT keyboard is
825     * in use, the same mapping is done for all Thai input.
826     * We just arbitary choose to use column 3 keysyms as the indices of
827     * this mapping.
828     * When the control key is also hold, this mapping has no effect.
829     */
830    if ((modifiers & Mod1Mask) &&
831	(modifiers & ShiftMask) &&
832	!(modifiers & ControlMask)) {
833	if (ThaiComposeConvert(dpy, syms[0], &sym, &lsym, &usym))
834	    *keysym_return = sym;
835    }
836
837    if (*keysym_return == XK_VoidSymbol)
838	*keysym_return = NoSymbol;
839    *lsym_return = lsym;
840    *usym_return = usym;
841    return 1;
842}
843
844/*
845 * XThaiTranslateKeySym
846 *
847 * Translate KeySym to TACTIS code output.
848 * The current implementation uses ISO latin-1 keysym.
849 * Should be changed to TACTIS keysyms when they are defined by the
850 * standard.
851 */
852Private int
853XThaiTranslateKeySym(
854    Display *dpy,
855    register KeySym symbol,
856    register KeySym lsym,
857    register KeySym usym,
858    unsigned int modifiers,
859    unsigned char *buffer,
860    int nbytes)
861{
862    KeySym ckey = 0;
863    register struct _XKeytrans *p;
864    int length;
865    unsigned long hiBytes;
866    register unsigned char c;
867
868    /*
869     * initialize length = 1 ;
870     */
871    length = 1;
872
873    if (!symbol)
874	return 0;
875    /* see if symbol rebound, if so, return that string. */
876    for (p = dpy->key_bindings; p; p = p->next) {
877	if (((modifiers & AllMods) == p->state) && (symbol == p->key)) {
878	    length = p->len;
879	    if (length > nbytes) length = nbytes;
880	    memcpy (buffer, p->string, length);
881	    return length;
882	}
883    }
884    /* try to convert to TACTIS, handling control */
885    hiBytes = symbol >> 8;
886    if (!(nbytes &&
887	  ((hiBytes == 0) ||
888	   ((hiBytes == 0xFF) &&
889	    (((symbol >= XK_BackSpace) && (symbol <= XK_Clear)) ||
890	     (symbol == XK_Return) ||
891	     (symbol == XK_Escape) ||
892	     (symbol == XK_KP_Space) ||
893	     (symbol == XK_KP_Tab) ||
894	     (symbol == XK_KP_Enter) ||
895	     ((symbol >= XK_KP_Multiply) && (symbol <= XK_KP_9)) ||
896	     (symbol == XK_KP_Equal) ||
897             (symbol == XK_Scroll_Lock) ||
898#ifdef DXK_PRIVATE /* DEC private keysyms */
899             (symbol == DXK_Remove) ||
900#endif
901             (symbol == NoSymbol) ||
902	     (symbol == XK_Delete))))))
903	return 0;
904
905    /* if X keysym, convert to ascii by grabbing low 7 bits */
906    if (symbol == XK_KP_Space)
907	c = XK_space & 0x7F; /* patch encoding botch */
908/* not for Thai
909    else if (symbol == XK_hyphen)
910	c = XK_minus & 0xFF; */ /* map to equiv character */
911    else if (hiBytes == 0xFF)
912	c = symbol & 0x7F;
913    else
914	c = symbol & 0xFF;
915    /* only apply Control key if it makes sense, else ignore it */
916    if (modifiers & ControlMask) {
917    if (!(IsKeypadKey(lsym) || lsym==XK_Return || lsym==XK_Tab)) {
918        if (IsISOControlKey(lsym)) ckey = lsym;
919        else if (IsISOControlKey(usym)) ckey = usym;
920        else if (lsym == XK_question) ckey = lsym;
921        else if (usym == XK_question) ckey = usym;
922        else if (IsValidControlKey(lsym)) ckey = lsym;
923        else if (IsValidControlKey(usym)) ckey = usym;
924        else length = 0;
925
926        if (length != 0) {
927        if (ckey == XK_2) c = '\000';
928        else if (ckey >= XK_3 && ckey <= XK_7)
929            c = (char)(ckey-('3'-'\033'));
930        else if (ckey == XK_8) c = '\177';
931        else if (ckey == XK_Delete) c = '\030';
932        else if (ckey == XK_question) c = '\037';
933        else if (ckey == XK_quoteleft) c = '\036';  /* KLee 1/24/91 */
934        else c = (char)(ckey & 0x1f);
935        }
936    }
937    }
938    /*
939     *  ThaiCat has a key that generates two TACTIS codes D1 & E9.
940     *  It is represented by the latin-1 keysym XK_thorn (0xfe).
941     *  If c is XK_thorn, this key is pressed and it is converted to
942     *  0xd1 0xe9.
943     */
944    if (c == XK_thorn) {
945	buffer[0] = 0xd1;
946	buffer[1] = 0xe9;
947	buffer[2] = '\0';
948	return 2;
949    }
950    else {
951	/* Normal case */
952        buffer[0] = c;
953	buffer[1] = '\0';
954        return 1;
955    }
956}
957
958/*
959 * given a KeySym, returns the first keycode containing it, if any.
960 */
961Private CARD8
962FindKeyCode(
963    register Display *dpy,
964    register KeySym code)
965{
966
967    register KeySym *kmax = dpy->keysyms +
968	(dpy->max_keycode - dpy->min_keycode + 1) * dpy->keysyms_per_keycode;
969    register KeySym *k = dpy->keysyms;
970    while (k < kmax) {
971	if (*k == code)
972	    return (((k - dpy->keysyms) / dpy->keysyms_per_keycode) +
973		    dpy->min_keycode);
974	k += 1;
975	}
976    return 0;
977}
978
979/*
980 * given a list of modifiers, computes the mask necessary for later matching.
981 * This routine must lookup the key in the Keymap and then search to see
982 * what modifier it is bound to, if any.  Sets the AnyModifier bit if it
983 * can't map some keysym to a modifier.
984 */
985Private void
986ComputeMaskFromKeytrans(
987    Display *dpy,
988    register struct _XKeytrans *p)
989{
990    register int i;
991    register CARD8 code;
992    register XModifierKeymap *m = dpy->modifiermap;
993
994    p->state = AnyModifier;
995    for (i = 0; i < p->mlen; i++) {
996	/* if not found, then not on current keyboard */
997	if ((code = FindKeyCode(dpy, p->modifiers[i])) == 0)
998		return;
999	/* code is now the keycode for the modifier you want */
1000	{
1001	    register int j = m->max_keypermod<<3;
1002
1003	    while ((--j >= 0) && (code != m->modifiermap[j]))
1004		;
1005	    if (j < 0)
1006		return;
1007	    p->state |= (1<<(j/m->max_keypermod));
1008	}
1009    }
1010    p->state &= AllMods;
1011}
1012
1013/************************************************************************
1014 *
1015 *
1016 * Compose handling routines - compose handlers 0,1,2
1017 *
1018 *
1019 ************************************************************************/
1020
1021#define NORMAL_KEY_STATE 0
1022#define FIRST_COMPOSE_KEY_STATE 1
1023#define SECOND_COMPOSE_KEY_STATE 2
1024
1025Private
1026KeySym HexIMNormalKey(
1027    XicThaiPart *thai_part,
1028    KeySym symbol,
1029    XKeyEvent *event)
1030{
1031    if (IsComposeKey (symbol, event))	/* start compose sequence	*/
1032	{
1033	SetLed (event->display,COMPOSE_LED, LedModeOn);
1034	thai_part->comp_state = FIRST_COMPOSE_KEY_STATE;
1035	return NoSymbol;
1036	}
1037    return symbol;
1038}
1039
1040
1041Private
1042KeySym HexIMFirstComposeKey(
1043    XicThaiPart *thai_part,
1044    KeySym symbol,
1045    XKeyEvent *event)
1046{
1047    if (IsModifierKey (symbol)) return symbol; /* ignore shift etc. */
1048    if (IsCancelComposeKey (&symbol, event))	/* cancel sequence */
1049	{
1050	SetLed (event->display,COMPOSE_LED, LedModeOff);
1051	thai_part->comp_state = NORMAL_KEY_STATE;
1052	return symbol;
1053	}
1054    if (IsComposeKey (symbol, event))		/* restart sequence ?? */
1055	{
1056	return NoSymbol;			/* no state change necessary */
1057	}
1058
1059    thai_part->keysym = symbol;		/* save key pressed */
1060    thai_part->comp_state = SECOND_COMPOSE_KEY_STATE;
1061    return NoSymbol;
1062}
1063
1064Private
1065KeySym HexIMSecondComposeKey(
1066    XicThaiPart *thai_part,
1067    KeySym symbol,
1068    XKeyEvent *event)
1069{
1070    if (IsModifierKey (symbol)) return symbol;	/* ignore shift etc. */
1071    if (IsComposeKey (symbol, event))		/* restart sequence ? */
1072	{
1073	thai_part->comp_state =FIRST_COMPOSE_KEY_STATE;
1074	return NoSymbol;
1075	}
1076    SetLed (event->display,COMPOSE_LED, LedModeOff);
1077    if (IsCancelComposeKey (&symbol, event))	/* cancel sequence ? */
1078	{
1079	thai_part->comp_state = NORMAL_KEY_STATE;
1080	return symbol;
1081	}
1082
1083    if ((symbol = HexIMComposeSequence (thai_part->keysym, symbol))
1084								==NoSymbol)
1085	{ /* invalid compose sequence */
1086	XBell(event->display, BellVolume);
1087	}
1088    thai_part->comp_state = NORMAL_KEY_STATE; /* reset to normal state */
1089    return symbol;
1090}
1091
1092
1093/*
1094 * Interprets two keysyms entered as hex digits and return the Thai keysym
1095 * correspond to the TACTIS code formed.
1096 * The current implementation of this routine returns ISO Latin Keysyms.
1097 */
1098
1099Private
1100KeySym HexIMComposeSequence(KeySym ks1, KeySym ks2)
1101{
1102int	hi_digit;
1103int	lo_digit;
1104int	tactis_code;
1105
1106    if ((ks1 >= XK_0) && (ks1 <= XK_9))
1107	hi_digit = ks1 - XK_0;
1108    else if ((ks1 >= XK_A) && (ks1 <= XK_F))
1109	hi_digit = ks1 - XK_A + 10;
1110    else if ((ks1 >= XK_a) && (ks1 <= XK_f))
1111	hi_digit = ks1 - XK_a + 10;
1112    else	/* out of range */
1113	return NoSymbol;
1114
1115    if ((ks2 >= XK_0) && (ks2 <= XK_9))
1116	lo_digit = ks2 - XK_0;
1117    else if ((ks2 >= XK_A) && (ks2 <= XK_F))
1118	lo_digit = ks2 - XK_A + 10;
1119    else if ((ks2 >= XK_a) && (ks2 <= XK_f))
1120	lo_digit = ks2 - XK_a + 10;
1121    else	/* out of range */
1122	return NoSymbol;
1123
1124    tactis_code = hi_digit * 0x10 + lo_digit ;
1125
1126    return (KeySym)tactis_code;
1127
1128}
1129
1130/*
1131 * routine determines
1132 *	1) whether key event should cancel a compose sequence
1133 *	2) whether cancelling key event should be processed or ignored
1134 */
1135
1136Private
1137int IsCancelComposeKey(
1138    KeySym *symbol,
1139    XKeyEvent *event)
1140{
1141    if (*symbol==XK_Delete && !IsControl(event->state) &&
1142						!IsMod1(event->state)) {
1143	*symbol=NoSymbol;  /* cancel compose sequence, and ignore key */
1144	return True;
1145    }
1146    if (IsComposeKey(*symbol, event)) return False;
1147    return (
1148	IsControl (event->state) ||
1149	IsMod1(event->state) ||
1150	IsKeypadKey (*symbol) ||
1151	IsFunctionKey (*symbol) ||
1152	IsMiscFunctionKey (*symbol) ||
1153#ifdef DXK_PRIVATE /* DEC private keysyms */
1154	*symbol == DXK_Remove ||
1155#endif
1156	IsPFKey (*symbol) ||
1157	IsCursorKey (*symbol) ||
1158	(*symbol >= XK_Tab && *symbol < XK_Multi_key)
1159		? True : False);	/* cancel compose sequence and pass */
1160					/* cancelling key through	    */
1161}
1162
1163
1164/*
1165 *	set specified keyboard LED on or off
1166 */
1167
1168Private
1169void SetLed(
1170    Display *dpy,
1171    int num,
1172    int state)
1173{
1174    XKeyboardControl led_control;
1175
1176    led_control.led_mode = state;
1177    led_control.led = num;
1178    XChangeKeyboardControl (dpy, KBLed | KBLedMode,	&led_control);
1179}
1180#endif
1181
1182/*
1183 * Initialize ISC mode from im modifier
1184 */
1185Private void InitIscMode(Xic ic)
1186{
1187    Xim im;
1188    char *im_modifier_name;
1189
1190    /* If already defined, just return */
1191
1192    if (IC_IscMode(ic)) return;
1193
1194    /* Get IM modifier */
1195
1196    im = (Xim) XIMOfIC((XIC)ic);
1197    im_modifier_name = im->core.im_name;
1198
1199    /* Match with predefined value, default is Basic Check */
1200
1201    if (!strncmp(im_modifier_name,"BasicCheck",MAXTHAIIMMODLEN+1))
1202	IC_IscMode(ic) = WTT_ISC1;
1203    else if (!strncmp(im_modifier_name,"Strict",MAXTHAIIMMODLEN+1))
1204	IC_IscMode(ic) = WTT_ISC2;
1205    else if (!strncmp(im_modifier_name,"Thaicat",MAXTHAIIMMODLEN+1))
1206	IC_IscMode(ic) = THAICAT_ISC;
1207    else if (!strncmp(im_modifier_name,"Passthrough",MAXTHAIIMMODLEN+1))
1208	IC_IscMode(ic) = NOISC;
1209    else
1210	IC_IscMode(ic) = WTT_ISC1;
1211
1212    return;
1213}
1214
1215/*
1216 * Helper functions for _XimThaiFilter()
1217 */
1218Private Bool
1219ThaiFltAcceptInput(Xic ic, unsigned char new_char, KeySym symbol)
1220{
1221    DefTreeBase *b = &ic->private.local.base;
1222    b->wc[b->tree[ic->private.local.composed].wc+0] = tis2ucs(new_char);
1223    b->wc[b->tree[ic->private.local.composed].wc+1] = '\0';
1224
1225    if ((new_char <= 0x1f) || (new_char == 0x7f))
1226        b->tree[ic->private.local.composed].keysym = symbol;
1227    else
1228        b->tree[ic->private.local.composed].keysym = NoSymbol;
1229
1230    return True;
1231}
1232
1233Private Bool
1234ThaiFltReorderInput(Xic ic, unsigned char previous_char, unsigned char new_char)
1235{
1236    DefTreeBase *b = &ic->private.local.base;
1237    if (!IC_DeletePreviousChar(ic)) return False;
1238    b->wc[b->tree[ic->private.local.composed].wc+0] = tis2ucs(new_char);
1239    b->wc[b->tree[ic->private.local.composed].wc+1] = tis2ucs(previous_char);
1240    b->wc[b->tree[ic->private.local.composed].wc+2] = '\0';
1241
1242    b->tree[ic->private.local.composed].keysym = NoSymbol;
1243
1244    return True;
1245}
1246
1247Private Bool
1248ThaiFltReplaceInput(Xic ic, unsigned char new_char, KeySym symbol)
1249{
1250    DefTreeBase *b = &ic->private.local.base;
1251    if (!IC_DeletePreviousChar(ic)) return False;
1252    b->wc[b->tree[ic->private.local.composed].wc+0] = tis2ucs(new_char);
1253    b->wc[b->tree[ic->private.local.composed].wc+1] = '\0';
1254
1255    if ((new_char <= 0x1f) || (new_char == 0x7f))
1256        b->tree[ic->private.local.composed].keysym = symbol;
1257    else
1258        b->tree[ic->private.local.composed].keysym = NoSymbol;
1259
1260    return True;
1261}
1262
1263Private unsigned
1264NumLockMask(Display *d)
1265{
1266    int i;
1267    XModifierKeymap *map = XGetModifierMapping (d);
1268    KeyCode numlock_keycode = XKeysymToKeycode (d, XK_Num_Lock);
1269    if (numlock_keycode == NoSymbol)
1270        return 0;
1271
1272    for (i = 0; i < 8; i++) {
1273        if (map->modifiermap[map->max_keypermod * i] == numlock_keycode)
1274            return 1 << i;
1275    }
1276    return 0;
1277}
1278
1279/*
1280 * Filter function for TACTIS
1281 */
1282Bool
1283_XimThaiFilter(Display *d, Window w, XEvent *ev, XPointer client_data)
1284{
1285    Xic		    ic = (Xic)client_data;
1286    KeySym 	    symbol;
1287    int 	    isc_mode; /* Thai Input Sequence Check mode */
1288    unsigned char   previous_char; /* Last inputted Thai char */
1289    unsigned char   new_char;
1290#ifdef UNUSED
1291    unsigned int    modifiers;
1292    KeySym	    lsym,usym;
1293    int		    state;
1294    XicThaiPart     *thai_part;
1295    char	    buf[10];
1296#endif
1297    wchar_t	    wbuf[10];
1298    Bool            isReject;
1299    DefTreeBase    *b = &ic->private.local.base;
1300
1301    if ((ev->type != KeyPress)
1302        || (ev->xkey.keycode == 0))
1303        return False;
1304
1305    if (!IC_IscMode(ic)) InitIscMode(ic);
1306
1307    XwcLookupString((XIC)ic, &ev->xkey, wbuf, sizeof(wbuf) / sizeof(wbuf[0]),
1308		    &symbol, NULL);
1309
1310    if ((ev->xkey.state & (AllMods & ~(ShiftMask|LockMask|NumLockMask(d)))) ||
1311         ((symbol >> 8 == 0xFF) &&
1312         ((XK_BackSpace <= symbol && symbol <= XK_Clear) ||
1313           (symbol == XK_Return) ||
1314           (symbol == XK_Pause) ||
1315           (symbol == XK_Scroll_Lock) ||
1316           (symbol == XK_Sys_Req) ||
1317           (symbol == XK_Escape) ||
1318           (symbol == XK_Delete) ||
1319           IsCursorKey(symbol) ||
1320           IsKeypadKey(symbol) ||
1321           IsMiscFunctionKey(symbol) ||
1322           IsFunctionKey(symbol))))
1323        {
1324            IC_ClearPreviousChar(ic);
1325            return False;
1326        }
1327    if (((symbol >> 8 == 0xFF) &&
1328         IsModifierKey(symbol)) ||
1329#ifdef XK_XKB_KEYS
1330        ((symbol >> 8 == 0xFE) &&
1331         (XK_ISO_Lock <= symbol && symbol <= XK_ISO_Last_Group_Lock)) ||
1332#endif
1333        (symbol == NoSymbol))
1334    {
1335        return False;
1336    }
1337#ifdef UNUSED
1338    if (! XThaiTranslateKey(ev->xkey.display, ev->xkey.keycode, ev->xkey.state,
1339	 		&modifiers, &symbol, &lsym, &usym))
1340	return False;
1341
1342    /*
1343     *  Hex input method processing
1344     */
1345
1346    thai_part = &ic->private.local.thai;
1347    state = thai_part->comp_state;
1348    if (state >= 0 && state < nstate_handlers) /* call handler for state */
1349    {
1350        symbol = (* state_handler[state])(thai_part, symbol, (XKeyEvent *)ev);
1351    }
1352
1353    /*
1354     *  Translate KeySym into mb.
1355     */
1356    count = XThaiTranslateKeySym(ev->xkey.display, symbol, lsym,
1357				usym, ev->xkey.state, buf, 10);
1358
1359    if (!symbol && !count)
1360	return True;
1361
1362    /* Return symbol if cannot convert to character */
1363    if (!count)
1364	return False;
1365#endif
1366
1367    /*
1368     *  Thai Input sequence check
1369     */
1370    isc_mode = IC_IscMode(ic);
1371    if (!(previous_char = IC_GetPreviousChar(ic))) previous_char = ' ';
1372    new_char = ucs2tis(wbuf[0]);
1373    isReject = True;
1374    if (THAI_isaccepted(new_char, previous_char, isc_mode)) {
1375        ThaiFltAcceptInput(ic, new_char, symbol);
1376        isReject = False;
1377    } else {
1378        unsigned char context_char;
1379
1380        context_char = IC_GetContextChar(ic);
1381        if (context_char) {
1382            if (THAI_iscomposible(new_char, context_char)) {
1383                if (THAI_iscomposible(previous_char, new_char)) {
1384                    isReject = !ThaiFltReorderInput(ic, previous_char, new_char);
1385                } else if (THAI_iscomposible(previous_char, context_char)) {
1386                    isReject = !ThaiFltReplaceInput(ic, new_char, symbol);
1387                } else if (THAI_chtype(previous_char) == FV1
1388                           && THAI_chtype(new_char) == TONE) {
1389                    isReject = !ThaiFltReorderInput(ic, previous_char, new_char);
1390                }
1391            } else if (THAI_isaccepted(new_char, context_char, isc_mode)) {
1392                isReject = !ThaiFltReplaceInput(ic, new_char, symbol);
1393            }
1394        }
1395    }
1396    if (isReject) {
1397        /* reject character */
1398        XBell(ev->xkey.display, BellVolume);
1399        return True;
1400    }
1401
1402    _Xlcwcstombs(ic->core.im->core.lcd, &b->mb[b->tree[ic->private.local.composed].mb],
1403		 &b->wc[b->tree[ic->private.local.composed].wc], 10);
1404
1405    _Xlcmbstoutf8(ic->core.im->core.lcd, &b->utf8[b->tree[ic->private.local.composed].utf8],
1406		  &b->mb[b->tree[ic->private.local.composed].mb], 10);
1407
1408    /* Remember the last character inputted
1409     * (as fallback in case StringConversionCallback is not provided)
1410     */
1411    IC_SavePreviousChar(ic, new_char);
1412
1413    ev->xkey.keycode = 0;
1414    XPutBackEvent(d, ev);
1415    return True;
1416}
1417