ClangFormat: apply to source, most of intern
[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       glSettings.numOfAASamples,
283       parentWindow,
284       ((glSettings.flags & GHOST_glDebugContext) != 0));
285
286   if (window->getValid()) {
287     // Store the pointer to the window
288     m_windowManager->addWindow(window);
289     m_windowManager->setActiveWindow(window);
290   }
291   else {
292     GHOST_PRINT("GHOST_SystemWin32::createWindow(): window invalid\n");
293     delete window;
294     window = NULL;
295   }
296
297   return window;
298 }
299
300 /**
301  * Create a new offscreen context.
302  * Never explicitly delete the window, use #disposeContext() instead.
303  * \return The new context (or 0 if creation failed).
304  */
305 GHOST_IContext *GHOST_SystemWin32::createOffscreenContext()
306 {
307   bool debug_context = false; /* TODO: inform as a parameter */
308
309   GHOST_Context *context;
310
311   HWND wnd = CreateWindowA("STATIC",
312                            "BlenderGLEW",
313                            WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
314                            0,
315                            0,
316                            64,
317                            64,
318                            NULL,
319                            NULL,
320                            GetModuleHandle(NULL),
321                            NULL);
322
323   HDC mHDC = GetDC(wnd);
324   HDC prev_hdc = wglGetCurrentDC();
325   HGLRC prev_context = wglGetCurrentContext();
326 #if defined(WITH_GL_PROFILE_CORE)
327   for (int minor = 5; minor >= 0; --minor) {
328     context = new GHOST_ContextWGL(false,
329                                    true,
330                                    0,
331                                    wnd,
332                                    mHDC,
333                                    WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
334                                    4,
335                                    minor,
336                                    (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
337                                    GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
338
339     if (context->initializeDrawingContext()) {
340       goto finished;
341     }
342     else {
343       delete context;
344     }
345   }
346
347   context = new GHOST_ContextWGL(false,
348                                  true,
349                                  0,
350                                  wnd,
351                                  mHDC,
352                                  WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
353                                  3,
354                                  3,
355                                  (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
356                                  GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
357
358   if (context->initializeDrawingContext()) {
359     goto finished;
360   }
361   else {
362     MessageBox(NULL,
363                "A graphics card and driver with support for OpenGL 3.3 or higher is required.\n"
364                "Installing the latest driver for your graphics card may resolve the issue.\n\n"
365                "The program will now close.",
366                "Blender - Unsupported Graphics Card or Driver",
367                MB_OK | MB_ICONERROR);
368     delete context;
369     exit();
370   }
371
372 #elif defined(WITH_GL_PROFILE_COMPAT)
373   // ask for 2.1 context, driver gives any GL version >= 2.1 (hopefully the latest compatibility profile)
374   // 2.1 ignores the profile bit & is incompatible with core profile
375   context = new GHOST_ContextWGL(false,
376                                  true,
377                                  0,
378                                  NULL,
379                                  NULL,
380                                  0,  // no profile bit
381                                  2,
382                                  1,
383                                  (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
384                                  GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
385
386   if (context->initializeDrawingContext()) {
387     return context;
388   }
389   else {
390     delete context;
391   }
392 #else
393 #  error  // must specify either core or compat at build time
394 #endif
395 finished:
396   wglMakeCurrent(prev_hdc, prev_context);
397   return context;
398 }
399
400 /**
401  * Dispose of a context.
402  * \param   context Pointer to the context to be disposed.
403  * \return  Indication of success.
404  */
405 GHOST_TSuccess GHOST_SystemWin32::disposeContext(GHOST_IContext *context)
406 {
407   delete context;
408
409   return GHOST_kSuccess;
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       hasEventHandled = true;
449     }
450   } while (waitForEvent && !hasEventHandled);
451
452   return hasEventHandled;
453 }
454
455 GHOST_TSuccess GHOST_SystemWin32::getCursorPosition(GHOST_TInt32 &x, GHOST_TInt32 &y) const
456 {
457   POINT point;
458   if (::GetCursorPos(&point)) {
459     x = point.x;
460     y = point.y;
461     return GHOST_kSuccess;
462   }
463   return GHOST_kFailure;
464 }
465
466 GHOST_TSuccess GHOST_SystemWin32::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y)
467 {
468   if (!::GetActiveWindow())
469     return GHOST_kFailure;
470   return ::SetCursorPos(x, y) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
471 }
472
473 GHOST_TSuccess GHOST_SystemWin32::getModifierKeys(GHOST_ModifierKeys &keys) const
474 {
475   bool down = HIBYTE(::GetKeyState(VK_LSHIFT)) != 0;
476   keys.set(GHOST_kModifierKeyLeftShift, down);
477   down = HIBYTE(::GetKeyState(VK_RSHIFT)) != 0;
478   keys.set(GHOST_kModifierKeyRightShift, down);
479
480   down = HIBYTE(::GetKeyState(VK_LMENU)) != 0;
481   keys.set(GHOST_kModifierKeyLeftAlt, down);
482   down = HIBYTE(::GetKeyState(VK_RMENU)) != 0;
483   keys.set(GHOST_kModifierKeyRightAlt, down);
484
485   down = HIBYTE(::GetKeyState(VK_LCONTROL)) != 0;
486   keys.set(GHOST_kModifierKeyLeftControl, down);
487   down = HIBYTE(::GetKeyState(VK_RCONTROL)) != 0;
488   keys.set(GHOST_kModifierKeyRightControl, down);
489
490   bool lwindown = HIBYTE(::GetKeyState(VK_LWIN)) != 0;
491   bool rwindown = HIBYTE(::GetKeyState(VK_RWIN)) != 0;
492   if (lwindown || rwindown)
493     keys.set(GHOST_kModifierKeyOS, true);
494   else
495     keys.set(GHOST_kModifierKeyOS, false);
496   return GHOST_kSuccess;
497 }
498
499 GHOST_TSuccess GHOST_SystemWin32::getButtons(GHOST_Buttons &buttons) const
500 {
501   /* Check for swapped buttons (left-handed mouse buttons)
502    * GetAsyncKeyState() will give back the state of the physical mouse buttons.
503    */
504   bool swapped = ::GetSystemMetrics(SM_SWAPBUTTON) == TRUE;
505
506   bool down = HIBYTE(::GetKeyState(VK_LBUTTON)) != 0;
507   buttons.set(swapped ? GHOST_kButtonMaskRight : GHOST_kButtonMaskLeft, down);
508
509   down = HIBYTE(::GetKeyState(VK_MBUTTON)) != 0;
510   buttons.set(GHOST_kButtonMaskMiddle, down);
511
512   down = HIBYTE(::GetKeyState(VK_RBUTTON)) != 0;
513   buttons.set(swapped ? GHOST_kButtonMaskLeft : GHOST_kButtonMaskRight, down);
514   return GHOST_kSuccess;
515 }
516
517 GHOST_TSuccess GHOST_SystemWin32::init()
518 {
519   GHOST_TSuccess success = GHOST_System::init();
520
521   /* Disable scaling on high DPI displays on Vista */
522   HMODULE
523   user32 = ::LoadLibraryA("user32.dll");
524   typedef BOOL(WINAPI * LPFNSETPROCESSDPIAWARE)();
525   LPFNSETPROCESSDPIAWARE SetProcessDPIAware = (LPFNSETPROCESSDPIAWARE)GetProcAddress(
526       user32, "SetProcessDPIAware");
527   if (SetProcessDPIAware)
528     SetProcessDPIAware();
529   FreeLibrary(user32);
530   initRawInput();
531
532   // Determine whether this system has a high frequency performance counter. */
533   m_hasPerformanceCounter = ::QueryPerformanceFrequency((LARGE_INTEGER *)&m_freq) == TRUE;
534   if (m_hasPerformanceCounter) {
535     GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer available\n");
536     ::QueryPerformanceCounter((LARGE_INTEGER *)&m_start);
537   }
538   else {
539     GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer not available\n");
540   }
541
542   if (success) {
543     WNDCLASSW wc = {0};
544     wc.style = CS_HREDRAW | CS_VREDRAW;
545     wc.lpfnWndProc = s_wndProc;
546     wc.cbClsExtra = 0;
547     wc.cbWndExtra = 0;
548     wc.hInstance = ::GetModuleHandle(0);
549     wc.hIcon = ::LoadIcon(wc.hInstance, "APPICON");
550
551     if (!wc.hIcon) {
552       ::LoadIcon(NULL, IDI_APPLICATION);
553     }
554     wc.hCursor = ::LoadCursor(0, IDC_ARROW);
555     wc.hbrBackground =
556 #ifdef INW32_COMPISITING
557         (HBRUSH)CreateSolidBrush
558 #endif
559         (0x00000000);
560     wc.lpszMenuName = 0;
561     wc.lpszClassName = L"GHOST_WindowClass";
562
563     // Use RegisterClassEx for setting small icon
564     if (::RegisterClassW(&wc) == 0) {
565       success = GHOST_kFailure;
566     }
567   }
568
569   return success;
570 }
571
572 GHOST_TSuccess GHOST_SystemWin32::exit()
573 {
574   return GHOST_System::exit();
575 }
576
577 GHOST_TKey GHOST_SystemWin32::hardKey(RAWINPUT const &raw, int *keyDown, char *vk)
578 {
579   GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
580   GHOST_TKey key = GHOST_kKeyUnknown;
581   GHOST_ModifierKeys modifiers;
582   system->retrieveModifierKeys(modifiers);
583
584   // RI_KEY_BREAK doesn't work for sticky keys release, so we also
585   // check for the up message
586   unsigned int msg = raw.data.keyboard.Message;
587   *keyDown = !(raw.data.keyboard.Flags & RI_KEY_BREAK) && msg != WM_KEYUP && msg != WM_SYSKEYUP;
588
589   key = this->convertKey(raw.data.keyboard.VKey,
590                          raw.data.keyboard.MakeCode,
591                          (raw.data.keyboard.Flags & (RI_KEY_E1 | RI_KEY_E0)));
592
593   // extra handling of modifier keys: don't send repeats out from GHOST
594   if (key >= GHOST_kKeyLeftShift && key <= GHOST_kKeyRightAlt) {
595     bool changed = false;
596     GHOST_TModifierKeyMask modifier;
597     switch (key) {
598       case GHOST_kKeyLeftShift: {
599         changed = (modifiers.get(GHOST_kModifierKeyLeftShift) != (bool)*keyDown);
600         modifier = GHOST_kModifierKeyLeftShift;
601         break;
602       }
603       case GHOST_kKeyRightShift: {
604         changed = (modifiers.get(GHOST_kModifierKeyRightShift) != (bool)*keyDown);
605         modifier = GHOST_kModifierKeyRightShift;
606         break;
607       }
608       case GHOST_kKeyLeftControl: {
609         changed = (modifiers.get(GHOST_kModifierKeyLeftControl) != (bool)*keyDown);
610         modifier = GHOST_kModifierKeyLeftControl;
611         break;
612       }
613       case GHOST_kKeyRightControl: {
614         changed = (modifiers.get(GHOST_kModifierKeyRightControl) != (bool)*keyDown);
615         modifier = GHOST_kModifierKeyRightControl;
616         break;
617       }
618       case GHOST_kKeyLeftAlt: {
619         changed = (modifiers.get(GHOST_kModifierKeyLeftAlt) != (bool)*keyDown);
620         modifier = GHOST_kModifierKeyLeftAlt;
621         break;
622       }
623       case GHOST_kKeyRightAlt: {
624         changed = (modifiers.get(GHOST_kModifierKeyRightAlt) != (bool)*keyDown);
625         modifier = GHOST_kModifierKeyRightAlt;
626         break;
627       }
628       default:
629         break;
630     }
631
632     if (changed) {
633       modifiers.set(modifier, (bool)*keyDown);
634       system->storeModifierKeys(modifiers);
635     }
636     else {
637       key = GHOST_kKeyUnknown;
638     }
639   }
640
641   if (vk)
642     *vk = raw.data.keyboard.VKey;
643
644   return key;
645 }
646
647 //! note: this function can be extended to include other exotic cases as they arise.
648 // This function was added in response to bug [#25715]
649 // This is going to be a long list [T42426]
650 GHOST_TKey GHOST_SystemWin32::processSpecialKey(short vKey, short scanCode) const
651 {
652   GHOST_TKey key = GHOST_kKeyUnknown;
653   switch (PRIMARYLANGID(m_langId)) {
654     case LANG_FRENCH:
655       if (vKey == VK_OEM_8)
656         key = GHOST_kKeyF13;  // oem key; used purely for shortcuts .
657       break;
658     case LANG_ENGLISH:
659       if (SUBLANGID(m_langId) == SUBLANG_ENGLISH_UK && vKey == VK_OEM_8)  // "`¬"
660         key = GHOST_kKeyAccentGrave;
661       break;
662   }
663
664   return key;
665 }
666
667 GHOST_TKey GHOST_SystemWin32::convertKey(short vKey, short scanCode, short extend) const
668 {
669   GHOST_TKey key;
670
671   if ((vKey >= '0') && (vKey <= '9')) {
672     // VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39)
673     key = (GHOST_TKey)(vKey - '0' + GHOST_kKey0);
674   }
675   else if ((vKey >= 'A') && (vKey <= 'Z')) {
676     // VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A)
677     key = (GHOST_TKey)(vKey - 'A' + GHOST_kKeyA);
678   }
679   else if ((vKey >= VK_F1) && (vKey <= VK_F24)) {
680     key = (GHOST_TKey)(vKey - VK_F1 + GHOST_kKeyF1);
681   }
682   else {
683     switch (vKey) {
684       case VK_RETURN:
685         key = (extend) ? GHOST_kKeyNumpadEnter : GHOST_kKeyEnter;
686         break;
687
688       case VK_BACK:
689         key = GHOST_kKeyBackSpace;
690         break;
691       case VK_TAB:
692         key = GHOST_kKeyTab;
693         break;
694       case VK_ESCAPE:
695         key = GHOST_kKeyEsc;
696         break;
697       case VK_SPACE:
698         key = GHOST_kKeySpace;
699         break;
700
701       case VK_INSERT:
702       case VK_NUMPAD0:
703         key = (extend) ? GHOST_kKeyInsert : GHOST_kKeyNumpad0;
704         break;
705       case VK_END:
706       case VK_NUMPAD1:
707         key = (extend) ? GHOST_kKeyEnd : GHOST_kKeyNumpad1;
708         break;
709       case VK_DOWN:
710       case VK_NUMPAD2:
711         key = (extend) ? GHOST_kKeyDownArrow : GHOST_kKeyNumpad2;
712         break;
713       case VK_NEXT:
714       case VK_NUMPAD3:
715         key = (extend) ? GHOST_kKeyDownPage : GHOST_kKeyNumpad3;
716         break;
717       case VK_LEFT:
718       case VK_NUMPAD4:
719         key = (extend) ? GHOST_kKeyLeftArrow : GHOST_kKeyNumpad4;
720         break;
721       case VK_CLEAR:
722       case VK_NUMPAD5:
723         key = (extend) ? GHOST_kKeyUnknown : GHOST_kKeyNumpad5;
724         break;
725       case VK_RIGHT:
726       case VK_NUMPAD6:
727         key = (extend) ? GHOST_kKeyRightArrow : GHOST_kKeyNumpad6;
728         break;
729       case VK_HOME:
730       case VK_NUMPAD7:
731         key = (extend) ? GHOST_kKeyHome : GHOST_kKeyNumpad7;
732         break;
733       case VK_UP:
734       case VK_NUMPAD8:
735         key = (extend) ? GHOST_kKeyUpArrow : GHOST_kKeyNumpad8;
736         break;
737       case VK_PRIOR:
738       case VK_NUMPAD9:
739         key = (extend) ? GHOST_kKeyUpPage : GHOST_kKeyNumpad9;
740         break;
741       case VK_DECIMAL:
742       case VK_DELETE:
743         key = (extend) ? GHOST_kKeyDelete : GHOST_kKeyNumpadPeriod;
744         break;
745
746       case VK_SNAPSHOT:
747         key = GHOST_kKeyPrintScreen;
748         break;
749       case VK_PAUSE:
750         key = GHOST_kKeyPause;
751         break;
752       case VK_MULTIPLY:
753         key = GHOST_kKeyNumpadAsterisk;
754         break;
755       case VK_SUBTRACT:
756         key = GHOST_kKeyNumpadMinus;
757         break;
758       case VK_DIVIDE:
759         key = GHOST_kKeyNumpadSlash;
760         break;
761       case VK_ADD:
762         key = GHOST_kKeyNumpadPlus;
763         break;
764
765       case VK_SEMICOLON:
766         key = GHOST_kKeySemicolon;
767         break;
768       case VK_EQUALS:
769         key = GHOST_kKeyEqual;
770         break;
771       case VK_COMMA:
772         key = GHOST_kKeyComma;
773         break;
774       case VK_MINUS:
775         key = GHOST_kKeyMinus;
776         break;
777       case VK_PERIOD:
778         key = GHOST_kKeyPeriod;
779         break;
780       case VK_SLASH:
781         key = GHOST_kKeySlash;
782         break;
783       case VK_BACK_QUOTE:
784         key = GHOST_kKeyAccentGrave;
785         break;
786       case VK_OPEN_BRACKET:
787         key = GHOST_kKeyLeftBracket;
788         break;
789       case VK_BACK_SLASH:
790         key = GHOST_kKeyBackslash;
791         break;
792       case VK_CLOSE_BRACKET:
793         key = GHOST_kKeyRightBracket;
794         break;
795       case VK_QUOTE:
796         key = GHOST_kKeyQuote;
797         break;
798       case VK_GR_LESS:
799         key = GHOST_kKeyGrLess;
800         break;
801
802       case VK_SHIFT:
803         /* Check single shift presses */
804         if (scanCode == 0x36) {
805           key = GHOST_kKeyRightShift;
806         }
807         else if (scanCode == 0x2a) {
808           key = GHOST_kKeyLeftShift;
809         }
810         else {
811           /* Must be a combination SHIFT (Left or Right) + a Key
812            * Ignore this as the next message will contain
813            * the desired "Key" */
814           key = GHOST_kKeyUnknown;
815         }
816         break;
817       case VK_CONTROL:
818         key = (extend) ? GHOST_kKeyRightControl : GHOST_kKeyLeftControl;
819         break;
820       case VK_MENU:
821         key = (extend) ? GHOST_kKeyRightAlt : GHOST_kKeyLeftAlt;
822         break;
823       case VK_LWIN:
824       case VK_RWIN:
825         key = GHOST_kKeyOS;
826         break;
827       case VK_NUMLOCK:
828         key = GHOST_kKeyNumLock;
829         break;
830       case VK_SCROLL:
831         key = GHOST_kKeyScrollLock;
832         break;
833       case VK_CAPITAL:
834         key = GHOST_kKeyCapsLock;
835         break;
836       case VK_OEM_8:
837         key = ((GHOST_SystemWin32 *)getSystem())->processSpecialKey(vKey, scanCode);
838         break;
839       case VK_MEDIA_PLAY_PAUSE:
840         key = GHOST_kKeyMediaPlay;
841         break;
842       case VK_MEDIA_STOP:
843         key = GHOST_kKeyMediaStop;
844         break;
845       case VK_MEDIA_PREV_TRACK:
846         key = GHOST_kKeyMediaFirst;
847         break;
848       case VK_MEDIA_NEXT_TRACK:
849         key = GHOST_kKeyMediaLast;
850         break;
851       default:
852         key = GHOST_kKeyUnknown;
853         break;
854     }
855   }
856
857   return key;
858 }
859
860 GHOST_EventButton *GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type,
861                                                          GHOST_WindowWin32 *window,
862                                                          GHOST_TButtonMask mask)
863 {
864   GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
865   if (window->useTabletAPI(GHOST_kTabletNative)) {
866     window->setTabletData(NULL);
867   }
868   return new GHOST_EventButton(system->getMilliSeconds(), type, window, mask);
869 }
870
871 GHOST_Event *GHOST_SystemWin32::processPointerEvent(GHOST_TEventType type,
872                                                     GHOST_WindowWin32 *window,
873                                                     WPARAM wParam,
874                                                     LPARAM lParam,
875                                                     bool &eventHandled)
876 {
877   GHOST_PointerInfoWin32 pointerInfo;
878   GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
879
880   if (!window->useTabletAPI(GHOST_kTabletNative)) {
881     return NULL;
882   }
883
884   if (window->getPointerInfo(&pointerInfo, wParam, lParam) != GHOST_kSuccess) {
885     return NULL;
886   }
887
888   if (!pointerInfo.isPrimary) {
889     eventHandled = true;
890     return NULL;  // For multi-touch displays we ignore these events
891   }
892
893   system->setCursorPosition(pointerInfo.pixelLocation.x, pointerInfo.pixelLocation.y);
894
895   switch (type) {
896     case GHOST_kEventButtonDown:
897       window->setTabletData(&pointerInfo.tabletData);
898       eventHandled = true;
899       return new GHOST_EventButton(
900           system->getMilliSeconds(), GHOST_kEventButtonDown, window, pointerInfo.buttonMask);
901     case GHOST_kEventButtonUp:
902       eventHandled = true;
903       return new GHOST_EventButton(
904           system->getMilliSeconds(), GHOST_kEventButtonUp, window, pointerInfo.buttonMask);
905     case GHOST_kEventCursorMove:
906       window->setTabletData(&pointerInfo.tabletData);
907       eventHandled = true;
908       return new GHOST_EventCursor(system->getMilliSeconds(),
909                                    GHOST_kEventCursorMove,
910                                    window,
911                                    pointerInfo.pixelLocation.x,
912                                    pointerInfo.pixelLocation.y);
913     default:
914       return NULL;
915   }
916 }
917
918 GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type,
919                                                          GHOST_WindowWin32 *window)
920 {
921   GHOST_TInt32 x_screen, y_screen;
922   GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
923
924   system->getCursorPosition(x_screen, y_screen);
925
926   /* TODO: CHECK IF THIS IS A TABLET EVENT */
927   bool is_tablet = false;
928
929   if (is_tablet == false && window->getCursorGrabModeIsWarp()) {
930     GHOST_TInt32 x_new = x_screen;
931     GHOST_TInt32 y_new = y_screen;
932     GHOST_TInt32 x_accum, y_accum;
933     GHOST_Rect bounds;
934
935     /* fallback to window bounds */
936     if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) {
937       window->getClientBounds(bounds);
938     }
939
940     /* could also clamp to screen bounds
941      * wrap with a window outside the view will fail atm  */
942
943     bounds.wrapPoint(x_new, y_new, 2); /* offset of one incase blender is at screen bounds */
944
945     window->getCursorGrabAccum(x_accum, y_accum);
946     if (x_new != x_screen || y_new != y_screen) {
947       /* when wrapping we don't need to add an event because the
948        * setCursorPosition call will cause a new event after */
949       system->setCursorPosition(x_new, y_new); /* wrap */
950       window->setCursorGrabAccum(x_accum + (x_screen - x_new), y_accum + (y_screen - y_new));
951     }
952     else {
953       return new GHOST_EventCursor(system->getMilliSeconds(),
954                                    GHOST_kEventCursorMove,
955                                    window,
956                                    x_screen + x_accum,
957                                    y_screen + y_accum);
958     }
959   }
960   else {
961     return new GHOST_EventCursor(
962         system->getMilliSeconds(), GHOST_kEventCursorMove, window, x_screen, y_screen);
963   }
964   return NULL;
965 }
966
967 void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam)
968 {
969   GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
970
971   int acc = system->m_wheelDeltaAccum;
972   int delta = GET_WHEEL_DELTA_WPARAM(wParam);
973
974   if (acc * delta < 0) {
975     // scroll direction reversed.
976     acc = 0;
977   }
978   acc += delta;
979   int direction = (acc >= 0) ? 1 : -1;
980   acc = abs(acc);
981
982   while (acc >= WHEEL_DELTA) {
983     system->pushEvent(new GHOST_EventWheel(system->getMilliSeconds(), window, direction));
984     acc -= WHEEL_DELTA;
985   }
986   system->m_wheelDeltaAccum = acc * direction;
987 }
988
989 GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RAWINPUT const &raw)
990 {
991   int keyDown = 0;
992   char vk;
993   GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
994   GHOST_TKey key = system->hardKey(raw, &keyDown, &vk);
995   GHOST_EventKey *event;
996
997   if (key != GHOST_kKeyUnknown) {
998     char utf8_char[6] = {0};
999     char ascii = 0;
1000
1001     wchar_t utf16[3] = {0};
1002     BYTE state[256] = {0};
1003     int r;
1004     GetKeyboardState((PBYTE)state);
1005
1006     // don't call ToUnicodeEx on dead keys as it clears the buffer and so won't allow diacritical composition.
1007     if (MapVirtualKeyW(vk, 2) != 0) {
1008       // todo: ToUnicodeEx can respond with up to 4 utf16 chars (only 2 here). Could be up to 24 utf8 bytes.
1009       if ((r = ToUnicodeEx(
1010                vk, raw.data.keyboard.MakeCode, state, utf16, 2, 0, system->m_keylayout))) {
1011         if ((r > 0 && r < 3)) {
1012           utf16[r] = 0;
1013           conv_utf_16_to_8(utf16, utf8_char, 6);
1014         }
1015         else if (r == -1) {
1016           utf8_char[0] = '\0';
1017         }
1018       }
1019     }
1020
1021     if (!keyDown) {
1022       utf8_char[0] = '\0';
1023       ascii = '\0';
1024     }
1025     else {
1026       ascii = utf8_char[0] & 0x80 ? '?' : utf8_char[0];
1027     }
1028
1029     event = new GHOST_EventKey(system->getMilliSeconds(),
1030                                keyDown ? GHOST_kEventKeyDown : GHOST_kEventKeyUp,
1031                                window,
1032                                key,
1033                                ascii,
1034                                utf8_char);
1035
1036     // GHOST_PRINTF("%c\n", ascii); // we already get this info via EventPrinter
1037   }
1038   else {
1039     event = NULL;
1040   }
1041   return event;
1042 }
1043
1044 GHOST_Event *GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type,
1045                                                    GHOST_WindowWin32 *window)
1046 {
1047   GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
1048
1049   if (type == GHOST_kEventWindowActivate) {
1050     system->getWindowManager()->setActiveWindow(window);
1051     window->bringTabletContextToFront();
1052   }
1053
1054   return new GHOST_Event(system->getMilliSeconds(), type, window);
1055 }
1056
1057 #ifdef WITH_INPUT_IME
1058 GHOST_Event *GHOST_SystemWin32::processImeEvent(GHOST_TEventType type,
1059                                                 GHOST_WindowWin32 *window,
1060                                                 GHOST_TEventImeData *data)
1061 {
1062   GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
1063   return new GHOST_EventIME(system->getMilliSeconds(), type, window, data);
1064 }
1065 #endif
1066
1067 GHOST_TSuccess GHOST_SystemWin32::pushDragDropEvent(GHOST_TEventType eventType,
1068                                                     GHOST_TDragnDropTypes draggedObjectType,
1069                                                     GHOST_WindowWin32 *window,
1070                                                     int mouseX,
1071                                                     int mouseY,
1072                                                     void *data)
1073 {
1074   GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
1075   return system->pushEvent(new GHOST_EventDragnDrop(
1076       system->getMilliSeconds(), eventType, draggedObjectType, window, mouseX, mouseY, data));
1077 }
1078
1079 void GHOST_SystemWin32::processMinMaxInfo(MINMAXINFO *minmax)
1080 {
1081   minmax->ptMinTrackSize.x = 320;
1082   minmax->ptMinTrackSize.y = 240;
1083 }
1084
1085 #ifdef WITH_INPUT_NDOF
1086 bool GHOST_SystemWin32::processNDOF(RAWINPUT const &raw)
1087 {
1088   bool eventSent = false;
1089   GHOST_TUns64 now = getMilliSeconds();
1090
1091   static bool firstEvent = true;
1092   if (firstEvent) {  // determine exactly which device is plugged in
1093     RID_DEVICE_INFO info;
1094     unsigned infoSize = sizeof(RID_DEVICE_INFO);
1095     info.cbSize = infoSize;
1096
1097     GetRawInputDeviceInfo(raw.header.hDevice, RIDI_DEVICEINFO, &info, &infoSize);
1098     if (info.dwType == RIM_TYPEHID)
1099       m_ndofManager->setDevice(info.hid.dwVendorId, info.hid.dwProductId);
1100     else
1101       GHOST_PRINT("<!> not a HID device... mouse/kb perhaps?\n");
1102
1103     firstEvent = false;
1104   }
1105
1106   // The NDOF manager sends button changes immediately, and *pretends* to
1107   // send motion. Mark as 'sent' so motion will always get dispatched.
1108   eventSent = true;
1109
1110   BYTE const *data = raw.data.hid.bRawData;
1111
1112   BYTE packetType = data[0];
1113   switch (packetType) {
1114     case 1:  // translation
1115     {
1116       const short *axis = (short *)(data + 1);
1117       // massage into blender view coords (same goes for rotation)
1118       const int t[3] = {axis[0], -axis[2], axis[1]};
1119       m_ndofManager->updateTranslation(t, now);
1120
1121       if (raw.data.hid.dwSizeHid == 13) {
1122         // this report also includes rotation
1123         const int r[3] = {-axis[3], axis[5], -axis[4]};
1124         m_ndofManager->updateRotation(r, now);
1125
1126         // I've never gotten one of these, has anyone else?
1127         GHOST_PRINT("ndof: combined T + R\n");
1128       }
1129       break;
1130     }
1131     case 2:  // rotation
1132     {
1133       const short *axis = (short *)(data + 1);
1134       const int r[3] = {-axis[0], axis[2], -axis[1]};
1135       m_ndofManager->updateRotation(r, now);
1136       break;
1137     }
1138     case 3:  // buttons
1139     {
1140       int button_bits;
1141       memcpy(&button_bits, data + 1, sizeof(button_bits));
1142       m_ndofManager->updateButtons(button_bits, now);
1143       break;
1144     }
1145   }
1146   return eventSent;
1147 }
1148 #endif  // WITH_INPUT_NDOF
1149
1150 LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1151 {
1152   GHOST_Event *event = NULL;
1153   bool eventHandled = false;
1154
1155   LRESULT lResult = 0;
1156   GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
1157   GHOST_EventManager *eventManager = system->getEventManager();
1158   GHOST_ASSERT(system, "GHOST_SystemWin32::s_wndProc(): system not initialized");
1159
1160   if (hwnd) {
1161 #if 0
1162     // Disabled due to bug in Intel drivers, see T51959
1163     if(msg == WM_NCCREATE) {
1164       // Tell Windows to automatically handle scaling of non-client areas
1165       // such as the caption bar. EnableNonClientDpiScaling was introduced in Windows 10
1166       HMODULE m_user32 = ::LoadLibrary("User32.dll");
1167       if (m_user32) {
1168         GHOST_WIN32_EnableNonClientDpiScaling fpEnableNonClientDpiScaling =
1169           (GHOST_WIN32_EnableNonClientDpiScaling) ::GetProcAddress(m_user32, "EnableNonClientDpiScaling");
1170
1171         if (fpEnableNonClientDpiScaling) {
1172           fpEnableNonClientDpiScaling(hwnd);
1173         }
1174       }
1175     }
1176 #endif
1177
1178     GHOST_WindowWin32 *window = (GHOST_WindowWin32 *)::GetWindowLongPtr(hwnd, GWLP_USERDATA);
1179     if (window) {
1180       switch (msg) {
1181         // we need to check if new key layout has AltGr
1182         case WM_INPUTLANGCHANGE: {
1183           system->handleKeyboardChange();
1184 #ifdef WITH_INPUT_IME
1185           window->getImeInput()->SetInputLanguage();
1186 #endif
1187           break;
1188         }
1189         ////////////////////////////////////////////////////////////////////////
1190         // Keyboard events, processed
1191         ////////////////////////////////////////////////////////////////////////
1192         case WM_INPUT: {
1193           // check WM_INPUT from input sink when ghost window is not in the foreground
1194           if (wParam == RIM_INPUTSINK) {
1195             if (GetFocus() != hwnd)  // WM_INPUT message not for this window
1196               return 0;
1197           }  //else wParam == RIM_INPUT
1198
1199           RAWINPUT raw;
1200           RAWINPUT *raw_ptr = &raw;
1201           UINT rawSize = sizeof(RAWINPUT);
1202
1203           GetRawInputData((HRAWINPUT)lParam, RID_INPUT, raw_ptr, &rawSize, sizeof(RAWINPUTHEADER));
1204
1205           switch (raw.header.dwType) {
1206             case RIM_TYPEKEYBOARD:
1207               event = processKeyEvent(window, raw);
1208               if (!event) {
1209                 GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ");
1210                 GHOST_PRINT(msg);
1211                 GHOST_PRINT(" key ignored\n");
1212               }
1213               break;
1214 #ifdef WITH_INPUT_NDOF
1215             case RIM_TYPEHID:
1216               if (system->processNDOF(raw)) {
1217                 eventHandled = true;
1218               }
1219               break;
1220 #endif
1221           }
1222           break;
1223         }
1224 #ifdef WITH_INPUT_IME
1225         ////////////////////////////////////////////////////////////////////////
1226         // IME events, processed, read more in GHOST_IME.h
1227         ////////////////////////////////////////////////////////////////////////
1228         case WM_IME_SETCONTEXT: {
1229           GHOST_ImeWin32 *ime = window->getImeInput();
1230           ime->SetInputLanguage();
1231           ime->CreateImeWindow(hwnd);
1232           ime->CleanupComposition(hwnd);
1233           ime->CheckFirst(hwnd);
1234           break;
1235         }
1236         case WM_IME_STARTCOMPOSITION: {
1237           GHOST_ImeWin32 *ime = window->getImeInput();
1238           eventHandled = true;
1239           /* remove input event before start comp event, avoid redundant input */
1240           eventManager->removeTypeEvents(GHOST_kEventKeyDown, window);
1241           ime->CreateImeWindow(hwnd);
1242           ime->ResetComposition(hwnd);
1243           event = processImeEvent(GHOST_kEventImeCompositionStart, window, &ime->eventImeData);
1244           break;
1245         }
1246         case WM_IME_COMPOSITION: {
1247           GHOST_ImeWin32 *ime = window->getImeInput();
1248           eventHandled = true;
1249           ime->UpdateImeWindow(hwnd);
1250           ime->UpdateInfo(hwnd);
1251           if (ime->eventImeData.result_len) {
1252             /* remove redundant IME event */
1253             eventManager->removeTypeEvents(GHOST_kEventImeComposition, window);
1254           }
1255           event = processImeEvent(GHOST_kEventImeComposition, window, &ime->eventImeData);
1256           break;
1257         }
1258         case WM_IME_ENDCOMPOSITION: {
1259           GHOST_ImeWin32 *ime = window->getImeInput();
1260           eventHandled = true;
1261           /* remove input event after end comp event, avoid redundant input */
1262           eventManager->removeTypeEvents(GHOST_kEventKeyDown, window);
1263           ime->ResetComposition(hwnd);
1264           ime->DestroyImeWindow(hwnd);
1265           event = processImeEvent(GHOST_kEventImeCompositionEnd, window, &ime->eventImeData);
1266           break;
1267         }
1268 #endif /* WITH_INPUT_IME */
1269         ////////////////////////////////////////////////////////////////////////
1270         // Keyboard events, ignored
1271         ////////////////////////////////////////////////////////////////////////
1272         case WM_KEYDOWN:
1273         case WM_SYSKEYDOWN:
1274         case WM_KEYUP:
1275         case WM_SYSKEYUP:
1276         /* These functions were replaced by WM_INPUT*/
1277         case WM_CHAR:
1278         /* The WM_CHAR message is posted to the window with the keyboard focus when
1279          * a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR
1280          * contains the character code of the key that was pressed.
1281          */
1282         case WM_DEADCHAR:
1283           /* The WM_DEADCHAR message is posted to the window with the keyboard focus when a
1284            * WM_KEYUP message is translated by the TranslateMessage function. WM_DEADCHAR
1285            * specifies a character code generated by a dead key. A dead key is a key that
1286            * generates a character, such as the umlaut (double-dot), that is combined with
1287            * another character to form a composite character. For example, the umlaut-O
1288            * character (Ö) is generated by typing the dead key for the umlaut character, and
1289            * then typing the O key.
1290            */
1291           break;
1292         case WM_SYSDEADCHAR:
1293         /* The WM_SYSDEADCHAR message is sent to the window with the keyboard focus when
1294          * a WM_SYSKEYDOWN message is translated by the TranslateMessage function.
1295          * WM_SYSDEADCHAR specifies the character code of a system dead key - that is,
1296          * a dead key that is pressed while holding down the alt key.
1297          */
1298         case WM_SYSCHAR:
1299           /* The WM_SYSCHAR message is sent to the window with the keyboard focus when
1300            * a WM_SYSCHAR message is translated by the TranslateMessage function.
1301            * WM_SYSCHAR specifies the character code of a dead key - that is,
1302            * a dead key that is pressed while holding down the alt key.
1303            * To prevent the sound, DefWindowProc must be avoided by return
1304            */
1305           break;
1306         case WM_SYSCOMMAND:
1307           /* The WM_SYSCHAR message is sent to the window when system commands such as
1308            * maximize, minimize  or close the window are triggered. Also it is sent when ALT
1309            * button is press for menu. To prevent this we must return preventing DefWindowProc.
1310            */
1311           if (wParam == SC_KEYMENU) {
1312             eventHandled = true;
1313           }
1314           break;
1315         ////////////////////////////////////////////////////////////////////////
1316         // Tablet events, processed
1317         ////////////////////////////////////////////////////////////////////////
1318         case WT_PACKET:
1319           window->processWin32TabletEvent(wParam, lParam);
1320           break;
1321         case WT_CSRCHANGE:
1322         case WT_PROXIMITY:
1323           window->processWin32TabletInitEvent();
1324           break;
1325         ////////////////////////////////////////////////////////////////////////
1326         // Pointer events, processed
1327         ////////////////////////////////////////////////////////////////////////
1328         case WM_POINTERDOWN:
1329           event = processPointerEvent(
1330               GHOST_kEventButtonDown, window, wParam, lParam, eventHandled);
1331           if (event && eventHandled) {
1332             window->registerMouseClickEvent(0);
1333           }
1334           break;
1335         case WM_POINTERUP:
1336           event = processPointerEvent(GHOST_kEventButtonUp, window, wParam, lParam, eventHandled);
1337           if (event && eventHandled) {
1338             window->registerMouseClickEvent(1);
1339           }
1340           break;
1341         case WM_POINTERUPDATE:
1342           event = processPointerEvent(
1343               GHOST_kEventCursorMove, window, wParam, lParam, eventHandled);
1344           break;
1345         ////////////////////////////////////////////////////////////////////////
1346         // Mouse events, processed
1347         ////////////////////////////////////////////////////////////////////////
1348         case WM_LBUTTONDOWN:
1349           window->registerMouseClickEvent(0);
1350           event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft);
1351           break;
1352         case WM_MBUTTONDOWN:
1353           window->registerMouseClickEvent(0);
1354           event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskMiddle);
1355           break;
1356         case WM_RBUTTONDOWN:
1357           window->registerMouseClickEvent(0);
1358           event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight);
1359           break;
1360         case WM_XBUTTONDOWN:
1361           window->registerMouseClickEvent(0);
1362           if ((short)HIWORD(wParam) == XBUTTON1) {
1363             event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton4);
1364           }
1365           else if ((short)HIWORD(wParam) == XBUTTON2) {
1366             event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton5);
1367           }
1368           break;
1369         case WM_LBUTTONUP:
1370           window->registerMouseClickEvent(1);
1371           event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft);
1372           break;
1373         case WM_MBUTTONUP:
1374           window->registerMouseClickEvent(1);
1375           event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle);
1376           break;
1377         case WM_RBUTTONUP:
1378           window->registerMouseClickEvent(1);
1379           event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight);
1380           break;
1381         case WM_XBUTTONUP:
1382           window->registerMouseClickEvent(1);
1383           if ((short)HIWORD(wParam) == XBUTTON1) {
1384             event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton4);
1385           }
1386           else if ((short)HIWORD(wParam) == XBUTTON2) {
1387             event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton5);
1388           }
1389           break;
1390         case WM_MOUSEMOVE:
1391           event = processCursorEvent(GHOST_kEventCursorMove, window);
1392           break;
1393         case WM_MOUSEWHEEL: {
1394           /* The WM_MOUSEWHEEL message is sent to the focus window
1395            * when the mouse wheel is rotated. The DefWindowProc
1396            * function propagates the message to the window's parent.
1397            * There should be no internal forwarding of the message,
1398            * since DefWindowProc propagates it up the parent chain
1399            * until it finds a window that processes it.
1400            */
1401
1402           /* Get the window under the mouse and send event to its queue. */
1403           POINT mouse_pos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
1404           HWND mouse_hwnd = ChildWindowFromPoint(HWND_DESKTOP, mouse_pos);
1405           GHOST_WindowWin32 *mouse_window = (GHOST_WindowWin32 *)::GetWindowLongPtr(mouse_hwnd,
1406                                                                                     GWLP_USERDATA);
1407
1408           processWheelEvent(mouse_window ? mouse_window : window, wParam, lParam);
1409           eventHandled = true;
1410 #ifdef BROKEN_PEEK_TOUCHPAD
1411           PostMessage(hwnd, WM_USER, 0, 0);
1412 #endif
1413           break;
1414         }
1415         case WM_SETCURSOR:
1416           /* The WM_SETCURSOR message is sent to a window if the mouse causes the cursor
1417            * to move within a window and mouse input is not captured.
1418            * This means we have to set the cursor shape every time the mouse moves!
1419            * The DefWindowProc function uses this message to set the cursor to an
1420            * arrow if it is not in the client area.
1421            */
1422           if (LOWORD(lParam) == HTCLIENT) {
1423             // Load the current cursor
1424             window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
1425             // Bypass call to DefWindowProc
1426             return 0;
1427           }
1428           else {
1429             // Outside of client area show standard cursor
1430             window->loadCursor(true, GHOST_kStandardCursorDefault);
1431           }
1432           break;
1433
1434         ////////////////////////////////////////////////////////////////////////
1435         // Mouse events, ignored
1436         ////////////////////////////////////////////////////////////////////////
1437         case WM_NCMOUSEMOVE:
1438         /* The WM_NCMOUSEMOVE message is posted to a window when the cursor is moved
1439          * within the nonclient area of the window. This message is posted to the window
1440          * that contains the cursor. If a window has captured the mouse, this message is not posted.
1441          */
1442         case WM_NCHITTEST:
1443           /* The WM_NCHITTEST message is sent to a window when the cursor moves, or
1444            * when a mouse button is pressed or released. If the mouse is not captured,
1445            * the message is sent to the window beneath the cursor. Otherwise, the message
1446            * is sent to the window that has captured the mouse.
1447            */
1448           break;
1449
1450         ////////////////////////////////////////////////////////////////////////
1451         // Window events, processed
1452         ////////////////////////////////////////////////////////////////////////
1453         case WM_CLOSE:
1454           /* The WM_CLOSE message is sent as a signal that a window 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 being
1459            * deactivated. If the windows use the same input queue, the message is sent synchronously,
1460            * first to the window procedure of the top-level window being deactivated, then to the window
1461            * procedure of the top-level window being activated. If the windows use different input queues,
1462            * the message is sent asynchronously, so the window is activated immediately.
1463            */
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. So, do not dispatch before we continiously resizng */
1526           if (window->m_inLiveResize) {
1527             system->pushEvent(processWindowEvent(GHOST_kEventWindowSize, window));
1528             system->dispatchEvents();
1529           }
1530           else {
1531             event = processWindowEvent(GHOST_kEventWindowSize, window);
1532           }
1533           break;
1534         case WM_CAPTURECHANGED:
1535           window->lostMouseCapture();
1536           break;
1537         case WM_MOVING:
1538           /* The WM_MOVING message is sent to a window that the user is moving. By processing
1539            * this message, an application can monitor the size and position of the drag rectangle
1540            * and, if needed, change its size or position.
1541            */
1542         case WM_MOVE:
1543           /* The WM_SIZE and WM_MOVE messages are not sent if an application handles the
1544            * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
1545            * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
1546            * message without calling DefWindowProc.
1547            */
1548           /* see WM_SIZE comment*/
1549           if (window->m_inLiveResize) {
1550             system->pushEvent(processWindowEvent(GHOST_kEventWindowMove, window));
1551             system->dispatchEvents();
1552           }
1553           else {
1554             event = processWindowEvent(GHOST_kEventWindowMove, window);
1555           }
1556
1557           break;
1558         case WM_DPICHANGED:
1559           /* The WM_DPICHANGED message is sent when the effective dots per inch (dpi) for a window has changed.
1560            * The DPI is the scale factor for a window. There are multiple events that can cause the DPI to
1561            * change such as when the window is moved to a monitor with a different DPI.
1562            */
1563           {
1564             // The suggested new size and position of the window.
1565             RECT *const suggestedWindowRect = (RECT *)lParam;
1566
1567             // Push DPI change event first
1568             system->pushEvent(processWindowEvent(GHOST_kEventWindowDPIHintChanged, window));
1569             system->dispatchEvents();
1570             eventHandled = true;
1571
1572             // Then move and resize window
1573             SetWindowPos(hwnd,
1574                          NULL,
1575                          suggestedWindowRect->left,
1576                          suggestedWindowRect->top,
1577                          suggestedWindowRect->right - suggestedWindowRect->left,
1578                          suggestedWindowRect->bottom - suggestedWindowRect->top,
1579                          SWP_NOZORDER | SWP_NOACTIVATE);
1580           }
1581           break;
1582         ////////////////////////////////////////////////////////////////////////
1583         // Window events, ignored
1584         ////////////////////////////////////////////////////////////////////////
1585         case WM_WINDOWPOSCHANGED:
1586         /* The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place
1587          * in the Z order has changed as a result of a call to the SetWindowPos function or
1588          * another window-management function.
1589          * The WM_SIZE and WM_MOVE messages are not sent if an application handles the
1590          * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
1591          * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
1592          * message without calling DefWindowProc.
1593          */
1594         case WM_ERASEBKGND:
1595         /* An application sends the WM_ERASEBKGND message when the window background must be
1596          * erased (for example, when a window is resized). The message is sent to prepare an
1597          * invalidated portion of a window for painting.
1598          */
1599         case WM_NCPAINT:
1600         /* An application sends the WM_NCPAINT message to a window when its frame must be painted. */
1601         case WM_NCACTIVATE:
1602         /* The WM_NCACTIVATE message is sent to a window when its nonclient area needs to be changed
1603          * to indicate an active or inactive state.
1604          */
1605         case WM_DESTROY:
1606         /* The WM_DESTROY message is sent when a window is being destroyed. It is sent to the window
1607          * procedure of the window being destroyed after the window is removed from the screen.
1608          * This message is sent first to the window being destroyed and then to the child windows
1609          * (if any) as they are destroyed. During the processing of the message, it can be assumed
1610          * that all child windows still exist.
1611          */
1612         case WM_NCDESTROY:
1613           /* The WM_NCDESTROY message informs a window that its nonclient area is being destroyed. The
1614            * DestroyWindow function sends the WM_NCDESTROY message to the window following the WM_DESTROY
1615            * message. WM_DESTROY is used to free the allocated memory object associated with the window.
1616            */
1617           break;
1618         case WM_KILLFOCUS:
1619           /* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard focus.
1620            * We want to prevent this if a window is still active and it loses focus to nowhere*/
1621           if (!wParam && hwnd == ::GetActiveWindow())
1622             ::SetFocus(hwnd);
1623         case WM_SHOWWINDOW:
1624         /* The WM_SHOWWINDOW message is sent to a window when the window is about to be hidden or shown. */
1625         case WM_WINDOWPOSCHANGING:
1626         /* The WM_WINDOWPOSCHANGING message is sent to a window whose size, position, or place in
1627          * the Z order is about to change as a result of a call to the SetWindowPos function or
1628          * another window-management function.
1629          */
1630         case WM_SETFOCUS:
1631           /* The WM_SETFOCUS message is sent to a window after it has gained the keyboard focus. */
1632           break;
1633         ////////////////////////////////////////////////////////////////////////
1634         // Other events
1635         ////////////////////////////////////////////////////////////////////////
1636         case WM_GETTEXT:
1637         /* An application sends a WM_GETTEXT message to copy the text that
1638          * corresponds to a window into a buffer provided by the caller.
1639          */
1640         case WM_ACTIVATEAPP:
1641         /* The WM_ACTIVATEAPP message is sent when a window belonging to a
1642          * different application than the active window is about to be activated.
1643          * The message is sent to the application whose window is being activated
1644          * and to the application whose window is being deactivated.
1645          */
1646         case WM_TIMER:
1647           /* The WIN32 docs say:
1648            * The WM_TIMER message is posted to the installing thread's message queue
1649            * when a timer expires. You can process the message by providing a WM_TIMER
1650            * case in the window procedure. Otherwise, the default window procedure will
1651            * call the TimerProc callback function specified in the call to the SetTimer
1652            * function used to install the timer.
1653            *
1654            * In GHOST, we let DefWindowProc call the timer callback.
1655            */
1656           break;
1657       }
1658     }
1659     else {
1660       // Event found for a window before the pointer to the class has been set.
1661       GHOST_PRINT("GHOST_SystemWin32::wndProc: GHOST window event before creation\n");
1662       /* These are events we typically miss at this point:
1663        * WM_GETMINMAXINFO 0x24
1664        * WM_NCCREATE          0x81
1665        * WM_NCCALCSIZE        0x83
1666        * WM_CREATE            0x01
1667        * We let DefWindowProc do the work.
1668        */
1669     }
1670   }
1671   else {
1672     // Events without valid hwnd
1673     GHOST_PRINT("GHOST_SystemWin32::wndProc: event without window\n");
1674   }
1675
1676   if (event) {
1677     system->pushEvent(event);
1678     eventHandled = true;
1679   }
1680
1681   if (!eventHandled)
1682     lResult = ::DefWindowProcW(hwnd, msg, wParam, lParam);
1683
1684   return lResult;
1685 }
1686
1687 GHOST_TUns8 *GHOST_SystemWin32::getClipboard(bool selection) const
1688 {
1689   char *temp_buff;
1690
1691   if (IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(NULL)) {
1692     wchar_t *buffer;
1693     HANDLE hData = GetClipboardData(CF_UNICODETEXT);
1694     if (hData == NULL) {
1695       CloseClipboard();
1696       return NULL;
1697     }
1698     buffer = (wchar_t *)GlobalLock(hData);
1699     if (!buffer) {
1700       CloseClipboard();
1701       return NULL;
1702     }
1703
1704     temp_buff = alloc_utf_8_from_16(buffer, 0);
1705
1706     /* Buffer mustn't be accessed after CloseClipboard
1707      * it would like accessing free-d memory */
1708     GlobalUnlock(hData);
1709     CloseClipboard();
1710
1711     return (GHOST_TUns8 *)temp_buff;
1712   }
1713   else if (IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(NULL)) {
1714     char *buffer;
1715     size_t len = 0;
1716     HANDLE hData = GetClipboardData(CF_TEXT);
1717     if (hData == NULL) {
1718       CloseClipboard();
1719       return NULL;
1720     }
1721     buffer = (char *)GlobalLock(hData);
1722     if (!buffer) {
1723       CloseClipboard();
1724       return NULL;
1725     }
1726
1727     len = strlen(buffer);
1728     temp_buff = (char *)malloc(len + 1);
1729     strncpy(temp_buff, buffer, len);
1730     temp_buff[len] = '\0';
1731
1732     /* Buffer mustn't be accessed after CloseClipboard
1733      * it would like accessing free-d memory */
1734     GlobalUnlock(hData);
1735     CloseClipboard();
1736
1737     return (GHOST_TUns8 *)temp_buff;
1738   }
1739   else {
1740     return NULL;
1741   }
1742 }
1743
1744 void GHOST_SystemWin32::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1745 {
1746   if (selection) {
1747     return;
1748   }  // for copying the selection, used on X11
1749
1750   if (OpenClipboard(NULL)) {
1751     HLOCAL clipbuffer;
1752     wchar_t *data;
1753
1754     if (buffer) {
1755       size_t len = count_utf_16_from_8(buffer);
1756       EmptyClipboard();
1757
1758       clipbuffer = LocalAlloc(LMEM_FIXED, sizeof(wchar_t) * len);
1759       data = (wchar_t *)GlobalLock(clipbuffer);
1760
1761       conv_utf_8_to_16(buffer, data, len);
1762
1763       LocalUnlock(clipbuffer);
1764       SetClipboardData(CF_UNICODETEXT, clipbuffer);
1765     }
1766     CloseClipboard();
1767   }
1768   else {
1769     return;
1770   }
1771 }
1772
1773 static DWORD GetParentProcessID(void)
1774 {
1775   HANDLE snapshot;
1776   PROCESSENTRY32 pe32 = {0};
1777   DWORD ppid = 0, pid = GetCurrentProcessId();
1778   snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
1779   if (snapshot == INVALID_HANDLE_VALUE) {
1780     return -1;
1781   }
1782   pe32.dwSize = sizeof(pe32);
1783   if (!Process32First(snapshot, &pe32)) {
1784     CloseHandle(snapshot);
1785     return -1;
1786   }
1787   do {
1788     if (pe32.th32ProcessID == pid) {
1789       ppid = pe32.th32ParentProcessID;
1790       break;
1791     }
1792   } while (Process32Next(snapshot, &pe32));
1793   CloseHandle(snapshot);
1794   return ppid;
1795 }
1796
1797 static bool getProcessName(int pid, char *buffer, int max_len)
1798 {
1799   bool result = false;
1800   HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
1801   if (handle) {
1802     GetModuleFileNameEx(handle, 0, buffer, max_len);
1803     result = true;
1804   }
1805   CloseHandle(handle);
1806   return result;
1807 }
1808
1809 static bool isStartedFromCommandPrompt()
1810 {
1811   HWND hwnd = GetConsoleWindow();
1812
1813   if (hwnd) {
1814     DWORD pid = (DWORD)-1;
1815     DWORD ppid = GetParentProcessID();
1816     char parent_name[MAX_PATH];
1817     bool start_from_launcher = false;
1818
1819     GetWindowThreadProcessId(hwnd, &pid);
1820     if (getProcessName(ppid, parent_name, sizeof(parent_name))) {
1821       char *filename = strrchr(parent_name, '\\');
1822       if (filename != NULL) {
1823         start_from_launcher = strstr(filename, "blender.exe") != NULL;
1824       }
1825     }
1826
1827     /* When we're starting from a wrapper we need to compare with parent process ID. */
1828     if (pid != (start_from_launcher ? ppid : GetCurrentProcessId()))
1829       return true;
1830   }
1831
1832   return false;
1833 }
1834
1835 int GHOST_SystemWin32::toggleConsole(int action)
1836 {
1837   HWND wnd = GetConsoleWindow();
1838
1839   switch (action) {
1840     case 3:  // startup: hide if not started from command prompt
1841     {
1842       if (!isStartedFromCommandPrompt()) {
1843         ShowWindow(wnd, SW_HIDE);
1844         m_consoleStatus = 0;
1845       }
1846       break;
1847     }
1848     case 0:  // hide
1849       ShowWindow(wnd, SW_HIDE);
1850       m_consoleStatus = 0;
1851       break;
1852     case 1:  // show
1853       ShowWindow(wnd, SW_SHOW);
1854       if (!isStartedFromCommandPrompt()) {
1855         DeleteMenu(GetSystemMenu(wnd, FALSE), SC_CLOSE, MF_BYCOMMAND);
1856       }
1857       m_consoleStatus = 1;
1858       break;
1859     case 2:  // toggle
1860       ShowWindow(wnd, m_consoleStatus ? SW_HIDE : SW_SHOW);
1861       m_consoleStatus = !m_consoleStatus;
1862       if (m_consoleStatus && !isStartedFromCommandPrompt()) {
1863         DeleteMenu(GetSystemMenu(wnd, FALSE), SC_CLOSE, MF_BYCOMMAND);
1864       }
1865       break;
1866   }
1867
1868   return m_consoleStatus;
1869 }
1870
1871 int GHOST_SystemWin32::confirmQuit(GHOST_IWindow *window) const
1872 {
1873   return (MessageBox(window ? ((GHOST_WindowWin32 *)window)->getHWND() : 0,
1874                      "Some changes have not been saved.\nDo you really want to quit?",
1875                      "Exit Blender",
1876                      MB_OKCANCEL | MB_ICONWARNING | MB_TOPMOST) == IDOK);
1877 }