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 *****
29 /** \file ghost/intern/GHOST_SystemWin32.cpp
37 * Copyright (C) 2001 NaN Technologies B.V.
38 * @author Maarten Gribnau
45 # define WINVER 0x0501 /* GetConsoleWindow() for MinGW */
48 #include "GHOST_SystemWin32.h"
49 #include "GHOST_EventDragnDrop.h"
51 #define WIN32_LEAN_AND_MEAN
55 #define _WIN32_IE 0x0501
59 // win64 doesn't define GWL_USERDATA
62 #define GWL_USERDATA GWLP_USERDATA
63 #define GWL_WNDPROC GWLP_WNDPROC
68 * According to the docs the mouse wheel message is supported from windows 98
69 * upwards. Leaving WINVER at default value, the WM_MOUSEWHEEL message and the
70 * wheel detent value are undefined.
73 #define WM_MOUSEWHEEL 0x020A
74 #endif // WM_MOUSEWHEEL
76 #define WHEEL_DELTA 120 /* Value for rolling one detent, (old convention! MS changed it) */
80 * Defines for mouse buttons 4 and 5 aka xbutton1 and xbutton2.
81 * MSDN: Declared in Winuser.h, include Windows.h
82 * This does not seem to work with MinGW so we define our own here.
85 #define XBUTTON1 0x0001
88 #define XBUTTON2 0x0002
91 #define WM_XBUTTONUP 524
92 #endif // WM_XBUTTONUP
93 #ifndef WM_XBUTTONDOWN
94 #define WM_XBUTTONDOWN 523
95 #endif // WM_XBUTTONDOWN
97 #include "GHOST_Debug.h"
98 #include "GHOST_DisplayManagerWin32.h"
99 #include "GHOST_EventButton.h"
100 #include "GHOST_EventCursor.h"
101 #include "GHOST_EventKey.h"
102 #include "GHOST_EventWheel.h"
103 #include "GHOST_EventNDOF.h"
104 #include "GHOST_TimerTask.h"
105 #include "GHOST_TimerManager.h"
106 #include "GHOST_WindowManager.h"
107 #include "GHOST_WindowWin32.h"
108 #include "GHOST_NDOFManager.h"
110 // Key code values not found in winuser.h
112 #define VK_MINUS 0xBD
115 #define VK_SEMICOLON 0xBA
116 #endif // VK_SEMICOLON
118 #define VK_PERIOD 0xBE
121 #define VK_COMMA 0xBC
124 #define VK_QUOTE 0xDE
126 #ifndef VK_BACK_QUOTE
127 #define VK_BACK_QUOTE 0xC0
128 #endif // VK_BACK_QUOTE
130 #define VK_SLASH 0xBF
132 #ifndef VK_BACK_SLASH
133 #define VK_BACK_SLASH 0xDC
134 #endif // VK_BACK_SLASH
136 #define VK_EQUALS 0xBB
138 #ifndef VK_OPEN_BRACKET
139 #define VK_OPEN_BRACKET 0xDB
140 #endif // VK_OPEN_BRACKET
141 #ifndef VK_CLOSE_BRACKET
142 #define VK_CLOSE_BRACKET 0xDD
143 #endif // VK_CLOSE_BRACKET
145 #define VK_GR_LESS 0xE2
148 #ifndef VK_MEDIA_NEXT_TRACK
149 #define VK_MEDIA_NEXT_TRACK 0xB0
150 #endif // VK_MEDIA_NEXT_TRACK
151 #ifndef VK_MEDIA_PREV_TRACK
152 #define VK_MEDIA_PREV_TRACK 0xB1
153 #endif // VK_MEDIA_PREV_TRACK
154 #ifndef VK_MEDIA_STOP
155 #define VK_MEDIA_STOP 0xB2
156 #endif // VK_MEDIA_STOP
157 #ifndef VK_MEDIA_PLAY_PAUSE
158 #define VK_MEDIA_PLAY_PAUSE 0xB3
159 #endif // VK_MEDIA_PLAY_PAUSE
162 Initiates WM_INPUT messages from keyboard
163 That way GHOST can retrieve true keys
165 GHOST_TInt32 GHOST_SystemWin32::initKeyboardRawInput(void)
167 RAWINPUTDEVICE device = {0};
168 device.usUsagePage = 0x01; /* usUsagePage & usUsage for keyboard*/
169 device.usUsage = 0x06; /* http://msdn.microsoft.com/en-us/windows/hardware/gg487473.aspx */
171 return RegisterRawInputDevices(&device, 1, sizeof(device));
174 GHOST_SystemWin32::GHOST_SystemWin32()
175 : m_hasPerformanceCounter(false), m_freq(0), m_start(0)
177 m_displayManager = new GHOST_DisplayManagerWin32 ();
178 GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::GHOST_SystemWin32(): m_displayManager==0\n");
179 m_displayManager->initialize();
183 // Check if current keyboard layout uses AltGr and save keylayout ID for
184 // specialized handling if keys like VK_OEM_*. I.e. french keylayout
185 // generates VK_OEM_8 for their exclamation key (key left of right shift)
186 this->handleKeyboardChange();
187 // Require COM for GHOST_DropTargetWin32 created in GHOST_WindowWin32.
191 GHOST_SystemWin32::~GHOST_SystemWin32()
199 GHOST_TUns64 GHOST_SystemWin32::getMilliSeconds() const
201 // Hardware does not support high resolution timers. We will use GetTickCount instead then.
202 if (!m_hasPerformanceCounter) {
203 return ::GetTickCount();
206 // Retrieve current count
208 ::QueryPerformanceCounter((LARGE_INTEGER*)&count);
210 // Calculate the time passed since system initialization.
211 __int64 delta = 1000*(count-m_start);
213 GHOST_TUns64 t = (GHOST_TUns64)(delta/m_freq);
218 GHOST_TUns8 GHOST_SystemWin32::getNumDisplays() const
220 GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::getNumDisplays(): m_displayManager==0\n");
221 GHOST_TUns8 numDisplays;
222 m_displayManager->getNumDisplays(numDisplays);
227 void GHOST_SystemWin32::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
229 width = ::GetSystemMetrics(SM_CXSCREEN);
230 height= ::GetSystemMetrics(SM_CYSCREEN);
234 GHOST_IWindow* GHOST_SystemWin32::createWindow(
235 const STR_String& title,
236 GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, GHOST_TUns32 height,
237 GHOST_TWindowState state, GHOST_TDrawingContextType type,
238 bool stereoVisual, const GHOST_TUns16 numOfAASamples, const GHOST_TEmbedderWindowID parentWindow )
240 GHOST_Window* window = 0;
241 window = new GHOST_WindowWin32 (this, title, left, top, width, height, state, type, stereoVisual, numOfAASamples, parentWindow);
243 if (window->getValid()) {
244 // Store the pointer to the window
245 // if (state != GHOST_kWindowStateFullScreen) {
246 m_windowManager->addWindow(window);
251 // Invalid parent window hwnd
252 if (((GHOST_WindowWin32*)window)->getNextWindow() == NULL) {
258 // An invalid window could be one that was used to test for AA
259 window = ((GHOST_WindowWin32*)window)->getNextWindow();
261 // If another window is found, let the wm know about that one, but not the old one
262 if (window->getValid()) {
263 m_windowManager->addWindow(window);
276 bool GHOST_SystemWin32::processEvents(bool waitForEvent)
279 bool anyProcessed = false;
282 GHOST_TimerManager* timerMgr = getTimerManager();
284 if (waitForEvent && !::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)) {
288 GHOST_TUns64 next = timerMgr->nextFireTime();
289 GHOST_TInt64 maxSleep = next - getMilliSeconds();
291 if (next == GHOST_kFireTimeNever) {
293 } else if(maxSleep >= 0.0) {
294 ::SetTimer(NULL, 0, maxSleep, NULL);
296 ::KillTimer(NULL, 0);
301 if (timerMgr->fireTimers(getMilliSeconds())) {
305 // Process all the events waiting for us
306 while (::PeekMessage(&msg, 0, 0, 0, PM_REMOVE) != 0) {
307 ::DispatchMessage(&msg);
310 } while (waitForEvent && !anyProcessed);
316 GHOST_TSuccess GHOST_SystemWin32::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
319 if(::GetCursorPos(&point)){
322 return GHOST_kSuccess;
324 return GHOST_kFailure;
328 GHOST_TSuccess GHOST_SystemWin32::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y)
330 if (!GetActiveWindow())
331 return GHOST_kFailure;
332 return ::SetCursorPos(x, y) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
336 GHOST_TSuccess GHOST_SystemWin32::getModifierKeys(GHOST_ModifierKeys& keys) const
338 bool down = HIBYTE(::GetKeyState(VK_LSHIFT)) != 0;
339 keys.set(GHOST_kModifierKeyLeftShift, down);
340 down = HIBYTE(::GetKeyState(VK_RSHIFT)) != 0;
341 keys.set(GHOST_kModifierKeyRightShift, down);
343 down = HIBYTE(::GetKeyState(VK_LMENU)) != 0;
344 keys.set(GHOST_kModifierKeyLeftAlt, down);
345 down = HIBYTE(::GetKeyState(VK_RMENU)) != 0;
346 keys.set(GHOST_kModifierKeyRightAlt, down);
348 down = HIBYTE(::GetKeyState(VK_LCONTROL)) != 0;
349 keys.set(GHOST_kModifierKeyLeftControl, down);
350 down = HIBYTE(::GetKeyState(VK_RCONTROL)) != 0;
351 keys.set(GHOST_kModifierKeyRightControl, down);
353 bool lwindown = HIBYTE(::GetKeyState(VK_LWIN)) != 0;
354 bool rwindown = HIBYTE(::GetKeyState(VK_RWIN)) != 0;
355 if(lwindown || rwindown)
356 keys.set(GHOST_kModifierKeyOS, true);
358 keys.set(GHOST_kModifierKeyOS, false);
359 return GHOST_kSuccess;
363 GHOST_TSuccess GHOST_SystemWin32::getButtons(GHOST_Buttons& buttons) const
365 /* Check for swapped buttons (left-handed mouse buttons)
366 * GetAsyncKeyState() will give back the state of the physical mouse buttons.
368 bool swapped = ::GetSystemMetrics(SM_SWAPBUTTON) == TRUE;
370 bool down = HIBYTE(::GetKeyState(VK_LBUTTON)) != 0;
371 buttons.set(swapped ? GHOST_kButtonMaskRight : GHOST_kButtonMaskLeft, down);
373 down = HIBYTE(::GetKeyState(VK_MBUTTON)) != 0;
374 buttons.set(GHOST_kButtonMaskMiddle, down);
376 down = HIBYTE(::GetKeyState(VK_RBUTTON)) != 0;
377 buttons.set(swapped ? GHOST_kButtonMaskLeft : GHOST_kButtonMaskRight, down);
378 return GHOST_kSuccess;
382 GHOST_TSuccess GHOST_SystemWin32::init()
384 GHOST_TSuccess success = GHOST_System::init();
386 /* Disable scaling on high DPI displays on Vista */
387 user32 = ::LoadLibraryA("user32.dll");
388 typedef BOOL (WINAPI * LPFNSETPROCESSDPIAWARE)();
389 LPFNSETPROCESSDPIAWARE SetProcessDPIAware =
390 (LPFNSETPROCESSDPIAWARE)GetProcAddress(user32, "SetProcessDPIAware");
391 if (SetProcessDPIAware)
392 SetProcessDPIAware();
394 pRegisterRawInputDevices = (LPFNDLLRRID)GetProcAddress(user32, "RegisterRawInputDevices");
395 pGetRawInputData = (LPFNDLLGRID)GetProcAddress(user32, "GetRawInputData");
400 /* Initiates WM_INPUT messages from keyboard */
401 initKeyboardRawInput();
404 // Determine whether this system has a high frequency performance counter. */
405 m_hasPerformanceCounter = ::QueryPerformanceFrequency((LARGE_INTEGER*)&m_freq) == TRUE;
406 if (m_hasPerformanceCounter) {
407 GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer available\n")
408 ::QueryPerformanceCounter((LARGE_INTEGER*)&m_start);
411 GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer not available\n")
416 wc.style= CS_HREDRAW | CS_VREDRAW;
417 wc.lpfnWndProc= s_wndProc;
420 wc.hInstance= ::GetModuleHandle(0);
421 wc.hIcon = ::LoadIcon(wc.hInstance, "APPICON");
424 ::LoadIcon(NULL, IDI_APPLICATION);
426 wc.hCursor = ::LoadCursor(0, IDC_ARROW);
427 wc.hbrBackground= (HBRUSH)::GetStockObject(BLACK_BRUSH);
429 wc.lpszClassName= GHOST_WindowWin32::getWindowClassName();
431 // Use RegisterClassEx for setting small icon
432 if (::RegisterClass(&wc) == 0) {
433 success = GHOST_kFailure;
441 GHOST_TSuccess GHOST_SystemWin32::exit()
447 return GHOST_System::exit();
450 GHOST_TKey GHOST_SystemWin32::hardKey(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam, int * keyDown, char * vk)
452 unsigned int size = 0;
454 GHOST_TKey key = GHOST_kKeyUnknown;
458 return GHOST_kKeyUnknown;
460 GetRawInputData((HRAWINPUT)lParam, RID_INPUT, 0, &size, sizeof(RAWINPUTHEADER));
463 if((data = (char*)malloc(size)) &&
464 GetRawInputData((HRAWINPUT)lParam, RID_INPUT, data, &size, sizeof(RAWINPUTHEADER)))
467 memcpy(&ri,data,(size < sizeof(ri)) ? size : sizeof(ri));
469 if (ri.header.dwType == RIM_TYPEKEYBOARD)
471 GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
473 GHOST_ModifierKeys modifiers;
474 system->retrieveModifierKeys(modifiers);
476 *keyDown = !(ri.data.keyboard.Flags & RI_KEY_BREAK);
477 key = this->convertKey(window, ri.data.keyboard.VKey, ri.data.keyboard.MakeCode, (ri.data.keyboard.Flags&(RI_KEY_E1|RI_KEY_E0)));
479 // extra handling of modifier keys: don't send repeats out from GHOST
480 if(key >= GHOST_kKeyLeftShift && key <= GHOST_kKeyRightAlt)
482 bool changed = false;
483 GHOST_TModifierKeyMask modifier;
485 case GHOST_kKeyLeftShift:
487 changed = (modifiers.get(GHOST_kModifierKeyLeftShift) != (bool)*keyDown);
488 modifier = GHOST_kModifierKeyLeftShift;
491 case GHOST_kKeyRightShift:
493 changed = (modifiers.get(GHOST_kModifierKeyRightShift) != (bool)*keyDown);
494 modifier = GHOST_kModifierKeyRightShift;
497 case GHOST_kKeyLeftControl:
499 changed = (modifiers.get(GHOST_kModifierKeyLeftControl) != (bool)*keyDown);
500 modifier = GHOST_kModifierKeyLeftControl;
503 case GHOST_kKeyRightControl:
505 changed = (modifiers.get(GHOST_kModifierKeyRightControl) != (bool)*keyDown);
506 modifier = GHOST_kModifierKeyRightControl;
509 case GHOST_kKeyLeftAlt:
511 changed = (modifiers.get(GHOST_kModifierKeyLeftAlt) != (bool)*keyDown);
512 modifier = GHOST_kModifierKeyLeftAlt;
515 case GHOST_kKeyRightAlt:
517 changed = (modifiers.get(GHOST_kModifierKeyRightAlt) != (bool)*keyDown);
518 modifier = GHOST_kModifierKeyRightAlt;
526 modifiers.set(modifier, (bool)*keyDown);
527 system->storeModifierKeys(modifiers);
531 key = GHOST_kKeyUnknown;
536 if(vk) *vk = ri.data.keyboard.VKey;
545 //! note: this function can be extended to include other exotic cases as they arise.
546 // This function was added in response to bug [#25715]
547 GHOST_TKey GHOST_SystemWin32::processSpecialKey(GHOST_IWindow *window, short vKey, short scanCode) const
549 GHOST_TKey key = GHOST_kKeyUnknown;
550 switch(PRIMARYLANGID(m_langId)) {
552 if(vKey==VK_OEM_8) key = GHOST_kKeyF13; // oem key; used purely for shortcuts .
559 GHOST_TKey GHOST_SystemWin32::convertKey(GHOST_IWindow *window, short vKey, short scanCode, short extend) const
563 if ((vKey >= '0') && (vKey <= '9')) {
564 // VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39)
565 key = (GHOST_TKey)(vKey - '0' + GHOST_kKey0);
567 else if ((vKey >= 'A') && (vKey <= 'Z')) {
568 // VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A)
569 key = (GHOST_TKey)(vKey - 'A' + GHOST_kKeyA);
571 else if ((vKey >= VK_F1) && (vKey <= VK_F24)) {
572 key = (GHOST_TKey)(vKey - VK_F1 + GHOST_kKeyF1);
577 key = (extend)?GHOST_kKeyNumpadEnter : GHOST_kKeyEnter; break;
579 case VK_BACK: key = GHOST_kKeyBackSpace; break;
580 case VK_TAB: key = GHOST_kKeyTab; break;
581 case VK_ESCAPE: key = GHOST_kKeyEsc; break;
582 case VK_SPACE: key = GHOST_kKeySpace; break;
586 key = (extend) ? GHOST_kKeyInsert : GHOST_kKeyNumpad0; break;
589 key = (extend) ? GHOST_kKeyEnd : GHOST_kKeyNumpad1; break;
592 key = (extend) ? GHOST_kKeyDownArrow : GHOST_kKeyNumpad2; break;
595 key = (extend) ? GHOST_kKeyDownPage : GHOST_kKeyNumpad3; break;
598 key = (extend) ? GHOST_kKeyLeftArrow : GHOST_kKeyNumpad4; break;
601 key = (extend) ? GHOST_kKeyUnknown: GHOST_kKeyNumpad5; break;
604 key = (extend) ? GHOST_kKeyRightArrow : GHOST_kKeyNumpad6; break;
607 key = (extend) ? GHOST_kKeyHome : GHOST_kKeyNumpad7; break;
610 key = (extend) ? GHOST_kKeyUpArrow : GHOST_kKeyNumpad8; break;
613 key = (extend) ? GHOST_kKeyUpPage : GHOST_kKeyNumpad9; break;
616 key = (extend) ? GHOST_kKeyDelete : GHOST_kKeyNumpadPeriod; break;
618 case VK_SNAPSHOT: key = GHOST_kKeyPrintScreen; break;
619 case VK_PAUSE: key = GHOST_kKeyPause; break;
620 case VK_MULTIPLY: key = GHOST_kKeyNumpadAsterisk; break;
621 case VK_SUBTRACT: key = GHOST_kKeyNumpadMinus; break;
622 case VK_DIVIDE: key = GHOST_kKeyNumpadSlash; break;
623 case VK_ADD: key = GHOST_kKeyNumpadPlus; break;
625 case VK_SEMICOLON: key = GHOST_kKeySemicolon; break;
626 case VK_EQUALS: key = GHOST_kKeyEqual; break;
627 case VK_COMMA: key = GHOST_kKeyComma; break;
628 case VK_MINUS: key = GHOST_kKeyMinus; break;
629 case VK_PERIOD: key = GHOST_kKeyPeriod; break;
630 case VK_SLASH: key = GHOST_kKeySlash; break;
631 case VK_BACK_QUOTE: key = GHOST_kKeyAccentGrave; break;
632 case VK_OPEN_BRACKET: key = GHOST_kKeyLeftBracket; break;
633 case VK_BACK_SLASH: key = GHOST_kKeyBackslash; break;
634 case VK_CLOSE_BRACKET: key = GHOST_kKeyRightBracket; break;
635 case VK_QUOTE: key = GHOST_kKeyQuote; break;
636 case VK_GR_LESS: key = GHOST_kKeyGrLess; break;
639 key = (scanCode == 0x36)? GHOST_kKeyRightShift : GHOST_kKeyLeftShift;
642 key = (extend)? GHOST_kKeyRightControl : GHOST_kKeyLeftControl;
645 key = (extend)? GHOST_kKeyRightAlt : GHOST_kKeyLeftAlt;
651 case VK_NUMLOCK: key = GHOST_kKeyNumLock; break;
652 case VK_SCROLL: key = GHOST_kKeyScrollLock; break;
653 case VK_CAPITAL: key = GHOST_kKeyCapsLock; break;
655 key = ((GHOST_SystemWin32*)getSystem())->processSpecialKey(window, vKey, scanCode);
657 case VK_MEDIA_PLAY_PAUSE: key = GHOST_kKeyMediaPlay; break;
658 case VK_MEDIA_STOP: key = GHOST_kKeyMediaStop; break;
659 case VK_MEDIA_PREV_TRACK: key = GHOST_kKeyMediaFirst; break;
660 case VK_MEDIA_NEXT_TRACK: key = GHOST_kKeyMediaLast; break;
662 key = GHOST_kKeyUnknown;
670 GHOST_EventButton* GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type, GHOST_IWindow *window, GHOST_TButtonMask mask)
672 return new GHOST_EventButton (getSystem()->getMilliSeconds(), type, window, mask);
676 GHOST_EventCursor* GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type, GHOST_IWindow *Iwindow)
678 GHOST_TInt32 x_screen, y_screen;
679 GHOST_SystemWin32 * system = ((GHOST_SystemWin32 * ) getSystem());
680 GHOST_WindowWin32 * window = ( GHOST_WindowWin32 * ) Iwindow;
682 system->getCursorPosition(x_screen, y_screen);
684 if(window->getCursorGrabMode() != GHOST_kGrabDisable && window->getCursorGrabMode() != GHOST_kGrabNormal)
686 GHOST_TInt32 x_new= x_screen;
687 GHOST_TInt32 y_new= y_screen;
688 GHOST_TInt32 x_accum, y_accum;
691 /* fallback to window bounds */
692 if(window->getCursorGrabBounds(bounds)==GHOST_kFailure){
693 window->getClientBounds(bounds);
696 /* could also clamp to screen bounds
697 * wrap with a window outside the view will fail atm */
699 bounds.wrapPoint(x_new, y_new, 2); /* offset of one incase blender is at screen bounds */
701 window->getCursorGrabAccum(x_accum, y_accum);
702 if(x_new != x_screen|| y_new != y_screen) {
703 /* when wrapping we don't need to add an event because the
704 * setCursorPosition call will cause a new event after */
705 system->setCursorPosition(x_new, y_new); /* wrap */
706 window->setCursorGrabAccum(x_accum + (x_screen - x_new), y_accum + (y_screen - y_new));
708 return new GHOST_EventCursor(system->getMilliSeconds(),
709 GHOST_kEventCursorMove,
718 return new GHOST_EventCursor(system->getMilliSeconds(),
719 GHOST_kEventCursorMove,
729 GHOST_EventWheel* GHOST_SystemWin32::processWheelEvent(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam)
731 // short fwKeys = LOWORD(wParam); // key flags
732 int zDelta = (short) HIWORD(wParam); // wheel rotation
734 // zDelta /= WHEEL_DELTA;
735 // temporary fix below: microsoft now has added more precision, making the above division not work
736 if (zDelta <= 0 ) zDelta= -1; else zDelta= 1;
738 // short xPos = (short) LOWORD(lParam); // horizontal position of pointer
739 // short yPos = (short) HIWORD(lParam); // vertical position of pointer
740 return new GHOST_EventWheel (getSystem()->getMilliSeconds(), window, zDelta);
744 GHOST_EventKey* GHOST_SystemWin32::processKeyEvent(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam)
748 GHOST_SystemWin32 * system = (GHOST_SystemWin32 *)getSystem();
749 GHOST_TKey key = system->hardKey(window, wParam, lParam, &keyDown, &vk);
750 GHOST_EventKey* event;
751 if (key != GHOST_kKeyUnknown) {
754 unsigned short utf16[2]={0};
756 GetKeyboardState((PBYTE)state);
758 if(ToAsciiEx(vk, 0, state, utf16, 0, system->m_keylayout))
759 WideCharToMultiByte(CP_ACP, 0x00000400,
764 event = new GHOST_EventKey(system->getMilliSeconds(), keyDown ? GHOST_kEventKeyDown: GHOST_kEventKeyUp, window, key, ascii);
767 std::cout << ascii << std::endl;
777 GHOST_Event* GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type, GHOST_IWindow* window)
779 return new GHOST_Event(getSystem()->getMilliSeconds(), type, window);
782 GHOST_TSuccess GHOST_SystemWin32::pushDragDropEvent(GHOST_TEventType eventType,
783 GHOST_TDragnDropTypes draggedObjectType,
784 GHOST_IWindow* window,
785 int mouseX, int mouseY,
788 GHOST_SystemWin32* system = ((GHOST_SystemWin32*)getSystem());
789 return system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(),
792 window,mouseX,mouseY,data)
796 void GHOST_SystemWin32::processMinMaxInfo(MINMAXINFO * minmax)
798 minmax->ptMinTrackSize.x=320;
799 minmax->ptMinTrackSize.y=240;
802 LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
804 GHOST_Event* event = 0;
806 GHOST_SystemWin32* system = ((GHOST_SystemWin32*)getSystem());
807 GHOST_ASSERT(system, "GHOST_SystemWin32::s_wndProc(): system not initialized")
810 GHOST_WindowWin32* window = (GHOST_WindowWin32*)::GetWindowLong(hwnd, GWL_USERDATA);
813 // we need to check if new key layout has AltGr
814 case WM_INPUTLANGCHANGE:
815 system->handleKeyboardChange();
817 ////////////////////////////////////////////////////////////////////////
818 // Keyboard events, processed
819 ////////////////////////////////////////////////////////////////////////
821 // check WM_INPUT from input sink when ghost window is not in the foreground
822 if (wParam == RIM_INPUTSINK) {
823 if (GetFocus() != hwnd) // WM_INPUT message not for this window
825 } //else wPAram == RIM_INPUT
826 event = processKeyEvent(window, wParam, lParam);
828 GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ")
830 GHOST_PRINT(" key ignored\n")
833 ////////////////////////////////////////////////////////////////////////
834 // Keyboard events, ignored
835 ////////////////////////////////////////////////////////////////////////
840 /* These functions were replaced by WM_INPUT*/
842 /* The WM_CHAR message is posted to the window with the keyboard focus when
843 * a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR
844 * contains the character code of the key that was pressed.
847 /* The WM_DEADCHAR message is posted to the window with the keyboard focus when a
848 * WM_KEYUP message is translated by the TranslateMessage function. WM_DEADCHAR
849 * specifies a character code generated by a dead key. A dead key is a key that
850 * generates a character, such as the umlaut (double-dot), that is combined with
851 * another character to form a composite character. For example, the umlaut-O
852 * character (Ö) is generated by typing the dead key for the umlaut character, and
853 * then typing the O key.
857 /* The WM_SYSDEADCHAR message is sent to the window with the keyboard focus when
858 * a WM_SYSKEYDOWN message is translated by the TranslateMessage function.
859 * WM_SYSDEADCHAR specifies the character code of a system dead key - that is,
860 * a dead key that is pressed while holding down the alt key.
863 /* The WM_SYSCHAR message is sent to the window with the keyboard focus when
864 * a WM_SYSCHAR message is translated by the TranslateMessage function.
865 * WM_SYSCHAR specifies the character code of a dead key - that is,
866 * a dead key that is pressed while holding down the alt key.
867 * To prevent the sound, DefWindowProc must be avoided by return
871 /* The WM_SYSCHAR message is sent to the window when system commands such as
872 * maximize, minimize or close the window are triggered. Also it is sent when ALT
873 * button is press for menu. To prevent this we must return preventing DefWindowProc.
875 if(wParam==SC_KEYMENU) return 0;
877 ////////////////////////////////////////////////////////////////////////
878 // Tablet events, processed
879 ////////////////////////////////////////////////////////////////////////
881 ((GHOST_WindowWin32*)window)->processWin32TabletEvent(wParam, lParam);
885 ((GHOST_WindowWin32*)window)->processWin32TabletInitEvent();
887 ////////////////////////////////////////////////////////////////////////
888 // Mouse events, processed
889 ////////////////////////////////////////////////////////////////////////
891 window->registerMouseClickEvent(0);
892 event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft);
895 window->registerMouseClickEvent(0);
896 event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskMiddle);
899 window->registerMouseClickEvent(0);
900 event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight);
903 window->registerMouseClickEvent(0);
904 if ((short) HIWORD(wParam) == XBUTTON1){
905 event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton4);
906 }else if((short) HIWORD(wParam) == XBUTTON2){
907 event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton5);
911 window->registerMouseClickEvent(1);
912 event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft);
915 window->registerMouseClickEvent(1);
916 event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle);
919 window->registerMouseClickEvent(1);
920 event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight);
923 window->registerMouseClickEvent(1);
924 if ((short) HIWORD(wParam) == XBUTTON1){
925 event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton4);
926 }else if((short) HIWORD(wParam) == XBUTTON2){
927 event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton5);
931 event = processCursorEvent(GHOST_kEventCursorMove, window);
934 /* The WM_MOUSEWHEEL message is sent to the focus window
935 * when the mouse wheel is rotated. The DefWindowProc
936 * function propagates the message to the window's parent.
937 * There should be no internal forwarding of the message,
938 * since DefWindowProc propagates it up the parent chain
939 * until it finds a window that processes it.
941 event = processWheelEvent(window, wParam, lParam);
944 /* The WM_SETCURSOR message is sent to a window if the mouse causes the cursor
945 * to move within a window and mouse input is not captured.
946 * This means we have to set the cursor shape every time the mouse moves!
947 * The DefWindowProc function uses this message to set the cursor to an
948 * arrow if it is not in the client area.
950 if (LOWORD(lParam) == HTCLIENT) {
951 // Load the current cursor
952 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
953 // Bypass call to DefWindowProc
957 // Outside of client area show standard cursor
958 window->loadCursor(true, GHOST_kStandardCursorDefault);
962 ////////////////////////////////////////////////////////////////////////
963 // Mouse events, ignored
964 ////////////////////////////////////////////////////////////////////////
966 /* The WM_NCMOUSEMOVE message is posted to a window when the cursor is moved
967 * within the nonclient area of the window. This message is posted to the window
968 * that contains the cursor. If a window has captured the mouse, this message is not posted.
971 /* The WM_NCHITTEST message is sent to a window when the cursor moves, or
972 * when a mouse button is pressed or released. If the mouse is not captured,
973 * the message is sent to the window beneath the cursor. Otherwise, the message
974 * is sent to the window that has captured the mouse.
978 ////////////////////////////////////////////////////////////////////////
979 // Window events, processed
980 ////////////////////////////////////////////////////////////////////////
982 /* The WM_CLOSE message is sent as a signal that a window or an application should terminate. */
983 event = processWindowEvent(GHOST_kEventWindowClose, window);
986 /* The WM_ACTIVATE message is sent to both the window being activated and the window being
987 * deactivated. If the windows use the same input queue, the message is sent synchronously,
988 * first to the window procedure of the top-level window being deactivated, then to the window
989 * procedure of the top-level window being activated. If the windows use different input queues,
990 * the message is sent asynchronously, so the window is activated immediately.
992 event = processWindowEvent(LOWORD(wParam) ? GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate, window);
993 /* WARNING: Let DefWindowProc handle WM_ACTIVATE, otherwise WM_MOUSEWHEEL
994 will not be dispatched to OUR active window if we minimize one of OUR windows. */
995 lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
998 /* An application sends the WM_PAINT message when the system or another application
999 * makes a request to paint a portion of an application's window. The message is sent
1000 * when the UpdateWindow or RedrawWindow function is called, or by the DispatchMessage
1001 * function when the application obtains a WM_PAINT message by using the GetMessage or
1002 * PeekMessage function.
1004 event = processWindowEvent(GHOST_kEventWindowUpdate, window);
1005 ::ValidateRect(hwnd, NULL);
1007 case WM_GETMINMAXINFO:
1008 /* The WM_GETMINMAXINFO message is sent to a window when the size or
1009 * position of the window is about to change. An application can use
1010 * this message to override the window's default maximized size and
1011 * position, or its default minimum or maximum tracking size.
1013 processMinMaxInfo((MINMAXINFO *) lParam);
1014 /* Let DefWindowProc handle it. */
1017 /* The WM_SIZE message is sent to a window after its size has changed.
1018 * The WM_SIZE and WM_MOVE messages are not sent if an application handles the
1019 * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
1020 * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
1021 * message without calling DefWindowProc.
1023 event = processWindowEvent(GHOST_kEventWindowSize, window);
1025 case WM_CAPTURECHANGED:
1026 window->lostMouseCapture();
1029 /* The WM_MOVING message is sent to a window that the user is moving. By processing
1030 * this message, an application can monitor the size and position of the drag rectangle
1031 * and, if needed, change its size or position.
1034 /* The WM_SIZE and WM_MOVE messages are not sent if an application handles the
1035 * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
1036 * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
1037 * message without calling DefWindowProc.
1039 event = processWindowEvent(GHOST_kEventWindowMove, window);
1041 ////////////////////////////////////////////////////////////////////////
1042 // Window events, ignored
1043 ////////////////////////////////////////////////////////////////////////
1044 case WM_WINDOWPOSCHANGED:
1045 /* The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place
1046 * in the Z order has changed as a result of a call to the SetWindowPos function or
1047 * another window-management function.
1048 * The WM_SIZE and WM_MOVE messages are not sent if an application handles the
1049 * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
1050 * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
1051 * message without calling DefWindowProc.
1054 /* An application sends the WM_ERASEBKGND message when the window background must be
1055 * erased (for example, when a window is resized). The message is sent to prepare an
1056 * invalidated portion of a window for painting.
1059 /* An application sends the WM_NCPAINT message to a window when its frame must be painted. */
1061 /* The WM_NCACTIVATE message is sent to a window when its nonclient area needs to be changed
1062 * to indicate an active or inactive state.
1065 /* The WM_DESTROY message is sent when a window is being destroyed. It is sent to the window
1066 * procedure of the window being destroyed after the window is removed from the screen.
1067 * This message is sent first to the window being destroyed and then to the child windows
1068 * (if any) as they are destroyed. During the processing of the message, it can be assumed
1069 * that all child windows still exist.
1072 /* The WM_NCDESTROY message informs a window that its nonclient area is being destroyed. The
1073 * DestroyWindow function sends the WM_NCDESTROY message to the window following the WM_DESTROY
1074 * message. WM_DESTROY is used to free the allocated memory object associated with the window.
1078 /* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard focus.
1079 * We want to prevent this if a window is still active and it loses focus to nowhere*/
1080 if(!wParam && hwnd==GetActiveWindow())
1083 /* The WM_SHOWWINDOW message is sent to a window when the window is about to be hidden or shown. */
1084 case WM_WINDOWPOSCHANGING:
1085 /* The WM_WINDOWPOSCHANGING message is sent to a window whose size, position, or place in
1086 * the Z order is about to change as a result of a call to the SetWindowPos function or
1087 * another window-management function.
1090 /* The WM_SETFOCUS message is sent to a window after it has gained the keyboard focus. */
1091 case WM_ENTERSIZEMOVE:
1092 /* The WM_ENTERSIZEMOVE message is sent one time to a window after it enters the moving
1093 * or sizing modal loop. The window enters the moving or sizing modal loop when the user
1094 * clicks the window's title bar or sizing border, or when the window passes the
1095 * WM_SYSCOMMAND message to the DefWindowProc function and the wParam parameter of the
1096 * message specifies the SC_MOVE or SC_SIZE value. The operation is complete when
1097 * DefWindowProc returns.
1101 ////////////////////////////////////////////////////////////////////////
1103 ////////////////////////////////////////////////////////////////////////
1105 /* An application sends a WM_GETTEXT message to copy the text that
1106 * corresponds to a window into a buffer provided by the caller.
1108 case WM_ACTIVATEAPP:
1109 /* The WM_ACTIVATEAPP message is sent when a window belonging to a
1110 * different application than the active window is about to be activated.
1111 * The message is sent to the application whose window is being activated
1112 * and to the application whose window is being deactivated.
1115 /* The WIN32 docs say:
1116 * The WM_TIMER message is posted to the installing thread's message queue
1117 * when a timer expires. You can process the message by providing a WM_TIMER
1118 * case in the window procedure. Otherwise, the default window procedure will
1119 * call the TimerProc callback function specified in the call to the SetTimer
1120 * function used to install the timer.
1122 * In GHOST, we let DefWindowProc call the timer callback.
1125 case WM_BLND_NDOF_AXIS:
1127 GHOST_TEventNDOFData ndofdata;
1128 system->m_ndofManager->GHOST_NDOFGetDatas(ndofdata);
1129 system->m_eventManager->
1130 pushEvent(new GHOST_EventNDOF(
1131 system->getMilliSeconds(),
1132 GHOST_kEventNDOFMotion,
1136 case WM_BLND_NDOF_BTN:
1138 GHOST_TEventNDOFData ndofdata;
1139 system->m_ndofManager->GHOST_NDOFGetDatas(ndofdata);
1140 system->m_eventManager->
1141 pushEvent(new GHOST_EventNDOF(
1142 system->getMilliSeconds(),
1143 GHOST_kEventNDOFButton,
1150 // Event found for a window before the pointer to the class has been set.
1151 GHOST_PRINT("GHOST_SystemWin32::wndProc: GHOST window event before creation\n")
1152 /* These are events we typically miss at this point:
1153 WM_GETMINMAXINFO 0x24
1157 We let DefWindowProc do the work.
1162 // Events without valid hwnd
1163 GHOST_PRINT("GHOST_SystemWin32::wndProc: event without window\n")
1167 system->pushEvent(event);
1170 lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
1175 GHOST_TUns8* GHOST_SystemWin32::getClipboard(bool selection) const
1180 if ( IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(NULL) ) {
1182 HANDLE hData = GetClipboardData( CF_TEXT );
1183 if (hData == NULL) {
1187 buffer = (char*)GlobalLock( hData );
1193 len = strlen(buffer);
1194 temp_buff = (char*) malloc(len+1);
1195 strncpy(temp_buff, buffer, len);
1196 temp_buff[len] = '\0';
1198 /* Buffer mustn't be accessed after CloseClipboard
1199 it would like accessing free-d memory */
1200 GlobalUnlock( hData );
1203 return (GHOST_TUns8*)temp_buff;
1209 void GHOST_SystemWin32::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1211 if(selection) {return;} // for copying the selection, used on X11
1213 if (OpenClipboard(NULL)) {
1220 clipbuffer = LocalAlloc(LMEM_FIXED,((strlen(buffer)+1)));
1221 data = (char*)GlobalLock(clipbuffer);
1223 strcpy(data, (char*)buffer);
1224 data[strlen(buffer)] = '\0';
1225 LocalUnlock(clipbuffer);
1226 SetClipboardData(CF_TEXT,clipbuffer);
1234 int GHOST_SystemWin32::toggleConsole(int action)
1238 case 3: //hide if no console
1240 CONSOLE_SCREEN_BUFFER_INFO csbi = {{0}};
1241 if(!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) || csbi.dwCursorPosition.X || csbi.dwCursorPosition.Y>1)
1245 ShowWindow(GetConsoleWindow(),SW_HIDE);
1246 m_consoleStatus = 0;
1249 ShowWindow(GetConsoleWindow(),SW_SHOW);
1250 m_consoleStatus = 1;
1253 ShowWindow(GetConsoleWindow(),m_consoleStatus?SW_HIDE:SW_SHOW);
1254 m_consoleStatus=!m_consoleStatus;
1260 return m_consoleStatus;