1 /*
   2  * Copyright (c) 1996, 2020, 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 #define CRITICAL_SECTION_ENTER(cs) { \
 137     J2dTraceLn4(J2D_TRACE_VERBOSE2, \
 138                 "CS.Wait:  tid, cs, file, line = 0x%x, 0x%x, %s, %d", \
 139                 GetCurrentThreadId(), &(cs), __FILE__, __LINE__); \
 140     (cs).Enter(); \
 141     J2dTraceLn4(J2D_TRACE_VERBOSE2, \
 142                 "CS.Enter: tid, cs, file, line = 0x%x, 0x%x, %s, %d", \
 143                 GetCurrentThreadId(), &(cs), __FILE__, __LINE__); \
 144 }
 145 
 146 #define CRITICAL_SECTION_LEAVE(cs) { \
 147     J2dTraceLn4(J2D_TRACE_VERBOSE2, \
 148                 "CS.Leave: tid, cs, file, line = 0x%x, 0x%x, %s, %d", \
 149                 GetCurrentThreadId(), &(cs), __FILE__, __LINE__); \
 150     (cs).Leave(); \
 151     J2dTraceLn4(J2D_TRACE_VERBOSE2, \
 152                 "CS.Left:  tid, cs, file, line = 0x%x, 0x%x, %s, %d", \
 153                 GetCurrentThreadId(), &(cs), __FILE__, __LINE__); \
 154 }
 155 
 156 // Redefine WinAPI values related to touch input, if OS < Windows 7.
 157 #if (!defined(WINVER) || ((WINVER) < 0x0601))
 158     /*
 159      * RegisterTouchWindow flag values
 160      */
 161     #define TWF_FINETOUCH       (0x00000001)
 162     #define TWF_WANTPALM        (0x00000002)
 163 
 164     #define WM_TOUCH                        0x0240
 165 
 166     /*
 167      * Touch input handle
 168      */
 169     typedef HANDLE HTOUCHINPUT;
 170 
 171     typedef struct tagTOUCHINPUT {
 172         LONG x;
 173         LONG y;
 174         HANDLE hSource;
 175         DWORD dwID;
 176         DWORD dwFlags;
 177         DWORD dwMask;
 178         DWORD dwTime;
 179         ULONG_PTR dwExtraInfo;
 180         DWORD cxContact;
 181         DWORD cyContact;
 182     } TOUCHINPUT, *PTOUCHINPUT;
 183     typedef TOUCHINPUT const * PCTOUCHINPUT;
 184 
 185     /*
 186      * Touch input flag values (TOUCHINPUT.dwFlags)
 187      */
 188     #define TOUCHEVENTF_MOVE            0x0001
 189     #define TOUCHEVENTF_DOWN            0x0002
 190     #define TOUCHEVENTF_UP              0x0004
 191     #define TOUCHEVENTF_INRANGE         0x0008
 192     #define TOUCHEVENTF_PRIMARY         0x0010
 193     #define TOUCHEVENTF_NOCOALESCE      0x0020
 194     #define TOUCHEVENTF_PEN             0x0040
 195     #define TOUCHEVENTF_PALM            0x0080
 196 #endif
 197 
 198 /************************************************************************
 199  * AwtToolkit class
 200  */
 201 
 202 class AwtToolkit {
 203 public:
 204     enum {
 205         KB_STATE_SIZE = 256
 206     };
 207 
 208     /* java.awt.Toolkit method ids */
 209     static jmethodID getDefaultToolkitMID;
 210     static jmethodID getFontMetricsMID;
 211     static jmethodID insetsMID;
 212 
 213     /* sun.awt.windows.WToolkit ids */
 214     static jmethodID windowsSettingChangeMID;
 215     static jmethodID displayChangeMID;
 216 
 217     static jmethodID userSessionMID;
 218     static jmethodID systemSleepMID;
 219 
 220     BOOL m_isDynamicLayoutSet;
 221 
 222     AwtToolkit();
 223     ~AwtToolkit();
 224 
 225     BOOL Initialize(BOOL localPump);
 226     BOOL Dispose();
 227 
 228     void SetDynamicLayout(BOOL dynamic);
 229     BOOL IsDynamicLayoutSet();
 230     BOOL IsDynamicLayoutSupported();
 231     BOOL IsDynamicLayoutActive();
 232     BOOL areExtraMouseButtonsEnabled();
 233     void setExtraMouseButtonsEnabled(BOOL enable);
 234     static UINT GetNumberOfButtons();
 235 
 236     bool IsWin8OrLater();
 237     bool IsTouchKeyboardAutoShowEnabled();
 238     bool IsAnyKeyboardAttached();
 239     bool IsTouchKeyboardAutoShowSystemEnabled();
 240     void ShowTouchKeyboard();
 241     void HideTouchKeyboard();
 242     BOOL TIRegisterTouchWindow(HWND hWnd, ULONG ulFlags);
 243     BOOL TIGetTouchInputInfo(HTOUCHINPUT hTouchInput,
 244         UINT cInputs, PTOUCHINPUT pInputs, int cbSize);
 245     BOOL TICloseTouchInputHandle(HTOUCHINPUT hTouchInput);
 246 
 247     LRESULT InvokeInputMethodFunction(UINT msg, WPARAM wParam=0, LPARAM lParam=0);
 248 
 249     INLINE BOOL localPump() { return m_localPump; }
 250     INLINE BOOL VerifyComponents() { return FALSE; } // TODO: Use new DebugHelper class to set this flag
 251     INLINE HWND GetHWnd() { return m_toolkitHWnd; }
 252 
 253     INLINE HMODULE GetModuleHandle() { return m_dllHandle; }
 254     INLINE void SetModuleHandle(HMODULE h) { m_dllHandle = h; }
 255 
 256     INLINE static DWORD MainThread() { return GetInstance().m_mainThreadId; }
 257     INLINE void VerifyActive() throw (awt_toolkit_shutdown) {
 258         if (!m_isActive && m_mainThreadId != ::GetCurrentThreadId()) {
 259             throw awt_toolkit_shutdown();
 260         }
 261     }
 262     INLINE BOOL IsDisposed() { return m_isDisposed; }
 263     static UINT GetMouseKeyState();
 264     static void GetKeyboardState(PBYTE keyboardState);
 265 
 266     static ATOM RegisterClass();
 267     static void UnregisterClass();
 268     INLINE LRESULT SendMessage(UINT msg, WPARAM wParam=0, LPARAM lParam=0) {
 269         if (!m_isDisposed) {
 270             return ::SendMessage(GetHWnd(), msg, wParam, lParam);
 271         } else {
 272             return NULL;
 273         }
 274     }
 275     static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
 276                                     LPARAM lParam);
 277     static LRESULT CALLBACK GetMessageFilter(int code, WPARAM wParam,
 278                                              LPARAM lParam);
 279     static LRESULT CALLBACK ForegroundIdleFilter(int code, WPARAM wParam,
 280                                                  LPARAM lParam);
 281     static LRESULT CALLBACK MouseLowLevelHook(int code, WPARAM wParam,
 282             LPARAM lParam);
 283 
 284     INLINE static AwtToolkit& GetInstance() { return theInstance; }
 285     INLINE void SetPeer(JNIEnv *env, jobject wToolkit) {
 286         AwtToolkit &tk = AwtToolkit::GetInstance();
 287         if (tk.m_peer != NULL) {
 288             env->DeleteGlobalRef(tk.m_peer);
 289         }
 290         tk.m_peer = (wToolkit != NULL) ? env->NewGlobalRef(wToolkit) : NULL;
 291     }
 292 
 293     INLINE jobject GetPeer() {
 294         return m_peer;
 295     }
 296 
 297     // is this thread the main thread?
 298 
 299     INLINE static BOOL IsMainThread() {
 300         return GetInstance().m_mainThreadId == ::GetCurrentThreadId();
 301     }
 302 
 303     // post a message to the message pump thread
 304 
 305     INLINE BOOL PostMessage(UINT msg, WPARAM wp=0, LPARAM lp=0) {
 306         return ::PostMessage(GetHWnd(), msg, wp, lp);
 307     }
 308 
 309     // cause the message pump thread to call the function synchronously now!
 310 
 311     INLINE void * InvokeFunction(void*(*ftn)(void)) {
 312         return (void *)SendMessage(WM_AWT_INVOKE_VOID_METHOD, (WPARAM)ftn, 0);
 313     }
 314     INLINE void InvokeFunction(void (*ftn)(void)) {
 315         InvokeFunction((void*(*)(void))ftn);
 316     }
 317     INLINE void * InvokeFunction(void*(*ftn)(void *), void* param) {
 318         return (void *)SendMessage(WM_AWT_INVOKE_METHOD, (WPARAM)ftn,
 319                                    (LPARAM)param);
 320     }
 321     INLINE void InvokeFunction(void (*ftn)(void *), void* param) {
 322         InvokeFunction((void*(*)(void*))ftn, param);
 323     }
 324 
 325     INLINE CriticalSection &GetSyncCS() { return m_Sync; }
 326 
 327     void *SyncCall(void*(*ftn)(void *), void* param);
 328     void SyncCall(void (*ftn)(void *), void *param);
 329     void *SyncCall(void *(*ftn)(void));
 330     void SyncCall(void (*ftn)(void));
 331 
 332     // cause the message pump thread to call the function later ...
 333 
 334     INLINE void InvokeFunctionLater(void (*ftn)(void *), void* param) {
 335         if (!PostMessage(WM_AWT_INVOKE_METHOD, (WPARAM)ftn, (LPARAM)param)) {
 336             JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 337             JNU_ThrowInternalError(env, "Message not posted, native event queue may be full.");
 338         }
 339     }
 340 
 341    // cause the message pump thread to synchronously synchronize on the handle
 342 
 343     INLINE void WaitForSingleObject(HANDLE handle) {
 344         SendMessage(WM_AWT_WAIT_FOR_SINGLE_OBJECT, 0, (LPARAM)handle);
 345     }
 346 
 347     /*
 348      * Create an AwtXxxx C++ component using a given factory
 349      */
 350     typedef void (*ComponentFactory)(void*, void*);
 351     static void CreateComponent(void* hComponent, void* hParent,
 352                                 ComponentFactory compFactory, BOOL isParentALocalReference=TRUE);
 353 
 354     static void DestroyComponentHWND(HWND hwnd);
 355 
 356     // constants used to PostQuitMessage
 357 
 358     static const int EXIT_ENCLOSING_LOOP;
 359     static const int EXIT_ALL_ENCLOSING_LOOPS;
 360 
 361     // ...
 362 
 363     void QuitMessageLoop(int status);
 364 
 365     UINT MessageLoop(IDLEPROC lpIdleFunc, PEEKMESSAGEPROC lpPeekMessageFunc);
 366     BOOL PumpWaitingMessages(PEEKMESSAGEPROC lpPeekMessageFunc);
 367     void PumpToDestroy(class AwtComponent* p);
 368     void ProcessMsg(MSG& msg);
 369     BOOL PreProcessMsg(MSG& msg);
 370     BOOL PreProcessMouseMsg(class AwtComponent* p, MSG& msg);
 371     BOOL PreProcessKeyMsg(class AwtComponent* p, MSG& msg);
 372 
 373     /* Checks that an free ID exists. */
 374     jboolean isFreeIDAvailable();
 375     /* Create an ID which maps to an AwtObject pointer, such as a menu. */
 376     UINT CreateCmdID(AwtObject* object);
 377 
 378     // removes cmd id mapping
 379     void RemoveCmdID(UINT id);
 380 
 381     /* Return the AwtObject associated with its ID. */
 382     AwtObject* LookupCmdID(UINT id);
 383 
 384     /* Return the current application icon. */
 385     HICON GetAwtIcon();
 386     HICON GetAwtIconSm();
 387 
 388     // Calculate a wave-like value out of the integer 'value' and
 389     // the specified period.
 390     // The argument 'value' is an integer 0, 1, 2, ... *infinity*.
 391     //
 392     // Examples:
 393     //    Period == 3
 394     //    Generated sequence: 0 1 2 1 0 .....
 395     //
 396     //    Period == 4
 397     //    Generated sequence: 0 1 2 3 2 1 0 .....
 398     static inline UINT CalculateWave(UINT value, const UINT period) {
 399         if (period < 2) {
 400             return 0;
 401         }
 402         // -2 is necessary to avoid repeating extreme values (0 and period-1)
 403         value %= period * 2 -2;
 404         if (value >= period) {
 405             value = period * 2 -2 - value;
 406         }
 407         return value;
 408     }
 409 
 410     HICON GetSecurityWarningIcon(UINT index, UINT w, UINT h);
 411 
 412     /* Turns on/off dialog modality for the system. */
 413     INLINE AwtDialog* SetModal(AwtDialog* frame) {
 414         AwtDialog* previousDialog = m_pModalDialog;
 415         m_pModalDialog = frame;
 416         return previousDialog;
 417     };
 418     INLINE void ResetModal(AwtDialog* oldFrame) { m_pModalDialog = oldFrame; };
 419     INLINE BOOL IsModal() { return (m_pModalDialog != NULL); };
 420     INLINE AwtDialog* GetModalDialog(void) { return m_pModalDialog; };
 421 
 422     /* Stops the current message pump (normally a modal dialog pump) */
 423     INLINE void StopMessagePump() { m_breakOnError = TRUE; }
 424 
 425     /* Debug settings */
 426     INLINE void SetVerbose(long flag)   { m_verbose = (flag != 0); }
 427     INLINE void SetVerify(long flag)    { m_verifyComponents = (flag != 0); }
 428     INLINE void SetBreak(long flag)     { m_breakOnError = (flag != 0); }
 429     INLINE void SetHeapCheck(long flag);
 430 
 431     static void SetBusy(BOOL busy);
 432 
 433     /* Set and get the default input method Window handler. */
 434     INLINE void SetInputMethodWindow(HWND inputMethodHWnd) { m_inputMethodHWnd = inputMethodHWnd; }
 435     INLINE HWND GetInputMethodWindow() { return m_inputMethodHWnd; }
 436 
 437     static VOID CALLBACK PrimaryIdleFunc();
 438     static VOID CALLBACK SecondaryIdleFunc();
 439     static BOOL CALLBACK CommonPeekMessageFunc(MSG& msg);
 440     static BOOL activateKeyboardLayout(HKL hkl);
 441 
 442     HANDLE m_waitEvent;
 443     volatile DWORD eventNumber;
 444     volatile BOOL isInDoDragDropLoop;
 445 private:
 446     HWND CreateToolkitWnd(LPCTSTR name);
 447 
 448     void InitTouchKeyboardExeFilePath();
 449     HWND GetTouchKeyboardWindow();
 450 
 451     BOOL m_localPump;
 452     DWORD m_mainThreadId;
 453     HWND m_toolkitHWnd;
 454     HWND m_inputMethodHWnd;
 455     BOOL m_verbose;
 456     BOOL m_isActive; // set to FALSE at beginning of Dispose
 457     BOOL m_isDisposed; // set to TRUE at end of Dispose
 458     BOOL m_areExtraMouseButtonsEnabled;
 459 
 460     typedef BOOL (WINAPI *RegisterTouchWindowFunc)(HWND hWnd, ULONG ulFlags);
 461     typedef BOOL (WINAPI *GetTouchInputInfoFunc)(HTOUCHINPUT hTouchInput,
 462         UINT cInputs, PTOUCHINPUT pInputs, int cbSize);
 463     typedef BOOL (WINAPI *CloseTouchInputHandleFunc)(HTOUCHINPUT hTouchInput);
 464 
 465     BOOL m_isWin8OrLater;
 466     BOOL m_touchKbrdAutoShowIsEnabled;
 467     TCHAR* m_touchKbrdExeFilePath;
 468     RegisterTouchWindowFunc m_pRegisterTouchWindow;
 469     GetTouchInputInfoFunc m_pGetTouchInputInfo;
 470     CloseTouchInputHandleFunc m_pCloseTouchInputHandle;
 471 
 472     BOOL m_vmSignalled; // set to TRUE if QUERYENDSESSION has successfully
 473                         // raised SIGTERM
 474 
 475     BOOL m_verifyComponents;
 476     BOOL m_breakOnError;
 477 
 478     BOOL  m_breakMessageLoop;
 479     UINT  m_messageLoopResult;
 480 
 481     class AwtComponent* m_lastMouseOver;
 482     BOOL                m_mouseDown;
 483 
 484     HHOOK m_hGetMessageHook;
 485     HHOOK m_hMouseLLHook;
 486     UINT_PTR  m_timer;
 487 
 488     class AwtCmdIDList* m_cmdIDs;
 489     BYTE                m_lastKeyboardState[KB_STATE_SIZE];
 490     CriticalSection     m_lockKB;
 491 
 492     static AwtToolkit theInstance;
 493 
 494     /* The current modal dialog frame (normally NULL). */
 495     AwtDialog* m_pModalDialog;
 496 
 497     /* The WToolkit peer instance */
 498     jobject m_peer;
 499 
 500     HMODULE m_dllHandle;  /* The module handle. */
 501 
 502     CriticalSection m_Sync;
 503     CriticalSection m_inputMethodLock;
 504 
 505     HANDLE m_inputMethodWaitEvent;
 506     LRESULT m_inputMethodData;
 507 
 508 /* track display changes - used by palette-updating code.
 509    This is a workaround for a windows bug that prevents
 510    WM_PALETTECHANGED event from occurring immediately after
 511    a WM_DISPLAYCHANGED event.
 512   */
 513 private:
 514     BOOL m_displayChanged;  /* Tracks displayChanged events */
 515     // 0 means we are not embedded.
 516     DWORD m_embedderProcessID;
 517 
 518 public:
 519     BOOL HasDisplayChanged() { return m_displayChanged; }
 520     void ResetDisplayChanged() { m_displayChanged = FALSE; }
 521     void RegisterEmbedderProcessId(HWND);
 522     BOOL IsEmbedderProcessId(const DWORD processID) const
 523     {
 524         return m_embedderProcessID && (processID == m_embedderProcessID);
 525     }
 526 
 527  private:
 528     static JNIEnv *m_env;
 529     static DWORD m_threadId;
 530  public:
 531     static void SetEnv(JNIEnv *env);
 532     static JNIEnv* GetEnv();
 533 
 534     static BOOL GetScreenInsets(int screenNum, RECT * rect);
 535 
 536     // If the DWM is active, this function uses
 537     // DwmGetWindowAttribute()/DWMWA_EXTENDED_FRAME_BOUNDS.
 538     // Otherwise, fall back to regular ::GetWindowRect().
 539     // See 6711576 for more details.
 540     static void GetWindowRect(HWND hWnd, LPRECT lpRect);
 541 
 542  private:
 543     // The window handle of a toplevel window last seen under the mouse cursor.
 544     // See MouseLowLevelHook() for details.
 545     HWND m_lastWindowUnderMouse;
 546  public:
 547     HWND GetWindowUnderMouse() { return m_lastWindowUnderMouse; }
 548 
 549     void InstallMouseLowLevelHook();
 550     void UninstallMouseLowLevelHook();
 551 
 552 
 553 /* AWT preloading (early Toolkit thread start)
 554  */
 555 public:
 556     /* Toolkit preload action class.
 557      * Preload actions should be registered with
 558      * AwtToolkit::getInstance().GetPreloadThread().AddAction().
 559      * AwtToolkit thread calls InitImpl method at the beghining
 560      * and CleanImpl(false) before exiting for all registered actions.
 561      * If an application provides own Toolkit thread
 562      * (sun.awt.windows.WToolkit.embeddedInit), the thread calls Clean(true)
 563      * for each action.
 564      */
 565     class PreloadThread;    // forward declaration
 566     class PreloadAction {
 567         friend class PreloadThread;
 568     public:
 569         PreloadAction() : initThreadId(0), pNext(NULL) {}
 570         virtual ~PreloadAction() {}
 571 
 572     protected:
 573         // called by PreloadThread or as result
 574         // of EnsureInited() call (on Toolkit thread!).
 575         virtual void InitImpl() = 0;
 576 
 577         // called by PreloadThread (before exiting).
 578         // reInit == false: normal shutdown;
 579         // reInit == true: PreloadThread is shutting down due external
 580         //   Toolkit thread was provided.
 581         virtual void CleanImpl(bool reInit) = 0;
 582 
 583     public:
 584         // Initialized the action on the Toolkit thread if not yet initialized.
 585         bool EnsureInited();
 586 
 587         // returns thread ID which the action was inited on (0 if not inited)
 588         DWORD GetInitThreadID();
 589 
 590         // Allows to deinitialize action earlier.
 591         // The method must be called on the Toolkit thread only.
 592         // returns true on success,
 593         //         false if the action was inited on other thread.
 594         bool Clean();
 595 
 596     private:
 597         unsigned initThreadId;
 598         // lock for Init/Clean
 599         CriticalSection initLock;
 600 
 601         // Chain support (for PreloadThread)
 602         PreloadAction *pNext;   // for action chain used by PreloadThread
 603         void SetNext(PreloadAction *pNext) { this->pNext = pNext; }
 604         PreloadAction *GetNext() { return pNext; }
 605 
 606         // wrapper for AwtToolkit::InvokeFunction
 607         static void InitWrapper(void *param);
 608 
 609         void Init();
 610         void Clean(bool reInit);
 611 
 612     };
 613 
 614     /** Toolkit preload thread class.
 615      */
 616     class PreloadThread {
 617     public:
 618         PreloadThread();
 619         ~PreloadThread();
 620 
 621         // adds action & start the thread if not yet started
 622         bool AddAction(PreloadAction *pAction);
 623 
 624         // sets termination flag; returns true if the thread is running.
 625         // wrongThread specifies cause of the termination:
 626         //   false means termination on the application shutdown;
 627         // wrongThread is used as reInit parameter for action cleanup.
 628         bool Terminate(bool wrongThread);
 629         bool InvokeAndTerminate(void(_cdecl *fn)(void *), void *param);
 630 
 631         // waits for the thread completion;
 632         // use the method after Terminate() only if Terminate() returned true
 633         INLINE void Wait4Finish() {
 634             ::WaitForSingleObject(hFinished, INFINITE);
 635         }
 636 
 637         INLINE unsigned GetThreadId() {
 638             CriticalSection::Lock lock(threadLock);
 639             return threadId;
 640         }
 641         INLINE bool IsWrongThread() {
 642             CriticalSection::Lock lock(threadLock);
 643             return wrongThread;
 644         }
 645         // returns true if the current thread is "preload" thread
 646         bool OnPreloadThread();
 647 
 648     private:
 649         // data access lock
 650         CriticalSection threadLock;
 651 
 652         // the thread status
 653         enum Status {
 654             None = -1,      // initial
 655             Preloading = 0, // preloading in progress
 656             RunningToolkit, // Running as Toolkit thread
 657             Cleaning,       // exited from Toolkit thread proc, cleaning
 658             Finished        //
 659         } status;
 660 
 661         // "wrong thread" flag
 662         bool wrongThread;
 663 
 664         // thread proc (calls (this)param->ThreadProc())
 665         static unsigned WINAPI StaticThreadProc(void *param);
 666         unsigned ThreadProc();
 667 
 668         INLINE void AwakeThread() {
 669             ::SetEvent(hAwake);
 670         }
 671 
 672         // if threadId != 0 -> we are running
 673         unsigned threadId;
 674         // ThreadProc sets the event on exit
 675         HANDLE hFinished;
 676         // ThreadProc waits on the event for NewAction/Terminate/InvokeAndTerminate
 677         HANDLE hAwake;
 678 
 679         // function/param to invoke (InvokeAndTerminate)
 680         // if execFunc == NULL => just terminate
 681         void(_cdecl *execFunc)(void *);
 682         void *execParam;
 683 
 684         // action chain
 685         PreloadAction *pActionChain;
 686         PreloadAction *pLastProcessedAction;
 687 
 688         // returns next action in the list (NULL if no more actions)
 689         PreloadAction* GetNextAction();
 690 
 691     };
 692 
 693     INLINE PreloadThread& GetPreloadThread() { return preloadThread; }
 694 
 695 private:
 696     PreloadThread preloadThread;
 697 
 698 };
 699 
 700 
 701 /*  creates an instance of T and assigns it to the argument, but only if
 702     the argument is initially NULL. Supposed to be thread-safe.
 703     returns the new value of the argument. I'm not using volatile here
 704     as InterlockedCompareExchange ensures volatile semantics
 705     and acquire/release.
 706     The function is useful when used with static POD NULL-initialized
 707     pointers, as they are guaranteed to be NULL before any dynamic
 708     initialization takes place. This function turns such a pointer
 709     into a thread-safe singleton, working regardless of dynamic
 710     initialization order. Destruction problem is not solved,
 711     we don't need it here.
 712 */
 713 
 714 template<typename T> inline T* SafeCreate(T* &pArg) {
 715     /*  this implementation has no locks, it just destroys the object if it
 716         fails to be the first to init. another way would be using a special
 717         flag pointer value to mark the pointer as "being initialized". */
 718     T* pTemp = (T*)InterlockedCompareExchangePointer((void**)&pArg, NULL, NULL);
 719     if (pTemp != NULL) return pTemp;
 720     T* pNew = new T;
 721     pTemp = (T*)InterlockedCompareExchangePointer((void**)&pArg, pNew, NULL);
 722     if (pTemp != NULL) {
 723         // we failed it - another thread has already initialized pArg
 724         delete pNew;
 725         return pTemp;
 726     } else {
 727         return pNew;
 728     }
 729 }
 730 
 731 #endif /* AWT_TOOLKIT_H */