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