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