1 /* 2 * Copyright (c) 1997, 2012, 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 const char * current = getJavaIDFromLangID(AwtComponent::GetInputLanguage()); 324 jboolean isCopy; 325 const char * requested = env->GetStringUTFChars(localeString, &isCopy); 326 if ((current != NULL) && (strcmp(current, requested) == 0)) { 327 env->ReleaseStringUTFChars(localeString, requested); 328 free((void *)current); 329 return JNI_TRUE; 330 } 331 332 // get list of available HKLs. Adding the user's preferred layout on top of the layout 333 // list which is returned by GetKeyboardLayoutList ensures to match first when 334 // looking up suitable layout. 335 int layoutCount = ::GetKeyboardLayoutList(0, NULL) + 1; // +1 for user's preferred HKL 336 HKL FAR * hKLList = (HKL FAR *)safe_Malloc(sizeof(HKL)*layoutCount); 337 DASSERT(!safe_ExceptionOccurred(env)); 338 ::GetKeyboardLayoutList(layoutCount - 1, &(hKLList[1])); 339 hKLList[0] = getDefaultKeyboardLayout(); // put user's preferred layout on top of the list 340 341 // lookup matching LangID 342 jboolean retValue = JNI_FALSE; 343 for (int i = 0; i < layoutCount; i++) { 344 const char * supported = getJavaIDFromLangID(LOWORD(hKLList[i])); 345 if ((supported != NULL) && (strcmp(supported, requested) == 0)) { 346 // use special message to call ActivateKeyboardLayout() in main thread. 347 if (AwtToolkit::GetInstance().SendMessage(WM_AWT_ACTIVATEKEYBOARDLAYOUT, (WPARAM)onActivate, (LPARAM)hKLList[i])) { 348 //also need to change the same keyboard layout for the Java AWT-EventQueue thread 349 AwtToolkit::activateKeyboardLayout(hKLList[i]); 350 retValue = JNI_TRUE; 351 } 352 break; 353 } 354 } 355 356 env->ReleaseStringUTFChars(localeString, requested); 357 free(hKLList); 358 free((void *)current); 359 return retValue; 360 361 CATCH_BAD_ALLOC_RET(JNI_FALSE); 362 } 363 364 /* 365 * Class: sun_awt_windows_WInputMethod 366 * Method: hideWindowsNative 367 * Signature: (Lsun/awt/windows/WComponentPeer;Z)V 368 */ 369 JNIEXPORT void JNICALL Java_sun_awt_windows_WInputMethod_setStatusWindowVisible 370 (JNIEnv *env, jobject self, jobject peer, jboolean visible) 371 { 372 /* Retrieve the default input method Window handler from AwtToolkit. 373 Windows system creates a default input method window for the 374 toolkit thread. 375 */ 376 377 HWND defaultIMEHandler = AwtToolkit::GetInstance().GetInputMethodWindow(); 378 379 if (defaultIMEHandler == NULL) 380 { 381 jobject peerGlobalRef = env->NewGlobalRef(peer); 382 383 // use special message to access pData on the toolkit thread 384 LRESULT res = AwtToolkit::GetInstance().SendMessage(WM_AWT_GET_DEFAULT_IME_HANDLER, 385 reinterpret_cast<WPARAM>(peerGlobalRef), 0); 386 // global ref is deleted in message handler 387 388 if (res == TRUE) { 389 defaultIMEHandler = AwtToolkit::GetInstance().GetInputMethodWindow(); 390 } 391 } 392 393 if (defaultIMEHandler != NULL) { 394 ::SendMessage(defaultIMEHandler, WM_IME_CONTROL, 395 visible ? IMC_OPENSTATUSWINDOW : IMC_CLOSESTATUSWINDOW, 0); 396 } 397 } 398 399 /* 400 * Class: sun_awt_windows_WInputMethod 401 * Method: openCandidateWindow 402 * Signature: (Lsun/awt/windows/WComponentPeer;II)V 403 */ 404 JNIEXPORT void JNICALL Java_sun_awt_windows_WInputMethod_openCandidateWindow 405 (JNIEnv *env, jobject self, jobject peer, jint x, jint y) 406 { 407 TRY; 408 409 PDATA pData; 410 JNI_CHECK_PEER_RETURN(peer); 411 412 jobject peerGlobalRef = env->NewGlobalRef(peer); 413 414 // WARNING! MAKELONG macro treats the given values as unsigned. 415 // This may lead to some bugs in multiscreen configurations, as 416 // coordinates can be negative numbers. So, while handling 417 // WM_AWT_OPENCANDIDATEWINDOW message in AwtToolkit, we should 418 // carefully extract right x and y values using GET_X_LPARAM and 419 // GET_Y_LPARAM, not LOWORD and HIWORD 420 // See CR 4805862, AwtToolkit::WndProc 421 422 // use special message to open candidate window in main thread. 423 AwtToolkit::GetInstance().SendMessage(WM_AWT_OPENCANDIDATEWINDOW, 424 (WPARAM)peerGlobalRef, MAKELONG(x, y)); 425 // global ref is deleted in message handler 426 427 CATCH_BAD_ALLOC; 428 } 429 430 431 /************************************************************************ 432 * WInputMethodDescriptor native methods 433 */ 434 435 /* 436 * Class: sun_awt_windows_WInputMethodDescriptor 437 * Method: getNativeAvailableLocales 438 * Signature: ()[Ljava/util/Locale; 439 */ 440 JNIEXPORT jobjectArray JNICALL Java_sun_awt_windows_WInputMethodDescriptor_getNativeAvailableLocales 441 (JNIEnv *env, jclass self) 442 { 443 TRY; 444 445 // get list of available HKLs 446 int layoutCount = ::GetKeyboardLayoutList(0, NULL); 447 HKL FAR * hKLList = (HKL FAR *)safe_Malloc(sizeof(HKL)*layoutCount); 448 DASSERT(!safe_ExceptionOccurred(env)); 449 ::GetKeyboardLayoutList(layoutCount, hKLList); 450 451 // get list of Java locale names while getting rid of duplicates 452 int srcIndex = 0; 453 int destIndex = 0; 454 int javaLocaleNameCount = 0; 455 int current = 0; 456 const char ** javaLocaleNames = (const char **)safe_Malloc(sizeof(char *)*layoutCount); 457 DASSERT(!safe_ExceptionOccurred(env)); 458 for (; srcIndex < layoutCount; srcIndex++) { 459 const char * srcLocaleName = getJavaIDFromLangID(LOWORD(hKLList[srcIndex])); 460 461 if (srcLocaleName == NULL) { 462 // could not find corresponding Java locale name for this HKL. 463 continue; 464 } 465 466 for (current = 0; current < destIndex; current++) { 467 if (strcmp(javaLocaleNames[current], srcLocaleName) == 0) { 468 // duplicated. ignore this HKL 469 break; 470 } 471 } 472 473 if (current == destIndex) { 474 javaLocaleNameCount++; 475 destIndex++; 476 javaLocaleNames[current] = srcLocaleName; 477 } 478 } 479 480 // convert it to an array of Java locale objects 481 jclass localeClass = env->FindClass("java/util/Locale"); 482 jobjectArray locales = env->NewObjectArray(javaLocaleNameCount, localeClass, NULL); 483 for (current = 0; current < javaLocaleNameCount; current++) { 484 env->SetObjectArrayElement(locales, 485 current, 486 CreateLocaleObject(env, javaLocaleNames[current])); 487 free((void *)javaLocaleNames[current]); 488 } 489 DASSERT(!safe_ExceptionOccurred(env)); 490 491 env->DeleteLocalRef(localeClass); 492 free(hKLList); 493 free(javaLocaleNames); 494 return locales; 495 496 CATCH_BAD_ALLOC_RET(NULL); 497 } 498 499 /** 500 * Class: sun_awt_windows_WInputMethod 501 * Method: getNativeIMMDescription 502 * Signature: ()Ljava/lang/String; 503 * 504 * This method tries to get the information about the input method associated with 505 * the current active thread. 506 * 507 */ 508 JNIEXPORT jstring JNICALL Java_sun_awt_windows_WInputMethod_getNativeIMMDescription 509 (JNIEnv *env, jobject self) { 510 511 TRY; 512 513 // Get the keyboard layout of the active thread. 514 HKL hkl = AwtComponent::GetKeyboardLayout(); 515 LPTSTR szImmDescription = NULL; 516 UINT buffSize = 0; 517 jstring infojStr = NULL; 518 519 if ((buffSize = ::ImmGetDescription(hkl, szImmDescription, 0)) > 0) { 520 szImmDescription = (LPTSTR) safe_Malloc((buffSize+1) * sizeof(TCHAR)); 521 522 if (szImmDescription != NULL) { 523 ImmGetDescription(hkl, szImmDescription, (buffSize+1)); 524 525 infojStr = JNU_NewStringPlatform(env, szImmDescription); 526 527 free(szImmDescription); 528 } 529 } 530 531 return infojStr; 532 533 CATCH_BAD_ALLOC_RET(NULL); 534 } 535 536 /* 537 * Create a Java locale object from its name string 538 */ 539 jobject CreateLocaleObject(JNIEnv *env, const char * name) 540 { 541 TRY; 542 543 // create Locale object 544 jobject langtagObj = env->NewStringUTF(name); 545 jobject localeObj = JNU_CallStaticMethodByName(env, 546 NULL, 547 "java/util/Locale", 548 "forLanguageTag", 549 "(Ljava/lang/String;)Ljava/util/Locale;", 550 langtagObj).l; 551 env->DeleteLocalRef(langtagObj); 552 553 return localeObj; 554 555 CATCH_BAD_ALLOC_RET(NULL); 556 } 557 558 559 /* 560 * Gets user's preferred keyboard layout 561 * Warning: This is version dependent code 562 */ 563 HKL getDefaultKeyboardLayout() { 564 LONG ret; 565 HKL hkl = 0; 566 HKEY hKey; 567 BYTE szHKL[16]; 568 DWORD cbHKL = 16; 569 LPTSTR end; 570 571 ret = ::RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Keyboard Layout\\Preload"), NULL, KEY_READ, &hKey); 572 573 if (ret == ERROR_SUCCESS) { 574 ret = ::RegQueryValueEx(hKey, TEXT("1"), 0, 0, szHKL, &cbHKL); 575 576 if (ret == ERROR_SUCCESS) { 577 hkl = reinterpret_cast<HKL>(static_cast<INT_PTR>( 578 _tcstoul((LPCTSTR)szHKL, &end, 16))); 579 } 580 581 ::RegCloseKey(hKey); 582 } 583 584 return hkl; 585 } 586 } /* extern "C" */