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