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