Merge branch 'blender2.7'
[blender.git] / intern / ghost / intern / GHOST_SystemWin32.cpp
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file ghost/intern/GHOST_SystemWin32.cpp
29  *  \ingroup GHOST
30  *
31  * \author      Maarten Gribnau
32  */
33
34
35 #include "GHOST_SystemWin32.h"
36 #include "GHOST_EventDragnDrop.h"
37
38 #ifndef _WIN32_IE
39 #  define _WIN32_IE 0x0501 /* shipped before XP, so doesn't impose additional requirements */
40 #endif
41
42 #include <shlobj.h>
43 #include <tlhelp32.h>
44 #include <psapi.h>
45 #include <windowsx.h>
46
47 #include "utfconv.h"
48
49 #include "GHOST_DisplayManagerWin32.h"
50 #include "GHOST_EventButton.h"
51 #include "GHOST_EventCursor.h"
52 #include "GHOST_EventKey.h"
53 #include "GHOST_EventWheel.h"
54 #include "GHOST_TimerTask.h"
55 #include "GHOST_TimerManager.h"
56 #include "GHOST_WindowManager.h"
57 #include "GHOST_WindowWin32.h"
58
59 #if defined(WITH_GL_EGL)
60 #  include "GHOST_ContextEGL.h"
61 #else
62 #  include "GHOST_ContextWGL.h"
63 #endif
64
65 #ifdef WITH_INPUT_NDOF
66   #include "GHOST_NDOFManagerWin32.h"
67 #endif
68
69 // Key code values not found in winuser.h
70 #ifndef VK_MINUS
71 #define VK_MINUS 0xBD
72 #endif // VK_MINUS
73 #ifndef VK_SEMICOLON
74 #define VK_SEMICOLON 0xBA
75 #endif // VK_SEMICOLON
76 #ifndef VK_PERIOD
77 #define VK_PERIOD 0xBE
78 #endif // VK_PERIOD
79 #ifndef VK_COMMA
80 #define VK_COMMA 0xBC
81 #endif // VK_COMMA
82 #ifndef VK_QUOTE
83 #define VK_QUOTE 0xDE
84 #endif // VK_QUOTE
85 #ifndef VK_BACK_QUOTE
86 #define VK_BACK_QUOTE 0xC0
87 #endif // VK_BACK_QUOTE
88 #ifndef VK_SLASH
89 #define VK_SLASH 0xBF
90 #endif // VK_SLASH
91 #ifndef VK_BACK_SLASH
92 #define VK_BACK_SLASH 0xDC
93 #endif // VK_BACK_SLASH
94 #ifndef VK_EQUALS
95 #define VK_EQUALS 0xBB
96 #endif // VK_EQUALS
97 #ifndef VK_OPEN_BRACKET
98 #define VK_OPEN_BRACKET 0xDB
99 #endif // VK_OPEN_BRACKET
100 #ifndef VK_CLOSE_BRACKET
101 #define VK_CLOSE_BRACKET 0xDD
102 #endif // VK_CLOSE_BRACKET
103 #ifndef VK_GR_LESS
104 #define VK_GR_LESS 0xE2
105 #endif // VK_GR_LESS
106
107 #ifndef VK_MEDIA_NEXT_TRACK
108 #define VK_MEDIA_NEXT_TRACK 0xB0
109 #endif // VK_MEDIA_NEXT_TRACK
110 #ifndef VK_MEDIA_PREV_TRACK
111 #define VK_MEDIA_PREV_TRACK 0xB1
112 #endif // VK_MEDIA_PREV_TRACK
113 #ifndef VK_MEDIA_STOP
114 #define VK_MEDIA_STOP 0xB2
115 #endif // VK_MEDIA_STOP
116 #ifndef VK_MEDIA_PLAY_PAUSE
117 #define VK_MEDIA_PLAY_PAUSE 0xB3
118 #endif // VK_MEDIA_PLAY_PAUSE
119
120 // Window message newer than Windows 7
121 #ifndef WM_DPICHANGED
122 #define WM_DPICHANGED 0x02E0
123 #endif // WM_DPICHANGED
124
125 #ifndef WM_POINTERUPDATE
126 #define WM_POINTERUPDATE 0x0245
127 #endif // WM_POINTERUPDATE
128
129 /* Workaround for some laptop touchpads, some of which seems to
130  * have driver issues which makes it so window function receives
131  * the message, but PeekMessage doesn't pick those messages for
132  * some reason.
133  *
134  * We send a dummy WM_USER message to force PeekMessage to receive
135  * something, making it so blender's window manager sees the new
136  * messages coming in.
137  */
138 #define BROKEN_PEEK_TOUCHPAD
139
140 static void initRawInput()
141 {
142 #ifdef WITH_INPUT_NDOF
143   #define DEVICE_COUNT 2
144 #else
145   #define DEVICE_COUNT 1
146 #endif
147
148         RAWINPUTDEVICE devices[DEVICE_COUNT];
149         memset(devices, 0, DEVICE_COUNT * sizeof(RAWINPUTDEVICE));
150
151         // Initiates WM_INPUT messages from keyboard
152         // That way GHOST can retrieve true keys
153         devices[0].usUsagePage = 0x01;
154         devices[0].usUsage = 0x06; /* http://msdn.microsoft.com/en-us/windows/hardware/gg487473.aspx */
155
156 #ifdef WITH_INPUT_NDOF
157         // multi-axis mouse (SpaceNavigator, etc.)
158         devices[1].usUsagePage = 0x01;
159         devices[1].usUsage = 0x08;
160 #endif
161
162         if (RegisterRawInputDevices(devices, DEVICE_COUNT, sizeof(RAWINPUTDEVICE)))
163                 ;  // yay!
164         else
165                 GHOST_PRINTF("could not register for RawInput: %d\n", (int)GetLastError());
166
167 #undef DEVICE_COUNT
168 }
169
170 #ifndef DPI_ENUMS_DECLARED
171 typedef enum PROCESS_DPI_AWARENESS {
172         PROCESS_DPI_UNAWARE = 0,
173         PROCESS_SYSTEM_DPI_AWARE = 1,
174         PROCESS_PER_MONITOR_DPI_AWARE = 2
175 } PROCESS_DPI_AWARENESS;
176
177 typedef enum MONITOR_DPI_TYPE {
178         MDT_EFFECTIVE_DPI = 0,
179         MDT_ANGULAR_DPI = 1,
180         MDT_RAW_DPI = 2,
181         MDT_DEFAULT = MDT_EFFECTIVE_DPI
182 } MONITOR_DPI_TYPE;
183
184 #define USER_DEFAULT_SCREEN_DPI 96
185
186 #define DPI_ENUMS_DECLARED
187 #endif
188 typedef HRESULT(API * GHOST_WIN32_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS);
189 typedef BOOL(API * GHOST_WIN32_EnableNonClientDpiScaling)(HWND);
190
191 GHOST_SystemWin32::GHOST_SystemWin32()
192         : m_hasPerformanceCounter(false), m_freq(0), m_start(0)
193 {
194         m_displayManager = new GHOST_DisplayManagerWin32();
195         GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::GHOST_SystemWin32(): m_displayManager==0\n");
196         m_displayManager->initialize();
197
198         m_consoleStatus = 1;
199
200         // Tell Windows we are per monitor DPI aware. This disables the default
201         // blurry scaling and enables WM_DPICHANGED to allow us to draw at proper DPI.
202         HMODULE m_shcore = ::LoadLibrary("Shcore.dll");
203         if (m_shcore) {
204                 GHOST_WIN32_SetProcessDpiAwareness fpSetProcessDpiAwareness =
205                         (GHOST_WIN32_SetProcessDpiAwareness) ::GetProcAddress(m_shcore, "SetProcessDpiAwareness");
206
207                 if (fpSetProcessDpiAwareness) {
208                         fpSetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
209                 }
210         }
211
212         // Check if current keyboard layout uses AltGr and save keylayout ID for
213         // specialized handling if keys like VK_OEM_*. I.e. french keylayout
214         // generates VK_OEM_8 for their exclamation key (key left of right shift)
215         this->handleKeyboardChange();
216         // Require COM for GHOST_DropTargetWin32 created in GHOST_WindowWin32.
217         OleInitialize(0);
218
219 #ifdef WITH_INPUT_NDOF
220         m_ndofManager = new GHOST_NDOFManagerWin32(*this);
221 #endif
222 }
223
224 GHOST_SystemWin32::~GHOST_SystemWin32()
225 {
226         // Shutdown COM
227         OleUninitialize();
228         toggleConsole(1);
229 }
230
231
232 GHOST_TUns64 GHOST_SystemWin32::getMilliSeconds() const
233 {
234         // Hardware does not support high resolution timers. We will use GetTickCount instead then.
235         if (!m_hasPerformanceCounter) {
236                 return ::GetTickCount();
237         }
238
239         // Retrieve current count
240         __int64 count = 0;
241         ::QueryPerformanceCounter((LARGE_INTEGER *)&count);
242
243         // Calculate the time passed since system initialization.
244         __int64 delta = 1000 * (count - m_start);
245
246         GHOST_TUns64 t = (GHOST_TUns64)(delta / m_freq);
247         return t;
248 }
249
250
251 GHOST_TUns8 GHOST_SystemWin32::getNumDisplays() const
252 {
253         GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::getNumDisplays(): m_displayManager==0\n");
254         GHOST_TUns8 numDisplays;
255         m_displayManager->getNumDisplays(numDisplays);
256         return numDisplays;
257 }
258
259
260 void GHOST_SystemWin32::getMainDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const
261 {
262         width = ::GetSystemMetrics(SM_CXSCREEN);
263         height = ::GetSystemMetrics(SM_CYSCREEN);
264 }
265
266 void GHOST_SystemWin32::getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const
267 {
268         width = ::GetSystemMetrics(SM_CXVIRTUALSCREEN);
269         height = ::GetSystemMetrics(SM_CYVIRTUALSCREEN);
270 }
271
272 GHOST_IWindow *GHOST_SystemWin32::createWindow(
273         const STR_String &title,
274         GHOST_TInt32 left, GHOST_TInt32 top,
275         GHOST_TUns32 width, GHOST_TUns32 height,
276         GHOST_TWindowState state, GHOST_TDrawingContextType type,
277         GHOST_GLSettings glSettings,
278         const bool exclusive,
279         const GHOST_TEmbedderWindowID parentWindow)
280 {
281         GHOST_WindowWin32 *window =
282                 new GHOST_WindowWin32(
283                         this,
284                         title,
285                         left,
286                         top,
287                         width,
288                         height,
289                         state,
290                         type,
291                         ((glSettings.flags & GHOST_glStereoVisual) != 0),
292                         ((glSettings.flags & GHOST_glAlphaBackground) != 0),
293                         glSettings.numOfAASamples,
294                         parentWindow,
295                         ((glSettings.flags & GHOST_glDebugContext) != 0));
296
297         if (window->getValid()) {
298                 // Store the pointer to the window
299                 m_windowManager->addWindow(window);
300                 m_windowManager->setActiveWindow(window);
301         }
302         else {
303                 GHOST_PRINT("GHOST_SystemWin32::createWindow(): window invalid\n");
304                 delete window;
305                 window = NULL;
306         }
307
308         return window;
309 }
310
311
312 /**
313  * Create a new offscreen context.
314  * Never explicitly delete the window, use disposeContext() instead.
315  * \return  The new context (or 0 if creation failed).
316  */
317 GHOST_IContext *GHOST_SystemWin32::createOffscreenContext()
318 {
319         bool debug_context = false; /* TODO: inform as a parameter */
320
321         GHOST_Context *context;
322
323         HWND wnd = CreateWindowA("STATIC",
324                 "BlenderGLEW",
325                 WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
326                 0, 0, 64, 64,
327                 NULL, NULL,
328                 GetModuleHandle(NULL), NULL
329         );
330
331         HDC  mHDC = GetDC(wnd);
332         HDC  prev_hdc = wglGetCurrentDC();
333         HGLRC prev_context = wglGetCurrentContext();
334 #if defined(WITH_GL_PROFILE_CORE)
335         for (int minor = 5; minor >= 0; --minor) {
336                         context = new GHOST_ContextWGL(
337                             false, true, 0,
338                             wnd, mHDC,
339                             WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
340                             4, minor,
341                             (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
342                             GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
343
344                         if (context->initializeDrawingContext()) {
345                                 goto finished;
346                         }
347                         else {
348                                 delete context;
349                         }
350                 }
351
352                 context = new GHOST_ContextWGL(
353                     false, true, 0,
354                     wnd, mHDC,
355                     WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
356                     3, 3,
357                     (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
358                     GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
359
360                 if (context->initializeDrawingContext()) {
361                         goto finished;
362                 }
363                 else {
364                         MessageBox(
365                                 NULL,
366                                 "Blender requires a graphics driver with at least OpenGL 3.3 support.\n\n"
367                                 "The program will now close.",
368                                 "Blender - Unsupported Graphics Driver",
369                                 MB_OK | MB_ICONERROR);
370                         delete context;
371                         exit();
372                 }
373
374 #elif defined(WITH_GL_PROFILE_COMPAT)
375                 // ask for 2.1 context, driver gives any GL version >= 2.1 (hopefully the latest compatibility profile)
376                 // 2.1 ignores the profile bit & is incompatible with core profile
377                 context = new GHOST_ContextWGL(
378                         false, true, 0,
379                         NULL, NULL,
380                         0, // no profile bit
381                         2, 1,
382                         (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
383                         GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
384
385                 if (context->initializeDrawingContext()) {
386                         return context;
387                 }
388                 else {
389                         delete context;
390                 }
391 #else
392 #  error // must specify either core or compat at build time
393 #endif
394 finished:
395         wglMakeCurrent(prev_hdc, prev_context);
396         return context;
397 }
398
399 /**
400  * Dispose of a context.
401  * \param   context Pointer to the context to be disposed.
402  * \return  Indication of success.
403  */
404 GHOST_TSuccess GHOST_SystemWin32::disposeContext(GHOST_IContext *context)
405 {
406         delete context;
407
408         return GHOST_kSuccess;
409 }
410
411
412 bool GHOST_SystemWin32::processEvents(bool waitForEvent)
413 {
414         MSG msg;
415         bool hasEventHandled = false;
416
417         do {
418                 GHOST_TimerManager *timerMgr = getTimerManager();
419
420                 if (waitForEvent && !::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
421 #if 1
422                         ::Sleep(1);
423 #else
424                         GHOST_TUns64 next = timerMgr->nextFireTime();
425                         GHOST_TInt64 maxSleep = next - getMilliSeconds();
426
427                         if (next == GHOST_kFireTimeNever) {
428                                 ::WaitMessage();
429                         }
430                         else if (maxSleep >= 0.0) {
431                                 ::SetTimer(NULL, 0, maxSleep, NULL);
432                                 ::WaitMessage();
433                                 ::KillTimer(NULL, 0);
434                         }
435 #endif
436                 }
437
438                 if (timerMgr->fireTimers(getMilliSeconds())) {
439                         hasEventHandled = true;
440                 }
441
442                 // Process all the events waiting for us
443                 while (::PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) != 0) {
444                         // TranslateMessage doesn't alter the message, and doesn't change our raw keyboard data.
445                         // Needed for MapVirtualKey or if we ever need to get chars from wm_ime_char or similar.
446                         ::TranslateMessage(&msg);
447                         ::DispatchMessageW(&msg);
448                 }
449
450                 if (hasEventHandled == false) {
451                         // Check if we have events handled by the system
452                         // (for example the `GHOST_kEventWindowClose`).
453                         hasEventHandled = m_eventManager->getNumEvents() != 0;
454                 }
455
456         } while (waitForEvent && !hasEventHandled);
457
458         return hasEventHandled;
459 }
460
461
462 GHOST_TSuccess GHOST_SystemWin32::getCursorPosition(GHOST_TInt32 &x, GHOST_TInt32 &y) const
463 {
464         POINT point;
465         if (::GetCursorPos(&point)) {
466                 x = point.x;
467                 y = point.y;
468                 return GHOST_kSuccess;
469         }
470         return GHOST_kFailure;
471 }
472
473
474 GHOST_TSuccess GHOST_SystemWin32::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y)
475 {
476         if (!::GetActiveWindow())
477                 return GHOST_kFailure;
478         return ::SetCursorPos(x, y) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
479 }
480
481
482 GHOST_TSuccess GHOST_SystemWin32::getModifierKeys(GHOST_ModifierKeys &keys) const
483 {
484         bool down = HIBYTE(::GetKeyState(VK_LSHIFT)) != 0;
485         keys.set(GHOST_kModifierKeyLeftShift, down);
486         down = HIBYTE(::GetKeyState(VK_RSHIFT)) != 0;
487         keys.set(GHOST_kModifierKeyRightShift, down);
488
489         down = HIBYTE(::GetKeyState(VK_LMENU)) != 0;
490         keys.set(GHOST_kModifierKeyLeftAlt, down);
491         down = HIBYTE(::GetKeyState(VK_RMENU)) != 0;
492         keys.set(GHOST_kModifierKeyRightAlt, down);
493
494         down = HIBYTE(::GetKeyState(VK_LCONTROL)) != 0;
495         keys.set(GHOST_kModifierKeyLeftControl, down);
496         down = HIBYTE(::GetKeyState(VK_RCONTROL)) != 0;
497         keys.set(GHOST_kModifierKeyRightControl, down);
498
499         bool lwindown = HIBYTE(::GetKeyState(VK_LWIN)) != 0;
500         bool rwindown = HIBYTE(::GetKeyState(VK_RWIN)) != 0;
501         if (lwindown || rwindown)
502                 keys.set(GHOST_kModifierKeyOS, true);
503         else
504                 keys.set(GHOST_kModifierKeyOS, false);
505         return GHOST_kSuccess;
506 }
507
508
509 GHOST_TSuccess GHOST_SystemWin32::getButtons(GHOST_Buttons &buttons) const
510 {
511         /* Check for swapped buttons (left-handed mouse buttons)
512          * GetAsyncKeyState() will give back the state of the physical mouse buttons.
513          */
514         bool swapped = ::GetSystemMetrics(SM_SWAPBUTTON) == TRUE;
515
516         bool down = HIBYTE(::GetKeyState(VK_LBUTTON)) != 0;
517         buttons.set(swapped ? GHOST_kButtonMaskRight : GHOST_kButtonMaskLeft, down);
518
519         down = HIBYTE(::GetKeyState(VK_MBUTTON)) != 0;
520         buttons.set(GHOST_kButtonMaskMiddle, down);
521
522         down = HIBYTE(::GetKeyState(VK_RBUTTON)) != 0;
523         buttons.set(swapped ? GHOST_kButtonMaskLeft : GHOST_kButtonMaskRight, down);
524         return GHOST_kSuccess;
525 }
526
527
528 GHOST_TSuccess GHOST_SystemWin32::init()
529 {
530         GHOST_TSuccess success = GHOST_System::init();
531
532         /* Disable scaling on high DPI displays on Vista */
533         HMODULE
534             user32 = ::LoadLibraryA("user32.dll");
535         typedef BOOL (WINAPI * LPFNSETPROCESSDPIAWARE)();
536         LPFNSETPROCESSDPIAWARE SetProcessDPIAware =
537             (LPFNSETPROCESSDPIAWARE)GetProcAddress(user32, "SetProcessDPIAware");
538         if (SetProcessDPIAware)
539                 SetProcessDPIAware();
540         FreeLibrary(user32);
541         initRawInput();
542
543         // Determine whether this system has a high frequency performance counter. */
544         m_hasPerformanceCounter = ::QueryPerformanceFrequency((LARGE_INTEGER *)&m_freq) == TRUE;
545         if (m_hasPerformanceCounter) {
546                 GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer available\n");
547                 ::QueryPerformanceCounter((LARGE_INTEGER *)&m_start);
548         }
549         else {
550                 GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer not available\n");
551         }
552
553         if (success) {
554                 WNDCLASSW wc = {0};
555                 wc.style = CS_HREDRAW | CS_VREDRAW;
556                 wc.lpfnWndProc = s_wndProc;
557                 wc.cbClsExtra = 0;
558                 wc.cbWndExtra = 0;
559                 wc.hInstance = ::GetModuleHandle(0);
560                 wc.hIcon = ::LoadIcon(wc.hInstance, "APPICON");
561
562                 if (!wc.hIcon) {
563                         ::LoadIcon(NULL, IDI_APPLICATION);
564                 }
565                 wc.hCursor = ::LoadCursor(0, IDC_ARROW);
566                 wc.hbrBackground =
567 #ifdef INW32_COMPISITING
568                         (HBRUSH)CreateSolidBrush
569 #endif
570                         (0x00000000);
571                 wc.lpszMenuName = 0;
572                 wc.lpszClassName = L"GHOST_WindowClass";
573
574                 // Use RegisterClassEx for setting small icon
575                 if (::RegisterClassW(&wc) == 0) {
576                         success = GHOST_kFailure;
577                 }
578         }
579
580         return success;
581 }
582
583
584 GHOST_TSuccess GHOST_SystemWin32::exit()
585 {
586         return GHOST_System::exit();
587 }
588
589 GHOST_TKey GHOST_SystemWin32::hardKey(RAWINPUT const &raw, int *keyDown, char *vk)
590 {
591         GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
592         GHOST_TKey key = GHOST_kKeyUnknown;
593         GHOST_ModifierKeys modifiers;
594         system->retrieveModifierKeys(modifiers);
595
596         // RI_KEY_BREAK doesn't work for sticky keys release, so we also
597         // check for the up message
598         unsigned int msg = raw.data.keyboard.Message;
599         *keyDown = !(raw.data.keyboard.Flags & RI_KEY_BREAK) && msg != WM_KEYUP && msg != WM_SYSKEYUP;
600
601         key = this->convertKey(raw.data.keyboard.VKey, raw.data.keyboard.MakeCode, (raw.data.keyboard.Flags & (RI_KEY_E1 | RI_KEY_E0)));
602
603         // extra handling of modifier keys: don't send repeats out from GHOST
604         if (key >= GHOST_kKeyLeftShift && key <= GHOST_kKeyRightAlt) {
605                 bool changed = false;
606                 GHOST_TModifierKeyMask modifier;
607                 switch (key) {
608                         case GHOST_kKeyLeftShift:
609                         {
610                                 changed = (modifiers.get(GHOST_kModifierKeyLeftShift) != (bool)*keyDown);
611                                 modifier = GHOST_kModifierKeyLeftShift;
612                                 break;
613                         }
614                         case GHOST_kKeyRightShift:
615                         {
616                                 changed = (modifiers.get(GHOST_kModifierKeyRightShift) != (bool)*keyDown);
617                                 modifier = GHOST_kModifierKeyRightShift;
618                                 break;
619                         }
620                         case GHOST_kKeyLeftControl:
621                         {
622                                 changed = (modifiers.get(GHOST_kModifierKeyLeftControl) != (bool)*keyDown);
623                                 modifier = GHOST_kModifierKeyLeftControl;
624                                 break;
625                         }
626                         case GHOST_kKeyRightControl:
627                         {
628                                 changed = (modifiers.get(GHOST_kModifierKeyRightControl) != (bool)*keyDown);
629                                 modifier = GHOST_kModifierKeyRightControl;
630                                 break;
631                         }
632                         case GHOST_kKeyLeftAlt:
633                         {
634                                 changed = (modifiers.get(GHOST_kModifierKeyLeftAlt) != (bool)*keyDown);
635                                 modifier = GHOST_kModifierKeyLeftAlt;
636                                 break;
637                         }
638                         case GHOST_kKeyRightAlt:
639                         {
640                                 changed = (modifiers.get(GHOST_kModifierKeyRightAlt) != (bool)*keyDown);
641                                 modifier = GHOST_kModifierKeyRightAlt;
642                                 break;
643                         }
644                         default:
645                                 break;
646                 }
647
648                 if (changed) {
649                         modifiers.set(modifier, (bool)*keyDown);
650                         system->storeModifierKeys(modifiers);
651                 }
652                 else {
653                         key = GHOST_kKeyUnknown;
654                 }
655         }
656
657
658         if (vk) *vk = raw.data.keyboard.VKey;
659
660         return key;
661 }
662
663 //! note: this function can be extended to include other exotic cases as they arise.
664 // This function was added in response to bug [#25715]
665 // This is going to be a long list [T42426]
666 GHOST_TKey GHOST_SystemWin32::processSpecialKey(short vKey, short scanCode) const
667 {
668         GHOST_TKey key = GHOST_kKeyUnknown;
669         switch (PRIMARYLANGID(m_langId)) {
670                 case LANG_FRENCH:
671                         if (vKey == VK_OEM_8) key = GHOST_kKeyF13;  // oem key; used purely for shortcuts .
672                         break;
673                 case LANG_ENGLISH:
674                         if (SUBLANGID(m_langId) == SUBLANG_ENGLISH_UK && vKey == VK_OEM_8) // "`¬"
675                                 key = GHOST_kKeyAccentGrave;
676                         break;
677         }
678
679         return key;
680 }
681
682 GHOST_TKey GHOST_SystemWin32::convertKey(short vKey, short scanCode, short extend) const
683 {
684         GHOST_TKey key;
685
686         if ((vKey >= '0') && (vKey <= '9')) {
687                 // VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39)
688                 key = (GHOST_TKey)(vKey - '0' + GHOST_kKey0);
689         }
690         else if ((vKey >= 'A') && (vKey <= 'Z')) {
691                 // VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A)
692                 key = (GHOST_TKey)(vKey - 'A' + GHOST_kKeyA);
693         }
694         else if ((vKey >= VK_F1) && (vKey <= VK_F24)) {
695                 key = (GHOST_TKey)(vKey - VK_F1 + GHOST_kKeyF1);
696         }
697         else {
698                 switch (vKey) {
699                         case VK_RETURN:
700                                 key = (extend) ? GHOST_kKeyNumpadEnter : GHOST_kKeyEnter; break;
701
702                         case VK_BACK:     key = GHOST_kKeyBackSpace;        break;
703                         case VK_TAB:      key = GHOST_kKeyTab;              break;
704                         case VK_ESCAPE:   key = GHOST_kKeyEsc;              break;
705                         case VK_SPACE:    key = GHOST_kKeySpace;            break;
706
707                         case VK_INSERT:
708                         case VK_NUMPAD0:
709                                 key = (extend) ? GHOST_kKeyInsert : GHOST_kKeyNumpad0;  break;
710                         case VK_END:
711                         case VK_NUMPAD1:
712                                 key = (extend) ? GHOST_kKeyEnd : GHOST_kKeyNumpad1; break;
713                         case VK_DOWN:
714                         case VK_NUMPAD2:
715                                 key = (extend) ? GHOST_kKeyDownArrow : GHOST_kKeyNumpad2;   break;
716                         case VK_NEXT:
717                         case VK_NUMPAD3:
718                                 key = (extend) ?  GHOST_kKeyDownPage : GHOST_kKeyNumpad3;   break;
719                         case VK_LEFT:
720                         case VK_NUMPAD4:
721                                 key = (extend) ? GHOST_kKeyLeftArrow : GHOST_kKeyNumpad4;   break;
722                         case VK_CLEAR:
723                         case VK_NUMPAD5:
724                                 key = (extend) ? GHOST_kKeyUnknown : GHOST_kKeyNumpad5;  break;
725                         case VK_RIGHT:
726                         case VK_NUMPAD6:
727                                 key = (extend) ? GHOST_kKeyRightArrow : GHOST_kKeyNumpad6;  break;
728                         case VK_HOME:
729                         case VK_NUMPAD7:
730                                 key = (extend) ? GHOST_kKeyHome : GHOST_kKeyNumpad7;    break;
731                         case VK_UP:
732                         case VK_NUMPAD8:
733                                 key = (extend) ? GHOST_kKeyUpArrow : GHOST_kKeyNumpad8; break;
734                         case VK_PRIOR:
735                         case VK_NUMPAD9:
736                                 key = (extend) ? GHOST_kKeyUpPage : GHOST_kKeyNumpad9;  break;
737                         case VK_DECIMAL:
738                         case VK_DELETE:
739                                 key = (extend) ? GHOST_kKeyDelete : GHOST_kKeyNumpadPeriod; break;
740
741                         case VK_SNAPSHOT: key = GHOST_kKeyPrintScreen;      break;
742                         case VK_PAUSE:    key = GHOST_kKeyPause;            break;
743                         case VK_MULTIPLY: key = GHOST_kKeyNumpadAsterisk;   break;
744                         case VK_SUBTRACT: key = GHOST_kKeyNumpadMinus;      break;
745                         case VK_DIVIDE:   key = GHOST_kKeyNumpadSlash;      break;
746                         case VK_ADD:      key = GHOST_kKeyNumpadPlus;       break;
747
748                         case VK_SEMICOLON:      key = GHOST_kKeySemicolon;      break;
749                         case VK_EQUALS:         key = GHOST_kKeyEqual;          break;
750                         case VK_COMMA:          key = GHOST_kKeyComma;          break;
751                         case VK_MINUS:          key = GHOST_kKeyMinus;          break;
752                         case VK_PERIOD:         key = GHOST_kKeyPeriod;         break;
753                         case VK_SLASH:          key = GHOST_kKeySlash;          break;
754                         case VK_BACK_QUOTE:     key = GHOST_kKeyAccentGrave;    break;
755                         case VK_OPEN_BRACKET:   key = GHOST_kKeyLeftBracket;    break;
756                         case VK_BACK_SLASH:     key = GHOST_kKeyBackslash;      break;
757                         case VK_CLOSE_BRACKET:  key = GHOST_kKeyRightBracket;   break;
758                         case VK_QUOTE:          key = GHOST_kKeyQuote;          break;
759                         case VK_GR_LESS:        key = GHOST_kKeyGrLess;         break;
760
761                         case VK_SHIFT:
762                                 /* Check single shift presses */
763                                 if (scanCode == 0x36) {
764                                         key = GHOST_kKeyRightShift;
765                                 }
766                                 else if (scanCode == 0x2a) {
767                                         key = GHOST_kKeyLeftShift;
768                                 }
769                                 else {
770                                         /* Must be a combination SHIFT (Left or Right) + a Key
771                                          * Ignore this as the next message will contain
772                                          * the desired "Key" */
773                                         key = GHOST_kKeyUnknown;
774                                 }
775                                 break;
776                         case VK_CONTROL:
777                                 key = (extend) ? GHOST_kKeyRightControl : GHOST_kKeyLeftControl;
778                                 break;
779                         case VK_MENU:
780                                 key = (extend) ? GHOST_kKeyRightAlt : GHOST_kKeyLeftAlt;
781                                 break;
782                         case VK_LWIN:
783                         case VK_RWIN:
784                                 key = GHOST_kKeyOS;
785                                 break;
786                         case VK_NUMLOCK: key = GHOST_kKeyNumLock; break;
787                         case VK_SCROLL: key = GHOST_kKeyScrollLock; break;
788                         case VK_CAPITAL: key = GHOST_kKeyCapsLock; break;
789                         case VK_OEM_8:
790                                 key = ((GHOST_SystemWin32 *)getSystem())->processSpecialKey(vKey, scanCode);
791                                 break;
792                         case VK_MEDIA_PLAY_PAUSE: key = GHOST_kKeyMediaPlay; break;
793                         case VK_MEDIA_STOP: key = GHOST_kKeyMediaStop; break;
794                         case VK_MEDIA_PREV_TRACK: key = GHOST_kKeyMediaFirst; break;
795                         case VK_MEDIA_NEXT_TRACK: key = GHOST_kKeyMediaLast; break;
796                         default:
797                                 key = GHOST_kKeyUnknown;
798                                 break;
799                 }
800         }
801
802         return key;
803 }
804
805 GHOST_EventButton *GHOST_SystemWin32::processButtonEvent(
806         GHOST_TEventType type,
807         GHOST_WindowWin32 *window,
808         GHOST_TButtonMask mask)
809 {
810         return new GHOST_EventButton(getSystem()->getMilliSeconds(), type, window, mask);
811 }
812
813
814 GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type, GHOST_WindowWin32 *window)
815 {
816         GHOST_TInt32 x_screen, y_screen;
817         GHOST_SystemWin32 *system = (GHOST_SystemWin32 *) getSystem();
818
819         system->getCursorPosition(x_screen, y_screen);
820
821         /* TODO: CHECK IF THIS IS A TABLET EVENT */
822         bool is_tablet = false;
823
824         if (is_tablet == false && window->getCursorGrabModeIsWarp()) {
825                 GHOST_TInt32 x_new = x_screen;
826                 GHOST_TInt32 y_new = y_screen;
827                 GHOST_TInt32 x_accum, y_accum;
828                 GHOST_Rect bounds;
829
830                 /* fallback to window bounds */
831                 if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) {
832                         window->getClientBounds(bounds);
833                 }
834
835                 /* could also clamp to screen bounds
836                  * wrap with a window outside the view will fail atm  */
837
838                 bounds.wrapPoint(x_new, y_new, 2); /* offset of one incase blender is at screen bounds */
839
840                 window->getCursorGrabAccum(x_accum, y_accum);
841                 if (x_new != x_screen || y_new != y_screen) {
842                         /* when wrapping we don't need to add an event because the
843                          * setCursorPosition call will cause a new event after */
844                         system->setCursorPosition(x_new, y_new); /* wrap */
845                         window->setCursorGrabAccum(x_accum + (x_screen - x_new), y_accum + (y_screen - y_new));
846                 }
847                 else {
848                         return new GHOST_EventCursor(system->getMilliSeconds(),
849                                                      GHOST_kEventCursorMove,
850                                                      window,
851                                                      x_screen + x_accum,
852                                                      y_screen + y_accum
853                                                      );
854                 }
855
856         }
857         else {
858                 return new GHOST_EventCursor(system->getMilliSeconds(),
859                                              GHOST_kEventCursorMove,
860                                              window,
861                                              x_screen,
862                                              y_screen
863                                              );
864         }
865         return NULL;
866 }
867
868
869 void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam)
870 {
871         GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
872
873         int acc = system->m_wheelDeltaAccum;
874         int delta = GET_WHEEL_DELTA_WPARAM(wParam);
875
876         if (acc * delta < 0) {
877                 // scroll direction reversed.
878                 acc = 0;
879         }
880         acc += delta;
881         int direction = (acc >= 0) ? 1 : -1;
882         acc = abs(acc);
883
884         while (acc >= WHEEL_DELTA) {
885                 system->pushEvent(new GHOST_EventWheel(system->getMilliSeconds(), window, direction));
886                 acc -= WHEEL_DELTA;
887         }
888         system->m_wheelDeltaAccum = acc * direction;
889 }
890
891
892 GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RAWINPUT const &raw)
893 {
894         int keyDown = 0;
895         char vk;
896         GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
897         GHOST_TKey key = system->hardKey(raw, &keyDown, &vk);
898         GHOST_EventKey *event;
899
900         if (key != GHOST_kKeyUnknown) {
901                 char utf8_char[6] = {0};
902                 char ascii = 0;
903
904                 wchar_t utf16[3] = {0};
905                 BYTE state[256] = {0};
906                 int r;
907                 GetKeyboardState((PBYTE)state);
908
909                 // don't call ToUnicodeEx on dead keys as it clears the buffer and so won't allow diacritical composition.
910                 if (MapVirtualKeyW(vk, 2) != 0) {
911                         // todo: ToUnicodeEx can respond with up to 4 utf16 chars (only 2 here). Could be up to 24 utf8 bytes.
912                         if ((r = ToUnicodeEx(vk, raw.data.keyboard.MakeCode, state, utf16, 2, 0, system->m_keylayout))) {
913                                 if ((r > 0 && r < 3)) {
914                                         utf16[r] = 0;
915                                         conv_utf_16_to_8(utf16, utf8_char, 6);
916                                 }
917                                 else if (r == -1) {
918                                         utf8_char[0] = '\0';
919                                 }
920                         }
921                 }
922
923                 if (!keyDown) {
924                         utf8_char[0] = '\0';
925                         ascii = '\0';
926                 }
927                 else {
928                         ascii = utf8_char[0] & 0x80 ? '?' : utf8_char[0];
929                 }
930
931                 event = new GHOST_EventKey(system->getMilliSeconds(), keyDown ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, key, ascii, utf8_char);
932
933                 // GHOST_PRINTF("%c\n", ascii); // we already get this info via EventPrinter
934         }
935         else {
936                 event = NULL;
937         }
938         return event;
939 }
940
941
942 GHOST_Event *GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type, GHOST_WindowWin32 *window)
943 {
944         GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
945
946         if (type == GHOST_kEventWindowActivate) {
947                 system->getWindowManager()->setActiveWindow(window);
948                 window->bringTabletContextToFront();
949         }
950
951         return new GHOST_Event(system->getMilliSeconds(), type, window);
952 }
953
954 #ifdef WITH_INPUT_IME
955 GHOST_Event *GHOST_SystemWin32::processImeEvent(GHOST_TEventType type, GHOST_WindowWin32 *window, GHOST_TEventImeData *data)
956 {
957         GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
958         return new GHOST_EventIME(system->getMilliSeconds(), type, window, data);
959 }
960 #endif
961
962
963 GHOST_TSuccess GHOST_SystemWin32::pushDragDropEvent(
964         GHOST_TEventType eventType,
965         GHOST_TDragnDropTypes draggedObjectType,
966         GHOST_WindowWin32 *window,
967         int mouseX, int mouseY,
968         void *data)
969 {
970         GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
971         return system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(),
972                                                           eventType,
973                                                           draggedObjectType,
974                                                           window, mouseX, mouseY, data)
975                                  );
976 }
977
978 void GHOST_SystemWin32::processMinMaxInfo(MINMAXINFO *minmax)
979 {
980         minmax->ptMinTrackSize.x = 320;
981         minmax->ptMinTrackSize.y = 240;
982 }
983
984 #ifdef WITH_INPUT_NDOF
985 bool GHOST_SystemWin32::processNDOF(RAWINPUT const &raw)
986 {
987         bool eventSent = false;
988         GHOST_TUns64 now = getMilliSeconds();
989
990         static bool firstEvent = true;
991         if (firstEvent) { // determine exactly which device is plugged in
992                 RID_DEVICE_INFO info;
993                 unsigned infoSize = sizeof(RID_DEVICE_INFO);
994                 info.cbSize = infoSize;
995
996                 GetRawInputDeviceInfo(raw.header.hDevice, RIDI_DEVICEINFO, &info, &infoSize);
997                 if (info.dwType == RIM_TYPEHID)
998                         m_ndofManager->setDevice(info.hid.dwVendorId, info.hid.dwProductId);
999                 else
1000                         GHOST_PRINT("<!> not a HID device... mouse/kb perhaps?\n");
1001
1002                 firstEvent = false;
1003         }
1004
1005         // The NDOF manager sends button changes immediately, and *pretends* to
1006         // send motion. Mark as 'sent' so motion will always get dispatched.
1007         eventSent = true;
1008
1009         BYTE const *data = raw.data.hid.bRawData;
1010
1011         BYTE packetType = data[0];
1012         switch (packetType) {
1013                 case 1: // translation
1014                 {
1015                         const short *axis = (short *)(data + 1);
1016                         // massage into blender view coords (same goes for rotation)
1017                         const int t[3] = {axis[0], -axis[2], axis[1]};
1018                         m_ndofManager->updateTranslation(t, now);
1019
1020                         if (raw.data.hid.dwSizeHid == 13) {
1021                                 // this report also includes rotation
1022                                 const int r[3] = {-axis[3], axis[5], -axis[4]};
1023                                 m_ndofManager->updateRotation(r, now);
1024
1025                                 // I've never gotten one of these, has anyone else?
1026                                 GHOST_PRINT("ndof: combined T + R\n");
1027                         }
1028                         break;
1029                 }
1030                 case 2: // rotation
1031                 {
1032                         const short *axis = (short *)(data + 1);
1033                         const int r[3] = {-axis[0], axis[2], -axis[1]};
1034                         m_ndofManager->updateRotation(r, now);
1035                         break;
1036                 }
1037                 case 3: // buttons
1038                 {
1039                         int button_bits;
1040                         memcpy(&button_bits, data + 1, sizeof(button_bits));
1041                         m_ndofManager->updateButtons(button_bits, now);
1042                         break;
1043                 }
1044         }
1045         return eventSent;
1046 }
1047 #endif // WITH_INPUT_NDOF
1048
1049 LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1050 {
1051         GHOST_Event *event = NULL;
1052         bool eventHandled = false;
1053
1054         LRESULT lResult = 0;
1055         GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
1056         GHOST_EventManager *eventManager = system->getEventManager();
1057         GHOST_ASSERT(system, "GHOST_SystemWin32::s_wndProc(): system not initialized");
1058
1059         if (hwnd) {
1060 #if 0
1061                 // Disabled due to bug in Intel drivers, see T51959
1062                 if(msg == WM_NCCREATE) {
1063                         // Tell Windows to automatically handle scaling of non-client areas
1064                         // such as the caption bar. EnableNonClientDpiScaling was introduced in Windows 10
1065                         HMODULE m_user32 = ::LoadLibrary("User32.dll");
1066                         if (m_user32) {
1067                                 GHOST_WIN32_EnableNonClientDpiScaling fpEnableNonClientDpiScaling =
1068                                         (GHOST_WIN32_EnableNonClientDpiScaling) ::GetProcAddress(m_user32, "EnableNonClientDpiScaling");
1069
1070                                 if (fpEnableNonClientDpiScaling) {
1071                                         fpEnableNonClientDpiScaling(hwnd);
1072                                 }
1073                         }
1074                 }
1075 #endif
1076
1077                 GHOST_WindowWin32 *window = (GHOST_WindowWin32 *)::GetWindowLongPtr(hwnd, GWLP_USERDATA);
1078                 if (window) {
1079                         switch (msg) {
1080                                 // we need to check if new key layout has AltGr
1081                                 case WM_INPUTLANGCHANGE:
1082                                 {
1083                                         system->handleKeyboardChange();
1084 #ifdef WITH_INPUT_IME
1085                                         window->getImeInput()->SetInputLanguage();
1086 #endif
1087                                         break;
1088                                 }
1089                                 ////////////////////////////////////////////////////////////////////////
1090                                 // Keyboard events, processed
1091                                 ////////////////////////////////////////////////////////////////////////
1092                                 case WM_INPUT:
1093                                 {
1094                                         // check WM_INPUT from input sink when ghost window is not in the foreground
1095                                         if (wParam == RIM_INPUTSINK) {
1096                                                 if (GetFocus() != hwnd) // WM_INPUT message not for this window
1097                                                         return 0;
1098                                         } //else wParam == RIM_INPUT
1099
1100                                         RAWINPUT raw;
1101                                         RAWINPUT *raw_ptr = &raw;
1102                                         UINT rawSize = sizeof(RAWINPUT);
1103
1104                                         GetRawInputData((HRAWINPUT)lParam, RID_INPUT, raw_ptr, &rawSize, sizeof(RAWINPUTHEADER));
1105
1106                                         switch (raw.header.dwType) {
1107                                                 case RIM_TYPEKEYBOARD:
1108                                                         event = processKeyEvent(window, raw);
1109                                                         if (!event) {
1110                                                                 GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ");
1111                                                                 GHOST_PRINT(msg);
1112                                                                 GHOST_PRINT(" key ignored\n");
1113                                                         }
1114                                                         break;
1115 #ifdef WITH_INPUT_NDOF
1116                                                 case RIM_TYPEHID:
1117                                                         if (system->processNDOF(raw)) {
1118                                                                 system->m_ndofManager->sendMotionEvent();
1119                                                                 eventHandled = true;
1120                                                         }
1121                                                         break;
1122 #endif
1123                                         }
1124                                         break;
1125                                 }
1126 #ifdef WITH_INPUT_IME
1127                                 ////////////////////////////////////////////////////////////////////////
1128                                 // IME events, processed, read more in GHOST_IME.h
1129                                 ////////////////////////////////////////////////////////////////////////
1130                                 case WM_IME_SETCONTEXT:
1131                                 {
1132                                         GHOST_ImeWin32 *ime = window->getImeInput();
1133                                         ime->SetInputLanguage();
1134                                         ime->CreateImeWindow(hwnd);
1135                                         ime->CleanupComposition(hwnd);
1136                                         ime->CheckFirst(hwnd);
1137                                         break;
1138                                 }
1139                                 case WM_IME_STARTCOMPOSITION:
1140                                 {
1141                                         GHOST_ImeWin32 *ime = window->getImeInput();
1142                                         eventHandled = true;
1143                                         /* remove input event before start comp event, avoid redundant input */
1144                                         eventManager->removeTypeEvents(GHOST_kEventKeyDown, window);
1145                                         ime->CreateImeWindow(hwnd);
1146                                         ime->ResetComposition(hwnd);
1147                                         event = processImeEvent(
1148                                                 GHOST_kEventImeCompositionStart,
1149                                                 window,
1150                                                 &ime->eventImeData);
1151                                         break;
1152                                 }
1153                                 case WM_IME_COMPOSITION:
1154                                 {
1155                                         GHOST_ImeWin32 *ime = window->getImeInput();
1156                                         eventHandled = true;
1157                                         ime->UpdateImeWindow(hwnd);
1158                                         ime->UpdateInfo(hwnd);
1159                                         if (ime->eventImeData.result_len) {
1160                                                 /* remove redundant IME event */
1161                                                 eventManager->removeTypeEvents(GHOST_kEventImeComposition, window);
1162                                         }
1163                                         event = processImeEvent(
1164                                                 GHOST_kEventImeComposition,
1165                                                 window,
1166                                                 &ime->eventImeData);
1167                                         break;
1168                                 }
1169                                 case WM_IME_ENDCOMPOSITION:
1170                                 {
1171                                         GHOST_ImeWin32 *ime = window->getImeInput();
1172                                         eventHandled = true;
1173                                         /* remove input event after end comp event, avoid redundant input */
1174                                         eventManager->removeTypeEvents(GHOST_kEventKeyDown, window);
1175                                         ime->ResetComposition(hwnd);
1176                                         ime->DestroyImeWindow(hwnd);
1177                                         event = processImeEvent(
1178                                                 GHOST_kEventImeCompositionEnd,
1179                                                 window,
1180                                                 &ime->eventImeData);
1181                                         break;
1182                                 }
1183 #endif /* WITH_INPUT_IME */
1184                                 ////////////////////////////////////////////////////////////////////////
1185                                 // Keyboard events, ignored
1186                                 ////////////////////////////////////////////////////////////////////////
1187                                 case WM_KEYDOWN:
1188                                 case WM_SYSKEYDOWN:
1189                                 case WM_KEYUP:
1190                                 case WM_SYSKEYUP:
1191                                 /* These functions were replaced by WM_INPUT*/
1192                                 case WM_CHAR:
1193                                 /* The WM_CHAR message is posted to the window with the keyboard focus when
1194                                  * a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR
1195                                  * contains the character code of the key that was pressed.
1196                                  */
1197                                 case WM_DEADCHAR:
1198                                         /* The WM_DEADCHAR message is posted to the window with the keyboard focus when a
1199                                          * WM_KEYUP message is translated by the TranslateMessage function. WM_DEADCHAR
1200                                          * specifies a character code generated by a dead key. A dead key is a key that
1201                                          * generates a character, such as the umlaut (double-dot), that is combined with
1202                                          * another character to form a composite character. For example, the umlaut-O
1203                                          * character (Ö) is generated by typing the dead key for the umlaut character, and
1204                                          * then typing the O key.
1205                                          */
1206                                         break;
1207                                 case WM_SYSDEADCHAR:
1208                                 /* The WM_SYSDEADCHAR message is sent to the window with the keyboard focus when
1209                                  * a WM_SYSKEYDOWN message is translated by the TranslateMessage function.
1210                                  * WM_SYSDEADCHAR specifies the character code of a system dead key - that is,
1211                                  * a dead key that is pressed while holding down the alt key.
1212                                  */
1213                                 case WM_SYSCHAR:
1214                                         /* The WM_SYSCHAR message is sent to the window with the keyboard focus when
1215                                          * a WM_SYSCHAR message is translated by the TranslateMessage function.
1216                                          * WM_SYSCHAR specifies the character code of a dead key - that is,
1217                                          * a dead key that is pressed while holding down the alt key.
1218                                          * To prevent the sound, DefWindowProc must be avoided by return
1219                                          */
1220                                         break;
1221                                 case WM_SYSCOMMAND:
1222                                         /* The WM_SYSCHAR message is sent to the window when system commands such as
1223                                          * maximize, minimize  or close the window are triggered. Also it is sent when ALT
1224                                          * button is press for menu. To prevent this we must return preventing DefWindowProc.
1225                                          */
1226                                         if (wParam == SC_KEYMENU) {
1227                                                 eventHandled = true;
1228                                         }
1229                                         break;
1230                                 ////////////////////////////////////////////////////////////////////////
1231                                 // Tablet events, processed
1232                                 ////////////////////////////////////////////////////////////////////////
1233                                 case WT_PACKET:
1234                                         window->processWin32TabletEvent(wParam, lParam);
1235                                         break;
1236                                 case WT_CSRCHANGE:
1237                                 case WT_PROXIMITY:
1238                                         window->processWin32TabletInitEvent();
1239                                         break;
1240                                 case WM_POINTERUPDATE:
1241                                         window->processWin32PointerEvent(wParam);
1242                                         break;
1243                                 ////////////////////////////////////////////////////////////////////////
1244                                 // Mouse events, processed
1245                                 ////////////////////////////////////////////////////////////////////////
1246                                 case WM_LBUTTONDOWN:
1247                                         window->registerMouseClickEvent(0);
1248                                         event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft);
1249                                         break;
1250                                 case WM_MBUTTONDOWN:
1251                                         window->registerMouseClickEvent(0);
1252                                         event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskMiddle);
1253                                         break;
1254                                 case WM_RBUTTONDOWN:
1255                                         window->registerMouseClickEvent(0);
1256                                         event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight);
1257                                         break;
1258                                 case WM_XBUTTONDOWN:
1259                                         window->registerMouseClickEvent(0);
1260                                         if ((short) HIWORD(wParam) == XBUTTON1) {
1261                                                 event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton4);
1262                                         }
1263                                         else if ((short) HIWORD(wParam) == XBUTTON2) {
1264                                                 event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton5);
1265                                         }
1266                                         break;
1267                                 case WM_LBUTTONUP:
1268                                         window->registerMouseClickEvent(1);
1269                                         event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft);
1270                                         break;
1271                                 case WM_MBUTTONUP:
1272                                         window->registerMouseClickEvent(1);
1273                                         event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle);
1274                                         break;
1275                                 case WM_RBUTTONUP:
1276                                         window->registerMouseClickEvent(1);
1277                                         event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight);
1278                                         break;
1279                                 case WM_XBUTTONUP:
1280                                         window->registerMouseClickEvent(1);
1281                                         if ((short) HIWORD(wParam) == XBUTTON1) {
1282                                                 event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton4);
1283                                         }
1284                                         else if ((short) HIWORD(wParam) == XBUTTON2) {
1285                                                 event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton5);
1286                                         }
1287                                         break;
1288                                 case WM_MOUSEMOVE:
1289                                         event = processCursorEvent(GHOST_kEventCursorMove, window);
1290                                         break;
1291                                 case WM_MOUSEWHEEL:
1292                                 {
1293                                         /* The WM_MOUSEWHEEL message is sent to the focus window
1294                                          * when the mouse wheel is rotated. The DefWindowProc
1295                                          * function propagates the message to the window's parent.
1296                                          * There should be no internal forwarding of the message,
1297                                          * since DefWindowProc propagates it up the parent chain
1298                                          * until it finds a window that processes it.
1299                                          */
1300
1301                                         /* Get the window under the mouse and send event to its queue. */
1302                                         POINT mouse_pos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
1303                                         HWND mouse_hwnd = ChildWindowFromPoint(HWND_DESKTOP, mouse_pos);
1304                                         GHOST_WindowWin32 *mouse_window = (GHOST_WindowWin32 *)::GetWindowLongPtr(mouse_hwnd, GWLP_USERDATA);
1305
1306                                         processWheelEvent(mouse_window ? mouse_window : window , wParam, lParam);
1307                                         eventHandled = true;
1308 #ifdef BROKEN_PEEK_TOUCHPAD
1309                                         PostMessage(hwnd, WM_USER, 0, 0);
1310 #endif
1311                                         break;
1312                                 }
1313                                 case WM_SETCURSOR:
1314                                         /* The WM_SETCURSOR message is sent to a window if the mouse causes the cursor
1315                                          * to move within a window and mouse input is not captured.
1316                                          * This means we have to set the cursor shape every time the mouse moves!
1317                                          * The DefWindowProc function uses this message to set the cursor to an
1318                                          * arrow if it is not in the client area.
1319                                          */
1320                                         if (LOWORD(lParam) == HTCLIENT) {
1321                                                 // Load the current cursor
1322                                                 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
1323                                                 // Bypass call to DefWindowProc
1324                                                 return 0;
1325                                         }
1326                                         else {
1327                                                 // Outside of client area show standard cursor
1328                                                 window->loadCursor(true, GHOST_kStandardCursorDefault);
1329                                         }
1330                                         break;
1331
1332                                 ////////////////////////////////////////////////////////////////////////
1333                                 // Mouse events, ignored
1334                                 ////////////////////////////////////////////////////////////////////////
1335                                 case WM_NCMOUSEMOVE:
1336                                 /* The WM_NCMOUSEMOVE message is posted to a window when the cursor is moved
1337                                  * within the nonclient area of the window. This message is posted to the window
1338                                  * that contains the cursor. If a window has captured the mouse, this message is not posted.
1339                                  */
1340                                 case WM_NCHITTEST:
1341                                         /* The WM_NCHITTEST message is sent to a window when the cursor moves, or
1342                                          * when a mouse button is pressed or released. If the mouse is not captured,
1343                                          * the message is sent to the window beneath the cursor. Otherwise, the message
1344                                          * is sent to the window that has captured the mouse.
1345                                          */
1346                                         break;
1347
1348                                 ////////////////////////////////////////////////////////////////////////
1349                                 // Window events, processed
1350                                 ////////////////////////////////////////////////////////////////////////
1351                                 case WM_CLOSE:
1352                                         /* The WM_CLOSE message is sent as a signal that a window or an application should terminate. */
1353                                         event = processWindowEvent(GHOST_kEventWindowClose, window);
1354                                         break;
1355                                 case WM_ACTIVATE:
1356                                         /* The WM_ACTIVATE message is sent to both the window being activated and the window being
1357                                          * deactivated. If the windows use the same input queue, the message is sent synchronously,
1358                                          * first to the window procedure of the top-level window being deactivated, then to the window
1359                                          * procedure of the top-level window being activated. If the windows use different input queues,
1360                                          * the message is sent asynchronously, so the window is activated immediately.
1361                                          */
1362                                 {
1363                                         GHOST_ModifierKeys modifiers;
1364                                         modifiers.clear();
1365                                         system->storeModifierKeys(modifiers);
1366                                         system->m_wheelDeltaAccum = 0;
1367                                         event = processWindowEvent(LOWORD(wParam) ? GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate, window);
1368                                         /* WARNING: Let DefWindowProc handle WM_ACTIVATE, otherwise WM_MOUSEWHEEL
1369                                          * will not be dispatched to OUR active window if we minimize one of OUR windows. */
1370                                         if (LOWORD(wParam) == WA_INACTIVE)
1371                                                 window->lostMouseCapture();
1372                                         window->processWin32TabletActivateEvent(GET_WM_ACTIVATE_STATE(wParam, lParam));
1373                                         lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
1374                                         break;
1375                                 }
1376                                 case WM_ENTERSIZEMOVE:
1377                                         /* The WM_ENTERSIZEMOVE message is sent one time to a window after it enters the moving
1378                                          * or sizing modal loop. The window enters the moving or sizing modal loop when the user
1379                                          * clicks the window's title bar or sizing border, or when the window passes the
1380                                          * WM_SYSCOMMAND message to the DefWindowProc function and the wParam parameter of the
1381                                          * message specifies the SC_MOVE or SC_SIZE value. The operation is complete when
1382                                          * DefWindowProc returns.
1383                                          */
1384                                         window->m_inLiveResize = 1;
1385                                         break;
1386                                 case WM_EXITSIZEMOVE:
1387                                         window->m_inLiveResize = 0;
1388                                         break;
1389                                 case WM_PAINT:
1390                                         /* An application sends the WM_PAINT message when the system or another application
1391                                          * makes a request to paint a portion of an application's window. The message is sent
1392                                          * when the UpdateWindow or RedrawWindow function is called, or by the DispatchMessage
1393                                          * function when the application obtains a WM_PAINT message by using the GetMessage or
1394                                          * PeekMessage function.
1395                                          */
1396                                         if (!window->m_inLiveResize) {
1397                                                 event = processWindowEvent(GHOST_kEventWindowUpdate, window);
1398                                                 ::ValidateRect(hwnd, NULL);
1399                                         }
1400                                         else {
1401                                                 eventHandled = true;
1402                                         }
1403                                         break;
1404                                 case WM_GETMINMAXINFO:
1405                                         /* The WM_GETMINMAXINFO message is sent to a window when the size or
1406                                          * position of the window is about to change. An application can use
1407                                          * this message to override the window's default maximized size and
1408                                          * position, or its default minimum or maximum tracking size.
1409                                          */
1410                                         processMinMaxInfo((MINMAXINFO *) lParam);
1411                                         /* Let DefWindowProc handle it. */
1412                                         break;
1413                                 case WM_SIZING:
1414                                 case WM_SIZE:
1415                                         /* The WM_SIZE message is sent to a window after its size has changed.
1416                                          * The WM_SIZE and WM_MOVE messages are not sent if an application handles the
1417                                          * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
1418                                          * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
1419                                          * message without calling DefWindowProc.
1420                                          */
1421                                         /* we get first WM_SIZE before we fully init. So, do not dispatch before we continiously resizng */
1422                                         if (window->m_inLiveResize) {
1423                                                 system->pushEvent(processWindowEvent(GHOST_kEventWindowSize, window));
1424                                                 system->dispatchEvents();
1425                                         }
1426                                         else {
1427                                                 event = processWindowEvent(GHOST_kEventWindowSize, window);
1428                                         }
1429                                         break;
1430                                 case WM_CAPTURECHANGED:
1431                                         window->lostMouseCapture();
1432                                         break;
1433                                 case WM_MOVING:
1434                                         /* The WM_MOVING message is sent to a window that the user is moving. By processing
1435                                          * this message, an application can monitor the size and position of the drag rectangle
1436                                          * and, if needed, change its size or position.
1437                                          */
1438                                 case WM_MOVE:
1439                                         /* The WM_SIZE and WM_MOVE messages are not sent if an application handles the
1440                                          * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
1441                                          * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
1442                                          * message without calling DefWindowProc.
1443                                          */
1444                                         /* see WM_SIZE comment*/
1445                                         if (window->m_inLiveResize) {
1446                                                 system->pushEvent(processWindowEvent(GHOST_kEventWindowMove, window));
1447                                                 system->dispatchEvents();
1448                                         }
1449                                         else {
1450                                                 event = processWindowEvent(GHOST_kEventWindowMove, window);
1451                                         }
1452
1453                                         break;
1454                                 case WM_DPICHANGED:
1455                                         /* The WM_DPICHANGED message is sent when the effective dots per inch (dpi) for a window has changed.
1456                                          * The DPI is the scale factor for a window. There are multiple events that can cause the DPI to
1457                                          * change such as when the window is moved to a monitor with a different DPI.
1458                                          */
1459                                         {
1460                                                 // The suggested new size and position of the window.
1461                                                 RECT* const suggestedWindowRect = (RECT*)lParam;
1462
1463                                                 // Push DPI change event first
1464                                                 system->pushEvent(processWindowEvent(GHOST_kEventWindowDPIHintChanged, window));
1465                                                 system->dispatchEvents();
1466                                                 eventHandled = true;
1467
1468                                                 // Then move and resize window
1469                                                 SetWindowPos(hwnd,
1470                                                         NULL,
1471                                                         suggestedWindowRect->left,
1472                                                         suggestedWindowRect->top,
1473                                                         suggestedWindowRect->right - suggestedWindowRect->left,
1474                                                         suggestedWindowRect->bottom - suggestedWindowRect->top,
1475                                                         SWP_NOZORDER | SWP_NOACTIVATE);
1476                                         }
1477                                         break;
1478                                 ////////////////////////////////////////////////////////////////////////
1479                                 // Window events, ignored
1480                                 ////////////////////////////////////////////////////////////////////////
1481                                 case WM_WINDOWPOSCHANGED:
1482                                 /* The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place
1483                                  * in the Z order has changed as a result of a call to the SetWindowPos function or
1484                                  * another window-management function.
1485                                  * The WM_SIZE and WM_MOVE messages are not sent if an application handles the
1486                                  * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
1487                                  * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
1488                                  * message without calling DefWindowProc.
1489                                  */
1490                                 case WM_ERASEBKGND:
1491                                 /* An application sends the WM_ERASEBKGND message when the window background must be
1492                                  * erased (for example, when a window is resized). The message is sent to prepare an
1493                                  * invalidated portion of a window for painting.
1494                                  */
1495                                 case WM_NCPAINT:
1496                                 /* An application sends the WM_NCPAINT message to a window when its frame must be painted. */
1497                                 case WM_NCACTIVATE:
1498                                 /* The WM_NCACTIVATE message is sent to a window when its nonclient area needs to be changed
1499                                  * to indicate an active or inactive state.
1500                                  */
1501                                 case WM_DESTROY:
1502                                 /* The WM_DESTROY message is sent when a window is being destroyed. It is sent to the window
1503                                  * procedure of the window being destroyed after the window is removed from the screen.
1504                                  * This message is sent first to the window being destroyed and then to the child windows
1505                                  * (if any) as they are destroyed. During the processing of the message, it can be assumed
1506                                  * that all child windows still exist.
1507                                  */
1508                                 case WM_NCDESTROY:
1509                                         /* The WM_NCDESTROY message informs a window that its nonclient area is being destroyed. The
1510                                          * DestroyWindow function sends the WM_NCDESTROY message to the window following the WM_DESTROY
1511                                          * message. WM_DESTROY is used to free the allocated memory object associated with the window.
1512                                          */
1513                                         break;
1514                                 case WM_KILLFOCUS:
1515                                         /* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard focus.
1516                                          * We want to prevent this if a window is still active and it loses focus to nowhere*/
1517                                         if (!wParam && hwnd == ::GetActiveWindow())
1518                                                 ::SetFocus(hwnd);
1519                                 case WM_SHOWWINDOW:
1520                                 /* The WM_SHOWWINDOW message is sent to a window when the window is about to be hidden or shown. */
1521                                 case WM_WINDOWPOSCHANGING:
1522                                 /* The WM_WINDOWPOSCHANGING message is sent to a window whose size, position, or place in
1523                                  * the Z order is about to change as a result of a call to the SetWindowPos function or
1524                                  * another window-management function.
1525                                  */
1526                                 case WM_SETFOCUS:
1527                                 /* The WM_SETFOCUS message is sent to a window after it has gained the keyboard focus. */
1528                                         break;
1529                                 ////////////////////////////////////////////////////////////////////////
1530                                 // Other events
1531                                 ////////////////////////////////////////////////////////////////////////
1532                                 case WM_GETTEXT:
1533                                 /* An application sends a WM_GETTEXT message to copy the text that
1534                                  * corresponds to a window into a buffer provided by the caller.
1535                                  */
1536                                 case WM_ACTIVATEAPP:
1537                                 /* The WM_ACTIVATEAPP message is sent when a window belonging to a
1538                                  * different application than the active window is about to be activated.
1539                                  * The message is sent to the application whose window is being activated
1540                                  * and to the application whose window is being deactivated.
1541                                  */
1542                                 case WM_TIMER:
1543                                         /* The WIN32 docs say:
1544                                          * The WM_TIMER message is posted to the installing thread's message queue
1545                                          * when a timer expires. You can process the message by providing a WM_TIMER
1546                                          * case in the window procedure. Otherwise, the default window procedure will
1547                                          * call the TimerProc callback function specified in the call to the SetTimer
1548                                          * function used to install the timer.
1549                                          *
1550                                          * In GHOST, we let DefWindowProc call the timer callback.
1551                                          */
1552                                         break;
1553
1554                         }
1555                 }
1556                 else {
1557                         // Event found for a window before the pointer to the class has been set.
1558                         GHOST_PRINT("GHOST_SystemWin32::wndProc: GHOST window event before creation\n");
1559                         /* These are events we typically miss at this point:
1560                          * WM_GETMINMAXINFO     0x24
1561                          * WM_NCCREATE                  0x81
1562                          * WM_NCCALCSIZE                0x83
1563                          * WM_CREATE                    0x01
1564                          * We let DefWindowProc do the work.
1565                          */
1566                 }
1567         }
1568         else {
1569                 // Events without valid hwnd
1570                 GHOST_PRINT("GHOST_SystemWin32::wndProc: event without window\n");
1571         }
1572
1573         if (event) {
1574                 system->pushEvent(event);
1575                 eventHandled = true;
1576         }
1577
1578         if (!eventHandled)
1579                 lResult = ::DefWindowProcW(hwnd, msg, wParam, lParam);
1580
1581         return lResult;
1582 }
1583
1584 GHOST_TUns8 *GHOST_SystemWin32::getClipboard(bool selection) const
1585 {
1586         char *temp_buff;
1587
1588         if (IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(NULL) ) {
1589                 wchar_t *buffer;
1590                 HANDLE hData = GetClipboardData(CF_UNICODETEXT);
1591                 if (hData == NULL) {
1592                         CloseClipboard();
1593                         return NULL;
1594                 }
1595                 buffer = (wchar_t *)GlobalLock(hData);
1596                 if (!buffer) {
1597                         CloseClipboard();
1598                         return NULL;
1599                 }
1600
1601                 temp_buff = alloc_utf_8_from_16(buffer, 0);
1602
1603                 /* Buffer mustn't be accessed after CloseClipboard
1604                  * it would like accessing free-d memory */
1605                 GlobalUnlock(hData);
1606                 CloseClipboard();
1607
1608                 return (GHOST_TUns8 *)temp_buff;
1609         }
1610         else if (IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(NULL) ) {
1611                 char *buffer;
1612                 size_t len = 0;
1613                 HANDLE hData = GetClipboardData(CF_TEXT);
1614                 if (hData == NULL) {
1615                         CloseClipboard();
1616                         return NULL;
1617                 }
1618                 buffer = (char *)GlobalLock(hData);
1619                 if (!buffer) {
1620                         CloseClipboard();
1621                         return NULL;
1622                 }
1623
1624                 len = strlen(buffer);
1625                 temp_buff = (char *) malloc(len + 1);
1626                 strncpy(temp_buff, buffer, len);
1627                 temp_buff[len] = '\0';
1628
1629                 /* Buffer mustn't be accessed after CloseClipboard
1630                  * it would like accessing free-d memory */
1631                 GlobalUnlock(hData);
1632                 CloseClipboard();
1633
1634                 return (GHOST_TUns8 *)temp_buff;
1635         }
1636         else {
1637                 return NULL;
1638         }
1639 }
1640
1641 void GHOST_SystemWin32::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1642 {
1643         if (selection) {return; } // for copying the selection, used on X11
1644
1645         if (OpenClipboard(NULL)) {
1646                 HLOCAL clipbuffer;
1647                 wchar_t *data;
1648
1649                 if (buffer) {
1650                         size_t len = count_utf_16_from_8(buffer);
1651                         EmptyClipboard();
1652
1653                         clipbuffer = LocalAlloc(LMEM_FIXED, sizeof(wchar_t) * len);
1654                         data = (wchar_t *)GlobalLock(clipbuffer);
1655
1656                         conv_utf_8_to_16(buffer, data, len);
1657
1658                         LocalUnlock(clipbuffer);
1659                         SetClipboardData(CF_UNICODETEXT, clipbuffer);
1660                 }
1661                 CloseClipboard();
1662         }
1663         else {
1664                 return;
1665         }
1666 }
1667
1668 static DWORD GetParentProcessID(void)
1669 {
1670         HANDLE snapshot;
1671         PROCESSENTRY32 pe32 = {0};
1672         DWORD ppid = 0, pid = GetCurrentProcessId();
1673         snapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
1674         if (snapshot == INVALID_HANDLE_VALUE) {
1675                 return -1;
1676         }
1677         pe32.dwSize = sizeof( pe32 );
1678         if (!Process32First(snapshot, &pe32)) {
1679                 CloseHandle(snapshot);
1680                 return -1;
1681         }
1682         do {
1683                 if (pe32.th32ProcessID == pid) {
1684                         ppid = pe32.th32ParentProcessID;
1685                         break;
1686                 }
1687         } while (Process32Next(snapshot, &pe32));
1688         CloseHandle(snapshot);
1689         return ppid;
1690 }
1691
1692 static bool getProcessName(int pid, char *buffer, int max_len)
1693 {
1694         bool result = false;
1695         HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
1696                                     FALSE, pid);
1697         if (handle) {
1698                 GetModuleFileNameEx(handle, 0, buffer, max_len);
1699                 result = true;
1700         }
1701         CloseHandle(handle);
1702         return result;
1703 }
1704
1705 static bool isStartedFromCommandPrompt()
1706 {
1707         HWND hwnd = GetConsoleWindow();
1708
1709         if (hwnd) {
1710                 DWORD pid = (DWORD)-1;
1711                 DWORD ppid = GetParentProcessID();
1712                 char parent_name[MAX_PATH];
1713                 bool start_from_launcher = false;
1714
1715                 GetWindowThreadProcessId(hwnd, &pid);
1716                 if (getProcessName(ppid, parent_name, sizeof(parent_name))) {
1717                         char *filename = strrchr(parent_name, '\\');
1718                         if (filename != NULL) {
1719                                 start_from_launcher = strstr(filename, "blender.exe") != NULL;
1720                         }
1721                 }
1722
1723                 /* When we're starting from a wrapper we need to compare with parent process ID. */
1724                 if (pid == (start_from_launcher ? ppid : GetCurrentProcessId()))
1725                         return true;
1726         }
1727
1728         return false;
1729 }
1730
1731 int GHOST_SystemWin32::toggleConsole(int action)
1732 {
1733         switch (action) {
1734                 case 3: // startup: hide if not started from command prompt
1735                 {
1736                         if (isStartedFromCommandPrompt()) {
1737                                 ShowWindow(GetConsoleWindow(), SW_HIDE);
1738                                 m_consoleStatus = 0;
1739                         }
1740                         break;
1741                 }
1742                 case 0: // hide
1743                         ShowWindow(GetConsoleWindow(), SW_HIDE);
1744                         m_consoleStatus = 0;
1745                         break;
1746                 case 1: // show
1747                         ShowWindow(GetConsoleWindow(), SW_SHOW);
1748                         m_consoleStatus = 1;
1749                         break;
1750                 case 2: // toggle
1751                         ShowWindow(GetConsoleWindow(), m_consoleStatus ? SW_HIDE : SW_SHOW);
1752                         m_consoleStatus = !m_consoleStatus;
1753                         break;
1754         }
1755
1756
1757         return m_consoleStatus;
1758 }
1759
1760 int GHOST_SystemWin32::confirmQuit(GHOST_IWindow *window) const
1761 {
1762         return (MessageBox(window ? ((GHOST_WindowWin32 *)window)->getHWND() : 0, "Some changes have not been saved.\nDo you really want to quit?",
1763                            "Exit Blender", MB_OKCANCEL | MB_ICONWARNING | MB_TOPMOST) == IDOK);
1764 }