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