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