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