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