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