Various changes made in the process of working on the UI code:
[blender.git] / intern / ghost / intern / GHOST_SystemWin32.cpp
1 /**
2  * $Id$
3  * ***** BEGIN GPL LICENSE BLOCK *****
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  *
19  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
20  * All rights reserved.
21  *
22  * The Original Code is: all of this file.
23  *
24  * Contributor(s): none yet.
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 /**
30
31  * $Id$
32  * Copyright (C) 2001 NaN Technologies B.V.
33  * @author      Maarten Gribnau
34  * @date        May 7, 2001
35  */
36
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 #include "GHOST_SystemWin32.h"
42
43 /*
44  * According to the docs the mouse wheel message is supported from windows 98 
45  * upwards. Leaving WINVER at default value, the WM_MOUSEWHEEL message and the 
46  * wheel detent value are undefined.
47  */
48 #ifndef WM_MOUSEWHEEL
49 #define WM_MOUSEWHEEL 0x020A
50 #endif // WM_MOUSEWHEEL
51 #ifndef WHEEL_DELTA
52 #define WHEEL_DELTA 120 /* Value for rolling one detent, (old convention! MS changed it) */
53 #endif // WHEEL_DELTA
54
55
56 #include "GHOST_Debug.h"
57 #include "GHOST_DisplayManagerWin32.h"
58 #include "GHOST_EventButton.h"
59 #include "GHOST_EventCursor.h"
60 #include "GHOST_EventKey.h"
61 #include "GHOST_EventWheel.h"
62 #include "GHOST_TimerTask.h"
63 #include "GHOST_TimerManager.h"
64 #include "GHOST_WindowManager.h"
65 #include "GHOST_WindowWin32.h"
66
67 // Key code values not found in winuser.h
68 #ifndef VK_MINUS
69 #define VK_MINUS 0xBD
70 #endif // VK_MINUS
71 #ifndef VK_SEMICOLON
72 #define VK_SEMICOLON 0xBA
73 #endif // VK_SEMICOLON
74 #ifndef VK_PERIOD
75 #define VK_PERIOD 0xBE
76 #endif // VK_PERIOD
77 #ifndef VK_COMMA
78 #define VK_COMMA 0xBC
79 #endif // VK_COMMA
80 #ifndef VK_QUOTE
81 #define VK_QUOTE 0xDE
82 #endif // VK_QUOTE
83 #ifndef VK_BACK_QUOTE
84 #define VK_BACK_QUOTE 0xC0
85 #endif // VK_BACK_QUOTE
86 #ifndef VK_SLASH
87 #define VK_SLASH 0xBF
88 #endif // VK_SLASH
89 #ifndef VK_BACK_SLASH
90 #define VK_BACK_SLASH 0xDC
91 #endif // VK_BACK_SLASH
92 #ifndef VK_EQUALS
93 #define VK_EQUALS 0xBB
94 #endif // VK_EQUALS
95 #ifndef VK_OPEN_BRACKET
96 #define VK_OPEN_BRACKET 0xDB
97 #endif // VK_OPEN_BRACKET
98 #ifndef VK_CLOSE_BRACKET
99 #define VK_CLOSE_BRACKET 0xDD
100 #endif // VK_CLOSE_BRACKET
101 #ifndef VK_GR_LESS
102 #define VK_GR_LESS 0xE2
103 #endif // VK_GR_LESS
104
105
106 GHOST_SystemWin32::GHOST_SystemWin32()
107 : m_hasPerformanceCounter(false), m_freq(0), m_start(0),
108   m_seperateLeftRight(false),
109   m_seperateLeftRightInitialized(false)
110 {
111         m_displayManager = new GHOST_DisplayManagerWin32 ();
112         GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::GHOST_SystemWin32(): m_displayManager==0\n");
113         m_displayManager->initialize();
114 }
115
116 GHOST_SystemWin32::~GHOST_SystemWin32()
117 {
118 }
119
120
121 GHOST_TUns64 GHOST_SystemWin32::getMilliSeconds() const
122 {
123         // Hardware does not support high resolution timers. We will use GetTickCount instead then.
124         if (!m_hasPerformanceCounter) {
125                 return ::GetTickCount();
126         }
127
128         // Retrieve current count
129         __int64 count = 0;
130         ::QueryPerformanceCounter((LARGE_INTEGER*)&count);
131
132         // Calculate the time passed since system initialization.
133         __int64 delta = 1000*(count-m_start);
134
135         GHOST_TUns64 t = (GHOST_TUns64)(delta/m_freq);
136         return t; 
137 }
138
139
140 GHOST_TUns8 GHOST_SystemWin32::getNumDisplays() const
141 {
142         GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::getNumDisplays(): m_displayManager==0\n");
143         GHOST_TUns8 numDisplays;
144         m_displayManager->getNumDisplays(numDisplays);
145         return numDisplays;
146 }
147
148
149 void GHOST_SystemWin32::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
150 {
151         width = ::GetSystemMetrics(SM_CXSCREEN);
152         height= ::GetSystemMetrics(SM_CYSCREEN);
153 }
154
155
156 GHOST_IWindow* GHOST_SystemWin32::createWindow(
157         const STR_String& title, 
158         GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, GHOST_TUns32 height,
159         GHOST_TWindowState state, GHOST_TDrawingContextType type,
160         bool stereoVisual)
161 {
162         GHOST_Window* window = 0;
163         window = new GHOST_WindowWin32 (title, left, top, width, height, state, type, stereoVisual);
164         if (window) {
165                 if (window->getValid()) {
166                         // Store the pointer to the window
167 //                      if (state != GHOST_kWindowStateFullScreen) {
168                                 m_windowManager->addWindow(window);
169 //                      }
170                 }
171                 else {
172                         delete window;
173                         window = 0;
174                 }
175         }
176         return window;
177 }
178
179
180 bool GHOST_SystemWin32::processEvents(bool waitForEvent)
181 {
182         MSG msg;
183         bool anyProcessed = false;
184
185         do {
186                 GHOST_TimerManager* timerMgr = getTimerManager();
187
188                 if (waitForEvent && !::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)) {
189 #if 1
190                         ::Sleep(1);
191 #else
192                         GHOST_TUns64 next = timerMgr->nextFireTime();
193                         GHOST_TInt64 maxSleep = next - getMilliSeconds();
194                         
195                         if (next == GHOST_kFireTimeNever) {
196                                 ::WaitMessage();
197                         } else if(maxSleep >= 0.0) {
198                                 ::SetTimer(NULL, 0, maxSleep, NULL);
199                                 ::WaitMessage();
200                                 ::KillTimer(NULL, 0);
201                         }
202 #endif
203                 }
204
205                 if (timerMgr->fireTimers(getMilliSeconds())) {
206                         anyProcessed = true;
207                 }
208
209                 // Process all the events waiting for us
210                 while (::PeekMessage(&msg, 0, 0, 0, PM_REMOVE) != 0) {
211                         ::TranslateMessage(&msg);
212                         ::DispatchMessage(&msg);
213                         anyProcessed = true;
214                 }
215         } while (waitForEvent && !anyProcessed);
216
217         return anyProcessed;
218 }
219
220
221 GHOST_TSuccess GHOST_SystemWin32::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
222 {
223         POINT point;
224         ::GetCursorPos(&point);
225         x = point.x;
226         y = point.y;
227         return GHOST_kSuccess;
228 }
229
230
231 GHOST_TSuccess GHOST_SystemWin32::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y) const
232 {
233         return ::SetCursorPos(x, y) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
234 }
235
236
237 GHOST_TSuccess GHOST_SystemWin32::getModifierKeys(GHOST_ModifierKeys& keys) const
238 {
239         /*
240         GetKeyState and GetAsyncKeyState only work with Win95, Win98, NT4,
241         Terminal Server and Windows 2000.
242         But on WinME it always returns zero. These two functions are simply
243         skipped by Millenium Edition!
244
245         Official explanation from Microsoft:
246         Intentionally disabled.
247         It didn't work all that well on some newer hardware, and worked less 
248         well with the passage of time, so it was fully disabled in ME.
249         */
250         if (m_seperateLeftRight && m_seperateLeftRightInitialized) {
251                 bool down = HIBYTE(::GetKeyState(VK_LSHIFT)) != 0;
252                 keys.set(GHOST_kModifierKeyLeftShift, down);
253                 down = HIBYTE(::GetKeyState(VK_RSHIFT)) != 0;
254                 keys.set(GHOST_kModifierKeyRightShift, down);
255                 down = HIBYTE(::GetKeyState(VK_LMENU)) != 0;
256                 keys.set(GHOST_kModifierKeyLeftAlt, down);
257                 down = HIBYTE(::GetKeyState(VK_RMENU)) != 0;
258                 keys.set(GHOST_kModifierKeyRightAlt, down);
259                 down = HIBYTE(::GetKeyState(VK_LCONTROL)) != 0;
260                 keys.set(GHOST_kModifierKeyLeftControl, down);
261                 down = HIBYTE(::GetKeyState(VK_RCONTROL)) != 0;
262                 keys.set(GHOST_kModifierKeyRightControl, down);
263         }
264         else {
265                 bool down = HIBYTE(::GetKeyState(VK_SHIFT)) != 0;
266                 keys.set(GHOST_kModifierKeyLeftShift, down);
267                 keys.set(GHOST_kModifierKeyRightShift, down);
268                 down = HIBYTE(::GetKeyState(VK_MENU)) != 0;
269                 keys.set(GHOST_kModifierKeyLeftAlt, down);
270                 keys.set(GHOST_kModifierKeyRightAlt, down);
271                 down = HIBYTE(::GetKeyState(VK_CONTROL)) != 0;
272                 keys.set(GHOST_kModifierKeyLeftControl, down);
273                 keys.set(GHOST_kModifierKeyRightControl, down);
274         }
275         return GHOST_kSuccess;
276 }
277
278
279 GHOST_TSuccess GHOST_SystemWin32::getButtons(GHOST_Buttons& buttons) const
280 {
281         /* Check for swapped buttons (left-handed mouse buttons)
282          * GetAsyncKeyState() will give back the state of the physical mouse buttons.
283          */
284         bool swapped = ::GetSystemMetrics(SM_SWAPBUTTON) == TRUE;
285
286         bool down = HIBYTE(::GetKeyState(VK_LBUTTON)) != 0;
287         buttons.set(swapped ? GHOST_kButtonMaskRight : GHOST_kButtonMaskLeft, down);
288
289         down = HIBYTE(::GetKeyState(VK_MBUTTON)) != 0;
290         buttons.set(GHOST_kButtonMaskMiddle, down);
291
292         down = HIBYTE(::GetKeyState(VK_RBUTTON)) != 0;
293         buttons.set(swapped ? GHOST_kButtonMaskLeft : GHOST_kButtonMaskRight, down);
294         return GHOST_kSuccess;
295 }
296
297
298 GHOST_TSuccess GHOST_SystemWin32::init()
299 {
300         GHOST_TSuccess success = GHOST_System::init();
301
302         // Determine whether this system has a high frequency performance counter. */
303         m_hasPerformanceCounter = ::QueryPerformanceFrequency((LARGE_INTEGER*)&m_freq) == TRUE;
304         if (m_hasPerformanceCounter) {
305                 GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer available\n")
306                 ::QueryPerformanceCounter((LARGE_INTEGER*)&m_start);
307         }
308         else {
309                 GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer not available\n")
310         }
311
312         if (success) {
313                 WNDCLASS wc;
314                 wc.style= CS_HREDRAW | CS_VREDRAW;
315                 wc.lpfnWndProc= s_wndProc;
316                 wc.cbClsExtra= 0;
317                 wc.cbWndExtra= 0;
318                 wc.hInstance= ::GetModuleHandle(0);
319                 wc.hIcon = ::LoadIcon(wc.hInstance, "APPICON");
320                 
321                 if (!wc.hIcon) {
322                         ::LoadIcon(NULL, IDI_APPLICATION);
323                 }
324                 wc.hCursor = ::LoadCursor(0, IDC_ARROW);
325                 wc.hbrBackground= (HBRUSH)::GetStockObject(BLACK_BRUSH);
326                 wc.lpszMenuName = 0;
327                 wc.lpszClassName= GHOST_WindowWin32::getWindowClassName();
328     
329                 // Use RegisterClassEx for setting small icon
330                 if (::RegisterClass(&wc) == 0) {
331                         success = GHOST_kFailure;
332                 }
333         }
334         return success;
335 }
336
337
338 GHOST_TSuccess GHOST_SystemWin32::exit()
339 {
340         return GHOST_System::exit();
341 }
342
343
344 GHOST_TKey GHOST_SystemWin32::convertKey(WPARAM wParam, LPARAM lParam) const
345 {
346         GHOST_TKey key;
347         bool isExtended = (lParam&(1<<24))?true:false;
348
349         if ((wParam >= '0') && (wParam <= '9')) {
350                 // VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39)
351                 key = (GHOST_TKey)(wParam - '0' + GHOST_kKey0);
352         }
353         else if ((wParam >= 'A') && (wParam <= 'Z')) {
354                 // VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A)
355                 key = (GHOST_TKey)(wParam - 'A' + GHOST_kKeyA);
356         }
357         else if ((wParam >= VK_F1) && (wParam <= VK_F24)) {
358                 key = (GHOST_TKey)(wParam - VK_F1 + GHOST_kKeyF1);
359         }
360         else {
361                 switch (wParam) {
362                 case VK_RETURN:
363                         key = isExtended?GHOST_kKeyNumpadEnter:GHOST_kKeyEnter;
364                         break;
365
366                 case VK_BACK:     key = GHOST_kKeyBackSpace;            break;
367                 case VK_TAB:      key = GHOST_kKeyTab;                          break;
368                 case VK_ESCAPE:   key = GHOST_kKeyEsc;                          break;
369                 case VK_SPACE:    key = GHOST_kKeySpace;                        break;
370                 case VK_PRIOR:    key = GHOST_kKeyUpPage;                       break;
371                 case VK_NEXT:     key = GHOST_kKeyDownPage;                     break;
372                 case VK_END:      key = GHOST_kKeyEnd;                          break;
373                 case VK_HOME:     key = GHOST_kKeyHome;                         break;
374                 case VK_INSERT:   key = GHOST_kKeyInsert;                       break;
375                 case VK_DELETE:   key = GHOST_kKeyDelete;                       break;
376                 case VK_LEFT:     key = GHOST_kKeyLeftArrow;            break;
377                 case VK_RIGHT:    key = GHOST_kKeyRightArrow;           break;
378                 case VK_UP:       key = GHOST_kKeyUpArrow;                      break;
379                 case VK_DOWN:     key = GHOST_kKeyDownArrow;            break;
380                 case VK_NUMPAD0:  key = GHOST_kKeyNumpad0;                      break;
381                 case VK_NUMPAD1:  key = GHOST_kKeyNumpad1;                      break;
382                 case VK_NUMPAD2:  key = GHOST_kKeyNumpad2;                      break;
383                 case VK_NUMPAD3:  key = GHOST_kKeyNumpad3;                      break;
384                 case VK_NUMPAD4:  key = GHOST_kKeyNumpad4;                      break;
385                 case VK_NUMPAD5:  key = GHOST_kKeyNumpad5;                      break;
386                 case VK_NUMPAD6:  key = GHOST_kKeyNumpad6;                      break;
387                 case VK_NUMPAD7:  key = GHOST_kKeyNumpad7;                      break;
388                 case VK_NUMPAD8:  key = GHOST_kKeyNumpad8;                      break;
389                 case VK_NUMPAD9:  key = GHOST_kKeyNumpad9;                      break;
390                 case VK_SNAPSHOT: key = GHOST_kKeyPrintScreen;          break;
391                 case VK_PAUSE:    key = GHOST_kKeyPause;                        break;
392                 case VK_MULTIPLY: key = GHOST_kKeyNumpadAsterisk;        break;
393                 case VK_SUBTRACT: key = GHOST_kKeyNumpadMinus;          break;
394                 case VK_DECIMAL:  key = GHOST_kKeyNumpadPeriod;         break;
395                 case VK_DIVIDE:   key = GHOST_kKeyNumpadSlash;          break;
396                 case VK_ADD:      key = GHOST_kKeyNumpadPlus;           break;
397
398                 case VK_SEMICOLON:              key = GHOST_kKeySemicolon;              break;
399                 case VK_EQUALS:                 key = GHOST_kKeyEqual;                  break;
400                 case VK_COMMA:                  key = GHOST_kKeyComma;                  break;
401                 case VK_MINUS:                  key = GHOST_kKeyMinus;                  break;
402                 case VK_PERIOD:                 key = GHOST_kKeyPeriod;                 break;
403                 case VK_SLASH:                  key = GHOST_kKeySlash;                  break;
404                 case VK_BACK_QUOTE:             key = GHOST_kKeyAccentGrave;    break;
405                 case VK_OPEN_BRACKET:   key = GHOST_kKeyLeftBracket;    break;
406                 case VK_BACK_SLASH:             key = GHOST_kKeyBackslash;              break;
407                 case VK_CLOSE_BRACKET:  key = GHOST_kKeyRightBracket;   break;
408                 case VK_QUOTE:                  key = GHOST_kKeyQuote;                  break;
409                 case VK_GR_LESS:                key = GHOST_kKeyGrLess;                 break;
410
411                 // Process these keys separately because we need to distinguish right from left modifier keys
412                 case VK_SHIFT:
413                 case VK_CONTROL:
414                 case VK_MENU:
415
416                 // Ignore these keys
417                 case VK_NUMLOCK:
418                 case VK_SCROLL:
419                 case VK_CAPITAL:
420                 default:
421                         key = GHOST_kKeyUnknown;
422                         break;
423                 }
424         }
425         return key;
426 }
427
428
429 void GHOST_SystemWin32::processModifierKeys(GHOST_IWindow *window)
430 {
431         GHOST_ModifierKeys oldModifiers, newModifiers;
432         // Retrieve old state of the modifier keys
433         ((GHOST_SystemWin32*)getSystem())->retrieveModifierKeys(oldModifiers);
434         // Retrieve current state of the modifier keys
435         ((GHOST_SystemWin32*)getSystem())->getModifierKeys(newModifiers);
436
437         // Compare the old and the new
438         if (!newModifiers.equals(oldModifiers)) {
439                 // Create events for the masks that changed
440                 for (int i = 0; i < GHOST_kModifierKeyNumMasks; i++) {
441                         if (newModifiers.get((GHOST_TModifierKeyMask)i) != oldModifiers.get((GHOST_TModifierKeyMask)i)) {
442                                 // Convert the mask to a key code
443                                 GHOST_TKey key = GHOST_ModifierKeys::getModifierKeyCode((GHOST_TModifierKeyMask)i);
444                                 bool keyDown = newModifiers.get((GHOST_TModifierKeyMask)i);
445                                 GHOST_EventKey* event;
446                                 if (key != GHOST_kKeyUnknown) {
447                                         // Create an event
448                                         event = new GHOST_EventKey(getSystem()->getMilliSeconds(), keyDown ? GHOST_kEventKeyDown: GHOST_kEventKeyUp, window, key);
449                                         pushEvent(event);
450                                 }
451                         }
452                 }
453         }
454
455         // Store new modifier keys state
456         ((GHOST_SystemWin32*)getSystem())->storeModifierKeys(newModifiers);
457 }
458
459
460 GHOST_EventButton* GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type, GHOST_IWindow *window, GHOST_TButtonMask mask)
461 {
462         return new GHOST_EventButton (getSystem()->getMilliSeconds(), type, window, mask);
463 }
464
465
466 GHOST_EventCursor* GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type, GHOST_IWindow *window)
467 {
468         GHOST_TInt32 x, y;
469         getSystem()->getCursorPosition(x, y);
470         return new GHOST_EventCursor (getSystem()->getMilliSeconds(), type, window, x, y);
471 }
472
473
474 GHOST_EventWheel* GHOST_SystemWin32::processWheelEvent(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam)
475 {
476         // short fwKeys = LOWORD(wParam);                       // key flags
477         int zDelta = (short) HIWORD(wParam);    // wheel rotation
478         
479         // zDelta /= WHEEL_DELTA;
480         // temporary fix below: microsoft now has added more precision, making the above division not work
481         if (zDelta <= 0 ) zDelta= -1; else zDelta= 1;   
482         
483         // short xPos = (short) LOWORD(lParam); // horizontal position of pointer
484         // short yPos = (short) HIWORD(lParam); // vertical position of pointer
485         return new GHOST_EventWheel (getSystem()->getMilliSeconds(), window, zDelta);
486 }
487
488
489 GHOST_EventKey* GHOST_SystemWin32::processKeyEvent(GHOST_IWindow *window, bool keyDown, WPARAM wParam, LPARAM lParam)
490 {
491         GHOST_TKey key = ((GHOST_SystemWin32*)getSystem())->convertKey(wParam, lParam);
492         GHOST_EventKey* event;
493         if (key != GHOST_kKeyUnknown) {
494                 MSG keyMsg;
495                 char ascii = '\0';
496
497                         /* Eat any character related messages */
498                 if (::PeekMessage(&keyMsg, NULL, WM_CHAR, WM_SYSDEADCHAR, PM_REMOVE)) {
499                         ascii = (char) keyMsg.wParam;
500                 }
501
502                 event = new GHOST_EventKey(getSystem()->getMilliSeconds(), keyDown ? GHOST_kEventKeyDown: GHOST_kEventKeyUp, window, key, ascii);
503         }
504         else {
505                 event = 0;
506         }
507         return event;
508 }
509
510
511 GHOST_Event* GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type, GHOST_IWindow* window)
512 {
513         return new GHOST_Event(getSystem()->getMilliSeconds(), type, window);
514 }
515
516
517 LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
518 {
519         GHOST_Event* event = 0;
520         LRESULT lResult;
521         GHOST_SystemWin32* system = ((GHOST_SystemWin32*)getSystem());
522         GHOST_ASSERT(system, "GHOST_SystemWin32::s_wndProc(): system not initialized")
523
524         if (hwnd) {
525                 GHOST_WindowWin32* window = (GHOST_WindowWin32*)::GetWindowLong(hwnd, GWL_USERDATA);
526                 if (window) {
527                         switch (msg) {
528                                 ////////////////////////////////////////////////////////////////////////
529                                 // Keyboard events, processed
530                                 ////////////////////////////////////////////////////////////////////////
531                                 case WM_KEYDOWN:
532                                         /* The WM_KEYDOWN message is posted to the window with the keyboard focus when a 
533                                          * nonsystem key is pressed. A nonsystem key is a key that is pressed when the alt
534                                          * key is not pressed. 
535                                          */
536                                 case WM_SYSKEYDOWN:
537                                         /* The WM_SYSKEYDOWN message is posted to the window with the keyboard focus when 
538                                          * the user presses the F10 key (which activates the menu bar) or holds down the 
539                                          * alt key and then presses another key. It also occurs when no window currently 
540                                          * has the keyboard focus; in this case, the WM_SYSKEYDOWN message is sent to the 
541                                          * active window. The window that receives the message can distinguish between these 
542                                          * two contexts by checking the context code in the lKeyData parameter. 
543                                          */
544                                         switch (wParam) {
545                                                 case VK_SHIFT:
546                                                 case VK_CONTROL:
547                                                 case VK_MENU:
548                                                         if (!system->m_seperateLeftRightInitialized) {
549                                                                 // Check whether this system supports seperate left and right keys
550                                                                 switch (wParam) {
551                                                                         case VK_SHIFT:
552                                                                                 system->m_seperateLeftRight = 
553                                                                                         (HIBYTE(::GetKeyState(VK_LSHIFT)) != 0) ||
554                                                                                         (HIBYTE(::GetKeyState(VK_RSHIFT)) != 0) ?
555                                                                                         true : false;
556                                                                                 break;
557                                                                         case VK_CONTROL:
558                                                                                 system->m_seperateLeftRight = 
559                                                                                         (HIBYTE(::GetKeyState(VK_LCONTROL)) != 0) ||
560                                                                                         (HIBYTE(::GetKeyState(VK_RCONTROL)) != 0) ?
561                                                                                         true : false;
562                                                                                 break;
563                                                                         case VK_MENU:
564                                                                                 system->m_seperateLeftRight = 
565                                                                                         (HIBYTE(::GetKeyState(VK_LMENU)) != 0) ||
566                                                                                         (HIBYTE(::GetKeyState(VK_RMENU)) != 0) ?
567                                                                                         true : false;
568                                                                                 break;
569                                                                 }
570                                                                 system->m_seperateLeftRightInitialized = true;
571                                                         }
572                                                         system->processModifierKeys(window);
573                                                         // Bypass call to DefWindowProc
574                                                         return 0;
575                                                 default:
576                                                         event = processKeyEvent(window, true, wParam, lParam);
577                                                         if (!event) {
578                                                                 GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ")
579                                                                 GHOST_PRINT(msg)
580                                                                 GHOST_PRINT(" key ignored\n")
581                                                         }
582                                                         break;
583                                                 }
584                                         break;
585
586                                 case WM_KEYUP:
587                                 case WM_SYSKEYUP:
588                                         switch (wParam) {
589                                                 case VK_SHIFT:
590                                                 case VK_CONTROL:
591                                                 case VK_MENU:
592                                                         system->processModifierKeys(window);
593                                                         // Bypass call to DefWindowProc
594                                                         return 0;
595                                                 default:
596                                                         event = processKeyEvent(window, false, wParam, lParam);
597                                                         if (!event) {
598                                                                 GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ")
599                                                                 GHOST_PRINT(msg)
600                                                                 GHOST_PRINT(" key ignored\n")
601                                                         }
602                                                         break;
603                                         }
604                                         break;
605
606                                 ////////////////////////////////////////////////////////////////////////
607                                 // Keyboard events, ignored
608                                 ////////////////////////////////////////////////////////////////////////
609                                 case WM_CHAR:
610                                         /* The WM_CHAR message is posted to the window with the keyboard focus when 
611                                          * a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR 
612                                          * contains the character code of the key that was pressed. 
613                                          */
614                                 case WM_DEADCHAR:
615                                         /* The WM_DEADCHAR message is posted to the window with the keyboard focus when a
616                                          * WM_KEYUP message is translated by the TranslateMessage function. WM_DEADCHAR 
617                                          * specifies a character code generated by a dead key. A dead key is a key that 
618                                          * generates a character, such as the umlaut (double-dot), that is combined with 
619                                          * another character to form a composite character. For example, the umlaut-O 
620                                          * character (Ù) is generated by typing the dead key for the umlaut character, and 
621                                          * then typing the O key.
622                                          */
623                                 case WM_SYSDEADCHAR:
624                                         /* The WM_SYSDEADCHAR message is sent to the window with the keyboard focus when 
625                                          * a WM_SYSKEYDOWN message is translated by the TranslateMessage function. 
626                                          * WM_SYSDEADCHAR specifies the character code of a system dead key - that is, 
627                                          * a dead key that is pressed while holding down the alt key. 
628                                          */
629                                         break;
630                                 ////////////////////////////////////////////////////////////////////////
631                                 // Tablet events, processed
632                                 ////////////////////////////////////////////////////////////////////////
633                                 case WT_PACKET:
634                                         ((GHOST_WindowWin32*)window)->processWin32TabletEvent(wParam, lParam);
635                                         break;
636                                 case WT_CSRCHANGE:
637                                 case WT_PROXIMITY:
638                                         ((GHOST_WindowWin32*)window)->processWin32TabletInitEvent();
639                                         break;
640                                 ////////////////////////////////////////////////////////////////////////
641                                 // Mouse events, processed
642                                 ////////////////////////////////////////////////////////////////////////
643                                 case WM_LBUTTONDOWN:
644                                         window->registerMouseClickEvent(true);
645                                         event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft);
646                                         break;
647                                 case WM_MBUTTONDOWN:
648                                         window->registerMouseClickEvent(true);
649                                         event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskMiddle);
650                                         break;
651                                 case WM_RBUTTONDOWN:
652                                         window->registerMouseClickEvent(true);
653                                         event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight);
654                                         break;
655                                 case WM_LBUTTONUP:
656                                         window->registerMouseClickEvent(false);
657                                         event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft);
658                                         break;
659                                 case WM_MBUTTONUP:
660                                         window->registerMouseClickEvent(false);
661                                         event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle);
662                                         break;
663                                 case WM_RBUTTONUP:
664                                         window->registerMouseClickEvent(false);
665                                         event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight);
666                                         break;
667                                 case WM_MOUSEMOVE:
668                                         event = processCursorEvent(GHOST_kEventCursorMove, window);
669                                         break;
670                                 case WM_MOUSEWHEEL:
671                                         /* The WM_MOUSEWHEEL message is sent to the focus window 
672                                          * when the mouse wheel is rotated. The DefWindowProc 
673                                          * function propagates the message to the window's parent.
674                                          * There should be no internal forwarding of the message, 
675                                          * since DefWindowProc propagates it up the parent chain 
676                                          * until it finds a window that processes it.
677                                          */
678                                         event = processWheelEvent(window, wParam, lParam);
679                                         break;
680                                 case WM_SETCURSOR:
681                                         /* The WM_SETCURSOR message is sent to a window if the mouse causes the cursor
682                                          * to move within a window and mouse input is not captured.
683                                          * This means we have to set the cursor shape every time the mouse moves!
684                                          * The DefWindowProc function uses this message to set the cursor to an 
685                                          * arrow if it is not in the client area.
686                                          */
687                                         if (LOWORD(lParam) == HTCLIENT) {
688                                                 // Load the current cursor
689                                                 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
690                                                 // Bypass call to DefWindowProc
691                                                 return 0;
692                                         } 
693                                         else {
694                                                 // Outside of client area show standard cursor
695                                                 window->loadCursor(true, GHOST_kStandardCursorDefault);
696                                         }
697                                         break;
698
699                                 ////////////////////////////////////////////////////////////////////////
700                                 // Mouse events, ignored
701                                 ////////////////////////////////////////////////////////////////////////
702                                 case WM_NCMOUSEMOVE:
703                                         /* The WM_NCMOUSEMOVE message is posted to a window when the cursor is moved 
704                                          * within the nonclient area of the window. This message is posted to the window 
705                                          * that contains the cursor. If a window has captured the mouse, this message is not posted.
706                                          */
707                                 case WM_NCHITTEST:
708                                         /* The WM_NCHITTEST message is sent to a window when the cursor moves, or 
709                                          * when a mouse button is pressed or released. If the mouse is not captured, 
710                                          * the message is sent to the window beneath the cursor. Otherwise, the message 
711                                          * is sent to the window that has captured the mouse. 
712                                          */
713                                         break;
714
715                                 ////////////////////////////////////////////////////////////////////////
716                                 // Window events, processed
717                                 ////////////////////////////////////////////////////////////////////////
718                                 case WM_CLOSE:
719                                         /* The WM_CLOSE message is sent as a signal that a window or an application should terminate. */
720                                         event = processWindowEvent(GHOST_kEventWindowClose, window);
721                                         break;
722                                 case WM_ACTIVATE:
723                                         /* The WM_ACTIVATE message is sent to both the window being activated and the window being 
724                                          * deactivated. If the windows use the same input queue, the message is sent synchronously, 
725                                          * first to the window procedure of the top-level window being deactivated, then to the window
726                                          * procedure of the top-level window being activated. If the windows use different input queues,
727                                          * the message is sent asynchronously, so the window is activated immediately. 
728                                          */
729                                         event = processWindowEvent(LOWORD(wParam) ? GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate, window);
730                                         break;
731                                 case WM_PAINT:
732                                         /* An application sends the WM_PAINT message when the system or another application 
733                                          * makes a request to paint a portion of an application's window. The message is sent
734                                          * when the UpdateWindow or RedrawWindow function is called, or by the DispatchMessage 
735                                          * function when the application obtains a WM_PAINT message by using the GetMessage or 
736                                          * PeekMessage function. 
737                                          */
738                                         event = processWindowEvent(GHOST_kEventWindowUpdate, window);
739                                         ::ValidateRect(hwnd, NULL);
740                                         break;
741                                 case WM_SIZE:
742                                         /* The WM_SIZE message is sent to a window after its size has changed.
743                                          * The WM_SIZE and WM_MOVE messages are not sent if an application handles the 
744                                          * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
745                                          * to perform any move or size change processing during the WM_WINDOWPOSCHANGED 
746                                          * message without calling DefWindowProc.
747                                          */
748                                         event = processWindowEvent(GHOST_kEventWindowSize, window);
749                                         break;
750                                 case WM_CAPTURECHANGED:
751                                         window->lostMouseCapture();
752                                         break;
753                                 case WM_MOVING:
754                                         /* The WM_MOVING message is sent to a window that the user is moving. By processing 
755                                          * this message, an application can monitor the size and position of the drag rectangle
756                                          * and, if needed, change its size or position.
757                                          */
758                                 case WM_MOVE:
759                                         /* The WM_SIZE and WM_MOVE messages are not sent if an application handles the 
760                                          * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
761                                          * to perform any move or size change processing during the WM_WINDOWPOSCHANGED 
762                                          * message without calling DefWindowProc. 
763                                          */
764                                         event = processWindowEvent(GHOST_kEventWindowMove, window);
765                                         break;
766                                 ////////////////////////////////////////////////////////////////////////
767                                 // Window events, ignored
768                                 ////////////////////////////////////////////////////////////////////////
769                                 case WM_WINDOWPOSCHANGED:
770                                         /* The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place
771                                          * in the Z order has changed as a result of a call to the SetWindowPos function or 
772                                          * another window-management function.
773                                          * The WM_SIZE and WM_MOVE messages are not sent if an application handles the 
774                                          * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
775                                          * to perform any move or size change processing during the WM_WINDOWPOSCHANGED 
776                                          * message without calling DefWindowProc.
777                                          */
778                                 case WM_ERASEBKGND:
779                                         /* An application sends the WM_ERASEBKGND message when the window background must be 
780                                          * erased (for example, when a window is resized). The message is sent to prepare an 
781                                          * invalidated portion of a window for painting. 
782                                          */
783                                 case WM_NCPAINT:
784                                         /* An application sends the WM_NCPAINT message to a window when its frame must be painted. */
785                                 case WM_NCACTIVATE:
786                                         /* The WM_NCACTIVATE message is sent to a window when its nonclient area needs to be changed 
787                                          * to indicate an active or inactive state. 
788                                          */
789                                 case WM_DESTROY:
790                                         /* The WM_DESTROY message is sent when a window is being destroyed. It is sent to the window 
791                                          * procedure of the window being destroyed after the window is removed from the screen. 
792                                          * This message is sent first to the window being destroyed and then to the child windows 
793                                          * (if any) as they are destroyed. During the processing of the message, it can be assumed 
794                                          * that all child windows still exist. 
795                                          */
796                                 case WM_NCDESTROY:
797                                         /* The WM_NCDESTROY message informs a window that its nonclient area is being destroyed. The 
798                                          * DestroyWindow function sends the WM_NCDESTROY message to the window following the WM_DESTROY
799                                          * message. WM_DESTROY is used to free the allocated memory object associated with the window. 
800                                          */
801                                 case WM_KILLFOCUS:
802                                         /* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard focus. */
803                                 case WM_SHOWWINDOW:
804                                         /* The WM_SHOWWINDOW message is sent to a window when the window is about to be hidden or shown. */
805                                 case WM_WINDOWPOSCHANGING:
806                                         /* The WM_WINDOWPOSCHANGING message is sent to a window whose size, position, or place in 
807                                          * the Z order is about to change as a result of a call to the SetWindowPos function or 
808                                          * another window-management function. 
809                                          */
810                                 case WM_SETFOCUS:
811                                         /* The WM_SETFOCUS message is sent to a window after it has gained the keyboard focus. */
812                                 case WM_ENTERSIZEMOVE:
813                                         /* The WM_ENTERSIZEMOVE message is sent one time to a window after it enters the moving 
814                                          * or sizing modal loop. The window enters the moving or sizing modal loop when the user 
815                                          * clicks the window's title bar or sizing border, or when the window passes the 
816                                          * WM_SYSCOMMAND message to the DefWindowProc function and the wParam parameter of the 
817                                          * message specifies the SC_MOVE or SC_SIZE value. The operation is complete when 
818                                          * DefWindowProc returns. 
819                                          */
820                                         break;
821                                         
822                                 ////////////////////////////////////////////////////////////////////////
823                                 // Other events
824                                 ////////////////////////////////////////////////////////////////////////
825                                 case WM_GETTEXT:
826                                         /* An application sends a WM_GETTEXT message to copy the text that 
827                                          * corresponds to a window into a buffer provided by the caller. 
828                                          */
829                                 case WM_ACTIVATEAPP:
830                                         /* The WM_ACTIVATEAPP message is sent when a window belonging to a 
831                                          * different application than the active window is about to be activated.
832                                          * The message is sent to the application whose window is being activated
833                                          * and to the application whose window is being deactivated. 
834                                          */
835                                 case WM_TIMER:
836                                         /* The WIN32 docs say:
837                                          * The WM_TIMER message is posted to the installing thread's message queue
838                                          * when a timer expires. You can process the message by providing a WM_TIMER
839                                          * case in the window procedure. Otherwise, the default window procedure will
840                                          * call the TimerProc callback function specified in the call to the SetTimer
841                                          * function used to install the timer. 
842                                          *
843                                          * In GHOST, we let DefWindowProc call the timer callback.
844                                          */
845                                         break;
846                         }
847                 }
848                 else {
849                         // Event found for a window before the pointer to the class has been set.
850                         GHOST_PRINT("GHOST_SystemWin32::wndProc: GHOST window event before creation\n")
851                         /* These are events we typically miss at this point:
852                            WM_GETMINMAXINFO     0x24
853                            WM_NCCREATE                  0x81
854                            WM_NCCALCSIZE                0x83
855                            WM_CREATE                    0x01
856                            We let DefWindowProc do the work.
857                         */
858                 }
859         }
860         else {
861                 // Events without valid hwnd
862                 GHOST_PRINT("GHOST_SystemWin32::wndProc: event without window\n")
863         }
864
865         if (event) {
866                 system->pushEvent(event);
867                 lResult = 0;
868         }
869         else {
870                 lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
871         }
872         return lResult;
873 }