1 /* 2 * Copyright (c) 2011, 2016, 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 "common.h" 27 28 #include "GlassApplication.h" 29 #include "GlassClipboard.h" 30 #include "GlassScreen.h" 31 #include "GlassWindow.h" 32 #include "Timer.h" 33 34 #include "com_sun_glass_ui_win_WinApplication.h" 35 #include "com_sun_glass_ui_win_WinSystemClipboard.h" 36 37 38 /********************************** 39 * GlassApplication 40 **********************************/ 41 42 static LPCTSTR szGlassToolkitWindow = TEXT("GlassToolkitWindowClass"); 43 44 GlassApplication *GlassApplication::pInstance = NULL; 45 bool GlassApplication::sm_shouldLeaveNestedLoop = false; 46 JGlobalRef<jobject> GlassApplication::sm_nestedLoopReturnValue; 47 48 jobject GlassApplication::sm_glassClassLoader; 49 HINSTANCE GlassApplication::hInstace = NULL; 50 unsigned int GlassApplication::sm_mouseLLHookCounter = 0; 51 HHOOK GlassApplication::sm_hMouseLLHook = NULL; 52 53 jfloat GlassApplication::overrideUIScale = -1.0f; 54 55 /* static */ 56 void GlassApplication::SetGlassClassLoader(JNIEnv *env, jobject classLoader) 57 { 58 sm_glassClassLoader = env->NewGlobalRef(classLoader); 59 } 60 61 /* 62 * Function to find a glass class using the glass class loader. All glass 63 * classes except those called from initIDs must be looked up using this 64 * function rather than FindClass so that the correct ClassLoader is used. 65 * 66 * Note that the className passed to this function must use "." rather than "/" 67 * as a package separator. 68 */ 69 /* static */ 70 jclass GlassApplication::ClassForName(JNIEnv *env, char *className) 71 { 72 // TODO: cache classCls as JNI global ref 73 jclass classCls = env->FindClass("java/lang/Class"); 74 if (CheckAndClearException(env) || !classCls) { 75 fprintf(stderr, "ClassForName error: classCls == NULL"); 76 return NULL; 77 } 78 79 // TODO: cache forNameMID as static 80 jmethodID forNameMID = 81 env->GetStaticMethodID(classCls, "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"); 82 if (CheckAndClearException(env) || !forNameMID) { 83 fprintf(stderr, "ClassForName error: forNameMID == NULL"); 84 return NULL; 85 } 86 87 jstring classNameStr = env->NewStringUTF(className); 88 if (CheckAndClearException(env) || classNameStr == NULL) { 89 fprintf(stderr, "ClassForName error: classNameStrs == NULL"); 90 return NULL; 91 } 92 93 jclass foundClass = (jclass)env->CallStaticObjectMethod(classCls, 94 forNameMID, classNameStr, JNI_TRUE, sm_glassClassLoader); 95 if (CheckAndClearException(env)) return NULL; 96 97 env->DeleteLocalRef(classNameStr); 98 env->DeleteLocalRef(classCls); 99 100 return foundClass; 101 } 102 103 GlassApplication::GlassApplication(jobject jrefThis) : BaseWnd() 104 { 105 m_grefThis = GetEnv()->NewGlobalRef(jrefThis); 106 m_clipboard = NULL; 107 m_hNextClipboardView = NULL; 108 m_mainThreadId = ::GetCurrentThreadId(); 109 110 Create(NULL, 0, 0, 400, 300, TEXT(""), 0, 0, NULL); 111 } 112 113 GlassApplication::~GlassApplication() 114 { 115 if (m_grefThis) { 116 GetEnv()->DeleteGlobalRef(m_grefThis); 117 } 118 if (m_clipboard) { 119 GetEnv()->DeleteGlobalRef(m_clipboard); 120 } 121 } 122 123 LPCTSTR GlassApplication::GetWindowClassNameSuffix() 124 { 125 return szGlassToolkitWindow; 126 } 127 128 jstring GlassApplication::GetThemeName(JNIEnv* env) 129 { 130 HIGHCONTRAST contrastInfo; 131 contrastInfo.cbSize = sizeof(HIGHCONTRAST); 132 ::SystemParametersInfo(SPI_GETHIGHCONTRAST, sizeof(HIGHCONTRAST), &contrastInfo, 0); 133 if (contrastInfo.dwFlags & HCF_HIGHCONTRASTON) { 134 jsize length = (jsize) wcslen(contrastInfo.lpszDefaultScheme); 135 jstring jstr = env->NewString((jchar*) contrastInfo.lpszDefaultScheme, length); 136 if (CheckAndClearException(env)) return NULL; 137 return jstr; 138 } 139 return NULL; 140 } 141 142 LRESULT GlassApplication::WindowProc(UINT msg, WPARAM wParam, LPARAM lParam) 143 { 144 switch (msg) { 145 case WM_DO_ACTION: 146 case WM_DO_ACTION_LATER: 147 { 148 Action * action = (Action *)wParam; 149 action->Do(); 150 if (msg == WM_DO_ACTION_LATER) { 151 delete action; 152 } 153 } 154 return 0; 155 case WM_CREATE: 156 pInstance = this; 157 STRACE(_T("GlassApplication: created.")); 158 break; 159 case WM_DESTROY: 160 //Alarm clipboard dispose if any. 161 //Please, use RegisterClipboardViewer(NULL) instead of UnregisterClipboardViewer. 162 RegisterClipboardViewer(NULL); 163 return 0; 164 case WM_NCDESTROY: 165 // pInstance is deleted in BaseWnd::StaticWindowProc 166 pInstance = NULL; 167 STRACE(_T("GlassApplication: destroyed.")); 168 return 0; 169 case WM_CHANGECBCHAIN: 170 if ((HWND)wParam == m_hNextClipboardView) { 171 m_hNextClipboardView = (HWND)lParam; 172 } else if (NULL != m_hNextClipboardView) { 173 ::SendMessage(m_hNextClipboardView, WM_CHANGECBCHAIN, wParam, lParam); 174 } 175 break; 176 case WM_DRAWCLIPBOARD: 177 if (NULL != m_clipboard) { 178 GetEnv()->CallVoidMethod(m_clipboard, midContentChanged); 179 CheckAndClearException(GetEnv()); 180 } 181 if (NULL != m_hNextClipboardView) { 182 ::SendMessage(m_hNextClipboardView, WM_DRAWCLIPBOARD, wParam, lParam); 183 } 184 break; 185 case WM_SETTINGCHANGE: 186 if ((UINT)wParam != SPI_SETWORKAREA) { 187 break; 188 } 189 // Fall through 190 case WM_DISPLAYCHANGE: 191 GlassScreen::HandleDisplayChange(); 192 break; 193 case WM_THEMECHANGED: { 194 JNIEnv* env = GetEnv(); 195 jstring themeName = GlassApplication::GetThemeName(env); 196 jboolean result = env->CallBooleanMethod(m_grefThis, javaIDs.Application.notifyThemeChangedMID, themeName); 197 if (CheckAndClearException(env)) return 1; 198 return !result; 199 } 200 } 201 return ::DefWindowProc(GetHWND(), msg, wParam, lParam); 202 } 203 204 LRESULT CALLBACK GlassApplication::MouseLLHook(int nCode, WPARAM wParam, LPARAM lParam) 205 { 206 if (nCode >= 0) { 207 switch (wParam) { 208 case WM_LBUTTONDOWN: 209 case WM_RBUTTONDOWN: 210 case WM_MBUTTONDOWN: 211 case WM_NCLBUTTONDOWN: 212 case WM_NCMBUTTONDOWN: 213 case WM_NCRBUTTONDOWN: 214 case WM_NCXBUTTONDOWN: 215 case WM_MOUSEACTIVATE: 216 { 217 POINT pt = ((MSLLHOOKSTRUCT*)lParam)->pt; 218 HWND hwnd = ::GetAncestor(::WindowFromPoint(pt), GA_ROOT); 219 220 BaseWnd *pWindow = BaseWnd::FromHandle(hwnd); 221 if (!pWindow) { 222 // A click on a non-Glass, supposedly browser window 223 GlassWindow::ResetGrab(); 224 } 225 } 226 break; 227 } 228 } 229 return ::CallNextHookEx(GlassApplication::sm_hMouseLLHook, nCode, wParam, lParam); 230 } 231 232 void GlassApplication::InstallMouseLLHook() 233 { 234 if (++GlassApplication::sm_mouseLLHookCounter == 1) { 235 GlassApplication::sm_hMouseLLHook = 236 ::SetWindowsHookEx(WH_MOUSE_LL, 237 (HOOKPROC)GlassApplication::MouseLLHook, 238 GlassApplication::GetHInstance(), 0); 239 } 240 } 241 242 void GlassApplication::UninstallMouseLLHook() 243 { 244 if (--GlassApplication::sm_mouseLLHookCounter == 0) { 245 ::UnhookWindowsHookEx(GlassApplication::sm_hMouseLLHook); 246 } 247 } 248 249 /* static */ 250 void GlassApplication::RegisterClipboardViewer(jobject clipboard) 251 { 252 JNIEnv *env = GetEnv(); 253 if (NULL != m_clipboard) { 254 //Alarm dispose. We need to release all native resources 255 //of previous instance. 256 //It means that user skipped ClipboardAssistance close. 257 JLObject _clipboard(env, env->NewLocalRef(m_clipboard)); 258 Java_com_sun_glass_ui_win_WinSystemClipboard_dispose(env, _clipboard); 259 } 260 if (NULL != clipboard) { 261 m_clipboard = env->NewGlobalRef(clipboard); 262 m_hNextClipboardView = ::SetClipboardViewer(GetHWND()) ; 263 STRACE(_T("RegisterClipboardViewer")); 264 } 265 } 266 267 /* static */ 268 void GlassApplication::UnregisterClipboardViewer() 269 { 270 if (NULL != m_hNextClipboardView) { 271 ::ChangeClipboardChain(GetHWND(), m_hNextClipboardView); 272 m_hNextClipboardView = NULL; 273 STRACE(_T("UnregisterClipboardViewer")); 274 } 275 if (NULL != m_clipboard) { 276 GetEnv()->DeleteGlobalRef(m_clipboard); 277 m_clipboard = NULL; 278 } 279 } 280 281 /* static */ 282 void GlassApplication::ExecAction(Action *action) 283 { 284 if (!pInstance) { 285 return; 286 } 287 ::SendMessage(pInstance->GetHWND(), WM_DO_ACTION, (WPARAM)action, (LPARAM)0); 288 } 289 290 /* static */ 291 void GlassApplication::ExecActionLater(Action *action) 292 { 293 if (!pInstance) { 294 delete action; 295 return; 296 } 297 ::PostMessage(pInstance->GetHWND(), WM_DO_ACTION_LATER, (WPARAM)action, (LPARAM)0); 298 } 299 300 /* static */ 301 jobject GlassApplication::EnterNestedEventLoop(JNIEnv * env) 302 { 303 sm_shouldLeaveNestedLoop = false; 304 305 MSG msg; 306 while (GlassApplication::GetInstance() 307 && !sm_shouldLeaveNestedLoop 308 && ::GetMessage(&msg, NULL, 0, 0) > 0) 309 { 310 ::TranslateMessage(&msg); 311 ::DispatchMessage(&msg); 312 } 313 314 sm_shouldLeaveNestedLoop = false; 315 316 if (!sm_nestedLoopReturnValue) { 317 return NULL; 318 } 319 320 jobject ret = env->NewLocalRef(sm_nestedLoopReturnValue); 321 sm_nestedLoopReturnValue.Attach(env, NULL); 322 return ret; 323 } 324 325 /* static */ 326 void GlassApplication::LeaveNestedEventLoop(JNIEnv * env, jobject retValue) 327 { 328 sm_nestedLoopReturnValue.Attach(env, retValue); 329 sm_shouldLeaveNestedLoop = true; 330 } 331 332 ULONG GlassApplication::s_accessibilityCount = 0; 333 334 /* static */ 335 ULONG GlassApplication::IncrementAccessibility() 336 { 337 return InterlockedIncrement(&GlassApplication::s_accessibilityCount); 338 } 339 340 /* static */ 341 ULONG GlassApplication::DecrementAccessibility() 342 { 343 return InterlockedDecrement(&GlassApplication::s_accessibilityCount); 344 } 345 346 /* static */ 347 ULONG GlassApplication::GetAccessibilityCount() 348 { 349 return GlassApplication::s_accessibilityCount; 350 } 351 352 /******************************************************* 353 * JNI section 354 *******************************************************/ 355 356 extern "C" { 357 358 BOOL WINAPI DllMain(HANDLE hinstDLL, DWORD dwReason, LPVOID lpvReserved) 359 { 360 if (dwReason == DLL_PROCESS_ATTACH) { 361 GlassApplication::SetHInstance((HINSTANCE)hinstDLL); 362 } 363 return TRUE; 364 } 365 366 /* 367 * Class: com_sun_glass_ui_win_WinApplication 368 * Method: initIDs 369 * Signature: ()V 370 */ 371 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinApplication_initIDs 372 (JNIEnv *env, jclass cls, jfloat overrideUIScale) 373 { 374 GlassApplication::overrideUIScale = overrideUIScale; 375 376 javaIDs.Application.reportExceptionMID = 377 env->GetStaticMethodID(cls, "reportException", "(Ljava/lang/Throwable;)V"); 378 ASSERT(javaIDs.Application.reportExceptionMID); 379 if (CheckAndClearException(env)) return; 380 381 javaIDs.Application.notifyThemeChangedMID = 382 env->GetMethodID(cls, "notifyThemeChanged", "(Ljava/lang/String;)Z"); 383 ASSERT(javaIDs.Application.notifyThemeChangedMID); 384 if (CheckAndClearException(env)) return; 385 386 //NOTE: substitute the cls 387 cls = (jclass)env->FindClass("java/lang/Runnable"); 388 if (CheckAndClearException(env)) return; 389 390 javaIDs.Runnable.run = env->GetMethodID(cls, "run", "()V"); 391 ASSERT(javaIDs.Runnable.run); 392 if (CheckAndClearException(env)) return; 393 } 394 395 /* 396 * Class: com_sun_glass_ui_win_WinApplication 397 * Method: _init 398 * Signature: (I)J 399 */ 400 JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_win_WinApplication__1init 401 (JNIEnv *env, jobject _this, jint awareRequested) 402 { 403 // TODO: if/when we introduce JavaFX launcher, DPI awareness should 404 // be specified in its manifest instead of this call below 405 // Specifying awareness in the manifest ensures that it happens before 406 // any system calls that might depend on it. The downside is losing 407 // the ability to control the awareness level programmatically via 408 // property settings. 409 if (IS_WINVISTA) { 410 GlassScreen::LoadDPIFuncs(awareRequested); 411 } 412 413 GlassApplication *pApp = new GlassApplication(_this); 414 415 HWND hWnd = GlassApplication::GetToolkitHWND(); 416 if (hWnd == NULL) { 417 delete pApp; 418 } 419 420 return (jlong)hWnd; 421 } 422 423 /* 424 * Class: com_sun_glass_ui_win_WinApplication 425 * Method: _setClassLoader 426 * Signature: (Ljava/lang/ClassLoader;)V 427 */ 428 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinApplication__1setClassLoader 429 (JNIEnv * env, jobject self, jobject jClassLoader) 430 { 431 GlassApplication::SetGlassClassLoader(env, jClassLoader); 432 } 433 434 /* 435 * Class: com_sun_glass_ui_win_WinApplication 436 * Method: _runLoop 437 * Signature: (Ljava/lang/Runnable;)V 438 */ 439 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinApplication__1runLoop 440 (JNIEnv * env, jobject self, jobject jLaunchable) 441 { 442 OLEHolder _ole_; 443 if (jLaunchable != NULL) { 444 env->CallVoidMethod(jLaunchable, javaIDs.Runnable.run); 445 CheckAndClearException(env); 446 } 447 448 MSG msg; 449 // The GlassApplication instance may be destroyed in a nested loop. 450 // Note that we leave the WM_QUIT message on the queue but who cares? 451 while (GlassApplication::GetInstance() && ::GetMessage(&msg, NULL, 0, 0) > 0) { 452 ::TranslateMessage(&msg); 453 ::DispatchMessage(&msg); 454 } 455 456 if (GlassApplication::GetAccessibilityCount() > 0 && !IS_WIN8) { 457 // Bug in Windows 7. For some reason, JavaFX crashes when the application 458 // is shutting down while Narrator (the screen reader) is running. It is 459 // suspected the crash happens because the event thread is finalized while 460 // accessible objects are still receiving release messages. Not all the 461 // circumstances around this crash are well understood, but calling 462 // GetMessage() one last time fixes the crash. 463 UINT_PTR timerId = ::SetTimer(NULL, NULL, 1000, NULL); 464 ::GetMessage(&msg, NULL, 0, 0); 465 ::KillTimer(NULL, timerId); 466 } 467 } 468 469 /* 470 * Class: com_sun_glass_ui_win_WinApplication 471 * Method: _terminateLoop 472 * Signature: ()V 473 */ 474 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinApplication__1terminateLoop 475 (JNIEnv * env, jobject self) 476 { 477 HWND hWnd = GlassApplication::GetToolkitHWND(); 478 if (::IsWindow(hWnd)) { 479 ::DestroyWindow(hWnd); 480 } 481 } 482 483 /* 484 * Class: com_sun_glass_ui_win_WinApplication 485 * Method: _enterNestedEventLoopImpl 486 * Signature: ()Ljava/lang/Object; 487 */ 488 JNIEXPORT jobject JNICALL Java_com_sun_glass_ui_win_WinApplication__1enterNestedEventLoopImpl 489 (JNIEnv * env, jobject self) 490 { 491 return GlassApplication::EnterNestedEventLoop(env); 492 } 493 494 /* 495 * Class: com_sun_glass_ui_win_WinApplication 496 * Method: _getHighContrastTheme 497 * Signature: ()Ljava/lang/String; 498 */ 499 JNIEXPORT jstring JNICALL Java_com_sun_glass_ui_win_WinApplication__1getHighContrastTheme 500 (JNIEnv * env, jobject self) 501 { 502 return GlassApplication::GetThemeName(env); 503 } 504 505 /* 506 * Class: com_sun_glass_ui_win_WinApplication 507 * Method: _leaveNestedEventLoopImpl 508 * Signature: (Ljava/lang/Object;)V 509 */ 510 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinApplication__1leaveNestedEventLoopImpl 511 (JNIEnv * env, jobject self, jobject retValue) 512 { 513 GlassApplication::LeaveNestedEventLoop(env, retValue); 514 } 515 516 /* 517 * Class: com_sun_glass_ui_Application 518 * Method: _invokeAndWait 519 * Signature: (Ljava/lang/Runnable;)V 520 */ 521 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinApplication__1invokeAndWait 522 (JNIEnv * env, jobject japplication, jobject runnable) 523 { 524 ENTER_MAIN_THREAD() 525 { 526 GetEnv()->CallVoidMethod(runnable, javaIDs.Runnable.run); 527 CheckAndClearException(GetEnv()); 528 } 529 DECL_jobject(runnable); 530 LEAVE_MAIN_THREAD; 531 532 ARG(runnable) = runnable; 533 PERFORM(); 534 } 535 536 /* 537 * Class: com_sun_glass_ui_Application 538 * Method: _submitForLaterInvocation 539 * Signature: (Ljava/lang/Runnable;)V 540 */ 541 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinApplication__1submitForLaterInvocation 542 (JNIEnv * env, jobject japplication, jobject runnable) 543 { 544 ENTER_MAIN_THREAD() 545 { 546 GetEnv()->CallVoidMethod(runnable, javaIDs.Runnable.run); 547 CheckAndClearException(GetEnv()); 548 } 549 DECL_jobject(runnable); 550 LEAVE_MAIN_THREAD_LATER; 551 552 ARG(runnable) = runnable; 553 PERFORM_LATER(); 554 } 555 556 /* 557 * Class: com_sun_glass_ui_Application 558 * Method: _supportsUnifiedWindows 559 * Signature: ()Z 560 */ 561 JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_win_WinApplication__1supportsUnifiedWindows 562 (JNIEnv * env, jobject japplication) 563 { 564 return (IS_WINVISTA); 565 } 566 567 /* 568 * Class: com_sun_glass_ui_Application 569 * Method: staticScreen_getScreens 570 * Signature: ()[Lcom/sun/glass/ui/Screen; 571 */ 572 JNIEXPORT jobjectArray JNICALL Java_com_sun_glass_ui_win_WinApplication_staticScreen_1getScreens 573 (JNIEnv * env, jobject japplication) 574 { 575 return GlassScreen::CreateJavaScreens(env); 576 } 577 578 } // extern "C" 579