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