3 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
20 * All rights reserved.
22 * The Original Code is: all of this file.
24 * Contributor(s): none yet.
26 * ***** END GPL LICENSE BLOCK *****
32 * Copyright (C) 2001 NaN Technologies B.V.
33 * @author Maarten Gribnau
37 #include "GHOST_SystemWin32.h"
38 #include "GHOST_EventDragnDrop.h"
40 #define WIN32_LEAN_AND_MEAN
44 #define _WIN32_IE 0x0501
48 // win64 doesn't define GWL_USERDATA
51 #define GWL_USERDATA GWLP_USERDATA
52 #define GWL_WNDPROC GWLP_WNDPROC
57 * According to the docs the mouse wheel message is supported from windows 98
58 * upwards. Leaving WINVER at default value, the WM_MOUSEWHEEL message and the
59 * wheel detent value are undefined.
62 #define WM_MOUSEWHEEL 0x020A
63 #endif // WM_MOUSEWHEEL
65 #define WHEEL_DELTA 120 /* Value for rolling one detent, (old convention! MS changed it) */
69 * Defines for mouse buttons 4 and 5 aka xbutton1 and xbutton2.
70 * MSDN: Declared in Winuser.h, include Windows.h
71 * This does not seem to work with MinGW so we define our own here.
74 #define XBUTTON1 0x0001
77 #define XBUTTON2 0x0002
80 #define WM_XBUTTONUP 524
81 #endif // WM_XBUTTONUP
82 #ifndef WM_XBUTTONDOWN
83 #define WM_XBUTTONDOWN 523
84 #endif // WM_XBUTTONDOWN
86 #include "GHOST_Debug.h"
87 #include "GHOST_DisplayManagerWin32.h"
88 #include "GHOST_EventButton.h"
89 #include "GHOST_EventCursor.h"
90 #include "GHOST_EventKey.h"
91 #include "GHOST_EventWheel.h"
92 #include "GHOST_EventNDOF.h"
93 #include "GHOST_TimerTask.h"
94 #include "GHOST_TimerManager.h"
95 #include "GHOST_WindowManager.h"
96 #include "GHOST_WindowWin32.h"
97 #include "GHOST_NDOFManager.h"
99 // Key code values not found in winuser.h
101 #define VK_MINUS 0xBD
104 #define VK_SEMICOLON 0xBA
105 #endif // VK_SEMICOLON
107 #define VK_PERIOD 0xBE
110 #define VK_COMMA 0xBC
113 #define VK_QUOTE 0xDE
115 #ifndef VK_BACK_QUOTE
116 #define VK_BACK_QUOTE 0xC0
117 #endif // VK_BACK_QUOTE
119 #define VK_SLASH 0xBF
121 #ifndef VK_BACK_SLASH
122 #define VK_BACK_SLASH 0xDC
123 #endif // VK_BACK_SLASH
125 #define VK_EQUALS 0xBB
127 #ifndef VK_OPEN_BRACKET
128 #define VK_OPEN_BRACKET 0xDB
129 #endif // VK_OPEN_BRACKET
130 #ifndef VK_CLOSE_BRACKET
131 #define VK_CLOSE_BRACKET 0xDD
132 #endif // VK_CLOSE_BRACKET
134 #define VK_GR_LESS 0xE2
138 GHOST_SystemWin32::GHOST_SystemWin32()
139 : m_hasPerformanceCounter(false), m_freq(0), m_start(0),
140 m_separateLeftRight(false),
141 m_separateLeftRightInitialized(false)
143 m_displayManager = new GHOST_DisplayManagerWin32 ();
144 GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::GHOST_SystemWin32(): m_displayManager==0\n");
145 m_displayManager->initialize();
147 // Require COM for GHOST_DropTargetWin32 created in GHOST_WindowWin32.
151 GHOST_SystemWin32::~GHOST_SystemWin32()
158 GHOST_TUns64 GHOST_SystemWin32::getMilliSeconds() const
160 // Hardware does not support high resolution timers. We will use GetTickCount instead then.
161 if (!m_hasPerformanceCounter) {
162 return ::GetTickCount();
165 // Retrieve current count
167 ::QueryPerformanceCounter((LARGE_INTEGER*)&count);
169 // Calculate the time passed since system initialization.
170 __int64 delta = 1000*(count-m_start);
172 GHOST_TUns64 t = (GHOST_TUns64)(delta/m_freq);
177 GHOST_TUns8 GHOST_SystemWin32::getNumDisplays() const
179 GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::getNumDisplays(): m_displayManager==0\n");
180 GHOST_TUns8 numDisplays;
181 m_displayManager->getNumDisplays(numDisplays);
186 void GHOST_SystemWin32::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
188 width = ::GetSystemMetrics(SM_CXSCREEN);
189 height= ::GetSystemMetrics(SM_CYSCREEN);
193 GHOST_IWindow* GHOST_SystemWin32::createWindow(
194 const STR_String& title,
195 GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, GHOST_TUns32 height,
196 GHOST_TWindowState state, GHOST_TDrawingContextType type,
197 bool stereoVisual, const GHOST_TUns16 numOfAASamples, const GHOST_TEmbedderWindowID parentWindow )
199 GHOST_Window* window = 0;
200 window = new GHOST_WindowWin32 (this, title, left, top, width, height, state, type, stereoVisual, numOfAASamples);
202 if (window->getValid()) {
203 // Store the pointer to the window
204 // if (state != GHOST_kWindowStateFullScreen) {
205 m_windowManager->addWindow(window);
209 // An invalid window could be one that was used to test for AA
210 window = ((GHOST_WindowWin32*)window)->getNextWindow();
212 // If another window is found, let the wm know about that one, but not the old one
213 if (window->getValid()) {
214 m_windowManager->addWindow(window);
227 bool GHOST_SystemWin32::processEvents(bool waitForEvent)
230 bool anyProcessed = false;
233 GHOST_TimerManager* timerMgr = getTimerManager();
235 if (waitForEvent && !::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)) {
239 GHOST_TUns64 next = timerMgr->nextFireTime();
240 GHOST_TInt64 maxSleep = next - getMilliSeconds();
242 if (next == GHOST_kFireTimeNever) {
244 } else if(maxSleep >= 0.0) {
245 ::SetTimer(NULL, 0, maxSleep, NULL);
247 ::KillTimer(NULL, 0);
252 if (timerMgr->fireTimers(getMilliSeconds())) {
256 // Process all the events waiting for us
257 while (::PeekMessage(&msg, 0, 0, 0, PM_REMOVE) != 0) {
258 ::TranslateMessage(&msg);
259 ::DispatchMessage(&msg);
262 } while (waitForEvent && !anyProcessed);
268 GHOST_TSuccess GHOST_SystemWin32::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
271 if(::GetCursorPos(&point)){
274 return GHOST_kSuccess;
276 return GHOST_kFailure;
280 GHOST_TSuccess GHOST_SystemWin32::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y)
282 if (!GetActiveWindow())
283 return GHOST_kFailure;
284 return ::SetCursorPos(x, y) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
288 GHOST_TSuccess GHOST_SystemWin32::getModifierKeys(GHOST_ModifierKeys& keys) const
290 if (m_separateLeftRight && m_separateLeftRightInitialized) {
291 bool down = HIBYTE(::GetKeyState(VK_LSHIFT)) != 0;
292 keys.set(GHOST_kModifierKeyLeftShift, down);
293 down = HIBYTE(::GetKeyState(VK_RSHIFT)) != 0;
294 keys.set(GHOST_kModifierKeyRightShift, down);
295 down = HIBYTE(::GetKeyState(VK_LMENU)) != 0;
296 keys.set(GHOST_kModifierKeyLeftAlt, down);
297 down = HIBYTE(::GetKeyState(VK_RMENU)) != 0;
298 keys.set(GHOST_kModifierKeyRightAlt, down);
299 down = HIBYTE(::GetKeyState(VK_LCONTROL)) != 0;
300 keys.set(GHOST_kModifierKeyLeftControl, down);
301 down = HIBYTE(::GetKeyState(VK_RCONTROL)) != 0;
302 keys.set(GHOST_kModifierKeyRightControl, down);
303 bool lwindown = HIBYTE(::GetKeyState(VK_LWIN)) != 0;
304 bool rwindown = HIBYTE(::GetKeyState(VK_RWIN)) != 0;
305 if(lwindown || rwindown)
306 keys.set(GHOST_kModifierKeyCommand, true);
308 keys.set(GHOST_kModifierKeyCommand, false);
311 bool down = HIBYTE(::GetKeyState(VK_SHIFT)) != 0;
312 keys.set(GHOST_kModifierKeyLeftShift, down);
313 keys.set(GHOST_kModifierKeyRightShift, down);
314 down = HIBYTE(::GetKeyState(VK_MENU)) != 0;
315 keys.set(GHOST_kModifierKeyLeftAlt, down);
316 keys.set(GHOST_kModifierKeyRightAlt, down);
317 down = HIBYTE(::GetKeyState(VK_CONTROL)) != 0;
318 keys.set(GHOST_kModifierKeyLeftControl, down);
319 keys.set(GHOST_kModifierKeyRightControl, down);
320 bool lwindown = HIBYTE(::GetKeyState(VK_LWIN)) != 0;
321 bool rwindown = HIBYTE(::GetKeyState(VK_RWIN)) != 0;
322 if(lwindown || rwindown)
323 keys.set(GHOST_kModifierKeyCommand, true);
325 keys.set(GHOST_kModifierKeyCommand, false);
327 return GHOST_kSuccess;
331 GHOST_TSuccess GHOST_SystemWin32::getButtons(GHOST_Buttons& buttons) const
333 /* Check for swapped buttons (left-handed mouse buttons)
334 * GetAsyncKeyState() will give back the state of the physical mouse buttons.
336 bool swapped = ::GetSystemMetrics(SM_SWAPBUTTON) == TRUE;
338 bool down = HIBYTE(::GetKeyState(VK_LBUTTON)) != 0;
339 buttons.set(swapped ? GHOST_kButtonMaskRight : GHOST_kButtonMaskLeft, down);
341 down = HIBYTE(::GetKeyState(VK_MBUTTON)) != 0;
342 buttons.set(GHOST_kButtonMaskMiddle, down);
344 down = HIBYTE(::GetKeyState(VK_RBUTTON)) != 0;
345 buttons.set(swapped ? GHOST_kButtonMaskLeft : GHOST_kButtonMaskRight, down);
346 return GHOST_kSuccess;
350 GHOST_TSuccess GHOST_SystemWin32::init()
352 GHOST_TSuccess success = GHOST_System::init();
354 /* Disable scaling on high DPI displays on Vista */
355 HMODULE user32 = ::LoadLibraryA("user32.dll");
356 typedef BOOL (WINAPI * LPFNSETPROCESSDPIAWARE)();
357 LPFNSETPROCESSDPIAWARE SetProcessDPIAware =
358 (LPFNSETPROCESSDPIAWARE)GetProcAddress(user32, "SetProcessDPIAware");
359 if (SetProcessDPIAware)
360 SetProcessDPIAware();
363 // Determine whether this system has a high frequency performance counter. */
364 m_hasPerformanceCounter = ::QueryPerformanceFrequency((LARGE_INTEGER*)&m_freq) == TRUE;
365 if (m_hasPerformanceCounter) {
366 GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer available\n")
367 ::QueryPerformanceCounter((LARGE_INTEGER*)&m_start);
370 GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer not available\n")
375 wc.style= CS_HREDRAW | CS_VREDRAW;
376 wc.lpfnWndProc= s_wndProc;
379 wc.hInstance= ::GetModuleHandle(0);
380 wc.hIcon = ::LoadIcon(wc.hInstance, "APPICON");
383 ::LoadIcon(NULL, IDI_APPLICATION);
385 wc.hCursor = ::LoadCursor(0, IDC_ARROW);
386 wc.hbrBackground= (HBRUSH)::GetStockObject(BLACK_BRUSH);
388 wc.lpszClassName= GHOST_WindowWin32::getWindowClassName();
390 // Use RegisterClassEx for setting small icon
391 if (::RegisterClass(&wc) == 0) {
392 success = GHOST_kFailure;
399 GHOST_TSuccess GHOST_SystemWin32::exit()
401 return GHOST_System::exit();
405 GHOST_TKey GHOST_SystemWin32::convertKey(WPARAM wParam, LPARAM lParam) const
408 bool isExtended = (lParam&(1<<24))?true:false;
410 if ((wParam >= '0') && (wParam <= '9')) {
411 // VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39)
412 key = (GHOST_TKey)(wParam - '0' + GHOST_kKey0);
414 else if ((wParam >= 'A') && (wParam <= 'Z')) {
415 // VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A)
416 key = (GHOST_TKey)(wParam - 'A' + GHOST_kKeyA);
418 else if ((wParam >= VK_F1) && (wParam <= VK_F24)) {
419 key = (GHOST_TKey)(wParam - VK_F1 + GHOST_kKeyF1);
424 key = isExtended?GHOST_kKeyNumpadEnter:GHOST_kKeyEnter;
427 case VK_BACK: key = GHOST_kKeyBackSpace; break;
428 case VK_TAB: key = GHOST_kKeyTab; break;
429 case VK_ESCAPE: key = GHOST_kKeyEsc; break;
430 case VK_SPACE: key = GHOST_kKeySpace; break;
431 case VK_PRIOR: key = GHOST_kKeyUpPage; break;
432 case VK_NEXT: key = GHOST_kKeyDownPage; break;
433 case VK_END: key = GHOST_kKeyEnd; break;
434 case VK_HOME: key = GHOST_kKeyHome; break;
435 case VK_INSERT: key = GHOST_kKeyInsert; break;
436 case VK_DELETE: key = GHOST_kKeyDelete; break;
437 case VK_LEFT: key = GHOST_kKeyLeftArrow; break;
438 case VK_RIGHT: key = GHOST_kKeyRightArrow; break;
439 case VK_UP: key = GHOST_kKeyUpArrow; break;
440 case VK_DOWN: key = GHOST_kKeyDownArrow; break;
441 case VK_NUMPAD0: key = GHOST_kKeyNumpad0; break;
442 case VK_NUMPAD1: key = GHOST_kKeyNumpad1; break;
443 case VK_NUMPAD2: key = GHOST_kKeyNumpad2; break;
444 case VK_NUMPAD3: key = GHOST_kKeyNumpad3; break;
445 case VK_NUMPAD4: key = GHOST_kKeyNumpad4; break;
446 case VK_NUMPAD5: key = GHOST_kKeyNumpad5; break;
447 case VK_NUMPAD6: key = GHOST_kKeyNumpad6; break;
448 case VK_NUMPAD7: key = GHOST_kKeyNumpad7; break;
449 case VK_NUMPAD8: key = GHOST_kKeyNumpad8; break;
450 case VK_NUMPAD9: key = GHOST_kKeyNumpad9; break;
451 case VK_SNAPSHOT: key = GHOST_kKeyPrintScreen; break;
452 case VK_PAUSE: key = GHOST_kKeyPause; break;
453 case VK_MULTIPLY: key = GHOST_kKeyNumpadAsterisk; break;
454 case VK_SUBTRACT: key = GHOST_kKeyNumpadMinus; break;
455 case VK_DECIMAL: key = GHOST_kKeyNumpadPeriod; break;
456 case VK_DIVIDE: key = GHOST_kKeyNumpadSlash; break;
457 case VK_ADD: key = GHOST_kKeyNumpadPlus; break;
459 case VK_SEMICOLON: key = GHOST_kKeySemicolon; break;
460 case VK_EQUALS: key = GHOST_kKeyEqual; break;
461 case VK_COMMA: key = GHOST_kKeyComma; break;
462 case VK_MINUS: key = GHOST_kKeyMinus; break;
463 case VK_PERIOD: key = GHOST_kKeyPeriod; break;
464 case VK_SLASH: key = GHOST_kKeySlash; break;
465 case VK_BACK_QUOTE: key = GHOST_kKeyAccentGrave; break;
466 case VK_OPEN_BRACKET: key = GHOST_kKeyLeftBracket; break;
467 case VK_BACK_SLASH: key = GHOST_kKeyBackslash; break;
468 case VK_CLOSE_BRACKET: key = GHOST_kKeyRightBracket; break;
469 case VK_QUOTE: key = GHOST_kKeyQuote; break;
470 case VK_GR_LESS: key = GHOST_kKeyGrLess; break;
472 // Process these keys separately because we need to distinguish right from left modifier keys
482 key = GHOST_kKeyUnknown;
490 void GHOST_SystemWin32::processModifierKeys(GHOST_IWindow *window)
492 GHOST_ModifierKeys oldModifiers, newModifiers;
493 // Retrieve old state of the modifier keys
494 ((GHOST_SystemWin32*)getSystem())->retrieveModifierKeys(oldModifiers);
495 // Retrieve current state of the modifier keys
496 ((GHOST_SystemWin32*)getSystem())->getModifierKeys(newModifiers);
498 // Compare the old and the new
499 if (!newModifiers.equals(oldModifiers)) {
500 // Create events for the masks that changed
501 for (int i = 0; i < GHOST_kModifierKeyNumMasks; i++) {
502 if (newModifiers.get((GHOST_TModifierKeyMask)i) != oldModifiers.get((GHOST_TModifierKeyMask)i)) {
503 // Convert the mask to a key code
504 GHOST_TKey key = GHOST_ModifierKeys::getModifierKeyCode((GHOST_TModifierKeyMask)i);
505 bool keyDown = newModifiers.get((GHOST_TModifierKeyMask)i);
506 GHOST_EventKey* event;
507 if (key != GHOST_kKeyUnknown) {
509 event = new GHOST_EventKey(getSystem()->getMilliSeconds(), keyDown ? GHOST_kEventKeyDown: GHOST_kEventKeyUp, window, key);
516 // Store new modifier keys state
517 ((GHOST_SystemWin32*)getSystem())->storeModifierKeys(newModifiers);
521 GHOST_EventButton* GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type, GHOST_IWindow *window, GHOST_TButtonMask mask)
523 return new GHOST_EventButton (getSystem()->getMilliSeconds(), type, window, mask);
527 GHOST_EventCursor* GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type, GHOST_IWindow *Iwindow)
529 GHOST_TInt32 x_screen, y_screen;
530 GHOST_SystemWin32 * system = ((GHOST_SystemWin32 * ) getSystem());
531 GHOST_WindowWin32 * window = ( GHOST_WindowWin32 * ) Iwindow;
533 system->getCursorPosition(x_screen, y_screen);
535 if(window->getCursorGrabMode() != GHOST_kGrabDisable && window->getCursorGrabMode() != GHOST_kGrabNormal)
537 GHOST_TInt32 x_new= x_screen;
538 GHOST_TInt32 y_new= y_screen;
539 GHOST_TInt32 x_accum, y_accum;
542 /* fallback to window bounds */
543 if(window->getCursorGrabBounds(bounds)==GHOST_kFailure){
544 window->getClientBounds(bounds);
547 /* could also clamp to screen bounds
548 * wrap with a window outside the view will fail atm */
550 bounds.wrapPoint(x_new, y_new, 2); /* offset of one incase blender is at screen bounds */
552 window->getCursorGrabAccum(x_accum, y_accum);
553 if(x_new != x_screen|| y_new != y_screen) {
554 /* when wrapping we don't need to add an event because the
555 * setCursorPosition call will cause a new event after */
556 system->setCursorPosition(x_new, y_new); /* wrap */
557 window->setCursorGrabAccum(x_accum + (x_screen - x_new), y_accum + (y_screen - y_new));
559 return new GHOST_EventCursor(system->getMilliSeconds(),
560 GHOST_kEventCursorMove,
569 return new GHOST_EventCursor(system->getMilliSeconds(),
570 GHOST_kEventCursorMove,
580 GHOST_EventWheel* GHOST_SystemWin32::processWheelEvent(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam)
582 // short fwKeys = LOWORD(wParam); // key flags
583 int zDelta = (short) HIWORD(wParam); // wheel rotation
585 // zDelta /= WHEEL_DELTA;
586 // temporary fix below: microsoft now has added more precision, making the above division not work
587 if (zDelta <= 0 ) zDelta= -1; else zDelta= 1;
589 // short xPos = (short) LOWORD(lParam); // horizontal position of pointer
590 // short yPos = (short) HIWORD(lParam); // vertical position of pointer
591 return new GHOST_EventWheel (getSystem()->getMilliSeconds(), window, zDelta);
595 GHOST_EventKey* GHOST_SystemWin32::processKeyEvent(GHOST_IWindow *window, bool keyDown, WPARAM wParam, LPARAM lParam)
597 GHOST_TKey key = ((GHOST_SystemWin32*)getSystem())->convertKey(wParam, lParam);
598 GHOST_EventKey* event;
599 if (key != GHOST_kKeyUnknown) {
603 /* Eat any character related messages */
604 if (::PeekMessage(&keyMsg, NULL, WM_CHAR, WM_SYSDEADCHAR, PM_REMOVE)) {
605 ascii = (char) keyMsg.wParam;
608 event = new GHOST_EventKey(getSystem()->getMilliSeconds(), keyDown ? GHOST_kEventKeyDown: GHOST_kEventKeyUp, window, key, ascii);
617 GHOST_Event* GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type, GHOST_IWindow* window)
619 return new GHOST_Event(getSystem()->getMilliSeconds(), type, window);
622 GHOST_TSuccess GHOST_SystemWin32::pushDragDropEvent(GHOST_TEventType eventType,
623 GHOST_TDragnDropTypes draggedObjectType,
624 GHOST_IWindow* window,
625 int mouseX, int mouseY,
628 GHOST_SystemWin32* system = ((GHOST_SystemWin32*)getSystem());
629 return system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(),
632 window,mouseX,mouseY,data)
636 void GHOST_SystemWin32::processMinMaxInfo(MINMAXINFO * minmax)
638 minmax->ptMinTrackSize.x=320;
639 minmax->ptMinTrackSize.y=240;
643 LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
645 GHOST_Event* event = 0;
647 GHOST_SystemWin32* system = ((GHOST_SystemWin32*)getSystem());
648 GHOST_ASSERT(system, "GHOST_SystemWin32::s_wndProc(): system not initialized")
651 GHOST_WindowWin32* window = (GHOST_WindowWin32*)::GetWindowLong(hwnd, GWL_USERDATA);
654 ////////////////////////////////////////////////////////////////////////
655 // Keyboard events, processed
656 ////////////////////////////////////////////////////////////////////////
658 /* The WM_KEYDOWN message is posted to the window with the keyboard focus when a
659 * nonsystem key is pressed. A nonsystem key is a key that is pressed when the alt
660 * key is not pressed.
663 /* The WM_SYSKEYDOWN message is posted to the window with the keyboard focus when
664 * the user presses the F10 key (which activates the menu bar) or holds down the
665 * alt key and then presses another key. It also occurs when no window currently
666 * has the keyboard focus; in this case, the WM_SYSKEYDOWN message is sent to the
667 * active window. The window that receives the message can distinguish between these
668 * two contexts by checking the context code in the lKeyData parameter.
676 if (!system->m_separateLeftRightInitialized) {
677 // Check whether this system supports separate left and right keys
680 system->m_separateLeftRight =
681 (HIBYTE(::GetKeyState(VK_LSHIFT)) != 0) ||
682 (HIBYTE(::GetKeyState(VK_RSHIFT)) != 0) ?
686 system->m_separateLeftRight =
687 (HIBYTE(::GetKeyState(VK_LCONTROL)) != 0) ||
688 (HIBYTE(::GetKeyState(VK_RCONTROL)) != 0) ?
692 system->m_separateLeftRight =
693 (HIBYTE(::GetKeyState(VK_LMENU)) != 0) ||
694 (HIBYTE(::GetKeyState(VK_RMENU)) != 0) ?
699 system->m_separateLeftRight = true;
702 system->m_separateLeftRightInitialized = true;
704 system->processModifierKeys(window);
705 // Bypass call to DefWindowProc
708 event = processKeyEvent(window, true, wParam, lParam);
710 GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ")
712 GHOST_PRINT(" key ignored\n")
726 system->processModifierKeys(window);
727 // Bypass call to DefWindowProc
730 event = processKeyEvent(window, false, wParam, lParam);
732 GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ")
734 GHOST_PRINT(" key ignored\n")
740 ////////////////////////////////////////////////////////////////////////
741 // Keyboard events, ignored
742 ////////////////////////////////////////////////////////////////////////
744 /* The WM_CHAR message is posted to the window with the keyboard focus when
745 * a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR
746 * contains the character code of the key that was pressed.
749 /* The WM_DEADCHAR message is posted to the window with the keyboard focus when a
750 * WM_KEYUP message is translated by the TranslateMessage function. WM_DEADCHAR
751 * specifies a character code generated by a dead key. A dead key is a key that
752 * generates a character, such as the umlaut (double-dot), that is combined with
753 * another character to form a composite character. For example, the umlaut-O
754 * character (Ù) is generated by typing the dead key for the umlaut character, and
755 * then typing the O key.
758 /* The WM_SYSDEADCHAR message is sent to the window with the keyboard focus when
759 * a WM_SYSKEYDOWN message is translated by the TranslateMessage function.
760 * WM_SYSDEADCHAR specifies the character code of a system dead key - that is,
761 * a dead key that is pressed while holding down the alt key.
764 ////////////////////////////////////////////////////////////////////////
765 // Tablet events, processed
766 ////////////////////////////////////////////////////////////////////////
768 ((GHOST_WindowWin32*)window)->processWin32TabletEvent(wParam, lParam);
772 ((GHOST_WindowWin32*)window)->processWin32TabletInitEvent();
774 ////////////////////////////////////////////////////////////////////////
775 // Mouse events, processed
776 ////////////////////////////////////////////////////////////////////////
778 window->registerMouseClickEvent(true);
779 event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft);
782 window->registerMouseClickEvent(true);
783 event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskMiddle);
786 window->registerMouseClickEvent(true);
787 event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight);
790 window->registerMouseClickEvent(true);
791 if ((short) HIWORD(wParam) == XBUTTON1){
792 event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton4);
793 }else if((short) HIWORD(wParam) == XBUTTON2){
794 event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton5);
798 window->registerMouseClickEvent(false);
799 event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft);
802 window->registerMouseClickEvent(false);
803 event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle);
806 window->registerMouseClickEvent(false);
807 event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight);
810 window->registerMouseClickEvent(false);
811 if ((short) HIWORD(wParam) == XBUTTON1){
812 event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton4);
813 }else if((short) HIWORD(wParam) == XBUTTON2){
814 event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton5);
818 event = processCursorEvent(GHOST_kEventCursorMove, window);
821 /* The WM_MOUSEWHEEL message is sent to the focus window
822 * when the mouse wheel is rotated. The DefWindowProc
823 * function propagates the message to the window's parent.
824 * There should be no internal forwarding of the message,
825 * since DefWindowProc propagates it up the parent chain
826 * until it finds a window that processes it.
828 event = processWheelEvent(window, wParam, lParam);
831 /* The WM_SETCURSOR message is sent to a window if the mouse causes the cursor
832 * to move within a window and mouse input is not captured.
833 * This means we have to set the cursor shape every time the mouse moves!
834 * The DefWindowProc function uses this message to set the cursor to an
835 * arrow if it is not in the client area.
837 if (LOWORD(lParam) == HTCLIENT) {
838 // Load the current cursor
839 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
840 // Bypass call to DefWindowProc
844 // Outside of client area show standard cursor
845 window->loadCursor(true, GHOST_kStandardCursorDefault);
849 ////////////////////////////////////////////////////////////////////////
850 // Mouse events, ignored
851 ////////////////////////////////////////////////////////////////////////
853 /* The WM_NCMOUSEMOVE message is posted to a window when the cursor is moved
854 * within the nonclient area of the window. This message is posted to the window
855 * that contains the cursor. If a window has captured the mouse, this message is not posted.
858 /* The WM_NCHITTEST message is sent to a window when the cursor moves, or
859 * when a mouse button is pressed or released. If the mouse is not captured,
860 * the message is sent to the window beneath the cursor. Otherwise, the message
861 * is sent to the window that has captured the mouse.
865 ////////////////////////////////////////////////////////////////////////
866 // Window events, processed
867 ////////////////////////////////////////////////////////////////////////
869 /* The WM_CLOSE message is sent as a signal that a window or an application should terminate. */
870 event = processWindowEvent(GHOST_kEventWindowClose, window);
873 /* The WM_ACTIVATE message is sent to both the window being activated and the window being
874 * deactivated. If the windows use the same input queue, the message is sent synchronously,
875 * first to the window procedure of the top-level window being deactivated, then to the window
876 * procedure of the top-level window being activated. If the windows use different input queues,
877 * the message is sent asynchronously, so the window is activated immediately.
879 event = processWindowEvent(LOWORD(wParam) ? GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate, window);
880 /* WARNING: Let DefWindowProc handle WM_ACTIVATE, otherwise WM_MOUSEWHEEL
881 will not be dispatched to OUR active window if we minimize one of OUR windows. */
882 lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
885 /* An application sends the WM_PAINT message when the system or another application
886 * makes a request to paint a portion of an application's window. The message is sent
887 * when the UpdateWindow or RedrawWindow function is called, or by the DispatchMessage
888 * function when the application obtains a WM_PAINT message by using the GetMessage or
889 * PeekMessage function.
891 event = processWindowEvent(GHOST_kEventWindowUpdate, window);
892 ::ValidateRect(hwnd, NULL);
894 case WM_GETMINMAXINFO:
895 /* The WM_GETMINMAXINFO message is sent to a window when the size or
896 * position of the window is about to change. An application can use
897 * this message to override the window's default maximized size and
898 * position, or its default minimum or maximum tracking size.
900 processMinMaxInfo((MINMAXINFO *) lParam);
901 /* Let DefWindowProc handle it. */
904 /* The WM_SIZE message is sent to a window after its size has changed.
905 * The WM_SIZE and WM_MOVE messages are not sent if an application handles the
906 * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
907 * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
908 * message without calling DefWindowProc.
910 event = processWindowEvent(GHOST_kEventWindowSize, window);
912 case WM_CAPTURECHANGED:
913 window->lostMouseCapture();
916 /* The WM_MOVING message is sent to a window that the user is moving. By processing
917 * this message, an application can monitor the size and position of the drag rectangle
918 * and, if needed, change its size or position.
921 /* The WM_SIZE and WM_MOVE messages are not sent if an application handles the
922 * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
923 * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
924 * message without calling DefWindowProc.
926 event = processWindowEvent(GHOST_kEventWindowMove, window);
928 ////////////////////////////////////////////////////////////////////////
929 // Window events, ignored
930 ////////////////////////////////////////////////////////////////////////
931 case WM_WINDOWPOSCHANGED:
932 /* The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place
933 * in the Z order has changed as a result of a call to the SetWindowPos function or
934 * another window-management function.
935 * The WM_SIZE and WM_MOVE messages are not sent if an application handles the
936 * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
937 * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
938 * message without calling DefWindowProc.
941 /* An application sends the WM_ERASEBKGND message when the window background must be
942 * erased (for example, when a window is resized). The message is sent to prepare an
943 * invalidated portion of a window for painting.
946 /* An application sends the WM_NCPAINT message to a window when its frame must be painted. */
948 /* The WM_NCACTIVATE message is sent to a window when its nonclient area needs to be changed
949 * to indicate an active or inactive state.
952 /* The WM_DESTROY message is sent when a window is being destroyed. It is sent to the window
953 * procedure of the window being destroyed after the window is removed from the screen.
954 * This message is sent first to the window being destroyed and then to the child windows
955 * (if any) as they are destroyed. During the processing of the message, it can be assumed
956 * that all child windows still exist.
959 /* The WM_NCDESTROY message informs a window that its nonclient area is being destroyed. The
960 * DestroyWindow function sends the WM_NCDESTROY message to the window following the WM_DESTROY
961 * message. WM_DESTROY is used to free the allocated memory object associated with the window.
964 /* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard focus. */
966 /* The WM_SHOWWINDOW message is sent to a window when the window is about to be hidden or shown. */
967 case WM_WINDOWPOSCHANGING:
968 /* The WM_WINDOWPOSCHANGING message is sent to a window whose size, position, or place in
969 * the Z order is about to change as a result of a call to the SetWindowPos function or
970 * another window-management function.
973 /* The WM_SETFOCUS message is sent to a window after it has gained the keyboard focus. */
974 case WM_ENTERSIZEMOVE:
975 /* The WM_ENTERSIZEMOVE message is sent one time to a window after it enters the moving
976 * or sizing modal loop. The window enters the moving or sizing modal loop when the user
977 * clicks the window's title bar or sizing border, or when the window passes the
978 * WM_SYSCOMMAND message to the DefWindowProc function and the wParam parameter of the
979 * message specifies the SC_MOVE or SC_SIZE value. The operation is complete when
980 * DefWindowProc returns.
984 ////////////////////////////////////////////////////////////////////////
986 ////////////////////////////////////////////////////////////////////////
988 /* An application sends a WM_GETTEXT message to copy the text that
989 * corresponds to a window into a buffer provided by the caller.
992 /* The WM_ACTIVATEAPP message is sent when a window belonging to a
993 * different application than the active window is about to be activated.
994 * The message is sent to the application whose window is being activated
995 * and to the application whose window is being deactivated.
998 /* The WIN32 docs say:
999 * The WM_TIMER message is posted to the installing thread's message queue
1000 * when a timer expires. You can process the message by providing a WM_TIMER
1001 * case in the window procedure. Otherwise, the default window procedure will
1002 * call the TimerProc callback function specified in the call to the SetTimer
1003 * function used to install the timer.
1005 * In GHOST, we let DefWindowProc call the timer callback.
1008 case WM_BLND_NDOF_AXIS:
1010 GHOST_TEventNDOFData ndofdata;
1011 system->m_ndofManager->GHOST_NDOFGetDatas(ndofdata);
1012 system->m_eventManager->
1013 pushEvent(new GHOST_EventNDOF(
1014 system->getMilliSeconds(),
1015 GHOST_kEventNDOFMotion,
1019 case WM_BLND_NDOF_BTN:
1021 GHOST_TEventNDOFData ndofdata;
1022 system->m_ndofManager->GHOST_NDOFGetDatas(ndofdata);
1023 system->m_eventManager->
1024 pushEvent(new GHOST_EventNDOF(
1025 system->getMilliSeconds(),
1026 GHOST_kEventNDOFButton,
1033 // Event found for a window before the pointer to the class has been set.
1034 GHOST_PRINT("GHOST_SystemWin32::wndProc: GHOST window event before creation\n")
1035 /* These are events we typically miss at this point:
1036 WM_GETMINMAXINFO 0x24
1040 We let DefWindowProc do the work.
1045 // Events without valid hwnd
1046 GHOST_PRINT("GHOST_SystemWin32::wndProc: event without window\n")
1050 system->pushEvent(event);
1053 lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
1058 GHOST_TUns8* GHOST_SystemWin32::getClipboard(bool selection) const
1063 if ( IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(NULL) ) {
1064 HANDLE hData = GetClipboardData( CF_TEXT );
1065 if (hData == NULL) {
1069 buffer = (char*)GlobalLock( hData );
1071 temp_buff = (char*) malloc(strlen(buffer)+1);
1072 strcpy(temp_buff, buffer);
1074 GlobalUnlock( hData );
1077 temp_buff[strlen(buffer)] = '\0';
1079 return (GHOST_TUns8*)temp_buff;
1088 void GHOST_SystemWin32::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1090 if(selection) {return;} // for copying the selection, used on X11
1092 if (OpenClipboard(NULL)) {
1099 clipbuffer = LocalAlloc(LMEM_FIXED,((strlen(buffer)+1)));
1100 data = (char*)GlobalLock(clipbuffer);
1102 strcpy(data, (char*)buffer);
1103 data[strlen(buffer)] = '\0';
1104 LocalUnlock(clipbuffer);
1105 SetClipboardData(CF_TEXT,clipbuffer);
1113 const GHOST_TUns8* GHOST_SystemWin32::getSystemDir() const
1115 static char knownpath[MAX_PATH];
1116 HRESULT hResult = SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, knownpath);
1118 if (hResult == S_OK)
1120 return (GHOST_TUns8*)knownpath;
1126 const GHOST_TUns8* GHOST_SystemWin32::getUserDir() const
1128 static char knownpath[MAX_PATH];
1129 HRESULT hResult = SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, knownpath);
1131 if (hResult == S_OK)
1133 return (GHOST_TUns8*)knownpath;
1139 const GHOST_TUns8* GHOST_SystemWin32::getBinaryDir() const
1141 static char fullname[MAX_PATH];
1142 if(GetModuleFileName(0, fullname, MAX_PATH)) {
1143 return (GHOST_TUns8*)fullname;