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