1 /* 2 * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include "jlong.h" 27 #include "awtmsg.h" 28 #include "awt_AWTEvent.h" 29 #include "awt_Component.h" 30 #include "awt_Toolkit.h" 31 #include "locale_str.h" 32 #include <sun_awt_windows_WInputMethod.h> 33 #include <sun_awt_windows_WInputMethodDescriptor.h> 34 #include <java_awt_event_InputMethodEvent.h> 35 36 const UINT SYSCOMMAND_IMM = 0xF000 - 100; 37 38 /************************************************************************ 39 * WInputMethod native methods 40 */ 41 42 extern "C" { 43 44 jobject CreateLocaleObject(JNIEnv *env, const char * name); 45 HKL getDefaultKeyboardLayout(); 46 47 extern BOOL g_bUserHasChangedInputLang; 48 49 /* 50 * Class: sun_awt_windows_WInputMethod 51 * Method: createNativeContext 52 * Signature: ()I 53 */ 54 JNIEXPORT jint JNICALL 55 Java_sun_awt_windows_WInputMethod_createNativeContext(JNIEnv *env, jobject self) 56 { 57 TRY; 58 59 // use special message to call ImmCreateContext() in main thread. 60 return (jint)AwtToolkit::GetInstance().SendMessage(WM_AWT_CREATECONTEXT); 61 62 CATCH_BAD_ALLOC_RET(0); 63 } 64 65 66 /* 67 * Class: sun_awt_windows_WInputMethod 68 * Method: destroyNativeContext 69 * Signature: (I)V 70 */ 71 JNIEXPORT void JNICALL 72 Java_sun_awt_windows_WInputMethod_destroyNativeContext(JNIEnv *env, jobject self, jint context) 73 { 74 TRY_NO_VERIFY; 75 76 // use special message to call ImmDestroyContext() in main thread. 77 AwtToolkit::GetInstance().SendMessage(WM_AWT_DESTROYCONTEXT, context, 0); 78 79 CATCH_BAD_ALLOC; 80 } 81 82 83 /* 84 * Class: sun_awt_windows_WInputMethod 85 * Method: enableNativeIME 86 * Signature: (Lsun/awt/windows/WComponentPeer;I)V 87 */ 88 JNIEXPORT void JNICALL 89 Java_sun_awt_windows_WInputMethod_enableNativeIME(JNIEnv *env, jobject self, jobject peer, 90 jint context, jboolean useNativeCompWindow) 91 { 92 TRY; 93 94 jobject selfGlobalRef = env->NewGlobalRef(self); 95 jobject peerGlobalRef = env->NewGlobalRef(peer); 96 97 EnableNativeIMEStruct *enis = new EnableNativeIMEStruct; 98 99 enis->self = selfGlobalRef; 100 enis->peer = peerGlobalRef; 101 enis->context = context; 102 enis->useNativeCompWindow = useNativeCompWindow; 103 104 AwtToolkit::GetInstance().SendMessage(WM_AWT_ASSOCIATECONTEXT, 105 reinterpret_cast<WPARAM>(enis), (LPARAM)0); 106 // global refs are deleted in message handler 107 108 CATCH_BAD_ALLOC; 109 } 110 111 112 /* 113 * Class: sun_awt_windows_WInputMethod 114 * Method: disableNativeIME 115 * Signature: (Lsun/awt/windows/WComponentPeer;)V 116 */ 117 JNIEXPORT void JNICALL 118 Java_sun_awt_windows_WInputMethod_disableNativeIME(JNIEnv *env, jobject self, jobject peer) 119 { 120 TRY_NO_VERIFY; 121 122 jobject peerGlobalRef = env->NewGlobalRef(peer); 123 // self reference is not used 124 125 EnableNativeIMEStruct *enis = new EnableNativeIMEStruct; 126 enis->self = NULL; 127 enis->peer = peerGlobalRef; 128 enis->context = NULL; 129 enis->useNativeCompWindow = JNI_TRUE; 130 131 AwtToolkit::GetInstance().SendMessage(WM_AWT_ASSOCIATECONTEXT, 132 reinterpret_cast<WPARAM>(enis), (LPARAM)0); 133 // global refs are deleted in message handler 134 135 CATCH_BAD_ALLOC; 136 } 137 138 139 /* 140 * Class: sun_awt_windows_WComponentPeer 141 * Method: handleEvent 142 * Signature: (Lsun/awt/windows/WComponentPeer;Ljava/awt/AWTEvent;)V 143 */ 144 JNIEXPORT void JNICALL 145 Java_sun_awt_windows_WInputMethod_handleNativeIMEEvent(JNIEnv *env, jobject self, 146 jobject peer, jobject event) 147 { 148 TRY; 149 150 PDATA pData; 151 JNI_CHECK_PEER_RETURN(peer); 152 AwtComponent* p = (AwtComponent *)pData; 153 154 JNI_CHECK_NULL_RETURN(event, "null AWTEvent"); 155 if (env->EnsureLocalCapacity(1) < 0) { 156 return; 157 } 158 jbyteArray bdata = (jbyteArray)(env)->GetObjectField(event, AwtAWTEvent::bdataID); 159 if (bdata == 0) { 160 return; 161 } 162 MSG msg; 163 (env)->GetByteArrayRegion(bdata, 0, sizeof(MSG), (jbyte *)&msg); 164 (env)->DeleteLocalRef(bdata); 165 BOOL isConsumed = 166 (BOOL)(env)->GetBooleanField(event, AwtAWTEvent::consumedID); 167 int id = (env)->GetIntField(event, AwtAWTEvent::idID); 168 DASSERT(!safe_ExceptionOccurred(env)); 169 170 if (isConsumed || p==NULL) return; 171 172 if (id >= java_awt_event_InputMethodEvent_INPUT_METHOD_FIRST && 173 id <= java_awt_event_InputMethodEvent_INPUT_METHOD_LAST) 174 { 175 jobject peerGlobalRef = env->NewGlobalRef(peer); 176 177 // use special message to access pData on the toolkit thread 178 AwtToolkit::GetInstance().SendMessage(WM_AWT_HANDLE_NATIVE_IME_EVENT, 179 reinterpret_cast<WPARAM>(peerGlobalRef), 180 reinterpret_cast<LPARAM>(&msg)); 181 // global ref is deleted in message handler 182 183 (env)->SetBooleanField(event, AwtAWTEvent::consumedID, JNI_TRUE); 184 } 185 186 CATCH_BAD_ALLOC; 187 } 188 189 /* 190 * Class: sun_awt_windows_WInputMethod 191 * Method: notifyNativeIME 192 * Signature: (Lsun/awt/windows/WComponentPeer;I)V 193 */ 194 JNIEXPORT void JNICALL 195 Java_sun_awt_windows_WInputMethod_endCompositionNative(JNIEnv *env, jobject self, 196 jint context, jboolean flag) 197 { 198 TRY; 199 200 // TODO: currently the flag parameter is ignored and the outstanding input is 201 // always discarded. 202 // If the flag value is Java_sun_awt_windows_WInputMethod_COMMIT_INPUT, 203 // then input text should be committed. Otherwise, should be discarded. 204 // 205 // 10/29/98 - Changed to commit it according to the flag. 206 207 // use special message to call ImmNotifyIME() in main thread. 208 AwtToolkit::GetInstance().SendMessage(WM_AWT_ENDCOMPOSITION, context, 209 (LPARAM)(flag != sun_awt_windows_WInputMethod_DISCARD_INPUT)); 210 211 CATCH_BAD_ALLOC; 212 } 213 214 /* 215 * Class: sun_awt_windows_WInputMethod 216 * Method: setConversionStatus 217 * Signature: (II)V 218 */ 219 JNIEXPORT void JNICALL 220 Java_sun_awt_windows_WInputMethod_setConversionStatus(JNIEnv *env, jobject self, jint context, jint request) 221 { 222 TRY; 223 224 // use special message to call ImmSetConversionStatus() in main thread. 225 AwtToolkit::GetInstance().SendMessage(WM_AWT_SETCONVERSIONSTATUS, 226 context, 227 MAKELPARAM((WORD)request, (WORD)0)); 228 229 CATCH_BAD_ALLOC; 230 } 231 232 /* 233 * Class: sun_awt_windows_WInputMethod 234 * Method: getConversionStatus 235 * Signature: (I)I 236 */ 237 JNIEXPORT jint JNICALL 238 Java_sun_awt_windows_WInputMethod_getConversionStatus(JNIEnv *env, jobject self, jint context) 239 { 240 TRY; 241 242 // use special message to call ImmSetConversionStatus() in main thread. 243 return (jint) AwtToolkit::GetInstance().SendMessage( 244 WM_AWT_GETCONVERSIONSTATUS, context, 0); 245 246 CATCH_BAD_ALLOC_RET(0); 247 } 248 249 /* 250 * Class: sun_awt_windows_WInputMethod 251 * Method: setOpenStatus 252 * Signature: (IZ)V 253 */ 254 JNIEXPORT void JNICALL 255 Java_sun_awt_windows_WInputMethod_setOpenStatus(JNIEnv *env, jobject self, jint context, jboolean flag) 256 { 257 TRY; 258 259 // use special message to call ImmSetConversionStatus() in main thread. 260 AwtToolkit::GetInstance().SendMessage(WM_AWT_SETOPENSTATUS, 261 context, flag); 262 263 CATCH_BAD_ALLOC; 264 } 265 266 /* 267 * Class: sun_awt_windows_WInputMethod 268 * Method: getConversionStatus 269 * Signature: (I)Z 270 */ 271 JNIEXPORT jboolean JNICALL 272 Java_sun_awt_windows_WInputMethod_getOpenStatus(JNIEnv *env, jobject self, jint context) 273 { 274 TRY; 275 276 // use special message to call ImmSetConversionStatus() in main thread. 277 return (jboolean)(AwtToolkit::GetInstance().SendMessage( 278 WM_AWT_GETOPENSTATUS, 279 context, 0)); 280 CATCH_BAD_ALLOC_RET(0); 281 } 282 283 /* 284 * Class: sun_awt_windows_WInputMethod 285 * Method: getNativeLocale 286 * Signature: ()Ljava/util/Locale; 287 */ 288 JNIEXPORT jobject JNICALL Java_sun_awt_windows_WInputMethod_getNativeLocale 289 (JNIEnv *env, jclass cls) 290 { 291 TRY; 292 293 const char * javaLocaleName = getJavaIDFromLangID(AwtComponent::GetInputLanguage()); 294 if (javaLocaleName != NULL) { 295 // Now WInputMethod.currentLocale and AwtComponent::m_idLang are get sync'ed, 296 // so we can reset this flag. 297 g_bUserHasChangedInputLang = FALSE; 298 299 jobject ret = CreateLocaleObject(env, javaLocaleName); 300 free((void *)javaLocaleName); 301 return ret; 302 } else { 303 return NULL; 304 } 305 306 CATCH_BAD_ALLOC_RET(NULL); 307 } 308 309 /* 310 * Class: sun_awt_windows_WInputMethod 311 * Method: setNativeLocale 312 * Signature: (Ljava/lang/String;Z)Z 313 */ 314 JNIEXPORT jboolean JNICALL Java_sun_awt_windows_WInputMethod_setNativeLocale 315 (JNIEnv *env, jclass cls, jstring localeString, jboolean onActivate) 316 { 317 TRY; 318 319 // check if current language ID is the requested one. Note that the 320 // current language ID (returned from 'getJavaIDFromLangID') is in 321 // ASCII encoding, so we use 'GetStringUTFChars' to retrieve requested 322 // language ID from the 'localeString' object. 323 jboolean isCopy; 324 const char * requested = env->GetStringUTFChars(localeString, &isCopy); 325 CHECK_NULL_RETURN(requested, JNI_FALSE); 326 327 const char * current = getJavaIDFromLangID(AwtComponent::GetInputLanguage()); 328 if (current != NULL) { 329 if (strcmp(current, requested) == 0) { 330 env->ReleaseStringUTFChars(localeString, requested); 331 free((void *)current); 332 return JNI_TRUE; 333 } 334 free((void *)current); 335 } 336 337 // get list of available HKLs. Adding the user's preferred layout on top of the layout 338 // list which is returned by GetKeyboardLayoutList ensures to match first when 339 // looking up suitable layout. 340 int layoutCount = ::GetKeyboardLayoutList(0, NULL) + 1; // +1 for user's preferred HKL 341 HKL FAR * hKLList = (HKL FAR *)SAFE_SIZE_ARRAY_ALLOC(safe_Malloc, sizeof(HKL), layoutCount); 342 if (hKLList == NULL) { 343 env->ReleaseStringUTFChars(localeString, requested); 344 return JNI_FALSE; 345 } 346 ::GetKeyboardLayoutList(layoutCount - 1, &(hKLList[1])); 347 hKLList[0] = getDefaultKeyboardLayout(); // put user's preferred layout on top of the list 348 349 // lookup matching LangID 350 jboolean retValue = JNI_FALSE; 351 for (int i = 0; i < layoutCount; i++) { 352 const char * supported = getJavaIDFromLangID(LOWORD(hKLList[i])); 353 if (supported != NULL) { 354 if (strcmp(supported, requested) == 0) { 355 // use special message to call ActivateKeyboardLayout() in main thread. 356 if (AwtToolkit::GetInstance().SendMessage(WM_AWT_ACTIVATEKEYBOARDLAYOUT, (WPARAM)onActivate, (LPARAM)hKLList[i])) { 357 //also need to change the same keyboard layout for the Java AWT-EventQueue thread 358 AwtToolkit::activateKeyboardLayout(hKLList[i]); 359 retValue = JNI_TRUE; 360 } 361 free((void *)supported); 362 break; 363 } 364 free((void *)supported); 365 } 366 } 367 368 env->ReleaseStringUTFChars(localeString, requested); 369 free(hKLList); 370 return retValue; 371 372 CATCH_BAD_ALLOC_RET(JNI_FALSE); 373 } 374 375 /* 376 * Class: sun_awt_windows_WInputMethod 377 * Method: hideWindowsNative 378 * Signature: (Lsun/awt/windows/WComponentPeer;Z)V 379 */ 380 JNIEXPORT void JNICALL Java_sun_awt_windows_WInputMethod_setStatusWindowVisible 381 (JNIEnv *env, jobject self, jobject peer, jboolean visible) 382 { 383 /* Retrieve the default input method Window handler from AwtToolkit. 384 Windows system creates a default input method window for the 385 toolkit thread. 386 */ 387 388 HWND defaultIMEHandler = AwtToolkit::GetInstance().GetInputMethodWindow(); 389 390 if (defaultIMEHandler == NULL) 391 { 392 jobject peerGlobalRef = env->NewGlobalRef(peer); 393 394 // use special message to access pData on the toolkit thread 395 LRESULT res = AwtToolkit::GetInstance().SendMessage(WM_AWT_GET_DEFAULT_IME_HANDLER, 396 reinterpret_cast<WPARAM>(peerGlobalRef), 0); 397 // global ref is deleted in message handler 398 399 if (res == TRUE) { 400 defaultIMEHandler = AwtToolkit::GetInstance().GetInputMethodWindow(); 401 } 402 } 403 404 if (defaultIMEHandler != NULL) { 405 ::SendMessage(defaultIMEHandler, WM_IME_CONTROL, 406 visible ? IMC_OPENSTATUSWINDOW : IMC_CLOSESTATUSWINDOW, 0); 407 } 408 } 409 410 /* 411 * Class: sun_awt_windows_WInputMethod 412 * Method: openCandidateWindow 413 * Signature: (Lsun/awt/windows/WComponentPeer;II)V 414 */ 415 JNIEXPORT void JNICALL Java_sun_awt_windows_WInputMethod_openCandidateWindow 416 (JNIEnv *env, jobject self, jobject peer, jint x, jint y) 417 { 418 TRY; 419 420 PDATA pData; 421 JNI_CHECK_PEER_RETURN(peer); 422 423 jobject peerGlobalRef = env->NewGlobalRef(peer); 424 425 // WARNING! MAKELONG macro treats the given values as unsigned. 426 // This may lead to some bugs in multiscreen configurations, as 427 // coordinates can be negative numbers. So, while handling 428 // WM_AWT_OPENCANDIDATEWINDOW message in AwtToolkit, we should 429 // carefully extract right x and y values using GET_X_LPARAM and 430 // GET_Y_LPARAM, not LOWORD and HIWORD 431 // See CR 4805862, AwtToolkit::WndProc 432 433 // use special message to open candidate window in main thread. 434 AwtToolkit::GetInstance().SendMessage(WM_AWT_OPENCANDIDATEWINDOW, 435 (WPARAM)peerGlobalRef, MAKELONG(x, y)); 436 // global ref is deleted in message handler 437 438 CATCH_BAD_ALLOC; 439 } 440 441 442 /************************************************************************ 443 * WInputMethodDescriptor native methods 444 */ 445 446 /* 447 * Class: sun_awt_windows_WInputMethodDescriptor 448 * Method: getNativeAvailableLocales 449 * Signature: ()[Ljava/util/Locale; 450 */ 451 JNIEXPORT jobjectArray JNICALL Java_sun_awt_windows_WInputMethodDescriptor_getNativeAvailableLocales 452 (JNIEnv *env, jclass self) 453 { 454 TRY; 455 456 // get list of available HKLs 457 const int layoutCount = ::GetKeyboardLayoutList(0, NULL); 458 HKL FAR * hKLList = (HKL FAR *)SAFE_SIZE_ARRAY_ALLOC(safe_Malloc, sizeof(HKL), layoutCount); 459 CHECK_NULL_RETURN(hKLList, NULL); 460 ::GetKeyboardLayoutList(layoutCount, hKLList); 461 462 // get list of Java locale names while getting rid of duplicates 463 int srcIndex = 0; 464 int destIndex = 0; 465 int javaLocaleNameCount = 0; 466 int current = 0; 467 468 const char ** javaLocaleNames = (const char **)SAFE_SIZE_ARRAY_ALLOC(safe_Malloc, sizeof(char *), layoutCount); 469 if (javaLocaleNames == NULL) { 470 free(hKLList); 471 return NULL; 472 } 473 474 for (; srcIndex < layoutCount; srcIndex++) { 475 const char * srcLocaleName = getJavaIDFromLangID(LOWORD(hKLList[srcIndex])); 476 477 if (srcLocaleName == NULL) { 478 // could not find corresponding Java locale name for this HKL. 479 continue; 480 } 481 482 for (current = 0; current < destIndex; current++) { 483 if (strcmp(javaLocaleNames[current], srcLocaleName) == 0) { 484 // duplicated. ignore this HKL 485 free((void *)srcLocaleName); 486 break; 487 } 488 } 489 490 if (current == destIndex) { 491 javaLocaleNameCount++; 492 destIndex++; 493 javaLocaleNames[current] = srcLocaleName; 494 } 495 } 496 497 jobjectArray locales = NULL; 498 // convert it to an array of Java locale objects 499 jclass localeClass = env->FindClass("java/util/Locale"); 500 if (localeClass != NULL) { 501 locales = env->NewObjectArray(javaLocaleNameCount, localeClass, NULL); 502 if (locales != NULL) { 503 504 for (current = 0; current < javaLocaleNameCount; current++) { 505 jobject obj = CreateLocaleObject(env, javaLocaleNames[current]); 506 if (env->ExceptionCheck()) { 507 env->DeleteLocalRef(locales); 508 locales = NULL; 509 break; 510 } 511 env->SetObjectArrayElement(locales, 512 current, 513 obj); 514 } 515 516 } 517 env->DeleteLocalRef(localeClass); 518 } 519 520 for (current = 0; current < javaLocaleNameCount; current++) { 521 free((void *)javaLocaleNames[current]); 522 } 523 524 free(hKLList); 525 free(javaLocaleNames); 526 return locales; 527 528 CATCH_BAD_ALLOC_RET(NULL); 529 } 530 531 /* 532 * Class: sun_awt_windows_WInputMethod 533 * Method: isCompositionStringAvailable 534 * Signature: (I)Z 535 */ 536 JNIEXPORT jboolean JNICALL Java_sun_awt_windows_WInputMethod_isCompositionStringAvailable 537 (JNIEnv *env, jobject self, jint context) 538 { 539 LONG length; 540 length = ImmGetCompositionString((HIMC)IntToPtr(context), GCS_COMPSTR, NULL, 0); 541 if (length > 0) { 542 return JNI_TRUE; 543 } else { 544 return JNI_FALSE; 545 } 546 } 547 548 /** 549 * Class: sun_awt_windows_WInputMethod 550 * Method: getNativeIMMDescription 551 * Signature: ()Ljava/lang/String; 552 * 553 * This method tries to get the information about the input method associated with 554 * the current active thread. 555 * 556 */ 557 JNIEXPORT jstring JNICALL Java_sun_awt_windows_WInputMethod_getNativeIMMDescription 558 (JNIEnv *env, jobject self) { 559 560 TRY; 561 562 // Get the keyboard layout of the active thread. 563 HKL hkl = AwtComponent::GetKeyboardLayout(); 564 LPTSTR szImmDescription = NULL; 565 UINT buffSize = 0; 566 jstring infojStr = NULL; 567 568 if ((buffSize = ::ImmGetDescription(hkl, szImmDescription, 0)) > 0) { 569 szImmDescription = (LPTSTR) SAFE_SIZE_ARRAY_ALLOC(safe_Malloc, (buffSize+1), sizeof(TCHAR)); 570 571 if (szImmDescription != NULL) { 572 ImmGetDescription(hkl, szImmDescription, (buffSize+1)); 573 574 infojStr = JNU_NewStringPlatform(env, szImmDescription); 575 576 free(szImmDescription); 577 } 578 } 579 580 return infojStr; 581 582 CATCH_BAD_ALLOC_RET(NULL); 583 } 584 585 /* 586 * Create a Java locale object from its name string 587 */ 588 jobject CreateLocaleObject(JNIEnv *env, const char * name) 589 { 590 TRY; 591 592 // create Locale object 593 jobject langtagObj = env->NewStringUTF(name); 594 CHECK_NULL_RETURN(langtagObj, NULL); 595 jobject localeObj = JNU_CallStaticMethodByName(env, 596 NULL, 597 "java/util/Locale", 598 "forLanguageTag", 599 "(Ljava/lang/String;)Ljava/util/Locale;", 600 langtagObj).l; 601 env->DeleteLocalRef(langtagObj); 602 603 return localeObj; 604 605 CATCH_BAD_ALLOC_RET(NULL); 606 } 607 608 609 /* 610 * Gets user's preferred keyboard layout 611 * Warning: This is version dependent code 612 */ 613 HKL getDefaultKeyboardLayout() { 614 LONG ret; 615 HKL hkl = 0; 616 HKEY hKey; 617 BYTE szHKL[16]; 618 DWORD cbHKL = 16; 619 LPTSTR end; 620 621 ret = ::RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Keyboard Layout\\Preload"), NULL, KEY_READ, &hKey); 622 623 if (ret == ERROR_SUCCESS) { 624 ret = ::RegQueryValueEx(hKey, TEXT("1"), 0, 0, szHKL, &cbHKL); 625 626 if (ret == ERROR_SUCCESS) { 627 hkl = reinterpret_cast<HKL>(static_cast<INT_PTR>( 628 _tcstoul((LPCTSTR)szHKL, &end, 16))); 629 } 630 631 ::RegCloseKey(hKey); 632 } 633 634 return hkl; 635 } 636 } /* extern "C" */