diff options
Diffstat (limited to 'intern/ghost/intern/GHOST_SystemWin32.cpp')
-rw-r--r-- | intern/ghost/intern/GHOST_SystemWin32.cpp | 3072 |
1 files changed, 1565 insertions, 1507 deletions
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 35a9cfac817..95f969bcb67 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -21,7 +21,6 @@ * \ingroup GHOST */ - #include "GHOST_SystemWin32.h" #include "GHOST_EventDragnDrop.h" @@ -53,71 +52,71 @@ #endif #ifdef WITH_INPUT_NDOF - #include "GHOST_NDOFManagerWin32.h" +# include "GHOST_NDOFManagerWin32.h" #endif // Key code values not found in winuser.h #ifndef VK_MINUS -#define VK_MINUS 0xBD -#endif // VK_MINUS +# define VK_MINUS 0xBD +#endif // VK_MINUS #ifndef VK_SEMICOLON -#define VK_SEMICOLON 0xBA -#endif // VK_SEMICOLON +# define VK_SEMICOLON 0xBA +#endif // VK_SEMICOLON #ifndef VK_PERIOD -#define VK_PERIOD 0xBE -#endif // VK_PERIOD +# define VK_PERIOD 0xBE +#endif // VK_PERIOD #ifndef VK_COMMA -#define VK_COMMA 0xBC -#endif // VK_COMMA +# define VK_COMMA 0xBC +#endif // VK_COMMA #ifndef VK_QUOTE -#define VK_QUOTE 0xDE -#endif // VK_QUOTE +# define VK_QUOTE 0xDE +#endif // VK_QUOTE #ifndef VK_BACK_QUOTE -#define VK_BACK_QUOTE 0xC0 -#endif // VK_BACK_QUOTE +# define VK_BACK_QUOTE 0xC0 +#endif // VK_BACK_QUOTE #ifndef VK_SLASH -#define VK_SLASH 0xBF -#endif // VK_SLASH +# define VK_SLASH 0xBF +#endif // VK_SLASH #ifndef VK_BACK_SLASH -#define VK_BACK_SLASH 0xDC -#endif // VK_BACK_SLASH +# define VK_BACK_SLASH 0xDC +#endif // VK_BACK_SLASH #ifndef VK_EQUALS -#define VK_EQUALS 0xBB -#endif // VK_EQUALS +# define VK_EQUALS 0xBB +#endif // VK_EQUALS #ifndef VK_OPEN_BRACKET -#define VK_OPEN_BRACKET 0xDB -#endif // VK_OPEN_BRACKET +# define VK_OPEN_BRACKET 0xDB +#endif // VK_OPEN_BRACKET #ifndef VK_CLOSE_BRACKET -#define VK_CLOSE_BRACKET 0xDD -#endif // VK_CLOSE_BRACKET +# define VK_CLOSE_BRACKET 0xDD +#endif // VK_CLOSE_BRACKET #ifndef VK_GR_LESS -#define VK_GR_LESS 0xE2 -#endif // VK_GR_LESS +# define VK_GR_LESS 0xE2 +#endif // VK_GR_LESS #ifndef VK_MEDIA_NEXT_TRACK -#define VK_MEDIA_NEXT_TRACK 0xB0 -#endif // VK_MEDIA_NEXT_TRACK +# define VK_MEDIA_NEXT_TRACK 0xB0 +#endif // VK_MEDIA_NEXT_TRACK #ifndef VK_MEDIA_PREV_TRACK -#define VK_MEDIA_PREV_TRACK 0xB1 -#endif // VK_MEDIA_PREV_TRACK +# define VK_MEDIA_PREV_TRACK 0xB1 +#endif // VK_MEDIA_PREV_TRACK #ifndef VK_MEDIA_STOP -#define VK_MEDIA_STOP 0xB2 -#endif // VK_MEDIA_STOP +# define VK_MEDIA_STOP 0xB2 +#endif // VK_MEDIA_STOP #ifndef VK_MEDIA_PLAY_PAUSE -#define VK_MEDIA_PLAY_PAUSE 0xB3 -#endif // VK_MEDIA_PLAY_PAUSE +# define VK_MEDIA_PLAY_PAUSE 0xB3 +#endif // VK_MEDIA_PLAY_PAUSE // Window message newer than Windows 7 #ifndef WM_DPICHANGED -#define WM_DPICHANGED 0x02E0 -#endif // WM_DPICHANGED +# define WM_DPICHANGED 0x02E0 +#endif // WM_DPICHANGED #ifndef WM_POINTERUPDATE -#define WM_POINTERUPDATE 0x0245 -#endif // WM_POINTERUPDATE +# define WM_POINTERUPDATE 0x0245 +#endif // WM_POINTERUPDATE -#define WM_POINTERDOWN 0x0246 -#define WM_POINTERUP 0x0247 +#define WM_POINTERDOWN 0x0246 +#define WM_POINTERUP 0x0247 /* Workaround for some laptop touchpads, some of which seems to * have driver issues which makes it so window function receives @@ -133,175 +132,171 @@ static void initRawInput() { #ifdef WITH_INPUT_NDOF - #define DEVICE_COUNT 2 +# define DEVICE_COUNT 2 #else - #define DEVICE_COUNT 1 +# define DEVICE_COUNT 1 #endif - RAWINPUTDEVICE devices[DEVICE_COUNT]; - memset(devices, 0, DEVICE_COUNT * sizeof(RAWINPUTDEVICE)); + RAWINPUTDEVICE devices[DEVICE_COUNT]; + memset(devices, 0, DEVICE_COUNT * sizeof(RAWINPUTDEVICE)); - // Initiates WM_INPUT messages from keyboard - // That way GHOST can retrieve true keys - devices[0].usUsagePage = 0x01; - devices[0].usUsage = 0x06; /* http://msdn.microsoft.com/en-us/windows/hardware/gg487473.aspx */ + // Initiates WM_INPUT messages from keyboard + // That way GHOST can retrieve true keys + devices[0].usUsagePage = 0x01; + devices[0].usUsage = 0x06; /* http://msdn.microsoft.com/en-us/windows/hardware/gg487473.aspx */ #ifdef WITH_INPUT_NDOF - // multi-axis mouse (SpaceNavigator, etc.) - devices[1].usUsagePage = 0x01; - devices[1].usUsage = 0x08; + // multi-axis mouse (SpaceNavigator, etc.) + devices[1].usUsagePage = 0x01; + devices[1].usUsage = 0x08; #endif - if (RegisterRawInputDevices(devices, DEVICE_COUNT, sizeof(RAWINPUTDEVICE))) - ; // yay! - else - GHOST_PRINTF("could not register for RawInput: %d\n", (int)GetLastError()); + if (RegisterRawInputDevices(devices, DEVICE_COUNT, sizeof(RAWINPUTDEVICE))) + ; // yay! + else + GHOST_PRINTF("could not register for RawInput: %d\n", (int)GetLastError()); #undef DEVICE_COUNT } #ifndef DPI_ENUMS_DECLARED typedef enum PROCESS_DPI_AWARENESS { - PROCESS_DPI_UNAWARE = 0, - PROCESS_SYSTEM_DPI_AWARE = 1, - PROCESS_PER_MONITOR_DPI_AWARE = 2 + PROCESS_DPI_UNAWARE = 0, + PROCESS_SYSTEM_DPI_AWARE = 1, + PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS; typedef enum MONITOR_DPI_TYPE { - MDT_EFFECTIVE_DPI = 0, - MDT_ANGULAR_DPI = 1, - MDT_RAW_DPI = 2, - MDT_DEFAULT = MDT_EFFECTIVE_DPI + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE; -#define USER_DEFAULT_SCREEN_DPI 96 +# define USER_DEFAULT_SCREEN_DPI 96 -#define DPI_ENUMS_DECLARED +# define DPI_ENUMS_DECLARED #endif -typedef HRESULT(API * GHOST_WIN32_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); -typedef BOOL(API * GHOST_WIN32_EnableNonClientDpiScaling)(HWND); +typedef HRESULT(API *GHOST_WIN32_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); +typedef BOOL(API *GHOST_WIN32_EnableNonClientDpiScaling)(HWND); -GHOST_SystemWin32::GHOST_SystemWin32() - : m_hasPerformanceCounter(false), m_freq(0), m_start(0) +GHOST_SystemWin32::GHOST_SystemWin32() : m_hasPerformanceCounter(false), m_freq(0), m_start(0) { - m_displayManager = new GHOST_DisplayManagerWin32(); - GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::GHOST_SystemWin32(): m_displayManager==0\n"); - m_displayManager->initialize(); - - m_consoleStatus = 1; - - // Tell Windows we are per monitor DPI aware. This disables the default - // blurry scaling and enables WM_DPICHANGED to allow us to draw at proper DPI. - HMODULE m_shcore = ::LoadLibrary("Shcore.dll"); - if (m_shcore) { - GHOST_WIN32_SetProcessDpiAwareness fpSetProcessDpiAwareness = - (GHOST_WIN32_SetProcessDpiAwareness) ::GetProcAddress(m_shcore, "SetProcessDpiAwareness"); - - if (fpSetProcessDpiAwareness) { - fpSetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); - } - } - - // Check if current keyboard layout uses AltGr and save keylayout ID for - // specialized handling if keys like VK_OEM_*. I.e. french keylayout - // generates VK_OEM_8 for their exclamation key (key left of right shift) - this->handleKeyboardChange(); - // Require COM for GHOST_DropTargetWin32 created in GHOST_WindowWin32. - OleInitialize(0); + m_displayManager = new GHOST_DisplayManagerWin32(); + GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::GHOST_SystemWin32(): m_displayManager==0\n"); + m_displayManager->initialize(); + + m_consoleStatus = 1; + + // Tell Windows we are per monitor DPI aware. This disables the default + // blurry scaling and enables WM_DPICHANGED to allow us to draw at proper DPI. + HMODULE m_shcore = ::LoadLibrary("Shcore.dll"); + if (m_shcore) { + GHOST_WIN32_SetProcessDpiAwareness fpSetProcessDpiAwareness = + (GHOST_WIN32_SetProcessDpiAwareness)::GetProcAddress(m_shcore, "SetProcessDpiAwareness"); + + if (fpSetProcessDpiAwareness) { + fpSetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); + } + } + + // Check if current keyboard layout uses AltGr and save keylayout ID for + // specialized handling if keys like VK_OEM_*. I.e. french keylayout + // generates VK_OEM_8 for their exclamation key (key left of right shift) + this->handleKeyboardChange(); + // Require COM for GHOST_DropTargetWin32 created in GHOST_WindowWin32. + OleInitialize(0); #ifdef WITH_INPUT_NDOF - m_ndofManager = new GHOST_NDOFManagerWin32(*this); + m_ndofManager = new GHOST_NDOFManagerWin32(*this); #endif } GHOST_SystemWin32::~GHOST_SystemWin32() { - // Shutdown COM - OleUninitialize(); - toggleConsole(1); + // Shutdown COM + OleUninitialize(); + toggleConsole(1); } - GHOST_TUns64 GHOST_SystemWin32::getMilliSeconds() const { - // Hardware does not support high resolution timers. We will use GetTickCount instead then. - if (!m_hasPerformanceCounter) { - return ::GetTickCount(); - } + // Hardware does not support high resolution timers. We will use GetTickCount instead then. + if (!m_hasPerformanceCounter) { + return ::GetTickCount(); + } - // Retrieve current count - __int64 count = 0; - ::QueryPerformanceCounter((LARGE_INTEGER *)&count); + // Retrieve current count + __int64 count = 0; + ::QueryPerformanceCounter((LARGE_INTEGER *)&count); - // Calculate the time passed since system initialization. - __int64 delta = 1000 * (count - m_start); + // Calculate the time passed since system initialization. + __int64 delta = 1000 * (count - m_start); - GHOST_TUns64 t = (GHOST_TUns64)(delta / m_freq); - return t; + GHOST_TUns64 t = (GHOST_TUns64)(delta / m_freq); + return t; } - GHOST_TUns8 GHOST_SystemWin32::getNumDisplays() const { - GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::getNumDisplays(): m_displayManager==0\n"); - GHOST_TUns8 numDisplays; - m_displayManager->getNumDisplays(numDisplays); - return numDisplays; + GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::getNumDisplays(): m_displayManager==0\n"); + GHOST_TUns8 numDisplays; + m_displayManager->getNumDisplays(numDisplays); + return numDisplays; } - void GHOST_SystemWin32::getMainDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const { - width = ::GetSystemMetrics(SM_CXSCREEN); - height = ::GetSystemMetrics(SM_CYSCREEN); + width = ::GetSystemMetrics(SM_CXSCREEN); + height = ::GetSystemMetrics(SM_CYSCREEN); } void GHOST_SystemWin32::getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const { - width = ::GetSystemMetrics(SM_CXVIRTUALSCREEN); - height = ::GetSystemMetrics(SM_CYVIRTUALSCREEN); + width = ::GetSystemMetrics(SM_CXVIRTUALSCREEN); + height = ::GetSystemMetrics(SM_CYVIRTUALSCREEN); } -GHOST_IWindow *GHOST_SystemWin32::createWindow( - const STR_String &title, - GHOST_TInt32 left, GHOST_TInt32 top, - GHOST_TUns32 width, GHOST_TUns32 height, - GHOST_TWindowState state, GHOST_TDrawingContextType type, - GHOST_GLSettings glSettings, - const bool exclusive, - const GHOST_TEmbedderWindowID parentWindow) +GHOST_IWindow *GHOST_SystemWin32::createWindow(const STR_String &title, + GHOST_TInt32 left, + GHOST_TInt32 top, + GHOST_TUns32 width, + GHOST_TUns32 height, + GHOST_TWindowState state, + GHOST_TDrawingContextType type, + GHOST_GLSettings glSettings, + const bool exclusive, + const GHOST_TEmbedderWindowID parentWindow) { - GHOST_WindowWin32 *window = - new GHOST_WindowWin32( - this, - title, - left, - top, - width, - height, - state, - type, - ((glSettings.flags & GHOST_glStereoVisual) != 0), - ((glSettings.flags & GHOST_glAlphaBackground) != 0), - glSettings.numOfAASamples, - parentWindow, - ((glSettings.flags & GHOST_glDebugContext) != 0)); - - if (window->getValid()) { - // Store the pointer to the window - m_windowManager->addWindow(window); - m_windowManager->setActiveWindow(window); - } - else { - GHOST_PRINT("GHOST_SystemWin32::createWindow(): window invalid\n"); - delete window; - window = NULL; - } - - return window; + GHOST_WindowWin32 *window = new GHOST_WindowWin32( + this, + title, + left, + top, + width, + height, + state, + type, + ((glSettings.flags & GHOST_glStereoVisual) != 0), + ((glSettings.flags & GHOST_glAlphaBackground) != 0), + glSettings.numOfAASamples, + parentWindow, + ((glSettings.flags & GHOST_glDebugContext) != 0)); + + if (window->getValid()) { + // Store the pointer to the window + m_windowManager->addWindow(window); + m_windowManager->setActiveWindow(window); + } + else { + GHOST_PRINT("GHOST_SystemWin32::createWindow(): window invalid\n"); + delete window; + window = NULL; + } + + return window; } - /** * Create a new offscreen context. * Never explicitly delete the window, use #disposeContext() instead. @@ -309,85 +304,97 @@ GHOST_IWindow *GHOST_SystemWin32::createWindow( */ GHOST_IContext *GHOST_SystemWin32::createOffscreenContext() { - bool debug_context = false; /* TODO: inform as a parameter */ - - GHOST_Context *context; - - HWND wnd = CreateWindowA("STATIC", - "BlenderGLEW", - WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, - 0, 0, 64, 64, - NULL, NULL, - GetModuleHandle(NULL), NULL - ); - - HDC mHDC = GetDC(wnd); - HDC prev_hdc = wglGetCurrentDC(); - HGLRC prev_context = wglGetCurrentContext(); + bool debug_context = false; /* TODO: inform as a parameter */ + + GHOST_Context *context; + + HWND wnd = CreateWindowA("STATIC", + "BlenderGLEW", + WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + 0, + 0, + 64, + 64, + NULL, + NULL, + GetModuleHandle(NULL), + NULL); + + HDC mHDC = GetDC(wnd); + HDC prev_hdc = wglGetCurrentDC(); + HGLRC prev_context = wglGetCurrentContext(); #if defined(WITH_GL_PROFILE_CORE) - for (int minor = 5; minor >= 0; --minor) { - context = new GHOST_ContextWGL( - false, true, 0, - wnd, mHDC, - WGL_CONTEXT_CORE_PROFILE_BIT_ARB, - 4, minor, - (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0), - GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY); - - if (context->initializeDrawingContext()) { - goto finished; - } - else { - delete context; - } - } - - context = new GHOST_ContextWGL( - false, true, 0, - wnd, mHDC, - WGL_CONTEXT_CORE_PROFILE_BIT_ARB, - 3, 3, - (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0), - GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY); - - if (context->initializeDrawingContext()) { - goto finished; - } - else { - MessageBox( - NULL, - "A graphics card and driver with support for OpenGL 3.3 or higher is required.\n" - "Installing the latest driver for your graphics card may resolve the issue.\n\n" - "The program will now close.", - "Blender - Unsupported Graphics Card or Driver", - MB_OK | MB_ICONERROR); - delete context; - exit(); - } + for (int minor = 5; minor >= 0; --minor) { + context = new GHOST_ContextWGL(false, + true, + 0, + wnd, + mHDC, + WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + 4, + minor, + (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0), + GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY); + + if (context->initializeDrawingContext()) { + goto finished; + } + else { + delete context; + } + } + + context = new GHOST_ContextWGL(false, + true, + 0, + wnd, + mHDC, + WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + 3, + 3, + (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0), + GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY); + + if (context->initializeDrawingContext()) { + goto finished; + } + else { + MessageBox(NULL, + "A graphics card and driver with support for OpenGL 3.3 or higher is required.\n" + "Installing the latest driver for your graphics card may resolve the issue.\n\n" + "The program will now close.", + "Blender - Unsupported Graphics Card or Driver", + MB_OK | MB_ICONERROR); + delete context; + exit(); + } #elif defined(WITH_GL_PROFILE_COMPAT) - // ask for 2.1 context, driver gives any GL version >= 2.1 (hopefully the latest compatibility profile) - // 2.1 ignores the profile bit & is incompatible with core profile - context = new GHOST_ContextWGL( - false, true, 0, - NULL, NULL, - 0, // no profile bit - 2, 1, - (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0), - GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY); - - if (context->initializeDrawingContext()) { - return context; - } - else { - delete context; - } + // ask for 2.1 context, driver gives any GL version >= 2.1 (hopefully the latest compatibility profile) + // 2.1 ignores the profile bit & is incompatible with core profile + context = new GHOST_ContextWGL(false, + true, + 0, + NULL, + NULL, + 0, // no profile bit + 2, + 1, + (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0), + GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY); + + if (context->initializeDrawingContext()) { + return context; + } + else { + delete context; + } #else -# error // must specify either core or compat at build time +# error // must specify either core or compat at build time #endif finished: - wglMakeCurrent(prev_hdc, prev_context); - return context; + wglMakeCurrent(prev_hdc, prev_context); + return context; } /** @@ -397,255 +404,244 @@ finished: */ GHOST_TSuccess GHOST_SystemWin32::disposeContext(GHOST_IContext *context) { - delete context; + delete context; - return GHOST_kSuccess; + return GHOST_kSuccess; } - bool GHOST_SystemWin32::processEvents(bool waitForEvent) { - MSG msg; - bool hasEventHandled = false; + MSG msg; + bool hasEventHandled = false; - do { - GHOST_TimerManager *timerMgr = getTimerManager(); + do { + GHOST_TimerManager *timerMgr = getTimerManager(); - if (waitForEvent && !::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { + if (waitForEvent && !::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { #if 1 - ::Sleep(1); + ::Sleep(1); #else - GHOST_TUns64 next = timerMgr->nextFireTime(); - GHOST_TInt64 maxSleep = next - getMilliSeconds(); - - if (next == GHOST_kFireTimeNever) { - ::WaitMessage(); - } - else if (maxSleep >= 0.0) { - ::SetTimer(NULL, 0, maxSleep, NULL); - ::WaitMessage(); - ::KillTimer(NULL, 0); - } + GHOST_TUns64 next = timerMgr->nextFireTime(); + GHOST_TInt64 maxSleep = next - getMilliSeconds(); + + if (next == GHOST_kFireTimeNever) { + ::WaitMessage(); + } + else if (maxSleep >= 0.0) { + ::SetTimer(NULL, 0, maxSleep, NULL); + ::WaitMessage(); + ::KillTimer(NULL, 0); + } #endif - } - - if (timerMgr->fireTimers(getMilliSeconds())) { - hasEventHandled = true; - } - - // Process all the events waiting for us - while (::PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) != 0) { - // TranslateMessage doesn't alter the message, and doesn't change our raw keyboard data. - // Needed for MapVirtualKey or if we ever need to get chars from wm_ime_char or similar. - ::TranslateMessage(&msg); - ::DispatchMessageW(&msg); - hasEventHandled = true; - } - } while (waitForEvent && !hasEventHandled); - - return hasEventHandled; + } + + if (timerMgr->fireTimers(getMilliSeconds())) { + hasEventHandled = true; + } + + // Process all the events waiting for us + while (::PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) != 0) { + // TranslateMessage doesn't alter the message, and doesn't change our raw keyboard data. + // Needed for MapVirtualKey or if we ever need to get chars from wm_ime_char or similar. + ::TranslateMessage(&msg); + ::DispatchMessageW(&msg); + hasEventHandled = true; + } + } while (waitForEvent && !hasEventHandled); + + return hasEventHandled; } - GHOST_TSuccess GHOST_SystemWin32::getCursorPosition(GHOST_TInt32 &x, GHOST_TInt32 &y) const { - POINT point; - if (::GetCursorPos(&point)) { - x = point.x; - y = point.y; - return GHOST_kSuccess; - } - return GHOST_kFailure; + POINT point; + if (::GetCursorPos(&point)) { + x = point.x; + y = point.y; + return GHOST_kSuccess; + } + return GHOST_kFailure; } - GHOST_TSuccess GHOST_SystemWin32::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y) { - if (!::GetActiveWindow()) - return GHOST_kFailure; - return ::SetCursorPos(x, y) == TRUE ? GHOST_kSuccess : GHOST_kFailure; + if (!::GetActiveWindow()) + return GHOST_kFailure; + return ::SetCursorPos(x, y) == TRUE ? GHOST_kSuccess : GHOST_kFailure; } - GHOST_TSuccess GHOST_SystemWin32::getModifierKeys(GHOST_ModifierKeys &keys) const { - bool down = HIBYTE(::GetKeyState(VK_LSHIFT)) != 0; - keys.set(GHOST_kModifierKeyLeftShift, down); - down = HIBYTE(::GetKeyState(VK_RSHIFT)) != 0; - keys.set(GHOST_kModifierKeyRightShift, down); - - down = HIBYTE(::GetKeyState(VK_LMENU)) != 0; - keys.set(GHOST_kModifierKeyLeftAlt, down); - down = HIBYTE(::GetKeyState(VK_RMENU)) != 0; - keys.set(GHOST_kModifierKeyRightAlt, down); - - down = HIBYTE(::GetKeyState(VK_LCONTROL)) != 0; - keys.set(GHOST_kModifierKeyLeftControl, down); - down = HIBYTE(::GetKeyState(VK_RCONTROL)) != 0; - keys.set(GHOST_kModifierKeyRightControl, down); - - bool lwindown = HIBYTE(::GetKeyState(VK_LWIN)) != 0; - bool rwindown = HIBYTE(::GetKeyState(VK_RWIN)) != 0; - if (lwindown || rwindown) - keys.set(GHOST_kModifierKeyOS, true); - else - keys.set(GHOST_kModifierKeyOS, false); - return GHOST_kSuccess; + bool down = HIBYTE(::GetKeyState(VK_LSHIFT)) != 0; + keys.set(GHOST_kModifierKeyLeftShift, down); + down = HIBYTE(::GetKeyState(VK_RSHIFT)) != 0; + keys.set(GHOST_kModifierKeyRightShift, down); + + down = HIBYTE(::GetKeyState(VK_LMENU)) != 0; + keys.set(GHOST_kModifierKeyLeftAlt, down); + down = HIBYTE(::GetKeyState(VK_RMENU)) != 0; + keys.set(GHOST_kModifierKeyRightAlt, down); + + down = HIBYTE(::GetKeyState(VK_LCONTROL)) != 0; + keys.set(GHOST_kModifierKeyLeftControl, down); + down = HIBYTE(::GetKeyState(VK_RCONTROL)) != 0; + keys.set(GHOST_kModifierKeyRightControl, down); + + bool lwindown = HIBYTE(::GetKeyState(VK_LWIN)) != 0; + bool rwindown = HIBYTE(::GetKeyState(VK_RWIN)) != 0; + if (lwindown || rwindown) + keys.set(GHOST_kModifierKeyOS, true); + else + keys.set(GHOST_kModifierKeyOS, false); + return GHOST_kSuccess; } - GHOST_TSuccess GHOST_SystemWin32::getButtons(GHOST_Buttons &buttons) const { - /* Check for swapped buttons (left-handed mouse buttons) - * GetAsyncKeyState() will give back the state of the physical mouse buttons. - */ - bool swapped = ::GetSystemMetrics(SM_SWAPBUTTON) == TRUE; + /* Check for swapped buttons (left-handed mouse buttons) + * GetAsyncKeyState() will give back the state of the physical mouse buttons. + */ + bool swapped = ::GetSystemMetrics(SM_SWAPBUTTON) == TRUE; - bool down = HIBYTE(::GetKeyState(VK_LBUTTON)) != 0; - buttons.set(swapped ? GHOST_kButtonMaskRight : GHOST_kButtonMaskLeft, down); + bool down = HIBYTE(::GetKeyState(VK_LBUTTON)) != 0; + buttons.set(swapped ? GHOST_kButtonMaskRight : GHOST_kButtonMaskLeft, down); - down = HIBYTE(::GetKeyState(VK_MBUTTON)) != 0; - buttons.set(GHOST_kButtonMaskMiddle, down); + down = HIBYTE(::GetKeyState(VK_MBUTTON)) != 0; + buttons.set(GHOST_kButtonMaskMiddle, down); - down = HIBYTE(::GetKeyState(VK_RBUTTON)) != 0; - buttons.set(swapped ? GHOST_kButtonMaskLeft : GHOST_kButtonMaskRight, down); - return GHOST_kSuccess; + down = HIBYTE(::GetKeyState(VK_RBUTTON)) != 0; + buttons.set(swapped ? GHOST_kButtonMaskLeft : GHOST_kButtonMaskRight, down); + return GHOST_kSuccess; } - GHOST_TSuccess GHOST_SystemWin32::init() { - GHOST_TSuccess success = GHOST_System::init(); - - /* Disable scaling on high DPI displays on Vista */ - HMODULE - user32 = ::LoadLibraryA("user32.dll"); - typedef BOOL (WINAPI * LPFNSETPROCESSDPIAWARE)(); - LPFNSETPROCESSDPIAWARE SetProcessDPIAware = - (LPFNSETPROCESSDPIAWARE)GetProcAddress(user32, "SetProcessDPIAware"); - if (SetProcessDPIAware) - SetProcessDPIAware(); - FreeLibrary(user32); - initRawInput(); - - // Determine whether this system has a high frequency performance counter. */ - m_hasPerformanceCounter = ::QueryPerformanceFrequency((LARGE_INTEGER *)&m_freq) == TRUE; - if (m_hasPerformanceCounter) { - GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer available\n"); - ::QueryPerformanceCounter((LARGE_INTEGER *)&m_start); - } - else { - GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer not available\n"); - } - - if (success) { - WNDCLASSW wc = {0}; - wc.style = CS_HREDRAW | CS_VREDRAW; - wc.lpfnWndProc = s_wndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hInstance = ::GetModuleHandle(0); - wc.hIcon = ::LoadIcon(wc.hInstance, "APPICON"); - - if (!wc.hIcon) { - ::LoadIcon(NULL, IDI_APPLICATION); - } - wc.hCursor = ::LoadCursor(0, IDC_ARROW); - wc.hbrBackground = + GHOST_TSuccess success = GHOST_System::init(); + + /* Disable scaling on high DPI displays on Vista */ + HMODULE + user32 = ::LoadLibraryA("user32.dll"); + typedef BOOL(WINAPI * LPFNSETPROCESSDPIAWARE)(); + LPFNSETPROCESSDPIAWARE SetProcessDPIAware = (LPFNSETPROCESSDPIAWARE)GetProcAddress( + user32, "SetProcessDPIAware"); + if (SetProcessDPIAware) + SetProcessDPIAware(); + FreeLibrary(user32); + initRawInput(); + + // Determine whether this system has a high frequency performance counter. */ + m_hasPerformanceCounter = ::QueryPerformanceFrequency((LARGE_INTEGER *)&m_freq) == TRUE; + if (m_hasPerformanceCounter) { + GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer available\n"); + ::QueryPerformanceCounter((LARGE_INTEGER *)&m_start); + } + else { + GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer not available\n"); + } + + if (success) { + WNDCLASSW wc = {0}; + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = s_wndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = ::GetModuleHandle(0); + wc.hIcon = ::LoadIcon(wc.hInstance, "APPICON"); + + if (!wc.hIcon) { + ::LoadIcon(NULL, IDI_APPLICATION); + } + wc.hCursor = ::LoadCursor(0, IDC_ARROW); + wc.hbrBackground = #ifdef INW32_COMPISITING - (HBRUSH)CreateSolidBrush + (HBRUSH)CreateSolidBrush #endif - (0x00000000); - wc.lpszMenuName = 0; - wc.lpszClassName = L"GHOST_WindowClass"; + (0x00000000); + wc.lpszMenuName = 0; + wc.lpszClassName = L"GHOST_WindowClass"; - // Use RegisterClassEx for setting small icon - if (::RegisterClassW(&wc) == 0) { - success = GHOST_kFailure; - } - } + // Use RegisterClassEx for setting small icon + if (::RegisterClassW(&wc) == 0) { + success = GHOST_kFailure; + } + } - return success; + return success; } - GHOST_TSuccess GHOST_SystemWin32::exit() { - return GHOST_System::exit(); + return GHOST_System::exit(); } GHOST_TKey GHOST_SystemWin32::hardKey(RAWINPUT const &raw, int *keyDown, char *vk) { - GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); - GHOST_TKey key = GHOST_kKeyUnknown; - GHOST_ModifierKeys modifiers; - system->retrieveModifierKeys(modifiers); - - // RI_KEY_BREAK doesn't work for sticky keys release, so we also - // check for the up message - unsigned int msg = raw.data.keyboard.Message; - *keyDown = !(raw.data.keyboard.Flags & RI_KEY_BREAK) && msg != WM_KEYUP && msg != WM_SYSKEYUP; - - key = this->convertKey(raw.data.keyboard.VKey, raw.data.keyboard.MakeCode, (raw.data.keyboard.Flags & (RI_KEY_E1 | RI_KEY_E0))); - - // extra handling of modifier keys: don't send repeats out from GHOST - if (key >= GHOST_kKeyLeftShift && key <= GHOST_kKeyRightAlt) { - bool changed = false; - GHOST_TModifierKeyMask modifier; - switch (key) { - case GHOST_kKeyLeftShift: - { - changed = (modifiers.get(GHOST_kModifierKeyLeftShift) != (bool)*keyDown); - modifier = GHOST_kModifierKeyLeftShift; - break; - } - case GHOST_kKeyRightShift: - { - changed = (modifiers.get(GHOST_kModifierKeyRightShift) != (bool)*keyDown); - modifier = GHOST_kModifierKeyRightShift; - break; - } - case GHOST_kKeyLeftControl: - { - changed = (modifiers.get(GHOST_kModifierKeyLeftControl) != (bool)*keyDown); - modifier = GHOST_kModifierKeyLeftControl; - break; - } - case GHOST_kKeyRightControl: - { - changed = (modifiers.get(GHOST_kModifierKeyRightControl) != (bool)*keyDown); - modifier = GHOST_kModifierKeyRightControl; - break; - } - case GHOST_kKeyLeftAlt: - { - changed = (modifiers.get(GHOST_kModifierKeyLeftAlt) != (bool)*keyDown); - modifier = GHOST_kModifierKeyLeftAlt; - break; - } - case GHOST_kKeyRightAlt: - { - changed = (modifiers.get(GHOST_kModifierKeyRightAlt) != (bool)*keyDown); - modifier = GHOST_kModifierKeyRightAlt; - break; - } - default: - break; - } - - if (changed) { - modifiers.set(modifier, (bool)*keyDown); - system->storeModifierKeys(modifiers); - } - else { - key = GHOST_kKeyUnknown; - } - } - - - if (vk) *vk = raw.data.keyboard.VKey; - - return key; + GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); + GHOST_TKey key = GHOST_kKeyUnknown; + GHOST_ModifierKeys modifiers; + system->retrieveModifierKeys(modifiers); + + // RI_KEY_BREAK doesn't work for sticky keys release, so we also + // check for the up message + unsigned int msg = raw.data.keyboard.Message; + *keyDown = !(raw.data.keyboard.Flags & RI_KEY_BREAK) && msg != WM_KEYUP && msg != WM_SYSKEYUP; + + key = this->convertKey(raw.data.keyboard.VKey, + raw.data.keyboard.MakeCode, + (raw.data.keyboard.Flags & (RI_KEY_E1 | RI_KEY_E0))); + + // extra handling of modifier keys: don't send repeats out from GHOST + if (key >= GHOST_kKeyLeftShift && key <= GHOST_kKeyRightAlt) { + bool changed = false; + GHOST_TModifierKeyMask modifier; + switch (key) { + case GHOST_kKeyLeftShift: { + changed = (modifiers.get(GHOST_kModifierKeyLeftShift) != (bool)*keyDown); + modifier = GHOST_kModifierKeyLeftShift; + break; + } + case GHOST_kKeyRightShift: { + changed = (modifiers.get(GHOST_kModifierKeyRightShift) != (bool)*keyDown); + modifier = GHOST_kModifierKeyRightShift; + break; + } + case GHOST_kKeyLeftControl: { + changed = (modifiers.get(GHOST_kModifierKeyLeftControl) != (bool)*keyDown); + modifier = GHOST_kModifierKeyLeftControl; + break; + } + case GHOST_kKeyRightControl: { + changed = (modifiers.get(GHOST_kModifierKeyRightControl) != (bool)*keyDown); + modifier = GHOST_kModifierKeyRightControl; + break; + } + case GHOST_kKeyLeftAlt: { + changed = (modifiers.get(GHOST_kModifierKeyLeftAlt) != (bool)*keyDown); + modifier = GHOST_kModifierKeyLeftAlt; + break; + } + case GHOST_kKeyRightAlt: { + changed = (modifiers.get(GHOST_kModifierKeyRightAlt) != (bool)*keyDown); + modifier = GHOST_kModifierKeyRightAlt; + break; + } + default: + break; + } + + if (changed) { + modifiers.set(modifier, (bool)*keyDown); + system->storeModifierKeys(modifiers); + } + else { + key = GHOST_kKeyUnknown; + } + } + + if (vk) + *vk = raw.data.keyboard.VKey; + + return key; } //! note: this function can be extended to include other exotic cases as they arise. @@ -653,1167 +649,1229 @@ GHOST_TKey GHOST_SystemWin32::hardKey(RAWINPUT const &raw, int *keyDown, char *v // This is going to be a long list [T42426] GHOST_TKey GHOST_SystemWin32::processSpecialKey(short vKey, short scanCode) const { - GHOST_TKey key = GHOST_kKeyUnknown; - switch (PRIMARYLANGID(m_langId)) { - case LANG_FRENCH: - if (vKey == VK_OEM_8) key = GHOST_kKeyF13; // oem key; used purely for shortcuts . - break; - case LANG_ENGLISH: - if (SUBLANGID(m_langId) == SUBLANG_ENGLISH_UK && vKey == VK_OEM_8) // "`¬" - key = GHOST_kKeyAccentGrave; - break; - } - - return key; + GHOST_TKey key = GHOST_kKeyUnknown; + switch (PRIMARYLANGID(m_langId)) { + case LANG_FRENCH: + if (vKey == VK_OEM_8) + key = GHOST_kKeyF13; // oem key; used purely for shortcuts . + break; + case LANG_ENGLISH: + if (SUBLANGID(m_langId) == SUBLANG_ENGLISH_UK && vKey == VK_OEM_8) // "`¬" + key = GHOST_kKeyAccentGrave; + break; + } + + return key; } GHOST_TKey GHOST_SystemWin32::convertKey(short vKey, short scanCode, short extend) const { - GHOST_TKey key; - - if ((vKey >= '0') && (vKey <= '9')) { - // VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39) - key = (GHOST_TKey)(vKey - '0' + GHOST_kKey0); - } - else if ((vKey >= 'A') && (vKey <= 'Z')) { - // VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A) - key = (GHOST_TKey)(vKey - 'A' + GHOST_kKeyA); - } - else if ((vKey >= VK_F1) && (vKey <= VK_F24)) { - key = (GHOST_TKey)(vKey - VK_F1 + GHOST_kKeyF1); - } - else { - switch (vKey) { - case VK_RETURN: - key = (extend) ? GHOST_kKeyNumpadEnter : GHOST_kKeyEnter; break; - - case VK_BACK: key = GHOST_kKeyBackSpace; break; - case VK_TAB: key = GHOST_kKeyTab; break; - case VK_ESCAPE: key = GHOST_kKeyEsc; break; - case VK_SPACE: key = GHOST_kKeySpace; break; - - case VK_INSERT: - case VK_NUMPAD0: - key = (extend) ? GHOST_kKeyInsert : GHOST_kKeyNumpad0; break; - case VK_END: - case VK_NUMPAD1: - key = (extend) ? GHOST_kKeyEnd : GHOST_kKeyNumpad1; break; - case VK_DOWN: - case VK_NUMPAD2: - key = (extend) ? GHOST_kKeyDownArrow : GHOST_kKeyNumpad2; break; - case VK_NEXT: - case VK_NUMPAD3: - key = (extend) ? GHOST_kKeyDownPage : GHOST_kKeyNumpad3; break; - case VK_LEFT: - case VK_NUMPAD4: - key = (extend) ? GHOST_kKeyLeftArrow : GHOST_kKeyNumpad4; break; - case VK_CLEAR: - case VK_NUMPAD5: - key = (extend) ? GHOST_kKeyUnknown : GHOST_kKeyNumpad5; break; - case VK_RIGHT: - case VK_NUMPAD6: - key = (extend) ? GHOST_kKeyRightArrow : GHOST_kKeyNumpad6; break; - case VK_HOME: - case VK_NUMPAD7: - key = (extend) ? GHOST_kKeyHome : GHOST_kKeyNumpad7; break; - case VK_UP: - case VK_NUMPAD8: - key = (extend) ? GHOST_kKeyUpArrow : GHOST_kKeyNumpad8; break; - case VK_PRIOR: - case VK_NUMPAD9: - key = (extend) ? GHOST_kKeyUpPage : GHOST_kKeyNumpad9; break; - case VK_DECIMAL: - case VK_DELETE: - key = (extend) ? GHOST_kKeyDelete : GHOST_kKeyNumpadPeriod; break; - - case VK_SNAPSHOT: key = GHOST_kKeyPrintScreen; break; - case VK_PAUSE: key = GHOST_kKeyPause; break; - case VK_MULTIPLY: key = GHOST_kKeyNumpadAsterisk; break; - case VK_SUBTRACT: key = GHOST_kKeyNumpadMinus; break; - case VK_DIVIDE: key = GHOST_kKeyNumpadSlash; break; - case VK_ADD: key = GHOST_kKeyNumpadPlus; break; - - case VK_SEMICOLON: key = GHOST_kKeySemicolon; break; - case VK_EQUALS: key = GHOST_kKeyEqual; break; - case VK_COMMA: key = GHOST_kKeyComma; break; - case VK_MINUS: key = GHOST_kKeyMinus; break; - case VK_PERIOD: key = GHOST_kKeyPeriod; break; - case VK_SLASH: key = GHOST_kKeySlash; break; - case VK_BACK_QUOTE: key = GHOST_kKeyAccentGrave; break; - case VK_OPEN_BRACKET: key = GHOST_kKeyLeftBracket; break; - case VK_BACK_SLASH: key = GHOST_kKeyBackslash; break; - case VK_CLOSE_BRACKET: key = GHOST_kKeyRightBracket; break; - case VK_QUOTE: key = GHOST_kKeyQuote; break; - case VK_GR_LESS: key = GHOST_kKeyGrLess; break; - - case VK_SHIFT: - /* Check single shift presses */ - if (scanCode == 0x36) { - key = GHOST_kKeyRightShift; - } - else if (scanCode == 0x2a) { - key = GHOST_kKeyLeftShift; - } - else { - /* Must be a combination SHIFT (Left or Right) + a Key - * Ignore this as the next message will contain - * the desired "Key" */ - key = GHOST_kKeyUnknown; - } - break; - case VK_CONTROL: - key = (extend) ? GHOST_kKeyRightControl : GHOST_kKeyLeftControl; - break; - case VK_MENU: - key = (extend) ? GHOST_kKeyRightAlt : GHOST_kKeyLeftAlt; - break; - case VK_LWIN: - case VK_RWIN: - key = GHOST_kKeyOS; - break; - case VK_NUMLOCK: key = GHOST_kKeyNumLock; break; - case VK_SCROLL: key = GHOST_kKeyScrollLock; break; - case VK_CAPITAL: key = GHOST_kKeyCapsLock; break; - case VK_OEM_8: - key = ((GHOST_SystemWin32 *)getSystem())->processSpecialKey(vKey, scanCode); - break; - case VK_MEDIA_PLAY_PAUSE: key = GHOST_kKeyMediaPlay; break; - case VK_MEDIA_STOP: key = GHOST_kKeyMediaStop; break; - case VK_MEDIA_PREV_TRACK: key = GHOST_kKeyMediaFirst; break; - case VK_MEDIA_NEXT_TRACK: key = GHOST_kKeyMediaLast; break; - default: - key = GHOST_kKeyUnknown; - break; - } - } - - return key; + GHOST_TKey key; + + if ((vKey >= '0') && (vKey <= '9')) { + // VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39) + key = (GHOST_TKey)(vKey - '0' + GHOST_kKey0); + } + else if ((vKey >= 'A') && (vKey <= 'Z')) { + // VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A) + key = (GHOST_TKey)(vKey - 'A' + GHOST_kKeyA); + } + else if ((vKey >= VK_F1) && (vKey <= VK_F24)) { + key = (GHOST_TKey)(vKey - VK_F1 + GHOST_kKeyF1); + } + else { + switch (vKey) { + case VK_RETURN: + key = (extend) ? GHOST_kKeyNumpadEnter : GHOST_kKeyEnter; + break; + + case VK_BACK: + key = GHOST_kKeyBackSpace; + break; + case VK_TAB: + key = GHOST_kKeyTab; + break; + case VK_ESCAPE: + key = GHOST_kKeyEsc; + break; + case VK_SPACE: + key = GHOST_kKeySpace; + break; + + case VK_INSERT: + case VK_NUMPAD0: + key = (extend) ? GHOST_kKeyInsert : GHOST_kKeyNumpad0; + break; + case VK_END: + case VK_NUMPAD1: + key = (extend) ? GHOST_kKeyEnd : GHOST_kKeyNumpad1; + break; + case VK_DOWN: + case VK_NUMPAD2: + key = (extend) ? GHOST_kKeyDownArrow : GHOST_kKeyNumpad2; + break; + case VK_NEXT: + case VK_NUMPAD3: + key = (extend) ? GHOST_kKeyDownPage : GHOST_kKeyNumpad3; + break; + case VK_LEFT: + case VK_NUMPAD4: + key = (extend) ? GHOST_kKeyLeftArrow : GHOST_kKeyNumpad4; + break; + case VK_CLEAR: + case VK_NUMPAD5: + key = (extend) ? GHOST_kKeyUnknown : GHOST_kKeyNumpad5; + break; + case VK_RIGHT: + case VK_NUMPAD6: + key = (extend) ? GHOST_kKeyRightArrow : GHOST_kKeyNumpad6; + break; + case VK_HOME: + case VK_NUMPAD7: + key = (extend) ? GHOST_kKeyHome : GHOST_kKeyNumpad7; + break; + case VK_UP: + case VK_NUMPAD8: + key = (extend) ? GHOST_kKeyUpArrow : GHOST_kKeyNumpad8; + break; + case VK_PRIOR: + case VK_NUMPAD9: + key = (extend) ? GHOST_kKeyUpPage : GHOST_kKeyNumpad9; + break; + case VK_DECIMAL: + case VK_DELETE: + key = (extend) ? GHOST_kKeyDelete : GHOST_kKeyNumpadPeriod; + break; + + case VK_SNAPSHOT: + key = GHOST_kKeyPrintScreen; + break; + case VK_PAUSE: + key = GHOST_kKeyPause; + break; + case VK_MULTIPLY: + key = GHOST_kKeyNumpadAsterisk; + break; + case VK_SUBTRACT: + key = GHOST_kKeyNumpadMinus; + break; + case VK_DIVIDE: + key = GHOST_kKeyNumpadSlash; + break; + case VK_ADD: + key = GHOST_kKeyNumpadPlus; + break; + + case VK_SEMICOLON: + key = GHOST_kKeySemicolon; + break; + case VK_EQUALS: + key = GHOST_kKeyEqual; + break; + case VK_COMMA: + key = GHOST_kKeyComma; + break; + case VK_MINUS: + key = GHOST_kKeyMinus; + break; + case VK_PERIOD: + key = GHOST_kKeyPeriod; + break; + case VK_SLASH: + key = GHOST_kKeySlash; + break; + case VK_BACK_QUOTE: + key = GHOST_kKeyAccentGrave; + break; + case VK_OPEN_BRACKET: + key = GHOST_kKeyLeftBracket; + break; + case VK_BACK_SLASH: + key = GHOST_kKeyBackslash; + break; + case VK_CLOSE_BRACKET: + key = GHOST_kKeyRightBracket; + break; + case VK_QUOTE: + key = GHOST_kKeyQuote; + break; + case VK_GR_LESS: + key = GHOST_kKeyGrLess; + break; + + case VK_SHIFT: + /* Check single shift presses */ + if (scanCode == 0x36) { + key = GHOST_kKeyRightShift; + } + else if (scanCode == 0x2a) { + key = GHOST_kKeyLeftShift; + } + else { + /* Must be a combination SHIFT (Left or Right) + a Key + * Ignore this as the next message will contain + * the desired "Key" */ + key = GHOST_kKeyUnknown; + } + break; + case VK_CONTROL: + key = (extend) ? GHOST_kKeyRightControl : GHOST_kKeyLeftControl; + break; + case VK_MENU: + key = (extend) ? GHOST_kKeyRightAlt : GHOST_kKeyLeftAlt; + break; + case VK_LWIN: + case VK_RWIN: + key = GHOST_kKeyOS; + break; + case VK_NUMLOCK: + key = GHOST_kKeyNumLock; + break; + case VK_SCROLL: + key = GHOST_kKeyScrollLock; + break; + case VK_CAPITAL: + key = GHOST_kKeyCapsLock; + break; + case VK_OEM_8: + key = ((GHOST_SystemWin32 *)getSystem())->processSpecialKey(vKey, scanCode); + break; + case VK_MEDIA_PLAY_PAUSE: + key = GHOST_kKeyMediaPlay; + break; + case VK_MEDIA_STOP: + key = GHOST_kKeyMediaStop; + break; + case VK_MEDIA_PREV_TRACK: + key = GHOST_kKeyMediaFirst; + break; + case VK_MEDIA_NEXT_TRACK: + key = GHOST_kKeyMediaLast; + break; + default: + key = GHOST_kKeyUnknown; + break; + } + } + + return key; } -GHOST_EventButton *GHOST_SystemWin32::processButtonEvent( - GHOST_TEventType type, - GHOST_WindowWin32 *window, - GHOST_TButtonMask mask) +GHOST_EventButton *GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type, + GHOST_WindowWin32 *window, + GHOST_TButtonMask mask) { - GHOST_SystemWin32 * system = (GHOST_SystemWin32 *)getSystem(); - if (window->useTabletAPI(GHOST_kTabletNative)) { - window->setTabletData(NULL); - } - return new GHOST_EventButton(system->getMilliSeconds(), type, window, mask); + GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); + if (window->useTabletAPI(GHOST_kTabletNative)) { + window->setTabletData(NULL); + } + return new GHOST_EventButton(system->getMilliSeconds(), type, window, mask); } -GHOST_Event *GHOST_SystemWin32::processPointerEvent( - GHOST_TEventType type, - GHOST_WindowWin32 *window, - WPARAM wParam, - LPARAM lParam, - bool& eventHandled) +GHOST_Event *GHOST_SystemWin32::processPointerEvent(GHOST_TEventType type, + GHOST_WindowWin32 *window, + WPARAM wParam, + LPARAM lParam, + bool &eventHandled) { - GHOST_PointerInfoWin32 pointerInfo; - GHOST_SystemWin32 * system = (GHOST_SystemWin32 *)getSystem(); - - if (!window->useTabletAPI(GHOST_kTabletNative)) { - return NULL; - } - - if (window->getPointerInfo(&pointerInfo, wParam, lParam) != GHOST_kSuccess) { - return NULL; - } - - if (!pointerInfo.isPrimary) { - eventHandled = true; - return NULL; // For multi-touch displays we ignore these events - } - - system->setCursorPosition(pointerInfo.pixelLocation.x, pointerInfo.pixelLocation.y); - - switch (type) { - case GHOST_kEventButtonDown: - window->setTabletData(&pointerInfo.tabletData); - eventHandled = true; - return new GHOST_EventButton(system->getMilliSeconds(), GHOST_kEventButtonDown, window, pointerInfo.buttonMask); - case GHOST_kEventButtonUp: - eventHandled = true; - return new GHOST_EventButton(system->getMilliSeconds(), GHOST_kEventButtonUp, window, pointerInfo.buttonMask); - case GHOST_kEventCursorMove: - window->setTabletData(&pointerInfo.tabletData); - eventHandled = true; - return new GHOST_EventCursor(system->getMilliSeconds(), GHOST_kEventCursorMove, window, - pointerInfo.pixelLocation.x, pointerInfo.pixelLocation.y); - default: - return NULL; - } + GHOST_PointerInfoWin32 pointerInfo; + GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); + + if (!window->useTabletAPI(GHOST_kTabletNative)) { + return NULL; + } + + if (window->getPointerInfo(&pointerInfo, wParam, lParam) != GHOST_kSuccess) { + return NULL; + } + + if (!pointerInfo.isPrimary) { + eventHandled = true; + return NULL; // For multi-touch displays we ignore these events + } + + system->setCursorPosition(pointerInfo.pixelLocation.x, pointerInfo.pixelLocation.y); + + switch (type) { + case GHOST_kEventButtonDown: + window->setTabletData(&pointerInfo.tabletData); + eventHandled = true; + return new GHOST_EventButton( + system->getMilliSeconds(), GHOST_kEventButtonDown, window, pointerInfo.buttonMask); + case GHOST_kEventButtonUp: + eventHandled = true; + return new GHOST_EventButton( + system->getMilliSeconds(), GHOST_kEventButtonUp, window, pointerInfo.buttonMask); + case GHOST_kEventCursorMove: + window->setTabletData(&pointerInfo.tabletData); + eventHandled = true; + return new GHOST_EventCursor(system->getMilliSeconds(), + GHOST_kEventCursorMove, + window, + pointerInfo.pixelLocation.x, + pointerInfo.pixelLocation.y); + default: + return NULL; + } } -GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type, GHOST_WindowWin32 *window) +GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type, + GHOST_WindowWin32 *window) { - GHOST_TInt32 x_screen, y_screen; - GHOST_SystemWin32 *system = (GHOST_SystemWin32 *) getSystem(); - - system->getCursorPosition(x_screen, y_screen); - - /* TODO: CHECK IF THIS IS A TABLET EVENT */ - bool is_tablet = false; - - if (is_tablet == false && window->getCursorGrabModeIsWarp()) { - GHOST_TInt32 x_new = x_screen; - GHOST_TInt32 y_new = y_screen; - GHOST_TInt32 x_accum, y_accum; - GHOST_Rect bounds; - - /* fallback to window bounds */ - if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) { - window->getClientBounds(bounds); - } - - /* could also clamp to screen bounds - * wrap with a window outside the view will fail atm */ - - bounds.wrapPoint(x_new, y_new, 2); /* offset of one incase blender is at screen bounds */ - - window->getCursorGrabAccum(x_accum, y_accum); - if (x_new != x_screen || y_new != y_screen) { - /* when wrapping we don't need to add an event because the - * setCursorPosition call will cause a new event after */ - system->setCursorPosition(x_new, y_new); /* wrap */ - window->setCursorGrabAccum(x_accum + (x_screen - x_new), y_accum + (y_screen - y_new)); - } - else { - return new GHOST_EventCursor(system->getMilliSeconds(), - GHOST_kEventCursorMove, - window, - x_screen + x_accum, - y_screen + y_accum - ); - } - - } - else { - return new GHOST_EventCursor(system->getMilliSeconds(), - GHOST_kEventCursorMove, - window, - x_screen, - y_screen - ); - } - return NULL; + GHOST_TInt32 x_screen, y_screen; + GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); + + system->getCursorPosition(x_screen, y_screen); + + /* TODO: CHECK IF THIS IS A TABLET EVENT */ + bool is_tablet = false; + + if (is_tablet == false && window->getCursorGrabModeIsWarp()) { + GHOST_TInt32 x_new = x_screen; + GHOST_TInt32 y_new = y_screen; + GHOST_TInt32 x_accum, y_accum; + GHOST_Rect bounds; + + /* fallback to window bounds */ + if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) { + window->getClientBounds(bounds); + } + + /* could also clamp to screen bounds + * wrap with a window outside the view will fail atm */ + + bounds.wrapPoint(x_new, y_new, 2); /* offset of one incase blender is at screen bounds */ + + window->getCursorGrabAccum(x_accum, y_accum); + if (x_new != x_screen || y_new != y_screen) { + /* when wrapping we don't need to add an event because the + * setCursorPosition call will cause a new event after */ + system->setCursorPosition(x_new, y_new); /* wrap */ + window->setCursorGrabAccum(x_accum + (x_screen - x_new), y_accum + (y_screen - y_new)); + } + else { + return new GHOST_EventCursor(system->getMilliSeconds(), + GHOST_kEventCursorMove, + window, + x_screen + x_accum, + y_screen + y_accum); + } + } + else { + return new GHOST_EventCursor( + system->getMilliSeconds(), GHOST_kEventCursorMove, window, x_screen, y_screen); + } + return NULL; } - void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam) { - GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); - - int acc = system->m_wheelDeltaAccum; - int delta = GET_WHEEL_DELTA_WPARAM(wParam); - - if (acc * delta < 0) { - // scroll direction reversed. - acc = 0; - } - acc += delta; - int direction = (acc >= 0) ? 1 : -1; - acc = abs(acc); - - while (acc >= WHEEL_DELTA) { - system->pushEvent(new GHOST_EventWheel(system->getMilliSeconds(), window, direction)); - acc -= WHEEL_DELTA; - } - system->m_wheelDeltaAccum = acc * direction; + GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); + + int acc = system->m_wheelDeltaAccum; + int delta = GET_WHEEL_DELTA_WPARAM(wParam); + + if (acc * delta < 0) { + // scroll direction reversed. + acc = 0; + } + acc += delta; + int direction = (acc >= 0) ? 1 : -1; + acc = abs(acc); + + while (acc >= WHEEL_DELTA) { + system->pushEvent(new GHOST_EventWheel(system->getMilliSeconds(), window, direction)); + acc -= WHEEL_DELTA; + } + system->m_wheelDeltaAccum = acc * direction; } - GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RAWINPUT const &raw) { - int keyDown = 0; - char vk; - GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); - GHOST_TKey key = system->hardKey(raw, &keyDown, &vk); - GHOST_EventKey *event; - - if (key != GHOST_kKeyUnknown) { - char utf8_char[6] = {0}; - char ascii = 0; - - wchar_t utf16[3] = {0}; - BYTE state[256] = {0}; - int r; - GetKeyboardState((PBYTE)state); - - // don't call ToUnicodeEx on dead keys as it clears the buffer and so won't allow diacritical composition. - if (MapVirtualKeyW(vk, 2) != 0) { - // todo: ToUnicodeEx can respond with up to 4 utf16 chars (only 2 here). Could be up to 24 utf8 bytes. - if ((r = ToUnicodeEx(vk, raw.data.keyboard.MakeCode, state, utf16, 2, 0, system->m_keylayout))) { - if ((r > 0 && r < 3)) { - utf16[r] = 0; - conv_utf_16_to_8(utf16, utf8_char, 6); - } - else if (r == -1) { - utf8_char[0] = '\0'; - } - } - } - - if (!keyDown) { - utf8_char[0] = '\0'; - ascii = '\0'; - } - else { - ascii = utf8_char[0] & 0x80 ? '?' : utf8_char[0]; - } - - event = new GHOST_EventKey(system->getMilliSeconds(), keyDown ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, key, ascii, utf8_char); - - // GHOST_PRINTF("%c\n", ascii); // we already get this info via EventPrinter - } - else { - event = NULL; - } - return event; + int keyDown = 0; + char vk; + GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); + GHOST_TKey key = system->hardKey(raw, &keyDown, &vk); + GHOST_EventKey *event; + + if (key != GHOST_kKeyUnknown) { + char utf8_char[6] = {0}; + char ascii = 0; + + wchar_t utf16[3] = {0}; + BYTE state[256] = {0}; + int r; + GetKeyboardState((PBYTE)state); + + // don't call ToUnicodeEx on dead keys as it clears the buffer and so won't allow diacritical composition. + if (MapVirtualKeyW(vk, 2) != 0) { + // todo: ToUnicodeEx can respond with up to 4 utf16 chars (only 2 here). Could be up to 24 utf8 bytes. + if ((r = ToUnicodeEx( + vk, raw.data.keyboard.MakeCode, state, utf16, 2, 0, system->m_keylayout))) { + if ((r > 0 && r < 3)) { + utf16[r] = 0; + conv_utf_16_to_8(utf16, utf8_char, 6); + } + else if (r == -1) { + utf8_char[0] = '\0'; + } + } + } + + if (!keyDown) { + utf8_char[0] = '\0'; + ascii = '\0'; + } + else { + ascii = utf8_char[0] & 0x80 ? '?' : utf8_char[0]; + } + + event = new GHOST_EventKey(system->getMilliSeconds(), + keyDown ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, + window, + key, + ascii, + utf8_char); + + // GHOST_PRINTF("%c\n", ascii); // we already get this info via EventPrinter + } + else { + event = NULL; + } + return event; } - -GHOST_Event *GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type, GHOST_WindowWin32 *window) +GHOST_Event *GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type, + GHOST_WindowWin32 *window) { - GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); + GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); - if (type == GHOST_kEventWindowActivate) { - system->getWindowManager()->setActiveWindow(window); - window->bringTabletContextToFront(); - } + if (type == GHOST_kEventWindowActivate) { + system->getWindowManager()->setActiveWindow(window); + window->bringTabletContextToFront(); + } - return new GHOST_Event(system->getMilliSeconds(), type, window); + return new GHOST_Event(system->getMilliSeconds(), type, window); } #ifdef WITH_INPUT_IME -GHOST_Event *GHOST_SystemWin32::processImeEvent(GHOST_TEventType type, GHOST_WindowWin32 *window, GHOST_TEventImeData *data) +GHOST_Event *GHOST_SystemWin32::processImeEvent(GHOST_TEventType type, + GHOST_WindowWin32 *window, + GHOST_TEventImeData *data) { - GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); - return new GHOST_EventIME(system->getMilliSeconds(), type, window, data); + GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); + return new GHOST_EventIME(system->getMilliSeconds(), type, window, data); } #endif - -GHOST_TSuccess GHOST_SystemWin32::pushDragDropEvent( - GHOST_TEventType eventType, - GHOST_TDragnDropTypes draggedObjectType, - GHOST_WindowWin32 *window, - int mouseX, int mouseY, - void *data) +GHOST_TSuccess GHOST_SystemWin32::pushDragDropEvent(GHOST_TEventType eventType, + GHOST_TDragnDropTypes draggedObjectType, + GHOST_WindowWin32 *window, + int mouseX, + int mouseY, + void *data) { - GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); - return system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(), - eventType, - draggedObjectType, - window, mouseX, mouseY, data) - ); + GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); + return system->pushEvent(new GHOST_EventDragnDrop( + system->getMilliSeconds(), eventType, draggedObjectType, window, mouseX, mouseY, data)); } void GHOST_SystemWin32::processMinMaxInfo(MINMAXINFO *minmax) { - minmax->ptMinTrackSize.x = 320; - minmax->ptMinTrackSize.y = 240; + minmax->ptMinTrackSize.x = 320; + minmax->ptMinTrackSize.y = 240; } #ifdef WITH_INPUT_NDOF bool GHOST_SystemWin32::processNDOF(RAWINPUT const &raw) { - bool eventSent = false; - GHOST_TUns64 now = getMilliSeconds(); - - static bool firstEvent = true; - if (firstEvent) { // determine exactly which device is plugged in - RID_DEVICE_INFO info; - unsigned infoSize = sizeof(RID_DEVICE_INFO); - info.cbSize = infoSize; - - GetRawInputDeviceInfo(raw.header.hDevice, RIDI_DEVICEINFO, &info, &infoSize); - if (info.dwType == RIM_TYPEHID) - m_ndofManager->setDevice(info.hid.dwVendorId, info.hid.dwProductId); - else - GHOST_PRINT("<!> not a HID device... mouse/kb perhaps?\n"); - - firstEvent = false; - } - - // The NDOF manager sends button changes immediately, and *pretends* to - // send motion. Mark as 'sent' so motion will always get dispatched. - eventSent = true; - - BYTE const *data = raw.data.hid.bRawData; - - BYTE packetType = data[0]; - switch (packetType) { - case 1: // translation - { - const short *axis = (short *)(data + 1); - // massage into blender view coords (same goes for rotation) - const int t[3] = {axis[0], -axis[2], axis[1]}; - m_ndofManager->updateTranslation(t, now); - - if (raw.data.hid.dwSizeHid == 13) { - // this report also includes rotation - const int r[3] = {-axis[3], axis[5], -axis[4]}; - m_ndofManager->updateRotation(r, now); - - // I've never gotten one of these, has anyone else? - GHOST_PRINT("ndof: combined T + R\n"); - } - break; - } - case 2: // rotation - { - const short *axis = (short *)(data + 1); - const int r[3] = {-axis[0], axis[2], -axis[1]}; - m_ndofManager->updateRotation(r, now); - break; - } - case 3: // buttons - { - int button_bits; - memcpy(&button_bits, data + 1, sizeof(button_bits)); - m_ndofManager->updateButtons(button_bits, now); - break; - } - } - return eventSent; + bool eventSent = false; + GHOST_TUns64 now = getMilliSeconds(); + + static bool firstEvent = true; + if (firstEvent) { // determine exactly which device is plugged in + RID_DEVICE_INFO info; + unsigned infoSize = sizeof(RID_DEVICE_INFO); + info.cbSize = infoSize; + + GetRawInputDeviceInfo(raw.header.hDevice, RIDI_DEVICEINFO, &info, &infoSize); + if (info.dwType == RIM_TYPEHID) + m_ndofManager->setDevice(info.hid.dwVendorId, info.hid.dwProductId); + else + GHOST_PRINT("<!> not a HID device... mouse/kb perhaps?\n"); + + firstEvent = false; + } + + // The NDOF manager sends button changes immediately, and *pretends* to + // send motion. Mark as 'sent' so motion will always get dispatched. + eventSent = true; + + BYTE const *data = raw.data.hid.bRawData; + + BYTE packetType = data[0]; + switch (packetType) { + case 1: // translation + { + const short *axis = (short *)(data + 1); + // massage into blender view coords (same goes for rotation) + const int t[3] = {axis[0], -axis[2], axis[1]}; + m_ndofManager->updateTranslation(t, now); + + if (raw.data.hid.dwSizeHid == 13) { + // this report also includes rotation + const int r[3] = {-axis[3], axis[5], -axis[4]}; + m_ndofManager->updateRotation(r, now); + + // I've never gotten one of these, has anyone else? + GHOST_PRINT("ndof: combined T + R\n"); + } + break; + } + case 2: // rotation + { + const short *axis = (short *)(data + 1); + const int r[3] = {-axis[0], axis[2], -axis[1]}; + m_ndofManager->updateRotation(r, now); + break; + } + case 3: // buttons + { + int button_bits; + memcpy(&button_bits, data + 1, sizeof(button_bits)); + m_ndofManager->updateButtons(button_bits, now); + break; + } + } + return eventSent; } -#endif // WITH_INPUT_NDOF +#endif // WITH_INPUT_NDOF LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - GHOST_Event *event = NULL; - bool eventHandled = false; + GHOST_Event *event = NULL; + bool eventHandled = false; - LRESULT lResult = 0; - GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); - GHOST_EventManager *eventManager = system->getEventManager(); - GHOST_ASSERT(system, "GHOST_SystemWin32::s_wndProc(): system not initialized"); + LRESULT lResult = 0; + GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); + GHOST_EventManager *eventManager = system->getEventManager(); + GHOST_ASSERT(system, "GHOST_SystemWin32::s_wndProc(): system not initialized"); - if (hwnd) { + if (hwnd) { #if 0 - // Disabled due to bug in Intel drivers, see T51959 - if(msg == WM_NCCREATE) { - // Tell Windows to automatically handle scaling of non-client areas - // such as the caption bar. EnableNonClientDpiScaling was introduced in Windows 10 - HMODULE m_user32 = ::LoadLibrary("User32.dll"); - if (m_user32) { - GHOST_WIN32_EnableNonClientDpiScaling fpEnableNonClientDpiScaling = - (GHOST_WIN32_EnableNonClientDpiScaling) ::GetProcAddress(m_user32, "EnableNonClientDpiScaling"); - - if (fpEnableNonClientDpiScaling) { - fpEnableNonClientDpiScaling(hwnd); - } - } - } + // Disabled due to bug in Intel drivers, see T51959 + if(msg == WM_NCCREATE) { + // Tell Windows to automatically handle scaling of non-client areas + // such as the caption bar. EnableNonClientDpiScaling was introduced in Windows 10 + HMODULE m_user32 = ::LoadLibrary("User32.dll"); + if (m_user32) { + GHOST_WIN32_EnableNonClientDpiScaling fpEnableNonClientDpiScaling = + (GHOST_WIN32_EnableNonClientDpiScaling) ::GetProcAddress(m_user32, "EnableNonClientDpiScaling"); + + if (fpEnableNonClientDpiScaling) { + fpEnableNonClientDpiScaling(hwnd); + } + } + } #endif - GHOST_WindowWin32 *window = (GHOST_WindowWin32 *)::GetWindowLongPtr(hwnd, GWLP_USERDATA); - if (window) { - switch (msg) { - // we need to check if new key layout has AltGr - case WM_INPUTLANGCHANGE: - { - system->handleKeyboardChange(); + GHOST_WindowWin32 *window = (GHOST_WindowWin32 *)::GetWindowLongPtr(hwnd, GWLP_USERDATA); + if (window) { + switch (msg) { + // we need to check if new key layout has AltGr + case WM_INPUTLANGCHANGE: { + system->handleKeyboardChange(); #ifdef WITH_INPUT_IME - window->getImeInput()->SetInputLanguage(); + window->getImeInput()->SetInputLanguage(); #endif - break; - } - //////////////////////////////////////////////////////////////////////// - // Keyboard events, processed - //////////////////////////////////////////////////////////////////////// - case WM_INPUT: - { - // check WM_INPUT from input sink when ghost window is not in the foreground - if (wParam == RIM_INPUTSINK) { - if (GetFocus() != hwnd) // WM_INPUT message not for this window - return 0; - } //else wParam == RIM_INPUT - - RAWINPUT raw; - RAWINPUT *raw_ptr = &raw; - UINT rawSize = sizeof(RAWINPUT); - - GetRawInputData((HRAWINPUT)lParam, RID_INPUT, raw_ptr, &rawSize, sizeof(RAWINPUTHEADER)); - - switch (raw.header.dwType) { - case RIM_TYPEKEYBOARD: - event = processKeyEvent(window, raw); - if (!event) { - GHOST_PRINT("GHOST_SystemWin32::wndProc: key event "); - GHOST_PRINT(msg); - GHOST_PRINT(" key ignored\n"); - } - break; + break; + } + //////////////////////////////////////////////////////////////////////// + // Keyboard events, processed + //////////////////////////////////////////////////////////////////////// + case WM_INPUT: { + // check WM_INPUT from input sink when ghost window is not in the foreground + if (wParam == RIM_INPUTSINK) { + if (GetFocus() != hwnd) // WM_INPUT message not for this window + return 0; + } //else wParam == RIM_INPUT + + RAWINPUT raw; + RAWINPUT *raw_ptr = &raw; + UINT rawSize = sizeof(RAWINPUT); + + GetRawInputData((HRAWINPUT)lParam, RID_INPUT, raw_ptr, &rawSize, sizeof(RAWINPUTHEADER)); + + switch (raw.header.dwType) { + case RIM_TYPEKEYBOARD: + event = processKeyEvent(window, raw); + if (!event) { + GHOST_PRINT("GHOST_SystemWin32::wndProc: key event "); + GHOST_PRINT(msg); + GHOST_PRINT(" key ignored\n"); + } + break; #ifdef WITH_INPUT_NDOF - case RIM_TYPEHID: - if (system->processNDOF(raw)) { - eventHandled = true; - } - break; + case RIM_TYPEHID: + if (system->processNDOF(raw)) { + eventHandled = true; + } + break; #endif - } - break; - } + } + break; + } #ifdef WITH_INPUT_IME - //////////////////////////////////////////////////////////////////////// - // IME events, processed, read more in GHOST_IME.h - //////////////////////////////////////////////////////////////////////// - case WM_IME_SETCONTEXT: - { - GHOST_ImeWin32 *ime = window->getImeInput(); - ime->SetInputLanguage(); - ime->CreateImeWindow(hwnd); - ime->CleanupComposition(hwnd); - ime->CheckFirst(hwnd); - break; - } - case WM_IME_STARTCOMPOSITION: - { - GHOST_ImeWin32 *ime = window->getImeInput(); - eventHandled = true; - /* remove input event before start comp event, avoid redundant input */ - eventManager->removeTypeEvents(GHOST_kEventKeyDown, window); - ime->CreateImeWindow(hwnd); - ime->ResetComposition(hwnd); - event = processImeEvent( - GHOST_kEventImeCompositionStart, - window, - &ime->eventImeData); - break; - } - case WM_IME_COMPOSITION: - { - GHOST_ImeWin32 *ime = window->getImeInput(); - eventHandled = true; - ime->UpdateImeWindow(hwnd); - ime->UpdateInfo(hwnd); - if (ime->eventImeData.result_len) { - /* remove redundant IME event */ - eventManager->removeTypeEvents(GHOST_kEventImeComposition, window); - } - event = processImeEvent( - GHOST_kEventImeComposition, - window, - &ime->eventImeData); - break; - } - case WM_IME_ENDCOMPOSITION: - { - GHOST_ImeWin32 *ime = window->getImeInput(); - eventHandled = true; - /* remove input event after end comp event, avoid redundant input */ - eventManager->removeTypeEvents(GHOST_kEventKeyDown, window); - ime->ResetComposition(hwnd); - ime->DestroyImeWindow(hwnd); - event = processImeEvent( - GHOST_kEventImeCompositionEnd, - window, - &ime->eventImeData); - break; - } + //////////////////////////////////////////////////////////////////////// + // IME events, processed, read more in GHOST_IME.h + //////////////////////////////////////////////////////////////////////// + case WM_IME_SETCONTEXT: { + GHOST_ImeWin32 *ime = window->getImeInput(); + ime->SetInputLanguage(); + ime->CreateImeWindow(hwnd); + ime->CleanupComposition(hwnd); + ime->CheckFirst(hwnd); + break; + } + case WM_IME_STARTCOMPOSITION: { + GHOST_ImeWin32 *ime = window->getImeInput(); + eventHandled = true; + /* remove input event before start comp event, avoid redundant input */ + eventManager->removeTypeEvents(GHOST_kEventKeyDown, window); + ime->CreateImeWindow(hwnd); + ime->ResetComposition(hwnd); + event = processImeEvent(GHOST_kEventImeCompositionStart, window, &ime->eventImeData); + break; + } + case WM_IME_COMPOSITION: { + GHOST_ImeWin32 *ime = window->getImeInput(); + eventHandled = true; + ime->UpdateImeWindow(hwnd); + ime->UpdateInfo(hwnd); + if (ime->eventImeData.result_len) { + /* remove redundant IME event */ + eventManager->removeTypeEvents(GHOST_kEventImeComposition, window); + } + event = processImeEvent(GHOST_kEventImeComposition, window, &ime->eventImeData); + break; + } + case WM_IME_ENDCOMPOSITION: { + GHOST_ImeWin32 *ime = window->getImeInput(); + eventHandled = true; + /* remove input event after end comp event, avoid redundant input */ + eventManager->removeTypeEvents(GHOST_kEventKeyDown, window); + ime->ResetComposition(hwnd); + ime->DestroyImeWindow(hwnd); + event = processImeEvent(GHOST_kEventImeCompositionEnd, window, &ime->eventImeData); + break; + } #endif /* WITH_INPUT_IME */ - //////////////////////////////////////////////////////////////////////// - // Keyboard events, ignored - //////////////////////////////////////////////////////////////////////// - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - case WM_KEYUP: - case WM_SYSKEYUP: - /* These functions were replaced by WM_INPUT*/ - case WM_CHAR: - /* The WM_CHAR message is posted to the window with the keyboard focus when - * a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR - * contains the character code of the key that was pressed. - */ - case WM_DEADCHAR: - /* The WM_DEADCHAR message is posted to the window with the keyboard focus when a - * WM_KEYUP message is translated by the TranslateMessage function. WM_DEADCHAR - * specifies a character code generated by a dead key. A dead key is a key that - * generates a character, such as the umlaut (double-dot), that is combined with - * another character to form a composite character. For example, the umlaut-O - * character (Ö) is generated by typing the dead key for the umlaut character, and - * then typing the O key. - */ - break; - case WM_SYSDEADCHAR: - /* The WM_SYSDEADCHAR message is sent to the window with the keyboard focus when - * a WM_SYSKEYDOWN message is translated by the TranslateMessage function. - * WM_SYSDEADCHAR specifies the character code of a system dead key - that is, - * a dead key that is pressed while holding down the alt key. - */ - case WM_SYSCHAR: - /* The WM_SYSCHAR message is sent to the window with the keyboard focus when - * a WM_SYSCHAR message is translated by the TranslateMessage function. - * WM_SYSCHAR specifies the character code of a dead key - that is, - * a dead key that is pressed while holding down the alt key. - * To prevent the sound, DefWindowProc must be avoided by return - */ - break; - case WM_SYSCOMMAND: - /* The WM_SYSCHAR message is sent to the window when system commands such as - * maximize, minimize or close the window are triggered. Also it is sent when ALT - * button is press for menu. To prevent this we must return preventing DefWindowProc. - */ - if (wParam == SC_KEYMENU) { - eventHandled = true; - } - break; - //////////////////////////////////////////////////////////////////////// - // Tablet events, processed - //////////////////////////////////////////////////////////////////////// - case WT_PACKET: - window->processWin32TabletEvent(wParam, lParam); - break; - case WT_CSRCHANGE: - case WT_PROXIMITY: - window->processWin32TabletInitEvent(); - break; - //////////////////////////////////////////////////////////////////////// - // Pointer events, processed - //////////////////////////////////////////////////////////////////////// - case WM_POINTERDOWN: - event = processPointerEvent(GHOST_kEventButtonDown, window, wParam, lParam, eventHandled); - if (event && eventHandled) { - window->registerMouseClickEvent(0); - } - break; - case WM_POINTERUP: - event = processPointerEvent(GHOST_kEventButtonUp, window, wParam, lParam, eventHandled); - if (event && eventHandled) { - window->registerMouseClickEvent(1); - } - break; - case WM_POINTERUPDATE: - event = processPointerEvent(GHOST_kEventCursorMove, window, wParam, lParam, eventHandled); - break; - //////////////////////////////////////////////////////////////////////// - // Mouse events, processed - //////////////////////////////////////////////////////////////////////// - case WM_LBUTTONDOWN: - window->registerMouseClickEvent(0); - event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft); - break; - case WM_MBUTTONDOWN: - window->registerMouseClickEvent(0); - event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskMiddle); - break; - case WM_RBUTTONDOWN: - window->registerMouseClickEvent(0); - event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight); - break; - case WM_XBUTTONDOWN: - window->registerMouseClickEvent(0); - if ((short) HIWORD(wParam) == XBUTTON1) { - event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton4); - } - else if ((short) HIWORD(wParam) == XBUTTON2) { - event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton5); - } - break; - case WM_LBUTTONUP: - window->registerMouseClickEvent(1); - event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft); - break; - case WM_MBUTTONUP: - window->registerMouseClickEvent(1); - event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle); - break; - case WM_RBUTTONUP: - window->registerMouseClickEvent(1); - event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight); - break; - case WM_XBUTTONUP: - window->registerMouseClickEvent(1); - if ((short) HIWORD(wParam) == XBUTTON1) { - event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton4); - } - else if ((short) HIWORD(wParam) == XBUTTON2) { - event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton5); - } - break; - case WM_MOUSEMOVE: - event = processCursorEvent(GHOST_kEventCursorMove, window); - break; - case WM_MOUSEWHEEL: - { - /* The WM_MOUSEWHEEL message is sent to the focus window - * when the mouse wheel is rotated. The DefWindowProc - * function propagates the message to the window's parent. - * There should be no internal forwarding of the message, - * since DefWindowProc propagates it up the parent chain - * until it finds a window that processes it. - */ - - /* Get the window under the mouse and send event to its queue. */ - POINT mouse_pos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; - HWND mouse_hwnd = ChildWindowFromPoint(HWND_DESKTOP, mouse_pos); - GHOST_WindowWin32 *mouse_window = (GHOST_WindowWin32 *)::GetWindowLongPtr(mouse_hwnd, GWLP_USERDATA); - - processWheelEvent(mouse_window ? mouse_window : window , wParam, lParam); - eventHandled = true; + //////////////////////////////////////////////////////////////////////// + // Keyboard events, ignored + //////////////////////////////////////////////////////////////////////// + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + case WM_KEYUP: + case WM_SYSKEYUP: + /* These functions were replaced by WM_INPUT*/ + case WM_CHAR: + /* The WM_CHAR message is posted to the window with the keyboard focus when + * a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR + * contains the character code of the key that was pressed. + */ + case WM_DEADCHAR: + /* The WM_DEADCHAR message is posted to the window with the keyboard focus when a + * WM_KEYUP message is translated by the TranslateMessage function. WM_DEADCHAR + * specifies a character code generated by a dead key. A dead key is a key that + * generates a character, such as the umlaut (double-dot), that is combined with + * another character to form a composite character. For example, the umlaut-O + * character (Ö) is generated by typing the dead key for the umlaut character, and + * then typing the O key. + */ + break; + case WM_SYSDEADCHAR: + /* The WM_SYSDEADCHAR message is sent to the window with the keyboard focus when + * a WM_SYSKEYDOWN message is translated by the TranslateMessage function. + * WM_SYSDEADCHAR specifies the character code of a system dead key - that is, + * a dead key that is pressed while holding down the alt key. + */ + case WM_SYSCHAR: + /* The WM_SYSCHAR message is sent to the window with the keyboard focus when + * a WM_SYSCHAR message is translated by the TranslateMessage function. + * WM_SYSCHAR specifies the character code of a dead key - that is, + * a dead key that is pressed while holding down the alt key. + * To prevent the sound, DefWindowProc must be avoided by return + */ + break; + case WM_SYSCOMMAND: + /* The WM_SYSCHAR message is sent to the window when system commands such as + * maximize, minimize or close the window are triggered. Also it is sent when ALT + * button is press for menu. To prevent this we must return preventing DefWindowProc. + */ + if (wParam == SC_KEYMENU) { + eventHandled = true; + } + break; + //////////////////////////////////////////////////////////////////////// + // Tablet events, processed + //////////////////////////////////////////////////////////////////////// + case WT_PACKET: + window->processWin32TabletEvent(wParam, lParam); + break; + case WT_CSRCHANGE: + case WT_PROXIMITY: + window->processWin32TabletInitEvent(); + break; + //////////////////////////////////////////////////////////////////////// + // Pointer events, processed + //////////////////////////////////////////////////////////////////////// + case WM_POINTERDOWN: + event = processPointerEvent( + GHOST_kEventButtonDown, window, wParam, lParam, eventHandled); + if (event && eventHandled) { + window->registerMouseClickEvent(0); + } + break; + case WM_POINTERUP: + event = processPointerEvent(GHOST_kEventButtonUp, window, wParam, lParam, eventHandled); + if (event && eventHandled) { + window->registerMouseClickEvent(1); + } + break; + case WM_POINTERUPDATE: + event = processPointerEvent( + GHOST_kEventCursorMove, window, wParam, lParam, eventHandled); + break; + //////////////////////////////////////////////////////////////////////// + // Mouse events, processed + //////////////////////////////////////////////////////////////////////// + case WM_LBUTTONDOWN: + window->registerMouseClickEvent(0); + event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft); + break; + case WM_MBUTTONDOWN: + window->registerMouseClickEvent(0); + event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskMiddle); + break; + case WM_RBUTTONDOWN: + window->registerMouseClickEvent(0); + event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight); + break; + case WM_XBUTTONDOWN: + window->registerMouseClickEvent(0); + if ((short)HIWORD(wParam) == XBUTTON1) { + event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton4); + } + else if ((short)HIWORD(wParam) == XBUTTON2) { + event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton5); + } + break; + case WM_LBUTTONUP: + window->registerMouseClickEvent(1); + event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft); + break; + case WM_MBUTTONUP: + window->registerMouseClickEvent(1); + event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle); + break; + case WM_RBUTTONUP: + window->registerMouseClickEvent(1); + event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight); + break; + case WM_XBUTTONUP: + window->registerMouseClickEvent(1); + if ((short)HIWORD(wParam) == XBUTTON1) { + event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton4); + } + else if ((short)HIWORD(wParam) == XBUTTON2) { + event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton5); + } + break; + case WM_MOUSEMOVE: + event = processCursorEvent(GHOST_kEventCursorMove, window); + break; + case WM_MOUSEWHEEL: { + /* The WM_MOUSEWHEEL message is sent to the focus window + * when the mouse wheel is rotated. The DefWindowProc + * function propagates the message to the window's parent. + * There should be no internal forwarding of the message, + * since DefWindowProc propagates it up the parent chain + * until it finds a window that processes it. + */ + + /* Get the window under the mouse and send event to its queue. */ + POINT mouse_pos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; + HWND mouse_hwnd = ChildWindowFromPoint(HWND_DESKTOP, mouse_pos); + GHOST_WindowWin32 *mouse_window = (GHOST_WindowWin32 *)::GetWindowLongPtr(mouse_hwnd, + GWLP_USERDATA); + + processWheelEvent(mouse_window ? mouse_window : window, wParam, lParam); + eventHandled = true; #ifdef BROKEN_PEEK_TOUCHPAD - PostMessage(hwnd, WM_USER, 0, 0); + PostMessage(hwnd, WM_USER, 0, 0); #endif - break; - } - case WM_SETCURSOR: - /* The WM_SETCURSOR message is sent to a window if the mouse causes the cursor - * to move within a window and mouse input is not captured. - * This means we have to set the cursor shape every time the mouse moves! - * The DefWindowProc function uses this message to set the cursor to an - * arrow if it is not in the client area. - */ - if (LOWORD(lParam) == HTCLIENT) { - // Load the current cursor - window->loadCursor(window->getCursorVisibility(), window->getCursorShape()); - // Bypass call to DefWindowProc - return 0; - } - else { - // Outside of client area show standard cursor - window->loadCursor(true, GHOST_kStandardCursorDefault); - } - break; - - //////////////////////////////////////////////////////////////////////// - // Mouse events, ignored - //////////////////////////////////////////////////////////////////////// - case WM_NCMOUSEMOVE: - /* The WM_NCMOUSEMOVE message is posted to a window when the cursor is moved - * within the nonclient area of the window. This message is posted to the window - * that contains the cursor. If a window has captured the mouse, this message is not posted. - */ - case WM_NCHITTEST: - /* The WM_NCHITTEST message is sent to a window when the cursor moves, or - * when a mouse button is pressed or released. If the mouse is not captured, - * the message is sent to the window beneath the cursor. Otherwise, the message - * is sent to the window that has captured the mouse. - */ - break; - - //////////////////////////////////////////////////////////////////////// - // Window events, processed - //////////////////////////////////////////////////////////////////////// - case WM_CLOSE: - /* The WM_CLOSE message is sent as a signal that a window or an application should terminate. */ - event = processWindowEvent(GHOST_kEventWindowClose, window); - break; - case WM_ACTIVATE: - /* The WM_ACTIVATE message is sent to both the window being activated and the window being - * deactivated. If the windows use the same input queue, the message is sent synchronously, - * first to the window procedure of the top-level window being deactivated, then to the window - * procedure of the top-level window being activated. If the windows use different input queues, - * the message is sent asynchronously, so the window is activated immediately. - */ - { - GHOST_ModifierKeys modifiers; - modifiers.clear(); - system->storeModifierKeys(modifiers); - system->m_wheelDeltaAccum = 0; - event = processWindowEvent(LOWORD(wParam) ? GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate, window); - /* WARNING: Let DefWindowProc handle WM_ACTIVATE, otherwise WM_MOUSEWHEEL - * will not be dispatched to OUR active window if we minimize one of OUR windows. */ - if (LOWORD(wParam) == WA_INACTIVE) - window->lostMouseCapture(); - window->processWin32TabletActivateEvent(GET_WM_ACTIVATE_STATE(wParam, lParam)); - lResult = ::DefWindowProc(hwnd, msg, wParam, lParam); - break; - } - case WM_ENTERSIZEMOVE: - /* The WM_ENTERSIZEMOVE message is sent one time to a window after it enters the moving - * or sizing modal loop. The window enters the moving or sizing modal loop when the user - * clicks the window's title bar or sizing border, or when the window passes the - * WM_SYSCOMMAND message to the DefWindowProc function and the wParam parameter of the - * message specifies the SC_MOVE or SC_SIZE value. The operation is complete when - * DefWindowProc returns. - */ - window->m_inLiveResize = 1; - break; - case WM_EXITSIZEMOVE: - window->m_inLiveResize = 0; - break; - case WM_PAINT: - /* An application sends the WM_PAINT message when the system or another application - * makes a request to paint a portion of an application's window. The message is sent - * when the UpdateWindow or RedrawWindow function is called, or by the DispatchMessage - * function when the application obtains a WM_PAINT message by using the GetMessage or - * PeekMessage function. - */ - if (!window->m_inLiveResize) { - event = processWindowEvent(GHOST_kEventWindowUpdate, window); - ::ValidateRect(hwnd, NULL); - } - else { - eventHandled = true; - } - break; - case WM_GETMINMAXINFO: - /* The WM_GETMINMAXINFO message is sent to a window when the size or - * position of the window is about to change. An application can use - * this message to override the window's default maximized size and - * position, or its default minimum or maximum tracking size. - */ - processMinMaxInfo((MINMAXINFO *) lParam); - /* Let DefWindowProc handle it. */ - break; - case WM_SIZING: - case WM_SIZE: - /* The WM_SIZE message is sent to a window after its size has changed. - * The WM_SIZE and WM_MOVE messages are not sent if an application handles the - * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient - * to perform any move or size change processing during the WM_WINDOWPOSCHANGED - * message without calling DefWindowProc. - */ - /* we get first WM_SIZE before we fully init. So, do not dispatch before we continiously resizng */ - if (window->m_inLiveResize) { - system->pushEvent(processWindowEvent(GHOST_kEventWindowSize, window)); - system->dispatchEvents(); - } - else { - event = processWindowEvent(GHOST_kEventWindowSize, window); - } - break; - case WM_CAPTURECHANGED: - window->lostMouseCapture(); - break; - case WM_MOVING: - /* The WM_MOVING message is sent to a window that the user is moving. By processing - * this message, an application can monitor the size and position of the drag rectangle - * and, if needed, change its size or position. - */ - case WM_MOVE: - /* The WM_SIZE and WM_MOVE messages are not sent if an application handles the - * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient - * to perform any move or size change processing during the WM_WINDOWPOSCHANGED - * message without calling DefWindowProc. - */ - /* see WM_SIZE comment*/ - if (window->m_inLiveResize) { - system->pushEvent(processWindowEvent(GHOST_kEventWindowMove, window)); - system->dispatchEvents(); - } - else { - event = processWindowEvent(GHOST_kEventWindowMove, window); - } - - break; - case WM_DPICHANGED: - /* The WM_DPICHANGED message is sent when the effective dots per inch (dpi) for a window has changed. - * The DPI is the scale factor for a window. There are multiple events that can cause the DPI to - * change such as when the window is moved to a monitor with a different DPI. - */ - { - // The suggested new size and position of the window. - RECT* const suggestedWindowRect = (RECT*)lParam; - - // Push DPI change event first - system->pushEvent(processWindowEvent(GHOST_kEventWindowDPIHintChanged, window)); - system->dispatchEvents(); - eventHandled = true; - - // Then move and resize window - SetWindowPos(hwnd, - NULL, - suggestedWindowRect->left, - suggestedWindowRect->top, - suggestedWindowRect->right - suggestedWindowRect->left, - suggestedWindowRect->bottom - suggestedWindowRect->top, - SWP_NOZORDER | SWP_NOACTIVATE); - } - break; - //////////////////////////////////////////////////////////////////////// - // Window events, ignored - //////////////////////////////////////////////////////////////////////// - case WM_WINDOWPOSCHANGED: - /* The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place - * in the Z order has changed as a result of a call to the SetWindowPos function or - * another window-management function. - * The WM_SIZE and WM_MOVE messages are not sent if an application handles the - * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient - * to perform any move or size change processing during the WM_WINDOWPOSCHANGED - * message without calling DefWindowProc. - */ - case WM_ERASEBKGND: - /* An application sends the WM_ERASEBKGND message when the window background must be - * erased (for example, when a window is resized). The message is sent to prepare an - * invalidated portion of a window for painting. - */ - case WM_NCPAINT: - /* An application sends the WM_NCPAINT message to a window when its frame must be painted. */ - case WM_NCACTIVATE: - /* The WM_NCACTIVATE message is sent to a window when its nonclient area needs to be changed - * to indicate an active or inactive state. - */ - case WM_DESTROY: - /* The WM_DESTROY message is sent when a window is being destroyed. It is sent to the window - * procedure of the window being destroyed after the window is removed from the screen. - * This message is sent first to the window being destroyed and then to the child windows - * (if any) as they are destroyed. During the processing of the message, it can be assumed - * that all child windows still exist. - */ - case WM_NCDESTROY: - /* The WM_NCDESTROY message informs a window that its nonclient area is being destroyed. The - * DestroyWindow function sends the WM_NCDESTROY message to the window following the WM_DESTROY - * message. WM_DESTROY is used to free the allocated memory object associated with the window. - */ - break; - case WM_KILLFOCUS: - /* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard focus. - * We want to prevent this if a window is still active and it loses focus to nowhere*/ - if (!wParam && hwnd == ::GetActiveWindow()) - ::SetFocus(hwnd); - case WM_SHOWWINDOW: - /* The WM_SHOWWINDOW message is sent to a window when the window is about to be hidden or shown. */ - case WM_WINDOWPOSCHANGING: - /* The WM_WINDOWPOSCHANGING message is sent to a window whose size, position, or place in - * the Z order is about to change as a result of a call to the SetWindowPos function or - * another window-management function. - */ - case WM_SETFOCUS: - /* The WM_SETFOCUS message is sent to a window after it has gained the keyboard focus. */ - break; - //////////////////////////////////////////////////////////////////////// - // Other events - //////////////////////////////////////////////////////////////////////// - case WM_GETTEXT: - /* An application sends a WM_GETTEXT message to copy the text that - * corresponds to a window into a buffer provided by the caller. - */ - case WM_ACTIVATEAPP: - /* The WM_ACTIVATEAPP message is sent when a window belonging to a - * different application than the active window is about to be activated. - * The message is sent to the application whose window is being activated - * and to the application whose window is being deactivated. - */ - case WM_TIMER: - /* The WIN32 docs say: - * The WM_TIMER message is posted to the installing thread's message queue - * when a timer expires. You can process the message by providing a WM_TIMER - * case in the window procedure. Otherwise, the default window procedure will - * call the TimerProc callback function specified in the call to the SetTimer - * function used to install the timer. - * - * In GHOST, we let DefWindowProc call the timer callback. - */ - break; - - } - } - else { - // Event found for a window before the pointer to the class has been set. - GHOST_PRINT("GHOST_SystemWin32::wndProc: GHOST window event before creation\n"); - /* These are events we typically miss at this point: - * WM_GETMINMAXINFO 0x24 - * WM_NCCREATE 0x81 - * WM_NCCALCSIZE 0x83 - * WM_CREATE 0x01 - * We let DefWindowProc do the work. - */ - } - } - else { - // Events without valid hwnd - GHOST_PRINT("GHOST_SystemWin32::wndProc: event without window\n"); - } - - if (event) { - system->pushEvent(event); - eventHandled = true; - } - - if (!eventHandled) - lResult = ::DefWindowProcW(hwnd, msg, wParam, lParam); - - return lResult; + break; + } + case WM_SETCURSOR: + /* The WM_SETCURSOR message is sent to a window if the mouse causes the cursor + * to move within a window and mouse input is not captured. + * This means we have to set the cursor shape every time the mouse moves! + * The DefWindowProc function uses this message to set the cursor to an + * arrow if it is not in the client area. + */ + if (LOWORD(lParam) == HTCLIENT) { + // Load the current cursor + window->loadCursor(window->getCursorVisibility(), window->getCursorShape()); + // Bypass call to DefWindowProc + return 0; + } + else { + // Outside of client area show standard cursor + window->loadCursor(true, GHOST_kStandardCursorDefault); + } + break; + + //////////////////////////////////////////////////////////////////////// + // Mouse events, ignored + //////////////////////////////////////////////////////////////////////// + case WM_NCMOUSEMOVE: + /* The WM_NCMOUSEMOVE message is posted to a window when the cursor is moved + * within the nonclient area of the window. This message is posted to the window + * that contains the cursor. If a window has captured the mouse, this message is not posted. + */ + case WM_NCHITTEST: + /* The WM_NCHITTEST message is sent to a window when the cursor moves, or + * when a mouse button is pressed or released. If the mouse is not captured, + * the message is sent to the window beneath the cursor. Otherwise, the message + * is sent to the window that has captured the mouse. + */ + break; + + //////////////////////////////////////////////////////////////////////// + // Window events, processed + //////////////////////////////////////////////////////////////////////// + case WM_CLOSE: + /* The WM_CLOSE message is sent as a signal that a window or an application should terminate. */ + event = processWindowEvent(GHOST_kEventWindowClose, window); + break; + case WM_ACTIVATE: + /* The WM_ACTIVATE message is sent to both the window being activated and the window being + * deactivated. If the windows use the same input queue, the message is sent synchronously, + * first to the window procedure of the top-level window being deactivated, then to the window + * procedure of the top-level window being activated. If the windows use different input queues, + * the message is sent asynchronously, so the window is activated immediately. + */ + { + GHOST_ModifierKeys modifiers; + modifiers.clear(); + system->storeModifierKeys(modifiers); + system->m_wheelDeltaAccum = 0; + event = processWindowEvent(LOWORD(wParam) ? GHOST_kEventWindowActivate : + GHOST_kEventWindowDeactivate, + window); + /* WARNING: Let DefWindowProc handle WM_ACTIVATE, otherwise WM_MOUSEWHEEL + * will not be dispatched to OUR active window if we minimize one of OUR windows. */ + if (LOWORD(wParam) == WA_INACTIVE) + window->lostMouseCapture(); + window->processWin32TabletActivateEvent(GET_WM_ACTIVATE_STATE(wParam, lParam)); + lResult = ::DefWindowProc(hwnd, msg, wParam, lParam); + break; + } + case WM_ENTERSIZEMOVE: + /* The WM_ENTERSIZEMOVE message is sent one time to a window after it enters the moving + * or sizing modal loop. The window enters the moving or sizing modal loop when the user + * clicks the window's title bar or sizing border, or when the window passes the + * WM_SYSCOMMAND message to the DefWindowProc function and the wParam parameter of the + * message specifies the SC_MOVE or SC_SIZE value. The operation is complete when + * DefWindowProc returns. + */ + window->m_inLiveResize = 1; + break; + case WM_EXITSIZEMOVE: + window->m_inLiveResize = 0; + break; + case WM_PAINT: + /* An application sends the WM_PAINT message when the system or another application + * makes a request to paint a portion of an application's window. The message is sent + * when the UpdateWindow or RedrawWindow function is called, or by the DispatchMessage + * function when the application obtains a WM_PAINT message by using the GetMessage or + * PeekMessage function. + */ + if (!window->m_inLiveResize) { + event = processWindowEvent(GHOST_kEventWindowUpdate, window); + ::ValidateRect(hwnd, NULL); + } + else { + eventHandled = true; + } + break; + case WM_GETMINMAXINFO: + /* The WM_GETMINMAXINFO message is sent to a window when the size or + * position of the window is about to change. An application can use + * this message to override the window's default maximized size and + * position, or its default minimum or maximum tracking size. + */ + processMinMaxInfo((MINMAXINFO *)lParam); + /* Let DefWindowProc handle it. */ + break; + case WM_SIZING: + case WM_SIZE: + /* The WM_SIZE message is sent to a window after its size has changed. + * The WM_SIZE and WM_MOVE messages are not sent if an application handles the + * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient + * to perform any move or size change processing during the WM_WINDOWPOSCHANGED + * message without calling DefWindowProc. + */ + /* we get first WM_SIZE before we fully init. So, do not dispatch before we continiously resizng */ + if (window->m_inLiveResize) { + system->pushEvent(processWindowEvent(GHOST_kEventWindowSize, window)); + system->dispatchEvents(); + } + else { + event = processWindowEvent(GHOST_kEventWindowSize, window); + } + break; + case WM_CAPTURECHANGED: + window->lostMouseCapture(); + break; + case WM_MOVING: + /* The WM_MOVING message is sent to a window that the user is moving. By processing + * this message, an application can monitor the size and position of the drag rectangle + * and, if needed, change its size or position. + */ + case WM_MOVE: + /* The WM_SIZE and WM_MOVE messages are not sent if an application handles the + * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient + * to perform any move or size change processing during the WM_WINDOWPOSCHANGED + * message without calling DefWindowProc. + */ + /* see WM_SIZE comment*/ + if (window->m_inLiveResize) { + system->pushEvent(processWindowEvent(GHOST_kEventWindowMove, window)); + system->dispatchEvents(); + } + else { + event = processWindowEvent(GHOST_kEventWindowMove, window); + } + + break; + case WM_DPICHANGED: + /* The WM_DPICHANGED message is sent when the effective dots per inch (dpi) for a window has changed. + * The DPI is the scale factor for a window. There are multiple events that can cause the DPI to + * change such as when the window is moved to a monitor with a different DPI. + */ + { + // The suggested new size and position of the window. + RECT *const suggestedWindowRect = (RECT *)lParam; + + // Push DPI change event first + system->pushEvent(processWindowEvent(GHOST_kEventWindowDPIHintChanged, window)); + system->dispatchEvents(); + eventHandled = true; + + // Then move and resize window + SetWindowPos(hwnd, + NULL, + suggestedWindowRect->left, + suggestedWindowRect->top, + suggestedWindowRect->right - suggestedWindowRect->left, + suggestedWindowRect->bottom - suggestedWindowRect->top, + SWP_NOZORDER | SWP_NOACTIVATE); + } + break; + //////////////////////////////////////////////////////////////////////// + // Window events, ignored + //////////////////////////////////////////////////////////////////////// + case WM_WINDOWPOSCHANGED: + /* The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place + * in the Z order has changed as a result of a call to the SetWindowPos function or + * another window-management function. + * The WM_SIZE and WM_MOVE messages are not sent if an application handles the + * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient + * to perform any move or size change processing during the WM_WINDOWPOSCHANGED + * message without calling DefWindowProc. + */ + case WM_ERASEBKGND: + /* An application sends the WM_ERASEBKGND message when the window background must be + * erased (for example, when a window is resized). The message is sent to prepare an + * invalidated portion of a window for painting. + */ + case WM_NCPAINT: + /* An application sends the WM_NCPAINT message to a window when its frame must be painted. */ + case WM_NCACTIVATE: + /* The WM_NCACTIVATE message is sent to a window when its nonclient area needs to be changed + * to indicate an active or inactive state. + */ + case WM_DESTROY: + /* The WM_DESTROY message is sent when a window is being destroyed. It is sent to the window + * procedure of the window being destroyed after the window is removed from the screen. + * This message is sent first to the window being destroyed and then to the child windows + * (if any) as they are destroyed. During the processing of the message, it can be assumed + * that all child windows still exist. + */ + case WM_NCDESTROY: + /* The WM_NCDESTROY message informs a window that its nonclient area is being destroyed. The + * DestroyWindow function sends the WM_NCDESTROY message to the window following the WM_DESTROY + * message. WM_DESTROY is used to free the allocated memory object associated with the window. + */ + break; + case WM_KILLFOCUS: + /* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard focus. + * We want to prevent this if a window is still active and it loses focus to nowhere*/ + if (!wParam && hwnd == ::GetActiveWindow()) + ::SetFocus(hwnd); + case WM_SHOWWINDOW: + /* The WM_SHOWWINDOW message is sent to a window when the window is about to be hidden or shown. */ + case WM_WINDOWPOSCHANGING: + /* The WM_WINDOWPOSCHANGING message is sent to a window whose size, position, or place in + * the Z order is about to change as a result of a call to the SetWindowPos function or + * another window-management function. + */ + case WM_SETFOCUS: + /* The WM_SETFOCUS message is sent to a window after it has gained the keyboard focus. */ + break; + //////////////////////////////////////////////////////////////////////// + // Other events + //////////////////////////////////////////////////////////////////////// + case WM_GETTEXT: + /* An application sends a WM_GETTEXT message to copy the text that + * corresponds to a window into a buffer provided by the caller. + */ + case WM_ACTIVATEAPP: + /* The WM_ACTIVATEAPP message is sent when a window belonging to a + * different application than the active window is about to be activated. + * The message is sent to the application whose window is being activated + * and to the application whose window is being deactivated. + */ + case WM_TIMER: + /* The WIN32 docs say: + * The WM_TIMER message is posted to the installing thread's message queue + * when a timer expires. You can process the message by providing a WM_TIMER + * case in the window procedure. Otherwise, the default window procedure will + * call the TimerProc callback function specified in the call to the SetTimer + * function used to install the timer. + * + * In GHOST, we let DefWindowProc call the timer callback. + */ + break; + } + } + else { + // Event found for a window before the pointer to the class has been set. + GHOST_PRINT("GHOST_SystemWin32::wndProc: GHOST window event before creation\n"); + /* These are events we typically miss at this point: + * WM_GETMINMAXINFO 0x24 + * WM_NCCREATE 0x81 + * WM_NCCALCSIZE 0x83 + * WM_CREATE 0x01 + * We let DefWindowProc do the work. + */ + } + } + else { + // Events without valid hwnd + GHOST_PRINT("GHOST_SystemWin32::wndProc: event without window\n"); + } + + if (event) { + system->pushEvent(event); + eventHandled = true; + } + + if (!eventHandled) + lResult = ::DefWindowProcW(hwnd, msg, wParam, lParam); + + return lResult; } GHOST_TUns8 *GHOST_SystemWin32::getClipboard(bool selection) const { - char *temp_buff; - - if (IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(NULL) ) { - wchar_t *buffer; - HANDLE hData = GetClipboardData(CF_UNICODETEXT); - if (hData == NULL) { - CloseClipboard(); - return NULL; - } - buffer = (wchar_t *)GlobalLock(hData); - if (!buffer) { - CloseClipboard(); - return NULL; - } - - temp_buff = alloc_utf_8_from_16(buffer, 0); - - /* Buffer mustn't be accessed after CloseClipboard - * it would like accessing free-d memory */ - GlobalUnlock(hData); - CloseClipboard(); - - return (GHOST_TUns8 *)temp_buff; - } - else if (IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(NULL) ) { - char *buffer; - size_t len = 0; - HANDLE hData = GetClipboardData(CF_TEXT); - if (hData == NULL) { - CloseClipboard(); - return NULL; - } - buffer = (char *)GlobalLock(hData); - if (!buffer) { - CloseClipboard(); - return NULL; - } - - len = strlen(buffer); - temp_buff = (char *) malloc(len + 1); - strncpy(temp_buff, buffer, len); - temp_buff[len] = '\0'; - - /* Buffer mustn't be accessed after CloseClipboard - * it would like accessing free-d memory */ - GlobalUnlock(hData); - CloseClipboard(); - - return (GHOST_TUns8 *)temp_buff; - } - else { - return NULL; - } + char *temp_buff; + + if (IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(NULL)) { + wchar_t *buffer; + HANDLE hData = GetClipboardData(CF_UNICODETEXT); + if (hData == NULL) { + CloseClipboard(); + return NULL; + } + buffer = (wchar_t *)GlobalLock(hData); + if (!buffer) { + CloseClipboard(); + return NULL; + } + + temp_buff = alloc_utf_8_from_16(buffer, 0); + + /* Buffer mustn't be accessed after CloseClipboard + * it would like accessing free-d memory */ + GlobalUnlock(hData); + CloseClipboard(); + + return (GHOST_TUns8 *)temp_buff; + } + else if (IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(NULL)) { + char *buffer; + size_t len = 0; + HANDLE hData = GetClipboardData(CF_TEXT); + if (hData == NULL) { + CloseClipboard(); + return NULL; + } + buffer = (char *)GlobalLock(hData); + if (!buffer) { + CloseClipboard(); + return NULL; + } + + len = strlen(buffer); + temp_buff = (char *)malloc(len + 1); + strncpy(temp_buff, buffer, len); + temp_buff[len] = '\0'; + + /* Buffer mustn't be accessed after CloseClipboard + * it would like accessing free-d memory */ + GlobalUnlock(hData); + CloseClipboard(); + + return (GHOST_TUns8 *)temp_buff; + } + else { + return NULL; + } } void GHOST_SystemWin32::putClipboard(GHOST_TInt8 *buffer, bool selection) const { - if (selection) {return; } // for copying the selection, used on X11 - - if (OpenClipboard(NULL)) { - HLOCAL clipbuffer; - wchar_t *data; - - if (buffer) { - size_t len = count_utf_16_from_8(buffer); - EmptyClipboard(); - - clipbuffer = LocalAlloc(LMEM_FIXED, sizeof(wchar_t) * len); - data = (wchar_t *)GlobalLock(clipbuffer); - - conv_utf_8_to_16(buffer, data, len); - - LocalUnlock(clipbuffer); - SetClipboardData(CF_UNICODETEXT, clipbuffer); - } - CloseClipboard(); - } - else { - return; - } + if (selection) { + return; + } // for copying the selection, used on X11 + + if (OpenClipboard(NULL)) { + HLOCAL clipbuffer; + wchar_t *data; + + if (buffer) { + size_t len = count_utf_16_from_8(buffer); + EmptyClipboard(); + + clipbuffer = LocalAlloc(LMEM_FIXED, sizeof(wchar_t) * len); + data = (wchar_t *)GlobalLock(clipbuffer); + + conv_utf_8_to_16(buffer, data, len); + + LocalUnlock(clipbuffer); + SetClipboardData(CF_UNICODETEXT, clipbuffer); + } + CloseClipboard(); + } + else { + return; + } } static DWORD GetParentProcessID(void) { - HANDLE snapshot; - PROCESSENTRY32 pe32 = {0}; - DWORD ppid = 0, pid = GetCurrentProcessId(); - snapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ); - if (snapshot == INVALID_HANDLE_VALUE) { - return -1; - } - pe32.dwSize = sizeof( pe32 ); - if (!Process32First(snapshot, &pe32)) { - CloseHandle(snapshot); - return -1; - } - do { - if (pe32.th32ProcessID == pid) { - ppid = pe32.th32ParentProcessID; - break; - } - } while (Process32Next(snapshot, &pe32)); - CloseHandle(snapshot); - return ppid; + HANDLE snapshot; + PROCESSENTRY32 pe32 = {0}; + DWORD ppid = 0, pid = GetCurrentProcessId(); + snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (snapshot == INVALID_HANDLE_VALUE) { + return -1; + } + pe32.dwSize = sizeof(pe32); + if (!Process32First(snapshot, &pe32)) { + CloseHandle(snapshot); + return -1; + } + do { + if (pe32.th32ProcessID == pid) { + ppid = pe32.th32ParentProcessID; + break; + } + } while (Process32Next(snapshot, &pe32)); + CloseHandle(snapshot); + return ppid; } static bool getProcessName(int pid, char *buffer, int max_len) { - bool result = false; - HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, - FALSE, pid); - if (handle) { - GetModuleFileNameEx(handle, 0, buffer, max_len); - result = true; - } - CloseHandle(handle); - return result; + bool result = false; + HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); + if (handle) { + GetModuleFileNameEx(handle, 0, buffer, max_len); + result = true; + } + CloseHandle(handle); + return result; } static bool isStartedFromCommandPrompt() { - HWND hwnd = GetConsoleWindow(); - - if (hwnd) { - DWORD pid = (DWORD)-1; - DWORD ppid = GetParentProcessID(); - char parent_name[MAX_PATH]; - bool start_from_launcher = false; - - GetWindowThreadProcessId(hwnd, &pid); - if (getProcessName(ppid, parent_name, sizeof(parent_name))) { - char *filename = strrchr(parent_name, '\\'); - if (filename != NULL) { - start_from_launcher = strstr(filename, "blender.exe") != NULL; - } - } - - /* When we're starting from a wrapper we need to compare with parent process ID. */ - if (pid != (start_from_launcher ? ppid : GetCurrentProcessId())) - return true; - } - - return false; + HWND hwnd = GetConsoleWindow(); + + if (hwnd) { + DWORD pid = (DWORD)-1; + DWORD ppid = GetParentProcessID(); + char parent_name[MAX_PATH]; + bool start_from_launcher = false; + + GetWindowThreadProcessId(hwnd, &pid); + if (getProcessName(ppid, parent_name, sizeof(parent_name))) { + char *filename = strrchr(parent_name, '\\'); + if (filename != NULL) { + start_from_launcher = strstr(filename, "blender.exe") != NULL; + } + } + + /* When we're starting from a wrapper we need to compare with parent process ID. */ + if (pid != (start_from_launcher ? ppid : GetCurrentProcessId())) + return true; + } + + return false; } int GHOST_SystemWin32::toggleConsole(int action) { - HWND wnd = GetConsoleWindow(); - - switch (action) { - case 3: // startup: hide if not started from command prompt - { - if (!isStartedFromCommandPrompt()) { - ShowWindow(wnd, SW_HIDE); - m_consoleStatus = 0; - } - break; - } - case 0: // hide - ShowWindow(wnd, SW_HIDE); - m_consoleStatus = 0; - break; - case 1: // show - ShowWindow(wnd, SW_SHOW); - if (!isStartedFromCommandPrompt()) { - DeleteMenu(GetSystemMenu(wnd, FALSE), SC_CLOSE, MF_BYCOMMAND); - } - m_consoleStatus = 1; - break; - case 2: // toggle - ShowWindow(wnd, m_consoleStatus ? SW_HIDE : SW_SHOW); - m_consoleStatus = !m_consoleStatus; - if (m_consoleStatus && !isStartedFromCommandPrompt()) { - DeleteMenu(GetSystemMenu(wnd, FALSE), SC_CLOSE, MF_BYCOMMAND); - } - break; - } - - return m_consoleStatus; + HWND wnd = GetConsoleWindow(); + + switch (action) { + case 3: // startup: hide if not started from command prompt + { + if (!isStartedFromCommandPrompt()) { + ShowWindow(wnd, SW_HIDE); + m_consoleStatus = 0; + } + break; + } + case 0: // hide + ShowWindow(wnd, SW_HIDE); + m_consoleStatus = 0; + break; + case 1: // show + ShowWindow(wnd, SW_SHOW); + if (!isStartedFromCommandPrompt()) { + DeleteMenu(GetSystemMenu(wnd, FALSE), SC_CLOSE, MF_BYCOMMAND); + } + m_consoleStatus = 1; + break; + case 2: // toggle + ShowWindow(wnd, m_consoleStatus ? SW_HIDE : SW_SHOW); + m_consoleStatus = !m_consoleStatus; + if (m_consoleStatus && !isStartedFromCommandPrompt()) { + DeleteMenu(GetSystemMenu(wnd, FALSE), SC_CLOSE, MF_BYCOMMAND); + } + break; + } + + return m_consoleStatus; } int GHOST_SystemWin32::confirmQuit(GHOST_IWindow *window) const { - return (MessageBox(window ? ((GHOST_WindowWin32 *)window)->getHWND() : 0, "Some changes have not been saved.\nDo you really want to quit?", - "Exit Blender", MB_OKCANCEL | MB_ICONWARNING | MB_TOPMOST) == IDOK); + return (MessageBox(window ? ((GHOST_WindowWin32 *)window)->getHWND() : 0, + "Some changes have not been saved.\nDo you really want to quit?", + "Exit Blender", + MB_OKCANCEL | MB_ICONWARNING | MB_TOPMOST) == IDOK); } |