1/* 2 * tkUnixKey.c -- 3 * 4 * This file contains routines for dealing with international keyboard 5 * input. 6 * 7 * Copyright (c) 1997 by Sun Microsystems, Inc. 8 * 9 * See the file "license.terms" for information on usage and redistribution of 10 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 11 * 12 * RCS: @(#) $Id$ 13 */ 14 15#include "tkInt.h" 16 17/* 18 * Prototypes for local functions defined in this file: 19 */ 20 21/* 22 *---------------------------------------------------------------------- 23 * 24 * Tk_SetCaretPos -- 25 * 26 * This enables correct placement of the XIM caret. This is called by 27 * widgets to indicate their cursor placement. This is currently only 28 * used for over-the-spot XIM. 29 * 30 *---------------------------------------------------------------------- 31 */ 32 33void 34Tk_SetCaretPos( 35 Tk_Window tkwin, 36 int x, 37 int y, 38 int height) 39{ 40 TkWindow *winPtr = (TkWindow *) tkwin; 41 TkDisplay *dispPtr = winPtr->dispPtr; 42 43 if ( dispPtr->caret.winPtr == winPtr 44 && dispPtr->caret.x == x 45 && dispPtr->caret.y == y 46 && dispPtr->caret.height == height) 47 { 48 return; 49 } 50 51 dispPtr->caret.winPtr = winPtr; 52 dispPtr->caret.x = x; 53 dispPtr->caret.y = y; 54 dispPtr->caret.height = height; 55 56#ifdef TK_USE_INPUT_METHODS 57 /* 58 * Adjust the XIM caret position. 59 */ 60 if ( (dispPtr->flags & TK_DISPLAY_USE_IM) 61 && (dispPtr->inputStyle & XIMPreeditPosition) 62 && (winPtr->inputContext != NULL) ) 63 { 64 XVaNestedList preedit_attr; 65 XPoint spot; 66 67 spot.x = dispPtr->caret.x; 68 spot.y = dispPtr->caret.y + dispPtr->caret.height; 69 preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL); 70 XSetICValues(winPtr->inputContext, 71 XNPreeditAttributes, preedit_attr, 72 NULL); 73 XFree(preedit_attr); 74 } 75#endif 76} 77 78/* 79 *---------------------------------------------------------------------- 80 * 81 * TkpGetString -- 82 * 83 * Retrieve the UTF string associated with a keyboard event. 84 * 85 * Results: 86 * Returns the UTF string. 87 * 88 * Side effects: 89 * Stores the input string in the specified Tcl_DString. Modifies the 90 * internal input state. This routine can only be called once for a given 91 * event. 92 * 93 *---------------------------------------------------------------------- 94 */ 95 96char * 97TkpGetString( 98 TkWindow *winPtr, /* Window where event occurred */ 99 XEvent *eventPtr, /* X keyboard event. */ 100 Tcl_DString *dsPtr) /* Initialized, empty string to hold result. */ 101{ 102 int len; 103 Tcl_DString buf; 104 TkKeyEvent *kePtr = (TkKeyEvent *) eventPtr; 105 106 /* 107 * If we have the value cached already, use it now. [Bug 1373712] 108 */ 109 110 if (kePtr->charValuePtr != NULL) { 111 Tcl_DStringSetLength(dsPtr, kePtr->charValueLen); 112 memcpy(Tcl_DStringValue(dsPtr), kePtr->charValuePtr, 113 (unsigned) kePtr->charValueLen+1); 114 return Tcl_DStringValue(dsPtr); 115 } 116 117#ifdef TK_USE_INPUT_METHODS 118 if ((winPtr->dispPtr->flags & TK_DISPLAY_USE_IM) 119 && (winPtr->inputContext != NULL) 120 && (eventPtr->type == KeyPress)) 121 { 122 Status status; 123 124#if X_HAVE_UTF8_STRING 125 Tcl_DStringSetLength(dsPtr, TCL_DSTRING_STATIC_SIZE-1); 126 len = Xutf8LookupString(winPtr->inputContext, &eventPtr->xkey, 127 Tcl_DStringValue(dsPtr), Tcl_DStringLength(dsPtr), 128 NULL, &status); 129 130 if (status == XBufferOverflow) { /* Expand buffer and try again */ 131 Tcl_DStringSetLength(dsPtr, len); 132 len = Xutf8LookupString(winPtr->inputContext, &eventPtr->xkey, 133 Tcl_DStringValue(dsPtr), Tcl_DStringLength(dsPtr), 134 NULL, &status); 135 } 136 if ((status != XLookupChars) && (status != XLookupBoth)) { 137 len = 0; 138 } 139 Tcl_DStringSetLength(dsPtr, len); 140#else /* !X_HAVE_UTF8_STRING */ 141 /* 142 * Overallocate the dstring to the maximum stack amount. 143 */ 144 145 Tcl_DStringInit(&buf); 146 Tcl_DStringSetLength(&buf, TCL_DSTRING_STATIC_SIZE-1); 147 148 len = XmbLookupString(winPtr->inputContext, &eventPtr->xkey, 149 Tcl_DStringValue(&buf), Tcl_DStringLength(&buf), NULL, 150 &status); 151 152 /* 153 * If the buffer wasn't big enough, grow the buffer and try again. 154 */ 155 156 if (status == XBufferOverflow) { 157 Tcl_DStringSetLength(&buf, len); 158 len = XmbLookupString(winPtr->inputContext, &eventPtr->xkey, 159 Tcl_DStringValue(&buf), len, NULL, &status); 160 } 161 if ((status != XLookupChars) && (status != XLookupBoth)) { 162 len = 0; 163 } 164 165 Tcl_DStringSetLength(&buf, len); 166 Tcl_ExternalToUtfDString(NULL, Tcl_DStringValue(&buf), len, dsPtr); 167 Tcl_DStringFree(&buf); 168#endif /* X_HAVE_UTF8_STRING */ 169 } else 170#endif /* TK_USE_INPUT_METHODS */ 171 { 172 /* 173 * Fall back to convert a keyboard event to a UTF-8 string using 174 * XLookupString. This is used when input methods are turned off and 175 * for KeyRelease events. 176 * 177 * Note: XLookupString() normally returns a single ISO Latin 1 or 178 * ASCII control character. 179 */ 180 181 Tcl_DStringInit(&buf); 182 Tcl_DStringSetLength(&buf, TCL_DSTRING_STATIC_SIZE-1); 183 len = XLookupString(&eventPtr->xkey, Tcl_DStringValue(&buf), 184 TCL_DSTRING_STATIC_SIZE, 0, 0); 185 Tcl_DStringValue(&buf)[len] = '\0'; 186 187 if (len == 1) { 188 len = Tcl_UniCharToUtf((unsigned char) Tcl_DStringValue(&buf)[0], 189 Tcl_DStringValue(dsPtr)); 190 Tcl_DStringSetLength(dsPtr, len); 191 } else { 192 /* 193 * len > 1 should only happen if someone has called XRebindKeysym. 194 * Assume UTF-8. 195 */ 196 197 Tcl_DStringSetLength(dsPtr, len); 198 strncpy(Tcl_DStringValue(dsPtr), Tcl_DStringValue(&buf), len); 199 } 200 } 201 202 /* 203 * Cache the string in the event so that if/when we return to this 204 * function, we will be able to produce it without asking X. This stops us 205 * from having to reenter the XIM engine. [Bug 1373712] 206 */ 207 208 kePtr->charValuePtr = ckalloc((unsigned) len + 1); 209 kePtr->charValueLen = len; 210 memcpy(kePtr->charValuePtr, Tcl_DStringValue(dsPtr), (unsigned) len + 1); 211 return Tcl_DStringValue(dsPtr); 212} 213 214/* 215 * When mapping from a keysym to a keycode, need information about the 216 * modifier state that should be used so that when they call XKeycodeToKeysym 217 * taking into account the xkey.state, they will get back the original keysym. 218 */ 219 220void 221TkpSetKeycodeAndState( 222 Tk_Window tkwin, 223 KeySym keySym, 224 XEvent *eventPtr) 225{ 226 Display *display; 227 int state; 228 KeyCode keycode; 229 230 display = Tk_Display(tkwin); 231 232 if (keySym == NoSymbol) { 233 keycode = 0; 234 } else { 235 keycode = XKeysymToKeycode(display, keySym); 236 } 237 if (keycode != 0) { 238 for (state = 0; state < 4; state++) { 239 if (XKeycodeToKeysym(display, keycode, state) == keySym) { 240 if (state & 1) { 241 eventPtr->xkey.state |= ShiftMask; 242 } 243 if (state & 2) { 244 TkDisplay *dispPtr; 245 246 dispPtr = ((TkWindow *) tkwin)->dispPtr; 247 eventPtr->xkey.state |= dispPtr->modeModMask; 248 } 249 break; 250 } 251 } 252 } 253 eventPtr->xkey.keycode = keycode; 254} 255 256/* 257 *---------------------------------------------------------------------- 258 * 259 * TkpGetKeySym -- 260 * 261 * Given an X KeyPress or KeyRelease event, map the keycode in the event 262 * into a KeySym. 263 * 264 * Results: 265 * The return value is the KeySym corresponding to eventPtr, or NoSymbol 266 * if no matching Keysym could be found. 267 * 268 * Side effects: 269 * In the first call for a given display, keycode-to-KeySym maps get 270 * loaded. 271 * 272 *---------------------------------------------------------------------- 273 */ 274 275KeySym 276TkpGetKeySym( 277 TkDisplay *dispPtr, /* Display in which to map keycode. */ 278 XEvent *eventPtr) /* Description of X event. */ 279{ 280 KeySym sym; 281 int index; 282 283 /* 284 * Refresh the mapping information if it's stale 285 */ 286 287 if (dispPtr->bindInfoStale) { 288 TkpInitKeymapInfo(dispPtr); 289 } 290 291 /* 292 * Figure out which of the four slots in the keymap vector to use for this 293 * key. Refer to Xlib documentation for more info on how this computation 294 * works. 295 */ 296 297 index = 0; 298 if (eventPtr->xkey.state & dispPtr->modeModMask) { 299 index = 2; 300 } 301 if ((eventPtr->xkey.state & ShiftMask) 302 || ((dispPtr->lockUsage != LU_IGNORE) 303 && (eventPtr->xkey.state & LockMask))) { 304 index += 1; 305 } 306 sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode, index); 307 308 /* 309 * Special handling: if the key was shifted because of Lock, but lock is 310 * only caps lock, not shift lock, and the shifted keysym isn't upper-case 311 * alphabetic, then switch back to the unshifted keysym. 312 */ 313 314 if ((index & 1) && !(eventPtr->xkey.state & ShiftMask) 315 && (dispPtr->lockUsage == LU_CAPS)) { 316 if (!(((sym >= XK_A) && (sym <= XK_Z)) 317 || ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis)) 318 || ((sym >= XK_Ooblique) && (sym <= XK_Thorn)))) { 319 index &= ~1; 320 sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode, 321 index); 322 } 323 } 324 325 /* 326 * Another bit of special handling: if this is a shifted key and there is 327 * no keysym defined, then use the keysym for the unshifted key. 328 */ 329 330 if ((index & 1) && (sym == NoSymbol)) { 331 sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode, 332 index & ~1); 333 } 334 return sym; 335} 336 337/* 338 *-------------------------------------------------------------- 339 * 340 * TkpInitKeymapInfo -- 341 * 342 * This function is invoked to scan keymap information to recompute stuff 343 * that's important for binding, such as the modifier key (if any) that 344 * corresponds to "mode switch". 345 * 346 * Results: 347 * None. 348 * 349 * Side effects: 350 * Keymap-related information in dispPtr is updated. 351 * 352 *-------------------------------------------------------------- 353 */ 354 355void 356TkpInitKeymapInfo( 357 TkDisplay *dispPtr) /* Display for which to recompute keymap 358 * information. */ 359{ 360 XModifierKeymap *modMapPtr; 361 KeyCode *codePtr; 362 KeySym keysym; 363 int count, i, j, max, arraySize; 364#define KEYCODE_ARRAY_SIZE 20 365 366 dispPtr->bindInfoStale = 0; 367 modMapPtr = XGetModifierMapping(dispPtr->display); 368 369 /* 370 * Check the keycodes associated with the Lock modifier. If any of them is 371 * associated with the XK_Shift_Lock modifier, then Lock has to be 372 * interpreted as Shift Lock, not Caps Lock. 373 */ 374 375 dispPtr->lockUsage = LU_IGNORE; 376 codePtr = modMapPtr->modifiermap + modMapPtr->max_keypermod*LockMapIndex; 377 for (count = modMapPtr->max_keypermod; count > 0; count--, codePtr++) { 378 if (*codePtr == 0) { 379 continue; 380 } 381 keysym = XKeycodeToKeysym(dispPtr->display, *codePtr, 0); 382 if (keysym == XK_Shift_Lock) { 383 dispPtr->lockUsage = LU_SHIFT; 384 break; 385 } 386 if (keysym == XK_Caps_Lock) { 387 dispPtr->lockUsage = LU_CAPS; 388 break; 389 } 390 } 391 392 /* 393 * Look through the keycodes associated with modifiers to see if the the 394 * "mode switch", "meta", or "alt" keysyms are associated with any 395 * modifiers. If so, remember their modifier mask bits. 396 */ 397 398 dispPtr->modeModMask = 0; 399 dispPtr->metaModMask = 0; 400 dispPtr->altModMask = 0; 401 codePtr = modMapPtr->modifiermap; 402 max = 8*modMapPtr->max_keypermod; 403 for (i = 0; i < max; i++, codePtr++) { 404 if (*codePtr == 0) { 405 continue; 406 } 407 keysym = XKeycodeToKeysym(dispPtr->display, *codePtr, 0); 408 if (keysym == XK_Mode_switch) { 409 dispPtr->modeModMask |= ShiftMask << (i/modMapPtr->max_keypermod); 410 } 411 if ((keysym == XK_Meta_L) || (keysym == XK_Meta_R)) { 412 dispPtr->metaModMask |= ShiftMask << (i/modMapPtr->max_keypermod); 413 } 414 if ((keysym == XK_Alt_L) || (keysym == XK_Alt_R)) { 415 dispPtr->altModMask |= ShiftMask << (i/modMapPtr->max_keypermod); 416 } 417 } 418 419 /* 420 * Create an array of the keycodes for all modifier keys. 421 */ 422 423 if (dispPtr->modKeyCodes != NULL) { 424 ckfree((char *) dispPtr->modKeyCodes); 425 } 426 dispPtr->numModKeyCodes = 0; 427 arraySize = KEYCODE_ARRAY_SIZE; 428 dispPtr->modKeyCodes = (KeyCode *) 429 ckalloc((unsigned) (KEYCODE_ARRAY_SIZE * sizeof(KeyCode))); 430 for (i = 0, codePtr = modMapPtr->modifiermap; i < max; i++, codePtr++) { 431 if (*codePtr == 0) { 432 continue; 433 } 434 435 /* 436 * Make sure that the keycode isn't already in the array. 437 */ 438 439 for (j = 0; j < dispPtr->numModKeyCodes; j++) { 440 if (dispPtr->modKeyCodes[j] == *codePtr) { 441 goto nextModCode; 442 } 443 } 444 if (dispPtr->numModKeyCodes >= arraySize) { 445 KeyCode *new; 446 447 /* 448 * Ran out of space in the array; grow it. 449 */ 450 451 arraySize *= 2; 452 new = (KeyCode *) 453 ckalloc((unsigned) (arraySize * sizeof(KeyCode))); 454 memcpy(new, dispPtr->modKeyCodes, 455 (dispPtr->numModKeyCodes * sizeof(KeyCode))); 456 ckfree((char *) dispPtr->modKeyCodes); 457 dispPtr->modKeyCodes = new; 458 } 459 dispPtr->modKeyCodes[dispPtr->numModKeyCodes] = *codePtr; 460 dispPtr->numModKeyCodes++; 461 nextModCode: 462 continue; 463 } 464 XFreeModifiermap(modMapPtr); 465} 466 467/* 468 * Local Variables: 469 * mode: c 470 * c-basic-offset: 4 471 * fill-column: 78 472 * End: 473 */ 474