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