1 /*
   2  * Copyright (c) 1996, 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 /*
  27  * The Toolkit class has two functions: it instantiates the AWT
  28  * ToolkitPeer's native methods, and provides the DLL's core functions.
  29  *
  30  * There are two ways this DLL can be used: either as a dynamically-
  31  * loaded Java native library from the interpreter, or by a Windows-
  32  * specific app.  The first manner requires that the Toolkit provide
  33  * all support needed so the app can function as a first-class Windows
  34  * app, while the second assumes that the app will provide that
  35  * functionality.  Which mode this DLL functions in is determined by
  36  * which initialization paradigm is used. If the Toolkit is constructed
  37  * normally, then the Toolkit will have its own pump. If it is explicitly
  38  * initialized for an embedded environment (via a static method on
  39  * sun.awt.windows.WToolkit), then it will rely on an external message
  40  * pump.
  41  *
  42  * The most basic functionality needed is a Windows message pump (also
  43  * known as a message loop).  When an Java app is started as a console
  44  * app by the interpreter, the Toolkit needs to provide that message
  45  * pump if the AWT is dynamically loaded.
  46  */
  47 
  48 #ifndef AWT_TOOLKIT_H
  49 #define AWT_TOOLKIT_H
  50 
  51 #include "awt.h"
  52 #include "awtmsg.h"
  53 #include "Trace.h"
  54 
  55 #include "sun_awt_windows_WToolkit.h"
  56 
  57 class AwtObject;
  58 class AwtDialog;
  59 class AwtDropTarget;
  60 
  61 typedef VOID (CALLBACK* IDLEPROC)(VOID);
  62 typedef BOOL (CALLBACK* PEEKMESSAGEPROC)(MSG&);
  63 
  64 // Struct for _WInputMethod_enable|disableNativeIME method
  65 struct EnableNativeIMEStruct {
  66     jobject self;
  67     jobject peer;
  68     jint context;
  69     jboolean useNativeCompWindow;
  70 };
  71 
  72 /*
  73  * class JNILocalFrame
  74  * Push/PopLocalFrame helper
  75  */
  76 class JNILocalFrame {
  77   public:
  78     INLINE JNILocalFrame(JNIEnv *env, int size) {
  79         m_env = env;
  80         int result = m_env->PushLocalFrame(size);
  81         if (result < 0) {
  82             DASSERT(FALSE);
  83             throw std::bad_alloc();
  84         }
  85     }
  86     INLINE ~JNILocalFrame() { m_env->PopLocalFrame(NULL); }
  87   private:
  88     JNIEnv* m_env;
  89 };
  90 
  91 /*
  92  * class CriticalSection
  93  * ~~~~~ ~~~~~~~~~~~~~~~~
  94  * Lightweight intra-process thread synchronization. Can only be used with
  95  * other critical sections, and only within the same process.
  96  */
  97 class CriticalSection {
  98   public:
  99     INLINE  CriticalSection() { ::InitializeCriticalSection(&rep); }
 100     INLINE ~CriticalSection() { ::DeleteCriticalSection(&rep); }
 101 
 102     class Lock {
 103       public:
 104         INLINE Lock(const CriticalSection& cs) : critSec(cs) {
 105             (const_cast<CriticalSection &>(critSec)).Enter();
 106         }
 107         INLINE ~Lock() {
 108             (const_cast<CriticalSection &>(critSec)).Leave();
 109         }
 110       private:
 111         const CriticalSection& critSec;
 112     };
 113     friend class Lock;
 114 
 115   private:
 116     CRITICAL_SECTION rep;
 117 
 118     CriticalSection(const CriticalSection&);
 119     const CriticalSection& operator =(const CriticalSection&);
 120 
 121   public:
 122     virtual void Enter() {
 123         ::EnterCriticalSection(&rep);
 124     }
 125     virtual BOOL TryEnter() {
 126         return ::TryEnterCriticalSection(&rep);
 127     }
 128     virtual void Leave() {
 129         ::LeaveCriticalSection(&rep);
 130     }
 131 };
 132 
 133 // Macros for using CriticalSection objects that help trace
 134 // lock/unlock actions
 135 
 136 /* Use THIS_FILE when it is available. */
 137 #ifndef THIS_FILE
 138     #define THIS_FILE __FILE__
 139 #endif
 140 
 141 #define CRITICAL_SECTION_ENTER(cs) { \
 142     J2dTraceLn4(J2D_TRACE_VERBOSE2, \
 143                 "CS.Wait:  tid, cs, file, line = 0x%x, 0x%x, %s, %d", \
 144                 GetCurrentThreadId(), &(cs), THIS_FILE, __LINE__); \
 145     (cs).Enter(); \
 146     J2dTraceLn4(J2D_TRACE_VERBOSE2, \
 147                 "CS.Enter: tid, cs, file, line = 0x%x, 0x%x, %s, %d", \
 148                 GetCurrentThreadId(), &(cs), THIS_FILE, __LINE__); \
 149 }
 150 
 151 #define CRITICAL_SECTION_LEAVE(cs) { \
 152     J2dTraceLn4(J2D_TRACE_VERBOSE2, \
 153                 "CS.Leave: tid, cs, file, line = 0x%x, 0x%x, %s, %d", \
 154                 GetCurrentThreadId(), &(cs), THIS_FILE, __LINE__); \
 155     (cs).Leave(); \
 156     J2dTraceLn4(J2D_TRACE_VERBOSE2, \
 157                 "CS.Left:  tid, cs, file, line = 0x%x, 0x%x, %s, %d", \
 158                 GetCurrentThreadId(), &(cs), THIS_FILE, __LINE__); \
 159 }
 160 
 161 // Redefine WinAPI values related to touch input, if OS < Windows 7.
 162 #if (!defined(WINVER) || ((WINVER) < 0x0601))
 163     typedef UINT32 POINTER_FLAGS;
 164     typedef UINT32 TOUCH_FLAGS;
 165     typedef UINT32 TOUCH_MASK;
 166 
 167     #define GET_POINTERID_WPARAM(wParam) (LOWORD (wParam))
 168 
 169     #define WM_POINTERENTER                 0x0249
 170     #define WM_NCPOINTERDOWN                0x0242
 171     #define WM_NCPOINTERUP                  0x0243
 172     #define WM_NCPOINTERUPDATE              0x0241
 173     #define WM_POINTERACTIVATE              0x024B
 174     #define WM_POINTERCAPTURECHANGED        0x024C
 175     #define WM_POINTERDOWN                  0x0246
 176     #define WM_POINTERLEAVE                 0x024A
 177     #define WM_POINTERUP                    0x0247
 178     #define WM_POINTERUPDATE                0x0245
 179 
 180     typedef enum _POINTER_BUTTON_CHANGE_TYPE {
 181       POINTER_CHANGE_NONE               ,
 182       POINTER_CHANGE_FIRSTBUTTON_DOWN   ,
 183       POINTER_CHANGE_FIRSTBUTTON_UP     ,
 184       POINTER_CHANGE_SECONDBUTTON_DOWN  ,
 185       POINTER_CHANGE_SECONDBUTTON_UP    ,
 186       POINTER_CHANGE_THIRDBUTTON_DOWN   ,
 187       POINTER_CHANGE_THIRDBUTTON_UP     ,
 188       POINTER_CHANGE_FOURTHBUTTON_DOWN  ,
 189       POINTER_CHANGE_FOURTHBUTTON_UP    ,
 190       POINTER_CHANGE_FIFTHBUTTON_DOWN   ,
 191       POINTER_CHANGE_FIFTHBUTTON_UP     ,
 192       } POINTER_BUTTON_CHANGE_TYPE;
 193 
 194     typedef enum tagPOINTER_INPUT_TYPE {
 195       PT_POINTER   = 0x00000001,
 196       PT_TOUCH     = 0x00000002,
 197       PT_PEN       = 0x00000003,
 198       PT_MOUSE     = 0x00000004,
 199       PT_TOUCHPAD  = 0x00000005
 200     } POINTER_INPUT_TYPE;
 201 
 202     typedef struct tagPOINTER_INFO {
 203       POINTER_INPUT_TYPE         pointerType;
 204       UINT32                     pointerId;
 205       UINT32                     frameId;
 206       POINTER_FLAGS              pointerFlags;
 207       HANDLE                     sourceDevice;
 208       HWND                       hwndTarget;
 209       POINT                      ptPixelLocation;
 210       POINT                      ptHimetricLocation;
 211       POINT                      ptPixelLocationRaw;
 212       POINT                      ptHimetricLocationRaw;
 213       DWORD                      dwTime;
 214       UINT32                     historyCount;
 215       INT32                      inputData;
 216       DWORD                      dwKeyStates;
 217       UINT64                     PerformanceCount;
 218       POINTER_BUTTON_CHANGE_TYPE ButtonChangeType;
 219     } POINTER_INFO;
 220 #endif
 221 
 222 /************************************************************************
 223  * AwtToolkit class
 224  */
 225 
 226 class AwtToolkit {
 227 public:
 228     enum {
 229         KB_STATE_SIZE = 256
 230     };
 231 
 232     /* java.awt.Toolkit method ids */
 233     static jmethodID getDefaultToolkitMID;
 234     static jmethodID getFontMetricsMID;
 235     static jmethodID insetsMID;
 236 
 237     /* sun.awt.windows.WToolkit ids */
 238     static jmethodID windowsSettingChangeMID;
 239     static jmethodID displayChangeMID;
 240 
 241     static jmethodID userSessionMID;
 242     static jmethodID systemSleepMID;
 243 
 244     BOOL m_isDynamicLayoutSet;
 245 
 246     AwtToolkit();
 247     ~AwtToolkit();
 248 
 249     BOOL Initialize(BOOL localPump);
 250     BOOL Dispose();
 251 
 252     void SetDynamicLayout(BOOL dynamic);
 253     BOOL IsDynamicLayoutSet();
 254     BOOL IsDynamicLayoutSupported();
 255     BOOL IsDynamicLayoutActive();
 256     BOOL areExtraMouseButtonsEnabled();
 257     void setExtraMouseButtonsEnabled(BOOL enable);
 258     static UINT GetNumberOfButtons();
 259 
 260     bool IsWin8OrLater();
 261     BOOL TIEnableMouseInPointer(BOOL enable);
 262     BOOL TIGetPointerInfo(UINT32 pointerID, POINTER_INFO *pointerInfo);
 263 
 264     INLINE BOOL localPump() { return m_localPump; }
 265     INLINE BOOL VerifyComponents() { return FALSE; } // TODO: Use new DebugHelper class to set this flag
 266     INLINE HWND GetHWnd() { return m_toolkitHWnd; }
 267 
 268     INLINE HMODULE GetModuleHandle() { return m_dllHandle; }
 269     INLINE void SetModuleHandle(HMODULE h) { m_dllHandle = h; }
 270 
 271     INLINE static DWORD MainThread() { return GetInstance().m_mainThreadId; }
 272     INLINE void VerifyActive() throw (awt_toolkit_shutdown) {
 273         if (!m_isActive && m_mainThreadId != ::GetCurrentThreadId()) {
 274             throw awt_toolkit_shutdown();
 275         }
 276     }
 277     INLINE BOOL IsDisposed() { return m_isDisposed; }
 278     static UINT GetMouseKeyState();
 279     static void GetKeyboardState(PBYTE keyboardState);
 280 
 281     static ATOM RegisterClass();
 282     static void UnregisterClass();
 283     INLINE LRESULT SendMessage(UINT msg, WPARAM wParam=0, LPARAM lParam=0) {
 284         if (!m_isDisposed) {
 285             return ::SendMessage(GetHWnd(), msg, wParam, lParam);
 286         } else {
 287             return NULL;
 288         }
 289     }
 290     static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
 291                                     LPARAM lParam);
 292     static LRESULT CALLBACK GetMessageFilter(int code, WPARAM wParam,
 293                                              LPARAM lParam);
 294     static LRESULT CALLBACK ForegroundIdleFilter(int code, WPARAM wParam,
 295                                                  LPARAM lParam);
 296     static LRESULT CALLBACK MouseLowLevelHook(int code, WPARAM wParam,
 297             LPARAM lParam);
 298 
 299     INLINE static AwtToolkit& GetInstance() { return theInstance; }
 300     INLINE void SetPeer(JNIEnv *env, jobject wToolkit) {
 301         AwtToolkit &tk = AwtToolkit::GetInstance();
 302         if (tk.m_peer != NULL) {
 303             env->DeleteGlobalRef(tk.m_peer);
 304         }
 305         tk.m_peer = (wToolkit != NULL) ? env->NewGlobalRef(wToolkit) : NULL;
 306     }
 307 
 308     INLINE jobject GetPeer() {
 309         return m_peer;
 310     }
 311 
 312     // is this thread the main thread?
 313 
 314     INLINE static BOOL IsMainThread() {
 315         return GetInstance().m_mainThreadId == ::GetCurrentThreadId();
 316     }
 317 
 318     // post a message to the message pump thread
 319 
 320     INLINE BOOL PostMessage(UINT msg, WPARAM wp=0, LPARAM lp=0) {
 321         return ::PostMessage(GetHWnd(), msg, wp, lp);
 322     }
 323 
 324     // cause the message pump thread to call the function synchronously now!
 325 
 326     INLINE void * InvokeFunction(void*(*ftn)(void)) {
 327         return (void *)SendMessage(WM_AWT_INVOKE_VOID_METHOD, (WPARAM)ftn, 0);
 328     }
 329     INLINE void InvokeFunction(void (*ftn)(void)) {
 330         InvokeFunction((void*(*)(void))ftn);
 331     }
 332     INLINE void * InvokeFunction(void*(*ftn)(void *), void* param) {
 333         return (void *)SendMessage(WM_AWT_INVOKE_METHOD, (WPARAM)ftn,
 334                                    (LPARAM)param);
 335     }
 336     INLINE void InvokeFunction(void (*ftn)(void *), void* param) {
 337         InvokeFunction((void*(*)(void*))ftn, param);
 338     }
 339 
 340     INLINE CriticalSection &GetSyncCS() { return m_Sync; }
 341 
 342     void *SyncCall(void*(*ftn)(void *), void* param);
 343     void SyncCall(void (*ftn)(void *), void *param);
 344     void *SyncCall(void *(*ftn)(void));
 345     void SyncCall(void (*ftn)(void));
 346 
 347     // cause the message pump thread to call the function later ...
 348 
 349     INLINE void InvokeFunctionLater(void (*ftn)(void *), void* param) {
 350         if (!PostMessage(WM_AWT_INVOKE_METHOD, (WPARAM)ftn, (LPARAM)param)) {
 351             JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 352             JNU_ThrowInternalError(env, "Message not posted, native event queue may be full.");
 353         }
 354     }
 355 
 356    // cause the message pump thread to synchronously synchronize on the handle
 357 
 358     INLINE void WaitForSingleObject(HANDLE handle) {
 359         SendMessage(WM_AWT_WAIT_FOR_SINGLE_OBJECT, 0, (LPARAM)handle);
 360     }
 361 
 362     /*
 363      * Create an AwtXxxx C++ component using a given factory
 364      */
 365     typedef void (*ComponentFactory)(void*, void*);
 366     static void CreateComponent(void* hComponent, void* hParent,
 367                                 ComponentFactory compFactory, BOOL isParentALocalReference=TRUE);
 368 
 369     static void DestroyComponentHWND(HWND hwnd);
 370 
 371     // constants used to PostQuitMessage
 372 
 373     static const int EXIT_ENCLOSING_LOOP;
 374     static const int EXIT_ALL_ENCLOSING_LOOPS;
 375 
 376     // ...
 377 
 378     void QuitMessageLoop(int status);
 379 
 380     UINT MessageLoop(IDLEPROC lpIdleFunc, PEEKMESSAGEPROC lpPeekMessageFunc);
 381     BOOL PumpWaitingMessages(PEEKMESSAGEPROC lpPeekMessageFunc);
 382     void PumpToDestroy(class AwtComponent* p);
 383     void ProcessMsg(MSG& msg);
 384     BOOL PreProcessMsg(MSG& msg);
 385     BOOL PreProcessMouseMsg(class AwtComponent* p, MSG& msg);
 386     BOOL PreProcessKeyMsg(class AwtComponent* p, MSG& msg);
 387 
 388     /* Create an ID which maps to an AwtObject pointer, such as a menu. */
 389     UINT CreateCmdID(AwtObject* object);
 390 
 391     // removes cmd id mapping
 392     void RemoveCmdID(UINT id);
 393 
 394     /* Return the AwtObject associated with its ID. */
 395     AwtObject* LookupCmdID(UINT id);
 396 
 397     /* Return the current application icon. */
 398     HICON GetAwtIcon();
 399     HICON GetAwtIconSm();
 400 
 401     // Calculate a wave-like value out of the integer 'value' and
 402     // the specified period.
 403     // The argument 'value' is an integer 0, 1, 2, ... *infinity*.
 404     //
 405     // Examples:
 406     //    Period == 3
 407     //    Generated sequence: 0 1 2 1 0 .....
 408     //
 409     //    Period == 4
 410     //    Generated sequence: 0 1 2 3 2 1 0 .....
 411     static inline UINT CalculateWave(UINT value, const UINT period) {
 412         if (period < 2) {
 413             return 0;
 414         }
 415         // -2 is necessary to avoid repeating extreme values (0 and period-1)
 416         value %= period * 2 -2;
 417         if (value >= period) {
 418             value = period * 2 -2 - value;
 419         }
 420         return value;
 421     }
 422 
 423     HICON GetSecurityWarningIcon(UINT index, UINT w, UINT h);
 424 
 425     /* Turns on/off dialog modality for the system. */
 426     INLINE AwtDialog* SetModal(AwtDialog* frame) {
 427         AwtDialog* previousDialog = m_pModalDialog;
 428         m_pModalDialog = frame;
 429         return previousDialog;
 430     };
 431     INLINE void ResetModal(AwtDialog* oldFrame) { m_pModalDialog = oldFrame; };
 432     INLINE BOOL IsModal() { return (m_pModalDialog != NULL); };
 433     INLINE AwtDialog* GetModalDialog(void) { return m_pModalDialog; };
 434 
 435     /* Stops the current message pump (normally a modal dialog pump) */
 436     INLINE void StopMessagePump() { m_breakOnError = TRUE; }
 437 
 438     /* Debug settings */
 439     INLINE void SetVerbose(long flag)   { m_verbose = (flag != 0); }
 440     INLINE void SetVerify(long flag)    { m_verifyComponents = (flag != 0); }
 441     INLINE void SetBreak(long flag)     { m_breakOnError = (flag != 0); }
 442     INLINE void SetHeapCheck(long flag);
 443 
 444     static void SetBusy(BOOL busy);
 445 
 446     /* Set and get the default input method Window handler. */
 447     INLINE void SetInputMethodWindow(HWND inputMethodHWnd) { m_inputMethodHWnd = inputMethodHWnd; }
 448     INLINE HWND GetInputMethodWindow() { return m_inputMethodHWnd; }
 449 
 450     static VOID CALLBACK PrimaryIdleFunc();
 451     static VOID CALLBACK SecondaryIdleFunc();
 452     static BOOL CALLBACK CommonPeekMessageFunc(MSG& msg);
 453     static BOOL activateKeyboardLayout(HKL hkl);
 454 
 455     HANDLE m_waitEvent;
 456     volatile DWORD eventNumber;
 457     volatile BOOL isInDoDragDropLoop;
 458 private:
 459     HWND CreateToolkitWnd(LPCTSTR name);
 460 
 461     BOOL m_localPump;
 462     DWORD m_mainThreadId;
 463     HWND m_toolkitHWnd;
 464     HWND m_inputMethodHWnd;
 465     BOOL m_verbose;
 466     BOOL m_isActive; // set to FALSE at beginning of Dispose
 467     BOOL m_isDisposed; // set to TRUE at end of Dispose
 468     BOOL m_areExtraMouseButtonsEnabled;
 469 
 470     typedef BOOL (WINAPI *EnableMouseInPointerHandleFunc)(BOOL enable);
 471     typedef BOOL (WINAPI *GetPointerInfoHandleFunc)(UINT32 pointerID, POINTER_INFO *pointerInfo);
 472 
 473     BOOL m_isWin8OrLater;
 474     EnableMouseInPointerHandleFunc m_pEnableMouseInPointerHandle;
 475     GetPointerInfoHandleFunc m_pGetPointerInfoHandle;
 476 
 477     BOOL m_vmSignalled; // set to TRUE if QUERYENDSESSION has successfully
 478                         // raised SIGTERM
 479 
 480     BOOL m_verifyComponents;
 481     BOOL m_breakOnError;
 482 
 483     BOOL  m_breakMessageLoop;
 484     UINT  m_messageLoopResult;
 485 
 486     class AwtComponent* m_lastMouseOver;
 487     BOOL                m_mouseDown;
 488 
 489     HHOOK m_hGetMessageHook;
 490     HHOOK m_hMouseLLHook;
 491     UINT_PTR  m_timer;
 492 
 493     class AwtCmdIDList* m_cmdIDs;
 494     BYTE                m_lastKeyboardState[KB_STATE_SIZE];
 495     CriticalSection     m_lockKB;
 496 
 497     static AwtToolkit theInstance;
 498 
 499     /* The current modal dialog frame (normally NULL). */
 500     AwtDialog* m_pModalDialog;
 501 
 502     /* The WToolkit peer instance */
 503     jobject m_peer;
 504 
 505     HMODULE m_dllHandle;  /* The module handle. */
 506 
 507     CriticalSection m_Sync;
 508 
 509 /* track display changes - used by palette-updating code.
 510    This is a workaround for a windows bug that prevents
 511    WM_PALETTECHANGED event from occurring immediately after
 512    a WM_DISPLAYCHANGED event.
 513   */
 514 private:
 515     BOOL m_displayChanged;  /* Tracks displayChanged events */
 516     // 0 means we are not embedded.
 517     DWORD m_embedderProcessID;
 518 
 519 public:
 520     BOOL HasDisplayChanged() { return m_displayChanged; }
 521     void ResetDisplayChanged() { m_displayChanged = FALSE; }
 522     void RegisterEmbedderProcessId(HWND);
 523     BOOL IsEmbedderProcessId(const DWORD processID) const
 524     {
 525         return m_embedderProcessID && (processID == m_embedderProcessID);
 526     }
 527 
 528  private:
 529     static JNIEnv *m_env;
 530     static DWORD m_threadId;
 531  public:
 532     static void SetEnv(JNIEnv *env);
 533     static JNIEnv* GetEnv();
 534 
 535     static BOOL GetScreenInsets(int screenNum, RECT * rect);
 536 
 537     // If the DWM is active, this function uses
 538     // DwmGetWindowAttribute()/DWMWA_EXTENDED_FRAME_BOUNDS.
 539     // Otherwise, fall back to regular ::GetWindowRect().
 540     // See 6711576 for more details.
 541     static void GetWindowRect(HWND hWnd, LPRECT lpRect);
 542 
 543  private:
 544     // The window handle of a toplevel window last seen under the mouse cursor.
 545     // See MouseLowLevelHook() for details.
 546     HWND m_lastWindowUnderMouse;
 547  public:
 548     HWND GetWindowUnderMouse() { return m_lastWindowUnderMouse; }
 549 
 550     void InstallMouseLowLevelHook();
 551     void UninstallMouseLowLevelHook();
 552 
 553 
 554 /* AWT preloading (early Toolkit thread start)
 555  */
 556 public:
 557     /* Toolkit preload action class.
 558      * Preload actions should be registered with
 559      * AwtToolkit::getInstance().GetPreloadThread().AddAction().
 560      * AwtToolkit thread calls InitImpl method at the beghining
 561      * and CleanImpl(false) before exiting for all registered actions.
 562      * If an application provides own Toolkit thread
 563      * (sun.awt.windows.WToolkit.embeddedInit), the thread calls Clean(true)
 564      * for each action.
 565      */
 566     class PreloadThread;    // forward declaration
 567     class PreloadAction {
 568         friend class PreloadThread;
 569     public:
 570         PreloadAction() : initThreadId(0), pNext(NULL) {}
 571         virtual ~PreloadAction() {}
 572 
 573     protected:
 574         // called by PreloadThread or as result
 575         // of EnsureInited() call (on Toolkit thread!).
 576         virtual void InitImpl() = 0;
 577 
 578         // called by PreloadThread (before exiting).
 579         // reInit == false: normal shutdown;
 580         // reInit == true: PreloadThread is shutting down due external
 581         //   Toolkit thread was provided.
 582         virtual void CleanImpl(bool reInit) = 0;
 583 
 584     public:
 585         // Initialized the action on the Toolkit thread if not yet initialized.
 586         bool EnsureInited();
 587 
 588         // returns thread ID which the action was inited on (0 if not inited)
 589         DWORD GetInitThreadID();
 590 
 591         // Allows to deinitialize action earlier.
 592         // The method must be called on the Toolkit thread only.
 593         // returns true on success,
 594         //         false if the action was inited on other thread.
 595         bool Clean();
 596 
 597     private:
 598         unsigned initThreadId;
 599         // lock for Init/Clean
 600         CriticalSection initLock;
 601 
 602         // Chain support (for PreloadThread)
 603         PreloadAction *pNext;   // for action chain used by PreloadThread
 604         void SetNext(PreloadAction *pNext) { this->pNext = pNext; }
 605         PreloadAction *GetNext() { return pNext; }
 606 
 607         // wrapper for AwtToolkit::InvokeFunction
 608         static void InitWrapper(void *param);
 609 
 610         void Init();
 611         void Clean(bool reInit);
 612 
 613     };
 614 
 615     /** Toolkit preload thread class.
 616      */
 617     class PreloadThread {
 618     public:
 619         PreloadThread();
 620         ~PreloadThread();
 621 
 622         // adds action & start the thread if not yet started
 623         bool AddAction(PreloadAction *pAction);
 624 
 625         // sets termination flag; returns true if the thread is running.
 626         // wrongThread specifies cause of the termination:
 627         //   false means termination on the application shutdown;
 628         // wrongThread is used as reInit parameter for action cleanup.
 629         bool Terminate(bool wrongThread);
 630         bool InvokeAndTerminate(void(_cdecl *fn)(void *), void *param);
 631 
 632         // waits for the thread completion;
 633         // use the method after Terminate() only if Terminate() returned true
 634         INLINE void Wait4Finish() {
 635             ::WaitForSingleObject(hFinished, INFINITE);
 636         }
 637 
 638         INLINE unsigned GetThreadId() {
 639             CriticalSection::Lock lock(threadLock);
 640             return threadId;
 641         }
 642         INLINE bool IsWrongThread() {
 643             CriticalSection::Lock lock(threadLock);
 644             return wrongThread;
 645         }
 646         // returns true if the current thread is "preload" thread
 647         bool OnPreloadThread();
 648 
 649     private:
 650         // data access lock
 651         CriticalSection threadLock;
 652 
 653         // the thread status
 654         enum Status {
 655             None = -1,      // initial
 656             Preloading = 0, // preloading in progress
 657             RunningToolkit, // Running as Toolkit thread
 658             Cleaning,       // exited from Toolkit thread proc, cleaning
 659             Finished        //
 660         } status;
 661 
 662         // "wrong thread" flag
 663         bool wrongThread;
 664 
 665         // thread proc (calls (this)param->ThreadProc())
 666         static unsigned WINAPI StaticThreadProc(void *param);
 667         unsigned ThreadProc();
 668 
 669         INLINE void AwakeThread() {
 670             ::SetEvent(hAwake);
 671         }
 672 
 673         // if threadId != 0 -> we are running
 674         unsigned threadId;
 675         // ThreadProc sets the event on exit
 676         HANDLE hFinished;
 677         // ThreadProc waits on the event for NewAction/Terminate/InvokeAndTerminate
 678         HANDLE hAwake;
 679 
 680         // function/param to invoke (InvokeAndTerminate)
 681         // if execFunc == NULL => just terminate
 682         void(_cdecl *execFunc)(void *);
 683         void *execParam;
 684 
 685         // action chain
 686         PreloadAction *pActionChain;
 687         PreloadAction *pLastProcessedAction;
 688 
 689         // returns next action in the list (NULL if no more actions)
 690         PreloadAction* GetNextAction();
 691 
 692     };
 693 
 694     INLINE PreloadThread& GetPreloadThread() { return preloadThread; }
 695 
 696 private:
 697     PreloadThread preloadThread;
 698 
 699 };
 700 
 701 
 702 /*  creates an instance of T and assigns it to the argument, but only if
 703     the argument is initially NULL. Supposed to be thread-safe.
 704     returns the new value of the argument. I'm not using volatile here
 705     as InterlockedCompareExchange ensures volatile semantics
 706     and acquire/release.
 707     The function is useful when used with static POD NULL-initialized
 708     pointers, as they are guaranteed to be NULL before any dynamic
 709     initialization takes place. This function turns such a pointer
 710     into a thread-safe singleton, working regardless of dynamic
 711     initialization order. Destruction problem is not solved,
 712     we don't need it here.
 713 */
 714 
 715 template<typename T> inline T* SafeCreate(T* &pArg) {
 716     /*  this implementation has no locks, it just destroys the object if it
 717         fails to be the first to init. another way would be using a special
 718         flag pointer value to mark the pointer as "being initialized". */
 719     T* pTemp = (T*)InterlockedCompareExchangePointer((void**)&pArg, NULL, NULL);
 720     if (pTemp != NULL) return pTemp;
 721     T* pNew = new T;
 722     pTemp = (T*)InterlockedCompareExchangePointer((void**)&pArg, pNew, NULL);
 723     if (pTemp != NULL) {
 724         // we failed it - another thread has already initialized pArg
 725         delete pNew;
 726         return pTemp;
 727     } else {
 728         return pNew;
 729     }
 730 }
 731 
 732 #endif /* AWT_TOOLKIT_H */