blob: 65ebca8a36edcd4072d695c24bd74770c063fbbb [file] [log] [blame]
/*
* Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* The Toolkit class has two functions: it instantiates the AWT
* ToolkitPeer's native methods, and provides the DLL's core functions.
*
* There are two ways this DLL can be used: either as a dynamically-
* loaded Java native library from the interpreter, or by a Windows-
* specific app. The first manner requires that the Toolkit provide
* all support needed so the app can function as a first-class Windows
* app, while the second assumes that the app will provide that
* functionality. Which mode this DLL functions in is determined by
* which initialization paradigm is used. If the Toolkit is constructed
* normally, then the Toolkit will have its own pump. If it is explicitly
* initialized for an embedded environment (via a static method on
* sun.awt.windows.WToolkit), then it will rely on an external message
* pump.
*
* The most basic functionality needed is a Windows message pump (also
* known as a message loop). When an Java app is started as a console
* app by the interpreter, the Toolkit needs to provide that message
* pump if the AWT is dynamically loaded.
*/
#ifndef AWT_TOOLKIT_H
#define AWT_TOOLKIT_H
#include "awt.h"
#include "awtmsg.h"
#include "Trace.h"
#include "sun_awt_windows_WToolkit.h"
class AwtObject;
class AwtDialog;
class AwtDropTarget;
typedef VOID (CALLBACK* IDLEPROC)(VOID);
typedef BOOL (CALLBACK* PEEKMESSAGEPROC)(MSG&);
// Struct for _WInputMethod_enable|disableNativeIME method
struct EnableNativeIMEStruct {
jobject self;
jobject peer;
jint context;
jboolean useNativeCompWindow;
};
/*
* class JNILocalFrame
* Push/PopLocalFrame helper
*/
class JNILocalFrame {
public:
INLINE JNILocalFrame(JNIEnv *env, int size) {
m_env = env;
int result = m_env->PushLocalFrame(size);
if (result < 0) {
DASSERT(FALSE);
throw std::bad_alloc();
}
}
INLINE ~JNILocalFrame() { m_env->PopLocalFrame(NULL); }
private:
JNIEnv* m_env;
};
/*
* class CriticalSection
* ~~~~~ ~~~~~~~~~~~~~~~~
* Lightweight intra-process thread synchronization. Can only be used with
* other critical sections, and only within the same process.
*/
class CriticalSection {
public:
INLINE CriticalSection() { ::InitializeCriticalSection(&rep); }
INLINE ~CriticalSection() { ::DeleteCriticalSection(&rep); }
class Lock {
public:
INLINE Lock(const CriticalSection& cs) : critSec(cs) {
(const_cast<CriticalSection &>(critSec)).Enter();
}
INLINE ~Lock() {
(const_cast<CriticalSection &>(critSec)).Leave();
}
private:
const CriticalSection& critSec;
};
friend class Lock;
private:
CRITICAL_SECTION rep;
CriticalSection(const CriticalSection&);
const CriticalSection& operator =(const CriticalSection&);
public:
virtual void Enter() {
::EnterCriticalSection(&rep);
}
virtual BOOL TryEnter() {
return ::TryEnterCriticalSection(&rep);
}
virtual void Leave() {
::LeaveCriticalSection(&rep);
}
};
// Macros for using CriticalSection objects that help trace
// lock/unlock actions
/* Use THIS_FILE when it is available. */
#ifndef THIS_FILE
#define THIS_FILE __FILE__
#endif
#define CRITICAL_SECTION_ENTER(cs) { \
J2dTraceLn4(J2D_TRACE_VERBOSE2, \
"CS.Wait: tid, cs, file, line = 0x%x, 0x%x, %s, %d", \
GetCurrentThreadId(), &(cs), THIS_FILE, __LINE__); \
(cs).Enter(); \
J2dTraceLn4(J2D_TRACE_VERBOSE2, \
"CS.Enter: tid, cs, file, line = 0x%x, 0x%x, %s, %d", \
GetCurrentThreadId(), &(cs), THIS_FILE, __LINE__); \
}
#define CRITICAL_SECTION_LEAVE(cs) { \
J2dTraceLn4(J2D_TRACE_VERBOSE2, \
"CS.Leave: tid, cs, file, line = 0x%x, 0x%x, %s, %d", \
GetCurrentThreadId(), &(cs), THIS_FILE, __LINE__); \
(cs).Leave(); \
J2dTraceLn4(J2D_TRACE_VERBOSE2, \
"CS.Left: tid, cs, file, line = 0x%x, 0x%x, %s, %d", \
GetCurrentThreadId(), &(cs), THIS_FILE, __LINE__); \
}
/************************************************************************
* AwtToolkit class
*/
class AwtToolkit {
public:
enum {
KB_STATE_SIZE = 256
};
/* java.awt.Toolkit method ids */
static jmethodID getDefaultToolkitMID;
static jmethodID getFontMetricsMID;
static jmethodID insetsMID;
/* sun.awt.windows.WToolkit ids */
static jmethodID windowsSettingChangeMID;
static jmethodID displayChangeMID;
BOOL m_isDynamicLayoutSet;
AwtToolkit();
~AwtToolkit();
BOOL Initialize(BOOL localPump);
BOOL Dispose();
void SetDynamicLayout(BOOL dynamic);
BOOL IsDynamicLayoutSet();
BOOL IsDynamicLayoutSupported();
BOOL IsDynamicLayoutActive();
BOOL areExtraMouseButtonsEnabled();
void setExtraMouseButtonsEnabled(BOOL enable);
static UINT GetNumberOfButtons();
INLINE BOOL localPump() { return m_localPump; }
INLINE BOOL VerifyComponents() { return FALSE; } // TODO: Use new DebugHelper class to set this flag
INLINE HWND GetHWnd() { return m_toolkitHWnd; }
INLINE HMODULE GetModuleHandle() { return m_dllHandle; }
INLINE void SetModuleHandle(HMODULE h) { m_dllHandle = h; }
INLINE static DWORD MainThread() { return GetInstance().m_mainThreadId; }
INLINE void VerifyActive() throw (awt_toolkit_shutdown) {
if (!m_isActive && m_mainThreadId != ::GetCurrentThreadId()) {
throw awt_toolkit_shutdown();
}
}
INLINE BOOL IsDisposed() { return m_isDisposed; }
static UINT GetMouseKeyState();
static void GetKeyboardState(PBYTE keyboardState);
static ATOM RegisterClass();
static void UnregisterClass();
INLINE LRESULT SendMessage(UINT msg, WPARAM wParam=0, LPARAM lParam=0) {
if (!m_isDisposed) {
return ::SendMessage(GetHWnd(), msg, wParam, lParam);
} else {
return NULL;
}
}
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam);
static LRESULT CALLBACK GetMessageFilter(int code, WPARAM wParam,
LPARAM lParam);
static LRESULT CALLBACK ForegroundIdleFilter(int code, WPARAM wParam,
LPARAM lParam);
static LRESULT CALLBACK MouseLowLevelHook(int code, WPARAM wParam,
LPARAM lParam);
INLINE static AwtToolkit& GetInstance() { return theInstance; }
INLINE void SetPeer(JNIEnv *env, jobject wToolkit) {
AwtToolkit &tk = AwtToolkit::GetInstance();
if (tk.m_peer != NULL) {
env->DeleteGlobalRef(tk.m_peer);
}
tk.m_peer = (wToolkit != NULL) ? env->NewGlobalRef(wToolkit) : NULL;
}
INLINE jobject GetPeer() {
return m_peer;
}
// is this thread the main thread?
INLINE static BOOL IsMainThread() {
return GetInstance().m_mainThreadId == ::GetCurrentThreadId();
}
// post a message to the message pump thread
INLINE BOOL PostMessage(UINT msg, WPARAM wp=0, LPARAM lp=0) {
return ::PostMessage(GetHWnd(), msg, wp, lp);
}
// cause the message pump thread to call the function synchronously now!
INLINE void * InvokeFunction(void*(*ftn)(void)) {
return (void *)SendMessage(WM_AWT_INVOKE_VOID_METHOD, (WPARAM)ftn, 0);
}
INLINE void InvokeFunction(void (*ftn)(void)) {
InvokeFunction((void*(*)(void))ftn);
}
INLINE void * InvokeFunction(void*(*ftn)(void *), void* param) {
return (void *)SendMessage(WM_AWT_INVOKE_METHOD, (WPARAM)ftn,
(LPARAM)param);
}
INLINE void InvokeFunction(void (*ftn)(void *), void* param) {
InvokeFunction((void*(*)(void*))ftn, param);
}
INLINE CriticalSection &GetSyncCS() { return m_Sync; }
void *SyncCall(void*(*ftn)(void *), void* param);
void SyncCall(void (*ftn)(void *), void *param);
void *SyncCall(void *(*ftn)(void));
void SyncCall(void (*ftn)(void));
// cause the message pump thread to call the function later ...
INLINE void InvokeFunctionLater(void (*ftn)(void *), void* param) {
if (!PostMessage(WM_AWT_INVOKE_METHOD, (WPARAM)ftn, (LPARAM)param)) {
JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
JNU_ThrowInternalError(env, "Message not posted, native event queue may be full.");
}
}
// cause the message pump thread to synchronously synchronize on the handle
INLINE void WaitForSingleObject(HANDLE handle) {
SendMessage(WM_AWT_WAIT_FOR_SINGLE_OBJECT, 0, (LPARAM)handle);
}
/*
* Create an AwtXxxx C++ component using a given factory
*/
typedef void (*ComponentFactory)(void*, void*);
static void CreateComponent(void* hComponent, void* hParent,
ComponentFactory compFactory, BOOL isParentALocalReference=TRUE);
static void DestroyComponentHWND(HWND hwnd);
// constants used to PostQuitMessage
static const int EXIT_ENCLOSING_LOOP;
static const int EXIT_ALL_ENCLOSING_LOOPS;
// ...
void QuitMessageLoop(int status);
UINT MessageLoop(IDLEPROC lpIdleFunc, PEEKMESSAGEPROC lpPeekMessageFunc);
BOOL PumpWaitingMessages(PEEKMESSAGEPROC lpPeekMessageFunc);
void PumpToDestroy(class AwtComponent* p);
void ProcessMsg(MSG& msg);
BOOL PreProcessMsg(MSG& msg);
BOOL PreProcessMouseMsg(class AwtComponent* p, MSG& msg);
BOOL PreProcessKeyMsg(class AwtComponent* p, MSG& msg);
/* Create an ID which maps to an AwtObject pointer, such as a menu. */
UINT CreateCmdID(AwtObject* object);
// removes cmd id mapping
void RemoveCmdID(UINT id);
/* Return the AwtObject associated with its ID. */
AwtObject* LookupCmdID(UINT id);
/* Return the current application icon. */
HICON GetAwtIcon();
HICON GetAwtIconSm();
// Calculate a wave-like value out of the integer 'value' and
// the specified period.
// The argument 'value' is an integer 0, 1, 2, ... *infinity*.
//
// Examples:
// Period == 3
// Generated sequence: 0 1 2 1 0 .....
//
// Period == 4
// Generated sequence: 0 1 2 3 2 1 0 .....
static inline UINT CalculateWave(UINT value, const UINT period) {
if (period < 2) {
return 0;
}
// -2 is necessary to avoid repeating extreme values (0 and period-1)
value %= period * 2 -2;
if (value >= period) {
value = period * 2 -2 - value;
}
return value;
}
HICON GetSecurityWarningIcon(UINT index, UINT w, UINT h);
/* Turns on/off dialog modality for the system. */
INLINE AwtDialog* SetModal(AwtDialog* frame) {
AwtDialog* previousDialog = m_pModalDialog;
m_pModalDialog = frame;
return previousDialog;
};
INLINE void ResetModal(AwtDialog* oldFrame) { m_pModalDialog = oldFrame; };
INLINE BOOL IsModal() { return (m_pModalDialog != NULL); };
INLINE AwtDialog* GetModalDialog(void) { return m_pModalDialog; };
/* Stops the current message pump (normally a modal dialog pump) */
INLINE void StopMessagePump() { m_breakOnError = TRUE; }
/* Debug settings */
INLINE void SetVerbose(long flag) { m_verbose = (flag != 0); }
INLINE void SetVerify(long flag) { m_verifyComponents = (flag != 0); }
INLINE void SetBreak(long flag) { m_breakOnError = (flag != 0); }
INLINE void SetHeapCheck(long flag);
static void SetBusy(BOOL busy);
static bool GetIntegerProperty(LPTSTR prop, int& value);
/* Set and get the default input method Window handler. */
INLINE void SetInputMethodWindow(HWND inputMethodHWnd) { m_inputMethodHWnd = inputMethodHWnd; }
INLINE HWND GetInputMethodWindow() { return m_inputMethodHWnd; }
static VOID CALLBACK PrimaryIdleFunc();
static VOID CALLBACK SecondaryIdleFunc();
static BOOL CALLBACK CommonPeekMessageFunc(MSG& msg);
static BOOL activateKeyboardLayout(HKL hkl);
static INLINE DPI_AWARENESS_CONTEXT GetToolkitDpiAwarenessContext()
{
return lpGetThreadDpiAwarenessContext != NULL ? lpGetThreadDpiAwarenessContext() : NULL;
}
static INLINE DPI_AWARENESS_CONTEXT SetToolkitDpiAwarenessContext(DPI_AWARENESS_CONTEXT context)
{
return lpSetThreadDpiAwarenessContext != NULL ? lpSetThreadDpiAwarenessContext(context) : NULL;
}
static INLINE void UpdateToolkitDpiAwarenessContext(DPI_AWARENESS_CONTEXT context = NULL)
{
if (AwtToolkit::IsMainThread()) {
_UpdateToolkitDpiAwarenessContext(context);
} else {
AwtToolkit::GetInstance().InvokeFunctionLater(_UpdateToolkitDpiAwarenessContext, static_cast<void*>(context));
}
}
static INLINE BOOL EnableNcDpiScaling(HWND hwnd)
{
return lpEnableNonClientDpiScaling != NULL ? lpEnableNonClientDpiScaling(hwnd) : FALSE;
}
HANDLE m_waitEvent;
DWORD eventNumber;
private:
HWND CreateToolkitWnd(LPCTSTR name);
BOOL m_localPump;
DWORD m_mainThreadId;
HWND m_toolkitHWnd;
HWND m_inputMethodHWnd;
BOOL m_verbose;
BOOL m_isActive; // set to FALSE at beginning of Dispose
BOOL m_isDisposed; // set to TRUE at end of Dispose
BOOL m_areExtraMouseButtonsEnabled;
BOOL m_vmSignalled; // set to TRUE if QUERYENDSESSION has successfully
// raised SIGTERM
BOOL m_verifyComponents;
BOOL m_breakOnError;
BOOL m_breakMessageLoop;
UINT m_messageLoopResult;
class AwtComponent* m_lastMouseOver;
BOOL m_mouseDown;
HHOOK m_hGetMessageHook;
HHOOK m_hMouseLLHook;
UINT_PTR m_timer;
class AwtCmdIDList* m_cmdIDs;
BYTE m_lastKeyboardState[KB_STATE_SIZE];
CriticalSection m_lockKB;
static AwtToolkit theInstance;
/* The current modal dialog frame (normally NULL). */
AwtDialog* m_pModalDialog;
/* The WToolkit peer instance */
jobject m_peer;
HMODULE m_dllHandle; /* The module handle. */
CriticalSection m_Sync;
static GetThreadDpiAwarenessContextFunc *lpGetThreadDpiAwarenessContext;
static SetThreadDpiAwarenessContextFunc *lpSetThreadDpiAwarenessContext;
static AreDpiAwarenessContextsEqualFunc *lpAreDpiAwarenessContextsEqual;
static EnableNonClientDpiScalingFunc *lpEnableNonClientDpiScaling;
static void _UpdateToolkitDpiAwarenessContext(void*);
/* track display changes - used by palette-updating code.
This is a workaround for a windows bug that prevents
WM_PALETTECHANGED event from occurring immediately after
a WM_DISPLAYCHANGED event.
*/
private:
BOOL m_displayChanged; /* Tracks displayChanged events */
// 0 means we are not embedded.
DWORD m_embedderProcessID;
public:
BOOL HasDisplayChanged() { return m_displayChanged; }
void ResetDisplayChanged() { m_displayChanged = FALSE; }
void RegisterEmbedderProcessId(HWND);
BOOL IsEmbedderProcessId(const DWORD processID) const
{
return m_embedderProcessID && (processID == m_embedderProcessID);
}
private:
static JNIEnv *m_env;
static DWORD m_threadId;
public:
static void SetEnv(JNIEnv *env);
static JNIEnv* GetEnv();
static BOOL GetScreenInsets(int screenNum, RECT * rect);
// If the DWM is active, this function uses
// DwmGetWindowAttribute()/DWMWA_EXTENDED_FRAME_BOUNDS.
// Otherwise, fall back to regular ::GetWindowRect().
// See 6711576 for more details.
static void GetWindowRect(HWND hWnd, LPRECT lpRect);
private:
// The window handle of a toplevel window last seen under the mouse cursor.
// See MouseLowLevelHook() for details.
HWND m_lastWindowUnderMouse;
public:
HWND GetWindowUnderMouse() { return m_lastWindowUnderMouse; }
void InstallMouseLowLevelHook();
void UninstallMouseLowLevelHook();
/* AWT preloading (early Toolkit thread start)
*/
public:
/* Toolkit preload action class.
* Preload actions should be registered with
* AwtToolkit::getInstance().GetPreloadThread().AddAction().
* AwtToolkit thread calls InitImpl method at the beghining
* and CleanImpl(false) before exiting for all registered actions.
* If an application provides own Toolkit thread
* (sun.awt.windows.WToolkit.embeddedInit), the thread calls Clean(true)
* for each action.
*/
class PreloadThread; // forward declaration
class PreloadAction {
friend class PreloadThread;
public:
PreloadAction() : initThreadId(0), pNext(NULL) {}
virtual ~PreloadAction() {}
protected:
// called by PreloadThread or as result
// of EnsureInited() call (on Toolkit thread!).
virtual void InitImpl() = 0;
// called by PreloadThread (before exiting).
// reInit == false: normal shutdown;
// reInit == true: PreloadThread is shutting down due external
// Toolkit thread was provided.
virtual void CleanImpl(bool reInit) = 0;
public:
// Initialized the action on the Toolkit thread if not yet initialized.
bool EnsureInited();
// returns thread ID which the action was inited on (0 if not inited)
DWORD GetInitThreadID();
// Allows to deinitialize action earlier.
// The method must be called on the Toolkit thread only.
// returns true on success,
// false if the action was inited on other thread.
bool Clean();
private:
unsigned initThreadId;
// lock for Init/Clean
CriticalSection initLock;
// Chain support (for PreloadThread)
PreloadAction *pNext; // for action chain used by PreloadThread
void SetNext(PreloadAction *pNext) { this->pNext = pNext; }
PreloadAction *GetNext() { return pNext; }
// wrapper for AwtToolkit::InvokeFunction
static void InitWrapper(void *param);
void Init();
void Clean(bool reInit);
};
/** Toolkit preload thread class.
*/
class PreloadThread {
public:
PreloadThread();
~PreloadThread();
// adds action & start the thread if not yet started
bool AddAction(PreloadAction *pAction);
// sets termination flag; returns true if the thread is running.
// wrongThread specifies cause of the termination:
// false means termination on the application shutdown;
// wrongThread is used as reInit parameter for action cleanup.
bool Terminate(bool wrongThread);
bool InvokeAndTerminate(void(_cdecl *fn)(void *), void *param);
// waits for the the thread completion;
// use the method after Terminate() only if Terminate() returned true
INLINE void Wait4Finish() {
::WaitForSingleObject(hFinished, INFINITE);
}
INLINE unsigned GetThreadId() {
CriticalSection::Lock lock(threadLock);
return threadId;
}
INLINE bool IsWrongThread() {
CriticalSection::Lock lock(threadLock);
return wrongThread;
}
// returns true if the current thread is "preload" thread
bool OnPreloadThread();
private:
// data access lock
CriticalSection threadLock;
// the thread status
enum Status {
None = -1, // initial
Preloading = 0, // preloading in progress
RunningToolkit, // Running as Toolkit thread
Cleaning, // exited from Toolkit thread proc, cleaning
Finished //
} status;
// "wrong thread" flag
bool wrongThread;
// thread proc (calls (this)param->ThreadProc())
static unsigned WINAPI StaticThreadProc(void *param);
unsigned ThreadProc();
INLINE void AwakeThread() {
::SetEvent(hAwake);
}
// if threadId != 0 -> we are running
unsigned threadId;
// ThreadProc sets the event on exit
HANDLE hFinished;
// ThreadProc waits on the event for NewAction/Terminate/InvokeAndTerminate
HANDLE hAwake;
// function/param to invoke (InvokeAndTerminate)
// if execFunc == NULL => just terminate
void(_cdecl *execFunc)(void *);
void *execParam;
// action chain
PreloadAction *pActionChain;
PreloadAction *pLastProcessedAction;
// returns next action in the list (NULL if no more actions)
PreloadAction* GetNextAction();
};
INLINE PreloadThread& GetPreloadThread() { return preloadThread; }
private:
PreloadThread preloadThread;
};
/* creates an instance of T and assigns it to the argument, but only if
the argument is initially NULL. Supposed to be thread-safe.
returns the new value of the argument. I'm not using volatile here
as InterlockedCompareExchange ensures volatile semantics
and acquire/release.
The function is useful when used with static POD NULL-initialized
pointers, as they are guaranteed to be NULL before any dynamic
initialization takes place. This function turns such a pointer
into a thread-safe singleton, working regardless of dynamic
initialization order. Destruction problem is not solved,
we don't need it here.
*/
template<typename T> inline T* SafeCreate(T* &pArg) {
/* this implementation has no locks, it just destroys the object if it
fails to be the first to init. another way would be using a special
flag pointer value to mark the pointer as "being initialized". */
T* pTemp = (T*)InterlockedCompareExchangePointer((void**)&pArg, NULL, NULL);
if (pTemp != NULL) return pTemp;
T* pNew = new T;
pTemp = (T*)InterlockedCompareExchangePointer((void**)&pArg, pNew, NULL);
if (pTemp != NULL) {
// we failed it - another thread has already initialized pArg
delete pNew;
return pTemp;
} else {
return pNew;
}
}
#endif /* AWT_TOOLKIT_H */