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