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
39 #include "GHOST_SystemWin32.h"
40 #include "GHOST_EventDragnDrop.h"
42 #define WIN32_LEAN_AND_MEAN
46 #define _WIN32_IE 0x0501
50 // win64 doesn't define GWL_USERDATA
53 #define GWL_USERDATA GWLP_USERDATA
54 #define GWL_WNDPROC GWLP_WNDPROC
59 * According to the docs the mouse wheel message is supported from windows 98
60 * upwards. Leaving WINVER at default value, the WM_MOUSEWHEEL message and the
61 * wheel detent value are undefined.
64 #define WM_MOUSEWHEEL 0x020A
65 #endif // WM_MOUSEWHEEL
67 #define WHEEL_DELTA 120 /* Value for rolling one detent, (old convention! MS changed it) */
71 * Defines for mouse buttons 4 and 5 aka xbutton1 and xbutton2.
72 * MSDN: Declared in Winuser.h, include Windows.h
73 * This does not seem to work with MinGW so we define our own here.
76 #define XBUTTON1 0x0001
79 #define XBUTTON2 0x0002
82 #define WM_XBUTTONUP 524
83 #endif // WM_XBUTTONUP
84 #ifndef WM_XBUTTONDOWN
85 #define WM_XBUTTONDOWN 523
86 #endif // WM_XBUTTONDOWN
88 #include "GHOST_Debug.h"
89 #include "GHOST_DisplayManagerWin32.h"
90 #include "GHOST_EventButton.h"
91 #include "GHOST_EventCursor.h"
92 #include "GHOST_EventKey.h"
93 #include "GHOST_EventWheel.h"
94 #include "GHOST_EventNDOF.h"
95 #include "GHOST_TimerTask.h"
96 #include "GHOST_TimerManager.h"
97 #include "GHOST_WindowManager.h"
98 #include "GHOST_WindowWin32.h"
99 #include "GHOST_NDOFManager.h"
101 // Key code values not found in winuser.h
103 #define VK_MINUS 0xBD
106 #define VK_SEMICOLON 0xBA
107 #endif // VK_SEMICOLON
109 #define VK_PERIOD 0xBE
112 #define VK_COMMA 0xBC
115 #define VK_QUOTE 0xDE
117 #ifndef VK_BACK_QUOTE
118 #define VK_BACK_QUOTE 0xC0
119 #endif // VK_BACK_QUOTE
121 #define VK_SLASH 0xBF
123 #ifndef VK_BACK_SLASH
124 #define VK_BACK_SLASH 0xDC
125 #endif // VK_BACK_SLASH
127 #define VK_EQUALS 0xBB
129 #ifndef VK_OPEN_BRACKET
130 #define VK_OPEN_BRACKET 0xDB
131 #endif // VK_OPEN_BRACKET
132 #ifndef VK_CLOSE_BRACKET
133 #define VK_CLOSE_BRACKET 0xDD
134 #endif // VK_CLOSE_BRACKET
136 #define VK_GR_LESS 0xE2
140 GHOST_SystemWin32::GHOST_SystemWin32()
141 : m_hasPerformanceCounter(false), m_freq(0), m_start(0)
143 m_displayManager = new GHOST_DisplayManagerWin32 ();
144 GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::GHOST_SystemWin32(): m_displayManager==0\n");
145 m_displayManager->initialize();
147 // Check if current keyboard layout uses AltGr
148 this->keyboardAltGr();
149 // Require COM for GHOST_DropTargetWin32 created in GHOST_WindowWin32.
153 GHOST_SystemWin32::~GHOST_SystemWin32()
160 GHOST_TUns64 GHOST_SystemWin32::getMilliSeconds() const
162 // Hardware does not support high resolution timers. We will use GetTickCount instead then.
163 if (!m_hasPerformanceCounter) {
164 return ::GetTickCount();
167 // Retrieve current count
169 ::QueryPerformanceCounter((LARGE_INTEGER*)&count);
171 // Calculate the time passed since system initialization.
172 __int64 delta = 1000*(count-m_start);
174 GHOST_TUns64 t = (GHOST_TUns64)(delta/m_freq);
179 GHOST_TUns8 GHOST_SystemWin32::getNumDisplays() const
181 GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::getNumDisplays(): m_displayManager==0\n");
182 GHOST_TUns8 numDisplays;
183 m_displayManager->getNumDisplays(numDisplays);
188 void GHOST_SystemWin32::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
190 width = ::GetSystemMetrics(SM_CXSCREEN);
191 height= ::GetSystemMetrics(SM_CYSCREEN);
195 GHOST_IWindow* GHOST_SystemWin32::createWindow(
196 const STR_String& title,
197 GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, GHOST_TUns32 height,
198 GHOST_TWindowState state, GHOST_TDrawingContextType type,
199 bool stereoVisual, const GHOST_TUns16 numOfAASamples, const GHOST_TEmbedderWindowID parentWindow )
201 GHOST_Window* window = 0;
202 window = new GHOST_WindowWin32 (this, title, left, top, width, height, state, type, stereoVisual, numOfAASamples);
204 if (window->getValid()) {
205 // Store the pointer to the window
206 // if (state != GHOST_kWindowStateFullScreen) {
207 m_windowManager->addWindow(window);
211 // An invalid window could be one that was used to test for AA
212 window = ((GHOST_WindowWin32*)window)->getNextWindow();
214 // If another window is found, let the wm know about that one, but not the old one
215 if (window->getValid()) {
216 m_windowManager->addWindow(window);
229 bool GHOST_SystemWin32::processEvents(bool waitForEvent)
232 bool anyProcessed = false;
235 GHOST_TimerManager* timerMgr = getTimerManager();
237 if (waitForEvent && !::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)) {
241 GHOST_TUns64 next = timerMgr->nextFireTime();
242 GHOST_TInt64 maxSleep = next - getMilliSeconds();
244 if (next == GHOST_kFireTimeNever) {
246 } else if(maxSleep >= 0.0) {
247 ::SetTimer(NULL, 0, maxSleep, NULL);
249 ::KillTimer(NULL, 0);
254 if (timerMgr->fireTimers(getMilliSeconds())) {
258 // Process all the events waiting for us
259 while (::PeekMessage(&msg, 0, 0, 0, PM_REMOVE) != 0) {
260 ::TranslateMessage(&msg);
261 ::DispatchMessage(&msg);
264 } while (waitForEvent && !anyProcessed);
270 GHOST_TSuccess GHOST_SystemWin32::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
273 if(::GetCursorPos(&point)){
276 return GHOST_kSuccess;
278 return GHOST_kFailure;
282 GHOST_TSuccess GHOST_SystemWin32::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y)
284 if (!GetActiveWindow())
285 return GHOST_kFailure;
286 return ::SetCursorPos(x, y) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
290 GHOST_TSuccess GHOST_SystemWin32::getModifierKeys(GHOST_ModifierKeys& keys) const
292 bool down = HIBYTE(::GetKeyState(VK_LSHIFT)) != 0;
293 keys.set(GHOST_kModifierKeyLeftShift, down);
294 down = HIBYTE(::GetKeyState(VK_RSHIFT)) != 0;
295 keys.set(GHOST_kModifierKeyRightShift, down);
297 down = HIBYTE(::GetKeyState(VK_LMENU)) != 0;
298 keys.set(GHOST_kModifierKeyLeftAlt, down);
299 down = HIBYTE(::GetKeyState(VK_RMENU)) != 0;
300 keys.set(GHOST_kModifierKeyRightAlt, down);
302 down = HIBYTE(::GetKeyState(VK_LCONTROL)) != 0;
303 keys.set(GHOST_kModifierKeyLeftControl, down);
304 down = HIBYTE(::GetKeyState(VK_RCONTROL)) != 0;
305 keys.set(GHOST_kModifierKeyRightControl, down);
307 bool lwindown = HIBYTE(::GetKeyState(VK_LWIN)) != 0;
308 bool rwindown = HIBYTE(::GetKeyState(VK_RWIN)) != 0;
309 if(lwindown || rwindown)
310 keys.set(GHOST_kModifierKeyOS, true);
312 keys.set(GHOST_kModifierKeyOS, false);
313 return GHOST_kSuccess;
317 GHOST_TSuccess GHOST_SystemWin32::getButtons(GHOST_Buttons& buttons) const
319 /* Check for swapped buttons (left-handed mouse buttons)
320 * GetAsyncKeyState() will give back the state of the physical mouse buttons.
322 bool swapped = ::GetSystemMetrics(SM_SWAPBUTTON) == TRUE;
324 bool down = HIBYTE(::GetKeyState(VK_LBUTTON)) != 0;
325 buttons.set(swapped ? GHOST_kButtonMaskRight : GHOST_kButtonMaskLeft, down);
327 down = HIBYTE(::GetKeyState(VK_MBUTTON)) != 0;
328 buttons.set(GHOST_kButtonMaskMiddle, down);
330 down = HIBYTE(::GetKeyState(VK_RBUTTON)) != 0;
331 buttons.set(swapped ? GHOST_kButtonMaskLeft : GHOST_kButtonMaskRight, down);
332 return GHOST_kSuccess;
336 GHOST_TSuccess GHOST_SystemWin32::init()
338 GHOST_TSuccess success = GHOST_System::init();
340 for(int i = 0; i < 255; i++) {
341 m_prevKeyStatus[i] = false;
342 m_curKeyStatus[i] = false;
345 /* Disable scaling on high DPI displays on Vista */
346 HMODULE user32 = ::LoadLibraryA("user32.dll");
347 typedef BOOL (WINAPI * LPFNSETPROCESSDPIAWARE)();
348 LPFNSETPROCESSDPIAWARE SetProcessDPIAware =
349 (LPFNSETPROCESSDPIAWARE)GetProcAddress(user32, "SetProcessDPIAware");
350 if (SetProcessDPIAware)
351 SetProcessDPIAware();
354 // Determine whether this system has a high frequency performance counter. */
355 m_hasPerformanceCounter = ::QueryPerformanceFrequency((LARGE_INTEGER*)&m_freq) == TRUE;
356 if (m_hasPerformanceCounter) {
357 GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer available\n")
358 ::QueryPerformanceCounter((LARGE_INTEGER*)&m_start);
361 GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer not available\n")
366 wc.style= CS_HREDRAW | CS_VREDRAW;
367 wc.lpfnWndProc= s_wndProc;
370 wc.hInstance= ::GetModuleHandle(0);
371 wc.hIcon = ::LoadIcon(wc.hInstance, "APPICON");
374 ::LoadIcon(NULL, IDI_APPLICATION);
376 wc.hCursor = ::LoadCursor(0, IDC_ARROW);
377 wc.hbrBackground= (HBRUSH)::GetStockObject(BLACK_BRUSH);
379 wc.lpszClassName= GHOST_WindowWin32::getWindowClassName();
381 // Use RegisterClassEx for setting small icon
382 if (::RegisterClass(&wc) == 0) {
383 success = GHOST_kFailure;
386 // Add low-level keyboard hook for our process.
387 m_llKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, s_llKeyboardProc, wc.hInstance, 0);
394 GHOST_TSuccess GHOST_SystemWin32::exit()
396 // remove our low-level keyboard hook.
397 UnhookWindowsHookEx(m_llKeyboardHook);
399 return GHOST_System::exit();
402 void GHOST_SystemWin32::triggerKey(GHOST_IWindow *window, bool down, GHOST_TKey key)
404 GHOST_Event *extra = new GHOST_EventKey(getSystem()->getMilliSeconds(), down ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, key, '\0');
405 ((GHOST_SystemWin32*)getSystem())->pushEvent(extra);
407 void GHOST_SystemWin32::handleModifierKeys(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam, GHOST_ModifierKeys &oldModifiers, GHOST_ModifierKeys &newModifiers) const
412 bool lchanged = oldModifiers.get(GHOST_kModifierKeyLeftAlt) != newModifiers.get(GHOST_kModifierKeyLeftAlt);
414 ((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyLeftAlt), GHOST_kKeyLeftAlt);
416 bool rchanged = oldModifiers.get(GHOST_kModifierKeyRightAlt) != newModifiers.get(GHOST_kModifierKeyRightAlt);
418 ((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyRightAlt), GHOST_kKeyRightAlt);
421 lchanged = oldModifiers.get(GHOST_kModifierKeyLeftControl) != newModifiers.get(GHOST_kModifierKeyLeftControl);
423 ((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyLeftControl), GHOST_kKeyLeftControl);
425 bool rchanged = oldModifiers.get(GHOST_kModifierKeyRightControl) != newModifiers.get(GHOST_kModifierKeyRightControl);
427 ((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyRightControl), GHOST_kKeyRightControl);
434 bool lchanged = oldModifiers.get(GHOST_kModifierKeyLeftAlt) != newModifiers.get(GHOST_kModifierKeyLeftAlt);
436 ((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyLeftAlt), GHOST_kKeyLeftAlt);
438 bool rchanged = oldModifiers.get(GHOST_kModifierKeyRightAlt) != newModifiers.get(GHOST_kModifierKeyRightAlt);
440 ((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyRightAlt), GHOST_kKeyRightAlt);
443 lchanged = oldModifiers.get(GHOST_kModifierKeyLeftShift) != newModifiers.get(GHOST_kModifierKeyLeftShift);
445 ((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyLeftShift), GHOST_kKeyLeftShift);
447 bool rchanged = oldModifiers.get(GHOST_kModifierKeyRightShift) != newModifiers.get(GHOST_kModifierKeyRightShift);
449 ((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyRightShift), GHOST_kKeyRightShift);
456 bool lchanged = oldModifiers.get(GHOST_kModifierKeyLeftShift) != newModifiers.get(GHOST_kModifierKeyLeftShift);
458 ((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyLeftShift), GHOST_kKeyLeftShift);
460 bool rchanged = oldModifiers.get(GHOST_kModifierKeyRightShift) != newModifiers.get(GHOST_kModifierKeyRightShift);
462 ((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyRightShift), GHOST_kKeyRightShift);
465 lchanged = oldModifiers.get(GHOST_kModifierKeyLeftControl) != newModifiers.get(GHOST_kModifierKeyLeftControl);
467 ((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyLeftControl), GHOST_kKeyLeftControl);
469 bool rchanged = oldModifiers.get(GHOST_kModifierKeyRightControl) != newModifiers.get(GHOST_kModifierKeyRightControl);
471 ((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyRightControl), GHOST_kKeyRightControl);
481 GHOST_TKey GHOST_SystemWin32::convertKey(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam) const
483 GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
484 bool isExtended = (lParam&(1<<24))?true:false;
487 GHOST_ModifierKeys oldModifiers, newModifiers;
488 system->retrieveModifierKeys(oldModifiers);
489 system->getModifierKeys(newModifiers);
491 // check if modifier keys different from this event have changed and trigger those
492 // This can happen when some action takes a long time (Blender not responding), resulting
493 // in dropped events.
494 system->handleModifierKeys(window, wParam, lParam, oldModifiers, newModifiers);
496 //std::cout << wParam << " " << system->m_curKeyStatus[wParam] << " shift pressed: " << system->shiftPressed() << std::endl;
498 if ((wParam >= '0') && (wParam <= '9')) {
499 // VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39)
500 key = (GHOST_TKey)(wParam - '0' + GHOST_kKey0);
502 else if ((wParam >= 'A') && (wParam <= 'Z')) {
503 // VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A)
504 key = (GHOST_TKey)(wParam - 'A' + GHOST_kKeyA);
506 else if ((wParam >= VK_F1) && (wParam <= VK_F24)) {
507 key = (GHOST_TKey)(wParam - VK_F1 + GHOST_kKeyF1);
512 key = isExtended?GHOST_kKeyNumpadEnter:GHOST_kKeyEnter;
515 case VK_BACK: key = GHOST_kKeyBackSpace; break;
516 case VK_TAB: key = GHOST_kKeyTab; break;
517 case VK_ESCAPE: key = GHOST_kKeyEsc; break;
518 case VK_SPACE: key = GHOST_kKeySpace; break;
519 case VK_PRIOR: key = GHOST_kKeyUpPage; break;
520 case VK_NEXT: key = GHOST_kKeyDownPage; break;
521 case VK_END: key = GHOST_kKeyEnd; break;
524 if(system->m_curKeyStatus[VK_NUMPAD7] && system->shiftPressed())
525 key = GHOST_kKeyNumpad7;
527 key = GHOST_kKeyHome;
530 case VK_INSERT: key = GHOST_kKeyInsert; break;
531 case VK_DELETE: key = GHOST_kKeyDelete; break;
532 case VK_LEFT: key = GHOST_kKeyLeftArrow; break;
533 case VK_RIGHT: key = GHOST_kKeyRightArrow; break;
534 case VK_UP: key = GHOST_kKeyUpArrow; break;
535 case VK_DOWN: key = GHOST_kKeyDownArrow; break;
536 case VK_NUMPAD0: key = GHOST_kKeyNumpad0; break;
537 case VK_NUMPAD1: key = GHOST_kKeyNumpad1; break;
538 case VK_NUMPAD2: key = GHOST_kKeyNumpad2; break;
539 case VK_NUMPAD3: key = GHOST_kKeyNumpad3; break;
540 case VK_NUMPAD4: key = GHOST_kKeyNumpad4; break;
541 case VK_NUMPAD5: key = GHOST_kKeyNumpad5; break;
542 case VK_NUMPAD6: key = GHOST_kKeyNumpad6; break;
543 case VK_NUMPAD7: key = GHOST_kKeyNumpad7; break;
544 case VK_NUMPAD8: key = GHOST_kKeyNumpad8; break;
545 case VK_NUMPAD9: key = GHOST_kKeyNumpad9; break;
546 case VK_SNAPSHOT: key = GHOST_kKeyPrintScreen; break;
547 case VK_PAUSE: key = GHOST_kKeyPause; break;
548 case VK_MULTIPLY: key = GHOST_kKeyNumpadAsterisk; break;
549 case VK_SUBTRACT: key = GHOST_kKeyNumpadMinus; break;
550 case VK_DECIMAL: key = GHOST_kKeyNumpadPeriod; break;
551 case VK_DIVIDE: key = GHOST_kKeyNumpadSlash; break;
552 case VK_ADD: key = GHOST_kKeyNumpadPlus; break;
554 case VK_SEMICOLON: key = GHOST_kKeySemicolon; break;
555 case VK_EQUALS: key = GHOST_kKeyEqual; break;
556 case VK_COMMA: key = GHOST_kKeyComma; break;
557 case VK_MINUS: key = GHOST_kKeyMinus; break;
558 case VK_PERIOD: key = GHOST_kKeyPeriod; break;
559 case VK_SLASH: key = GHOST_kKeySlash; break;
560 case VK_BACK_QUOTE: key = GHOST_kKeyAccentGrave; break;
561 case VK_OPEN_BRACKET: key = GHOST_kKeyLeftBracket; break;
562 case VK_BACK_SLASH: key = GHOST_kKeyBackslash; break;
563 case VK_CLOSE_BRACKET: key = GHOST_kKeyRightBracket; break;
564 case VK_QUOTE: key = GHOST_kKeyQuote; break;
565 case VK_GR_LESS: key = GHOST_kKeyGrLess; break;
569 bool lchanged = oldModifiers.get(GHOST_kModifierKeyLeftShift) != newModifiers.get(GHOST_kModifierKeyLeftShift);
571 key = GHOST_kKeyLeftShift;
573 bool rchanged = oldModifiers.get(GHOST_kModifierKeyRightShift) != newModifiers.get(GHOST_kModifierKeyRightShift);
575 key = GHOST_kKeyRightShift;
577 key = GHOST_kKeyUnknown;
584 bool lchanged = oldModifiers.get(GHOST_kModifierKeyLeftControl) != newModifiers.get(GHOST_kModifierKeyLeftControl);
586 key = GHOST_kKeyLeftControl;
588 bool rchanged = oldModifiers.get(GHOST_kModifierKeyRightControl) != newModifiers.get(GHOST_kModifierKeyRightControl);
590 key = GHOST_kKeyRightControl;
592 key = GHOST_kKeyUnknown;
599 if(m_hasAltGr && isExtended) {
600 // We have here an extended RAlt, which is AltGr. The keyboard driver on Windows sends before this a LControl, so
601 // to be able to input characters created with AltGr (normal on German, French, Finnish and other keyboards) we
602 // push an extra LControl up event. This ensures we don't have a 'hanging' ctrl event in Blender windowmanager
603 // when typing in Text editor or Console.
604 GHOST_Event *extra = new GHOST_EventKey(getSystem()->getMilliSeconds(), GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl, '\0');
605 ((GHOST_SystemWin32*)getSystem())->pushEvent(extra);
606 newModifiers.set(GHOST_kModifierKeyRightControl, false);
607 newModifiers.set(GHOST_kModifierKeyLeftControl, false);
609 bool lchanged = oldModifiers.get(GHOST_kModifierKeyLeftAlt) != newModifiers.get(GHOST_kModifierKeyLeftAlt);
611 key = GHOST_kKeyLeftAlt;
613 bool rchanged = oldModifiers.get(GHOST_kModifierKeyRightAlt) != newModifiers.get(GHOST_kModifierKeyRightAlt);
615 key = GHOST_kKeyRightAlt;
617 key = GHOST_kKeyUnknown;
626 case VK_NUMLOCK: key = GHOST_kKeyNumLock; break;
627 case VK_SCROLL: key = GHOST_kKeyScrollLock; break;
628 case VK_CAPITAL: key = GHOST_kKeyCapsLock; break;
630 key = GHOST_kKeyUnknown;
634 ((GHOST_SystemWin32*)getSystem())->storeModifierKeys(newModifiers);
638 GHOST_EventButton* GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type, GHOST_IWindow *window, GHOST_TButtonMask mask)
640 return new GHOST_EventButton (getSystem()->getMilliSeconds(), type, window, mask);
644 GHOST_EventCursor* GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type, GHOST_IWindow *Iwindow)
646 GHOST_TInt32 x_screen, y_screen;
647 GHOST_SystemWin32 * system = ((GHOST_SystemWin32 * ) getSystem());
648 GHOST_WindowWin32 * window = ( GHOST_WindowWin32 * ) Iwindow;
650 system->getCursorPosition(x_screen, y_screen);
652 if(window->getCursorGrabMode() != GHOST_kGrabDisable && window->getCursorGrabMode() != GHOST_kGrabNormal)
654 GHOST_TInt32 x_new= x_screen;
655 GHOST_TInt32 y_new= y_screen;
656 GHOST_TInt32 x_accum, y_accum;
659 /* fallback to window bounds */
660 if(window->getCursorGrabBounds(bounds)==GHOST_kFailure){
661 window->getClientBounds(bounds);
664 /* could also clamp to screen bounds
665 * wrap with a window outside the view will fail atm */
667 bounds.wrapPoint(x_new, y_new, 2); /* offset of one incase blender is at screen bounds */
669 window->getCursorGrabAccum(x_accum, y_accum);
670 if(x_new != x_screen|| y_new != y_screen) {
671 /* when wrapping we don't need to add an event because the
672 * setCursorPosition call will cause a new event after */
673 system->setCursorPosition(x_new, y_new); /* wrap */
674 window->setCursorGrabAccum(x_accum + (x_screen - x_new), y_accum + (y_screen - y_new));
676 return new GHOST_EventCursor(system->getMilliSeconds(),
677 GHOST_kEventCursorMove,
686 return new GHOST_EventCursor(system->getMilliSeconds(),
687 GHOST_kEventCursorMove,
697 GHOST_EventWheel* GHOST_SystemWin32::processWheelEvent(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam)
699 // short fwKeys = LOWORD(wParam); // key flags
700 int zDelta = (short) HIWORD(wParam); // wheel rotation
702 // zDelta /= WHEEL_DELTA;
703 // temporary fix below: microsoft now has added more precision, making the above division not work
704 if (zDelta <= 0 ) zDelta= -1; else zDelta= 1;
706 // short xPos = (short) LOWORD(lParam); // horizontal position of pointer
707 // short yPos = (short) HIWORD(lParam); // vertical position of pointer
708 return new GHOST_EventWheel (getSystem()->getMilliSeconds(), window, zDelta);
712 GHOST_EventKey* GHOST_SystemWin32::processKeyEvent(GHOST_IWindow *window, bool keyDown, WPARAM wParam, LPARAM lParam)
714 GHOST_TKey key = ((GHOST_SystemWin32*)getSystem())->convertKey(window, wParam, lParam);
715 GHOST_EventKey* event;
716 if (key != GHOST_kKeyUnknown) {
720 /* Eat any character related messages */
721 if (::PeekMessage(&keyMsg, NULL, WM_CHAR, WM_SYSDEADCHAR, PM_REMOVE)) {
722 ascii = (char) keyMsg.wParam;
726 event = new GHOST_EventKey(getSystem()->getMilliSeconds(), keyDown ? GHOST_kEventKeyDown: GHOST_kEventKeyUp, window, key, ascii);
735 GHOST_Event* GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type, GHOST_IWindow* window)
737 return new GHOST_Event(getSystem()->getMilliSeconds(), type, window);
740 GHOST_TSuccess GHOST_SystemWin32::pushDragDropEvent(GHOST_TEventType eventType,
741 GHOST_TDragnDropTypes draggedObjectType,
742 GHOST_IWindow* window,
743 int mouseX, int mouseY,
746 GHOST_SystemWin32* system = ((GHOST_SystemWin32*)getSystem());
747 return system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(),
750 window,mouseX,mouseY,data)
754 void GHOST_SystemWin32::processMinMaxInfo(MINMAXINFO * minmax)
756 minmax->ptMinTrackSize.x=320;
757 minmax->ptMinTrackSize.y=240;
760 /* Note that this function gets *all* key events from the entire system (all
761 * threads running in this desktop session. So when getting event here, don't assume
762 * it's for Blender. Thus we only do status bookkeeping, so we can check
763 * in s_wndProc and processKeyEvent what the real keyboard status is.
764 * This is needed for proper handling of shift+numpad keys for instance.
766 LRESULT CALLBACK GHOST_SystemWin32::s_llKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
768 GHOST_SystemWin32* system = ((GHOST_SystemWin32*)getSystem());
771 if(wParam==WM_KEYDOWN || wParam==WM_SYSKEYDOWN ){
776 return CallNextHookEx(system->m_llKeyboardHook, nCode, wParam, lParam);
778 KBDLLHOOKSTRUCT &keyb = *(PKBDLLHOOKSTRUCT)(lParam);
779 system->m_prevKeyStatus[keyb.vkCode] = system->m_curKeyStatus[keyb.vkCode];
780 //std::cout << "ll: " << keyb.vkCode << " " << down << " ";
782 if((keyb.flags & LLKHF_EXTENDED) == LLKHF_EXTENDED) {
783 //std::cout << "extended ";
785 if((keyb.flags & LLKHF_ALTDOWN) == LLKHF_ALTDOWN) {
787 if((keyb.flags & LLKHF_INJECTED)== LLKHF_INJECTED) {
788 //std::cout << "injected ";
790 if((keyb.flags & LLKHF_UP) == LLKHF_UP) {
791 system->m_curKeyStatus[keyb.vkCode] = false;
792 //std::cout << "up" << std::endl;
794 system->m_curKeyStatus[keyb.vkCode] = true;
795 //std::cout << "down" << std::endl;
799 system->m_curKeyStatus[keyb.vkCode] = true;
800 //std::cout << "down" << std::endl;
803 return CallNextHookEx(system->m_llKeyboardHook, nCode, wParam, lParam);
806 LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
808 GHOST_Event* event = 0;
810 GHOST_SystemWin32* system = ((GHOST_SystemWin32*)getSystem());
811 GHOST_ASSERT(system, "GHOST_SystemWin32::s_wndProc(): system not initialized")
814 GHOST_WindowWin32* window = (GHOST_WindowWin32*)::GetWindowLong(hwnd, GWL_USERDATA);
817 // we need to check if new key layout has AltGr
818 case WM_INPUTLANGCHANGE:
819 system->keyboardAltGr();
821 ////////////////////////////////////////////////////////////////////////
822 // Keyboard events, processed
823 ////////////////////////////////////////////////////////////////////////
826 event = processKeyEvent(window, true, wParam, lParam);
828 GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ")
830 GHOST_PRINT(" key ignored\n")
836 event = processKeyEvent(window, false, wParam, lParam);
838 GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ")
840 GHOST_PRINT(" key ignored\n")
844 ////////////////////////////////////////////////////////////////////////
845 // Keyboard events, ignored
846 ////////////////////////////////////////////////////////////////////////
848 /* The WM_CHAR message is posted to the window with the keyboard focus when
849 * a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR
850 * contains the character code of the key that was pressed.
853 /* The WM_DEADCHAR message is posted to the window with the keyboard focus when a
854 * WM_KEYUP message is translated by the TranslateMessage function. WM_DEADCHAR
855 * specifies a character code generated by a dead key. A dead key is a key that
856 * generates a character, such as the umlaut (double-dot), that is combined with
857 * another character to form a composite character. For example, the umlaut-O
858 * character (Ù) is generated by typing the dead key for the umlaut character, and
859 * then typing the O key.
862 /* The WM_SYSDEADCHAR message is sent to the window with the keyboard focus when
863 * a WM_SYSKEYDOWN message is translated by the TranslateMessage function.
864 * WM_SYSDEADCHAR specifies the character code of a system dead key - that is,
865 * a dead key that is pressed while holding down the alt key.
868 ////////////////////////////////////////////////////////////////////////
869 // Tablet events, processed
870 ////////////////////////////////////////////////////////////////////////
872 ((GHOST_WindowWin32*)window)->processWin32TabletEvent(wParam, lParam);
876 ((GHOST_WindowWin32*)window)->processWin32TabletInitEvent();
878 ////////////////////////////////////////////////////////////////////////
879 // Mouse events, processed
880 ////////////////////////////////////////////////////////////////////////
882 window->registerMouseClickEvent(true);
883 event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft);
886 window->registerMouseClickEvent(true);
887 event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskMiddle);
890 window->registerMouseClickEvent(true);
891 event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight);
894 window->registerMouseClickEvent(true);
895 if ((short) HIWORD(wParam) == XBUTTON1){
896 event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton4);
897 }else if((short) HIWORD(wParam) == XBUTTON2){
898 event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton5);
902 window->registerMouseClickEvent(false);
903 event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft);
906 window->registerMouseClickEvent(false);
907 event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle);
910 window->registerMouseClickEvent(false);
911 event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight);
914 window->registerMouseClickEvent(false);
915 if ((short) HIWORD(wParam) == XBUTTON1){
916 event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton4);
917 }else if((short) HIWORD(wParam) == XBUTTON2){
918 event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton5);
922 event = processCursorEvent(GHOST_kEventCursorMove, window);
925 /* The WM_MOUSEWHEEL message is sent to the focus window
926 * when the mouse wheel is rotated. The DefWindowProc
927 * function propagates the message to the window's parent.
928 * There should be no internal forwarding of the message,
929 * since DefWindowProc propagates it up the parent chain
930 * until it finds a window that processes it.
932 event = processWheelEvent(window, wParam, lParam);
935 /* The WM_SETCURSOR message is sent to a window if the mouse causes the cursor
936 * to move within a window and mouse input is not captured.
937 * This means we have to set the cursor shape every time the mouse moves!
938 * The DefWindowProc function uses this message to set the cursor to an
939 * arrow if it is not in the client area.
941 if (LOWORD(lParam) == HTCLIENT) {
942 // Load the current cursor
943 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
944 // Bypass call to DefWindowProc
948 // Outside of client area show standard cursor
949 window->loadCursor(true, GHOST_kStandardCursorDefault);
953 ////////////////////////////////////////////////////////////////////////
954 // Mouse events, ignored
955 ////////////////////////////////////////////////////////////////////////
957 /* The WM_NCMOUSEMOVE message is posted to a window when the cursor is moved
958 * within the nonclient area of the window. This message is posted to the window
959 * that contains the cursor. If a window has captured the mouse, this message is not posted.
962 /* The WM_NCHITTEST message is sent to a window when the cursor moves, or
963 * when a mouse button is pressed or released. If the mouse is not captured,
964 * the message is sent to the window beneath the cursor. Otherwise, the message
965 * is sent to the window that has captured the mouse.
969 ////////////////////////////////////////////////////////////////////////
970 // Window events, processed
971 ////////////////////////////////////////////////////////////////////////
973 /* The WM_CLOSE message is sent as a signal that a window or an application should terminate. */
974 event = processWindowEvent(GHOST_kEventWindowClose, window);
977 /* The WM_ACTIVATE message is sent to both the window being activated and the window being
978 * deactivated. If the windows use the same input queue, the message is sent synchronously,
979 * first to the window procedure of the top-level window being deactivated, then to the window
980 * procedure of the top-level window being activated. If the windows use different input queues,
981 * the message is sent asynchronously, so the window is activated immediately.
983 event = processWindowEvent(LOWORD(wParam) ? GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate, window);
984 /* WARNING: Let DefWindowProc handle WM_ACTIVATE, otherwise WM_MOUSEWHEEL
985 will not be dispatched to OUR active window if we minimize one of OUR windows. */
986 lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
989 /* An application sends the WM_PAINT message when the system or another application
990 * makes a request to paint a portion of an application's window. The message is sent
991 * when the UpdateWindow or RedrawWindow function is called, or by the DispatchMessage
992 * function when the application obtains a WM_PAINT message by using the GetMessage or
993 * PeekMessage function.
995 event = processWindowEvent(GHOST_kEventWindowUpdate, window);
996 ::ValidateRect(hwnd, NULL);
998 case WM_GETMINMAXINFO:
999 /* The WM_GETMINMAXINFO message is sent to a window when the size or
1000 * position of the window is about to change. An application can use
1001 * this message to override the window's default maximized size and
1002 * position, or its default minimum or maximum tracking size.
1004 processMinMaxInfo((MINMAXINFO *) lParam);
1005 /* Let DefWindowProc handle it. */
1008 /* The WM_SIZE message is sent to a window after its size has changed.
1009 * The WM_SIZE and WM_MOVE messages are not sent if an application handles the
1010 * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
1011 * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
1012 * message without calling DefWindowProc.
1014 event = processWindowEvent(GHOST_kEventWindowSize, window);
1016 case WM_CAPTURECHANGED:
1017 window->lostMouseCapture();
1020 /* The WM_MOVING message is sent to a window that the user is moving. By processing
1021 * this message, an application can monitor the size and position of the drag rectangle
1022 * and, if needed, change its size or position.
1025 /* The WM_SIZE and WM_MOVE messages are not sent if an application handles the
1026 * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
1027 * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
1028 * message without calling DefWindowProc.
1030 event = processWindowEvent(GHOST_kEventWindowMove, window);
1032 ////////////////////////////////////////////////////////////////////////
1033 // Window events, ignored
1034 ////////////////////////////////////////////////////////////////////////
1035 case WM_WINDOWPOSCHANGED:
1036 /* The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place
1037 * in the Z order has changed as a result of a call to the SetWindowPos function or
1038 * another window-management function.
1039 * The WM_SIZE and WM_MOVE messages are not sent if an application handles the
1040 * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
1041 * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
1042 * message without calling DefWindowProc.
1045 /* An application sends the WM_ERASEBKGND message when the window background must be
1046 * erased (for example, when a window is resized). The message is sent to prepare an
1047 * invalidated portion of a window for painting.
1050 /* An application sends the WM_NCPAINT message to a window when its frame must be painted. */
1052 /* The WM_NCACTIVATE message is sent to a window when its nonclient area needs to be changed
1053 * to indicate an active or inactive state.
1056 /* The WM_DESTROY message is sent when a window is being destroyed. It is sent to the window
1057 * procedure of the window being destroyed after the window is removed from the screen.
1058 * This message is sent first to the window being destroyed and then to the child windows
1059 * (if any) as they are destroyed. During the processing of the message, it can be assumed
1060 * that all child windows still exist.
1063 /* The WM_NCDESTROY message informs a window that its nonclient area is being destroyed. The
1064 * DestroyWindow function sends the WM_NCDESTROY message to the window following the WM_DESTROY
1065 * message. WM_DESTROY is used to free the allocated memory object associated with the window.
1068 /* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard focus. */
1070 /* The WM_SHOWWINDOW message is sent to a window when the window is about to be hidden or shown. */
1071 case WM_WINDOWPOSCHANGING:
1072 /* The WM_WINDOWPOSCHANGING message is sent to a window whose size, position, or place in
1073 * the Z order is about to change as a result of a call to the SetWindowPos function or
1074 * another window-management function.
1077 /* The WM_SETFOCUS message is sent to a window after it has gained the keyboard focus. */
1078 case WM_ENTERSIZEMOVE:
1079 /* The WM_ENTERSIZEMOVE message is sent one time to a window after it enters the moving
1080 * or sizing modal loop. The window enters the moving or sizing modal loop when the user
1081 * clicks the window's title bar or sizing border, or when the window passes the
1082 * WM_SYSCOMMAND message to the DefWindowProc function and the wParam parameter of the
1083 * message specifies the SC_MOVE or SC_SIZE value. The operation is complete when
1084 * DefWindowProc returns.
1088 ////////////////////////////////////////////////////////////////////////
1090 ////////////////////////////////////////////////////////////////////////
1092 /* An application sends a WM_GETTEXT message to copy the text that
1093 * corresponds to a window into a buffer provided by the caller.
1095 case WM_ACTIVATEAPP:
1096 /* The WM_ACTIVATEAPP message is sent when a window belonging to a
1097 * different application than the active window is about to be activated.
1098 * The message is sent to the application whose window is being activated
1099 * and to the application whose window is being deactivated.
1102 /* The WIN32 docs say:
1103 * The WM_TIMER message is posted to the installing thread's message queue
1104 * when a timer expires. You can process the message by providing a WM_TIMER
1105 * case in the window procedure. Otherwise, the default window procedure will
1106 * call the TimerProc callback function specified in the call to the SetTimer
1107 * function used to install the timer.
1109 * In GHOST, we let DefWindowProc call the timer callback.
1112 case WM_BLND_NDOF_AXIS:
1114 GHOST_TEventNDOFData ndofdata;
1115 system->m_ndofManager->GHOST_NDOFGetDatas(ndofdata);
1116 system->m_eventManager->
1117 pushEvent(new GHOST_EventNDOF(
1118 system->getMilliSeconds(),
1119 GHOST_kEventNDOFMotion,
1123 case WM_BLND_NDOF_BTN:
1125 GHOST_TEventNDOFData ndofdata;
1126 system->m_ndofManager->GHOST_NDOFGetDatas(ndofdata);
1127 system->m_eventManager->
1128 pushEvent(new GHOST_EventNDOF(
1129 system->getMilliSeconds(),
1130 GHOST_kEventNDOFButton,
1137 // Event found for a window before the pointer to the class has been set.
1138 GHOST_PRINT("GHOST_SystemWin32::wndProc: GHOST window event before creation\n")
1139 /* These are events we typically miss at this point:
1140 WM_GETMINMAXINFO 0x24
1144 We let DefWindowProc do the work.
1149 // Events without valid hwnd
1150 GHOST_PRINT("GHOST_SystemWin32::wndProc: event without window\n")
1154 system->pushEvent(event);
1157 lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
1162 GHOST_TUns8* GHOST_SystemWin32::getClipboard(bool selection) const
1167 if ( IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(NULL) ) {
1168 HANDLE hData = GetClipboardData( CF_TEXT );
1169 if (hData == NULL) {
1173 buffer = (char*)GlobalLock( hData );
1175 temp_buff = (char*) malloc(strlen(buffer)+1);
1176 strcpy(temp_buff, buffer);
1178 GlobalUnlock( hData );
1181 temp_buff[strlen(buffer)] = '\0';
1183 return (GHOST_TUns8*)temp_buff;
1192 void GHOST_SystemWin32::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1194 if(selection) {return;} // for copying the selection, used on X11
1196 if (OpenClipboard(NULL)) {
1203 clipbuffer = LocalAlloc(LMEM_FIXED,((strlen(buffer)+1)));
1204 data = (char*)GlobalLock(clipbuffer);
1206 strcpy(data, (char*)buffer);
1207 data[strlen(buffer)] = '\0';
1208 LocalUnlock(clipbuffer);
1209 SetClipboardData(CF_TEXT,clipbuffer);
1217 const GHOST_TUns8* GHOST_SystemWin32::getSystemDir() const
1219 static char knownpath[MAX_PATH];
1220 HRESULT hResult = SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, knownpath);
1222 if (hResult == S_OK)
1224 return (GHOST_TUns8*)knownpath;
1230 const GHOST_TUns8* GHOST_SystemWin32::getUserDir() const
1232 static char knownpath[MAX_PATH];
1233 HRESULT hResult = SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, knownpath);
1235 if (hResult == S_OK)
1237 return (GHOST_TUns8*)knownpath;
1243 const GHOST_TUns8* GHOST_SystemWin32::getBinaryDir() const
1245 static char fullname[MAX_PATH];
1246 if(GetModuleFileName(0, fullname, MAX_PATH)) {
1247 return (GHOST_TUns8*)fullname;