winDispatch.cpp
Engine/source/windowManager/win32/winDispatch.cpp
Classes:
Public Defines
_WIN32_WINNT() 0x0400
dIsStandardVK(c) (((0x08 <= (c)) && ((c) <= 0x12)) || \ ((c) == 0x1b) || \ ((0x20 <= (c)) && ((c) <= 0x2e)) || \ ((0x30 <= (c)) && ((c) <= 0x39)) || \ ((0x41 <= (c)) && ((c) <= 0x5a)) || \ ((0x70 <= (c)) && ((c) <= 0x7B)))
Public Variables
Public Functions
bool
_dispatch(HWND hWnd, UINT message, WPARAM wParam, WPARAM lParam)
_keyboardEvent(Win32Window * window, UINT message, WPARAM wParam, WPARAM lParam)
DIK_to_Key(U8 dikCode)
Dispatch(DispatchType type, HWND hWnd, UINT message, WPARAM wparam, WPARAM lparam)
Dispatch the event into the journaling system.
bool
Dispatch the next event in the delayed dispatch queue.
DispatchRemove(HWND hWnd)
Remove events related to the window from the dispatch queue.
RemoveMessages(HWND hWnd, UINT msgBegin, UINT msgEnd)
Remove messages from the event queue, matching a msg value range or hWnd.
TranslateOSKeyCode(U8 vcode)
Detailed Description
Public Defines
_WIN32_WINNT() 0x0400
dIsStandardVK(c) (((0x08 <= (c)) && ((c) <= 0x12)) || \ ((c) == 0x1b) || \ ((0x20 <= (c)) && ((c) <= 0x2e)) || \ ((0x30 <= (c)) && ((c) <= 0x39)) || \ ((0x41 <= (c)) && ((c) <= 0x5a)) || \ ((0x70 <= (c)) && ((c) <= 0x7B)))
NO_MINMAX()
WIN32_LEAN_AND_MEAN()
Public Variables
WinMessageQueue _MessageQueue
U32 _ModifierKeys
bool initKBState
BYTE keyboardState [256]
bool sgDoubleByteEnabled
Public Functions
_dispatch(HWND hWnd, UINT message, WPARAM wParam, WPARAM lParam)
_keyboardEvent(Win32Window * window, UINT message, WPARAM wParam, WPARAM lParam)
convertModifierBits(const U32 in)
DIK_to_Key(U8 dikCode)
Dispatch(DispatchType type, HWND hWnd, UINT message, WPARAM wparam, WPARAM lparam)
Dispatch the event into the journaling system.
Dispatch Win32 events into the journaling system. To avoid problems with journaling, events should normally use the DelayedDispatch type.
Delayed events are pushed onto a queue for later processing by DispatchNext().
DispatchNext()
Dispatch the next event in the delayed dispatch queue.
This function should be called outside of any journaled calls. Returns true if an event was dispatched.
DispatchRemove(HWND hWnd)
Remove events related to the window from the dispatch queue.
RemoveMessages(HWND hWnd, UINT msgBegin, UINT msgEnd)
Remove messages from the event queue, matching a msg value range or hWnd.
If no filter is specified, either HWND or MessageRange, nothing will be removed You may not match HWND and MsgRange both, currently.
Message Range is calculated as follows.
Both Begin and End are specified as message values, ex WM_MOUSEMOVE
Specifying an identical set of begin/end will remove all messages matching that message value (WM_MOUSEMOVE)
If you specify a range it will remove from that beginning value through the end value
note:Parameters:: The range is useful because on windows messages declared such that you can filter a block of messages just by specifying the beginning value and end.
ex. WM_MOUSEFIRST,WM_MOUSELAST range will match all mouse messages.
hWnd | The HWND to filter by, this cannot be combined with a msg range filter currently |
msgBegin | The beginning msg value to filter from |
msgEnd | The ending msg value to filter to |
TranslateOSKeyCode(U8 vcode)
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#define NO_MINMAX 25#define WIN32_LEAN_AND_MEAN 26 27// This is a fix for mouse wheel support on 28// older versions of VC++. 29#if _MSC_VER < 1500 30#define _WIN32_WINNT 0x0400 31#endif 32 33#include <windows.h> 34 35#include "platform/platformInput.h" 36#include "windowManager/win32/winDispatch.h" 37#include "windowManager/win32/win32Window.h" 38#include "windowManager/win32/win32CursorController.h" 39#include "platformWin32/winDirectInput.h" 40#include "core/util/journal/process.h" 41#include "core/util/journal/journaledSignal.h" 42 43#if !defined( TORQUE_SDL ) 44 45static U32 _ModifierKeys=0; 46static BYTE keyboardState[256]; 47static bool initKBState = false; 48static bool sgDoubleByteEnabled = false; 49 50// is keyboard input a standard (non-changing) VK keycode 51#define dIsStandardVK(c) (((0x08 <= (c)) && ((c) <= 0x12)) || \ 52 ((c) == 0x1b) || \ 53 ((0x20 <= (c)) && ((c) <= 0x2e)) || \ 54 ((0x30 <= (c)) && ((c) <= 0x39)) || \ 55 ((0x41 <= (c)) && ((c) <= 0x5a)) || \ 56 ((0x70 <= (c)) && ((c) <= 0x7B))) 57 58extern InputObjectInstances DIK_to_Key( U8 dikCode ); 59 60extern U8 TranslateOSKeyCode(U8 vcode ); 61 62extern InputModifiers convertModifierBits(const U32 in); 63 64static void _keyboardEvent(Win32Window* window,UINT message, WPARAM wParam, WPARAM lParam) 65{ 66 if(!initKBState) 67 { 68 dMemset(keyboardState, 0, sizeof(keyboardState)); 69 initKBState = true; 70 } 71 72 // Extract windows key info: 73 // S32 repeatCount = (lParam & 0xffff); 74 U32 scanCode = (lParam >> 16) & 0xff; 75 bool extended = lParam & (1 << 24); // Enhanced keyboard key 76 bool previous = lParam & (1 << 30); // Previously down 77 bool make = (message == WM_KEYDOWN || message == WM_SYSKEYDOWN); 78 79 // Translate the OS virtual key code to a Torque KEY_XXXX. 80 S32 nVirtkey = TranslateOSKeyCode( wParam ); 81 82 S32 keyCode; 83 if ( wParam == VK_PROCESSKEY && sgDoubleByteEnabled ) 84 keyCode = MapVirtualKey( scanCode, 1 ); // This is the REAL virtual key... 85 else 86 keyCode = wParam; 87 88 // Convert alt/shift/ctrl to left or right variant if needed. 89 S32 newVirtKey = nVirtkey; 90 switch(nVirtkey) 91 { 92 case KEY_ALT: 93 newVirtKey = extended ? KEY_RALT : KEY_LALT; 94 break; 95 case KEY_CONTROL: 96 newVirtKey = extended ? KEY_RCONTROL : KEY_LCONTROL; 97 break; 98 case KEY_SHIFT: 99 newVirtKey = (scanCode == 54) ? KEY_RSHIFT : KEY_LSHIFT; 100 break; 101 case KEY_RETURN: 102 if ( extended ) 103 newVirtKey = KEY_NUMPADENTER; 104 break; 105 } 106 107 // Track modifier keys 108 U32 modifier = 0; 109 switch (newVirtKey) 110 { 111 case KEY_LALT: modifier = IM_LALT; break; 112 case KEY_RALT: modifier = IM_RALT; break; 113 case KEY_LSHIFT: modifier = IM_LSHIFT; break; 114 case KEY_RSHIFT: modifier = IM_RSHIFT; break; 115 case KEY_LCONTROL: modifier = IM_LCTRL; break; 116 case KEY_RCONTROL: modifier = IM_RCTRL; break; 117 } 118 119 if (make) 120 { 121 _ModifierKeys |= modifier; 122 keyboardState[keyCode] |= 0x80; 123 } 124 else 125 { 126 _ModifierKeys &= ~modifier; 127 keyboardState[keyCode] &= 0x7f; 128 } 129 130 U32 torqueMods = convertModifierBits( _ModifierKeys ); 131 Input::setModifierKeys( torqueMods ); 132 133 // If character event translation is active and this isn't a key 134 // mapped in the global action map, try converting the event into 135 // a character event first. 136 137 if( make 138 && window->getKeyboardTranslation() 139 && !window->shouldNotTranslate( torqueMods, newVirtKey ) ) 140 { 141 wchar_t chars[ 64 ]; 142 dMemset( chars, 0, sizeof( chars ) ); 143 144 S32 res = ToUnicode( keyCode, scanCode, keyboardState, chars, sizeof( chars ) / sizeof( chars[ 0 ] ), 0 ); 145 146 // This should only happen on Window 9x/ME systems 147 if( res == 0 ) 148 res = ToAscii( keyCode, scanCode, keyboardState, (LPWORD)chars, 0 ); 149 150 if( res >= 1 ) 151 { 152 // Post chars, but filter them to not be control codes... this is a bit hacky. 153 154 bool handledCharEvent = false; 155 for( S32 i=0; i< res; i ++ ) 156 if( chars[i] >= 32) 157 { 158 window->charEvent.trigger(window->getWindowId(),_ModifierKeys,chars[i]); 159 handledCharEvent = true; 160 } 161 162 if( handledCharEvent ) 163 return; 164 } 165 } 166 167 // Produce a key event. 168 169 U32 action = make ? (previous ? IA_REPEAT : IA_MAKE ) : IA_BREAK; 170 window->keyEvent.trigger(window->getWindowId(),_ModifierKeys,action,newVirtKey); 171} 172 173 174//----------------------------------------------------------------------------- 175 176static bool _dispatch(HWND hWnd,UINT message,WPARAM wParam,WPARAM lParam) 177{ 178 static bool button[3] = {false,false,false}; 179 static S32 mouseNCState = -1; // -1 denotes unchanged, 180 // 0 denotes changed but was hidden 181 // 1 denotes changed but was visible 182 Win32Window* window = hWnd?(Win32Window*)GetWindowLongPtr(hWnd, GWLP_USERDATA): 0; 183 const WindowId devId = window ? window->getWindowId() : 0; 184 185 // State tracking for focus/lose focus cursor management 186 static bool cursorLocked = false; 187 static bool cursorVisible = true; 188 189 switch(message) 190 { 191 case WM_MOUSEMOVE: 192 { 193 // Skip it if we have no window! 194 if (!window || !window->getCursorController()) 195 break; 196 197 198 PlatformCursorController *pController = window->getCursorController(); 199 200 // If we're locked and unfocused, ignore it. 201 if(window->shouldLockMouse() && !window->isFocused()) 202 break; 203 204 // If the mouse was shown to accommodate a NC mouse move 205 // we need to change it back to what it was 206 if( mouseNCState != -1 ) 207 { 208 pController->setCursorVisible( mouseNCState ); 209 mouseNCState = -1; // reset to unchanged 210 } 211 212 // Let the cursor manager update the native cursor. 213 pController->refreshCursor(); 214 215 // Grab the mouse pos so we can modify it. 216 S32 mouseX = S16(LOWORD(lParam)); 217 S32 mouseY = S16(HIWORD(lParam)); 218 219 // Ensure mouse lock when appropriate 220 window->setMouseLocked( window->shouldLockMouse() ); 221 222 // Are we locked? 223 if(window->isMouseLocked()) 224 { 225 // Always invisible when locked. 226 if( window->isCursorVisible() ) 227 window->setCursorVisible( false ); 228 229 RECT r; 230 GetWindowRect(window->getHWND(), &r); 231 232 // See Win32Window::setMouseLocked for explanation 233 RECT rCopy = r; 234 rCopy.top += 32; rCopy.bottom -= 64; 235 rCopy.left += 32; rCopy.right -= 64; 236 ClipCursor(&rCopy); 237 238 // Recenter the mouse if necessary (don't flood the message pump) 239 Point2I curPos; 240 pController->getCursorPosition( curPos ); 241 242 const S32 centerX = (r.right + r.left) / 2; 243 const S32 centerY = (r.bottom + r.top) / 2; 244 245 if( curPos.x != centerX || curPos.y != centerY ) 246 pController->setCursorPosition(centerX, centerY); 247 248 // Convert the incoming client pos into a screen pos, so we can 249 // accurately convert to relative coordinates. 250 POINT mousePos; 251 mousePos.x = mouseX; 252 mousePos.y = mouseY; 253 254 ClientToScreen(window->getHWND(), &mousePos); 255 256 // Now we can calculate the position relative to the center we set. 257 mouseX = mousePos.x - centerX; 258 mouseY = mousePos.y - centerY; 259 } 260 else 261 { 262 // Probably don't need to call this all the time but better 263 // safe than sorry... 264 ClipCursor(NULL); 265 } 266 267 window->mouseEvent.trigger(devId,_ModifierKeys,mouseX,mouseY,window->isMouseLocked()); 268 break; 269 } 270 271 // We want to show the system cursor whenever we leave 272 // our window, and it'd be simple, except for one problem: 273 // showcursor isn't a toggle. so, keep hammering it until 274 // the cursor is *actually* going to be shown. 275 case WM_NCMOUSEMOVE: 276 { 277 if( window ) 278 { 279 mouseNCState = ( window->isCursorVisible() ? 1 : 0); 280 window->setCursorVisible( true ); 281 } 282 283 break; 284 } 285 286 case WM_LBUTTONDOWN: 287 case WM_MBUTTONDOWN: 288 case WM_RBUTTONDOWN: { 289 S32 index = (message - WM_LBUTTONDOWN) / 3; 290 button[index] = true; 291 292 // Capture the mouse on button down to allow dragging outside 293 // of the window boundary. 294 if (GetCapture() != hWnd) 295 SetCapture(hWnd); 296 297 if (window) 298 window->buttonEvent.trigger(devId,_ModifierKeys,IA_MAKE,index); 299 break; 300 } 301 302 case WM_LBUTTONUP: 303 case WM_MBUTTONUP: 304 case WM_RBUTTONUP: { 305 S32 index = (message - WM_LBUTTONUP) / 3; 306 button[index] = false; 307 308 // Release mouse capture from button down. 309 if (!button[0] && !button[1] && !button[2]) 310 ReleaseCapture(); 311 312 if (window) 313 window->buttonEvent.trigger(devId,_ModifierKeys,IA_BREAK,index); 314 break; 315 } 316 317 case WM_MOUSEWHEEL: // Vertical wheel. 318 if (window) 319 window->wheelEvent.trigger(devId,_ModifierKeys,0,GET_WHEEL_DELTA_WPARAM(wParam)); 320 break; 321 322#ifdef WM_MOUSEHWHEEL // Vista 323 case WM_MOUSEHWHEEL: // Horizontal wheel. 324 if( window ) 325 window->wheelEvent.trigger( devId, _ModifierKeys, GET_WHEEL_DELTA_WPARAM( wParam ), 0 ); 326 break; 327#endif 328 329 case WM_KEYUP: 330 case WM_SYSKEYUP: 331 case WM_KEYDOWN: 332 case WM_SYSKEYDOWN: 333 if (window) 334 _keyboardEvent(window,message,wParam,lParam); 335 break; 336 337 // NOTE: if wParam is NOT equal to our window handle then we are GAINING focus 338 case WM_SETFOCUS: 339 340 // clear any key states 341 _ModifierKeys = 0; 342 dMemset(keyboardState, 0, 256); 343 Input::setModifierKeys(0); 344 345 // We must have a window present; otherwise there's nothing further 346 // we can do about this event. 347 if (window && window->getHWND() != (HWND)wParam) 348 { 349 if (cursorVisible == false) 350 window->setCursorVisible(false); 351 352 if (cursorLocked == true) 353 window->setMouseLocked(true); 354 355 // Update window state. 356 window->setBackground(false); 357 358 // Fire event. 359 window->appEvent.trigger(devId, GainFocus); 360 361 if (!Input::isActive()) 362 Input::activate(); 363 } 364 break; 365 366 // NOTE: if wParam is NOT equal to our window handle then we are LOSING focus 367 case WM_KILLFOCUS: 368 369 // clear any key states 370 _ModifierKeys = 0; 371 dMemset(keyboardState, 0, 256); 372 Input::setModifierKeys(0); 373 374 // We must have a window present; otherwise there's nothing further 375 // we can do about this event. 376 if (window && window->getHWND() != (HWND)wParam) 377 { 378 HWND hwnd = (HWND)wParam; 379 UTF16 classBuf[256]; 380 381 if (hwnd) 382 GetClassName(hwnd, classBuf, sizeof(classBuf)); 383 384 // We toggle the mouse lock when we become inactive 385 // causing the subsequent lock call to defer itself 386 // until the window becomes active again. 387 if (window && window->isMouseLocked()) 388 { 389 window->setMouseLocked( false ); 390 window->setMouseLocked( true ); 391 } 392 393 // FIXME [tom, 5/1/2007] Hard coding this is lame since there's a const in win32Window.cpp 394 // CodeReview - this fails if there is a second jug app in the arena. 395 if (hwnd == NULL || String::compare(classBuf, L"TorqueJuggernaughtWindow") != 0) 396 { 397 // We are being made inactive and the window being made active isn't 398 // a jugg window. Thus, we need to deactivate input. 399 if (Input::isActive()) 400 Input::deactivate(); 401 } 402 403 cursorVisible = window->isCursorVisible(); 404 if (!cursorVisible) 405 window->setCursorVisible(true); 406 407 cursorLocked = window->isMouseLocked(); 408 if (cursorLocked) 409 window->setMouseLocked(false); 410 411 // Update window state. 412 window->setBackground(true); 413 414 // Fire event. 415 window->appEvent.trigger(devId, LoseFocus); 416 } 417 break; 418 419 case WM_ACTIVATEAPP: 420 if (wParam) 421 { 422 // Could extract current modifier state from windows. 423 _ModifierKeys = 0; 424 Input::setModifierKeys(_ModifierKeys); 425 } 426 break; 427 428 case WM_CLOSE: 429 if (window) 430 window->appEvent.trigger(devId,WindowClose); 431 432 // Force a quit if we're in play mode, otherwise there would be 433 // no way to stop a journal playback.( 434 if (Journal::IsPlaying()) 435 Process::requestShutdown(); 436 break; 437 438 case WM_TIMER: { 439 if (window) 440 window->appEvent.trigger(devId,Timer); 441 break; 442 } 443 444 case WM_DESTROY:{ 445 // Only people who care about this currently are web plugins, because 446 // everyone else will just handle the WM_CLOSE app event. 447 if(window) 448 window->appEvent.trigger(devId,WindowDestroy); 449 break; 450 } 451 452 case WM_QUIT: { 453 // Quit indicates that we're not going to receive anymore Win32 messages. 454 // Therefore, it's appropriate to flag our event loop for exit as well, 455 // since we won't be getting any more messages. 456 Process::requestShutdown((S32)wParam); 457 break; 458 } 459 460 // CodeReview - This is not used now and will incur an overhead for rendering 461 // since the renderThreadBlocked fix requires handling WM_PAINT and 462 // triggering the displayEvent. May need to revisit this at a later 463 // time if we want event driven rendering. 464 //case WM_PAINT: { 465 // // Checking for isOpen will keep us from generating an event 466 // // during the window creation process, which can cause problems 467 // // with the journaling. 468 // if (window && window->isOpen() && !winState.renderThreadBlocked ) 469 // window->displayEvent.trigger(devId); 470 //} 471 case WM_SIZE: { 472 if (window && wParam != SIZE_MINIMIZED && !Journal::IsPlaying()) 473 { 474 window->resizeEvent.trigger(window->getWindowId(), LOWORD(lParam),HIWORD(lParam)); 475 476 // Consume all existing mouse events and those posted to our own dispatch queue 477 MSG msg; 478 PeekMessage( &msg, 0,WM_MOUSEFIRST,WM_MOUSELAST , PM_QS_POSTMESSAGE | PM_NOYIELD | PM_REMOVE ); 479 RemoveMessages( NULL, WM_MOUSEMOVE, WM_MOUSEMOVE ); 480 481 if( window->isMouseLocked()) 482 { 483 RECT r; 484 GetWindowRect(window->getHWND(), &r); 485 486 S32 centerX = (r.right + r.left) >> 1; 487 S32 centerY = ((r.bottom + r.top) >> 1); 488 window->setCursorPosition( centerX, centerY ); 489 490 // Set the CursorPos 491 SetCursorPos(centerX, centerY); 492 } 493 } 494 495 } 496 497 } 498 return true; 499} 500 501 502//----------------------------------------------------------------------------- 503 504// Structure used to store Windows events for delayed dispatching 505struct WinMessageQueue 506{ 507public: 508 struct Message { 509 HWND hWnd; 510 UINT message; 511 WPARAM wparam; 512 WPARAM lparam; 513 }; 514 515 WinMessageQueue() 516 { 517 VECTOR_SET_ASSOCIATION( _messageList ); 518 } 519 bool isEmpty() { 520 return !_messageList.size(); 521 } 522 void post(HWND hWnd,UINT message,WPARAM wparam,WPARAM lparam) { 523 Message msg; 524 msg.hWnd = hWnd; 525 msg.message = message; 526 msg.wparam = wparam; 527 msg.lparam = lparam; 528 _messageList.push_back(msg); 529 } 530 bool next(Message* msg) { 531 if (!_messageList.size()) 532 return false; 533 *msg = _messageList.first(); 534 _messageList.pop_front(); 535 return true; 536 } 537 void remove(HWND hWnd, UINT msgBegin = -1, UINT msgEnd = -1) { 538 for (S32 i = 0; i < _messageList.size(); i++) 539 { 540 // Match Window 541 if( hWnd != NULL && _messageList[i].hWnd == hWnd) 542 _messageList.erase_fast(i--); 543 else if( msgBegin != -1 && msgEnd != -1 ) 544 { 545 // CodeReview - Match Message Range [6/30/2007 justind] 546 // 547 // Word of caution : Use this only if you know what you're doing. 548 // I cannot be responsible for you blowing your leg off destroying 549 // a bunch of messages you didn't intend to if you specify a ridiculous 550 // range of messages values. 551 // 552 // To filter a single message, pass the message as the begin and end. 553 if( _messageList[i].message >= msgBegin && _messageList[i].message <= msgEnd ) 554 _messageList.erase_fast(i--); 555 } 556 } 557 } 558 559private: 560 Vector<Message> _messageList; 561}; 562 563static WinMessageQueue _MessageQueue; 564 565 566void RemoveMessages(HWND hWnd,UINT msgBegin,UINT msgEnd ) 567{ 568 _MessageQueue.remove( hWnd, msgBegin, msgEnd ); 569} 570 571// Dispatch the window event, or queue up for later 572void Dispatch(DispatchType type,HWND hWnd,UINT message,WPARAM wparam,WPARAM lparam) 573{ 574 // If the message queue is not empty, then we'll need to delay 575 // this dispatch in order to preserve message order. 576 if (type == DelayedDispatch || !_MessageQueue.isEmpty()) 577 _MessageQueue.post(hWnd,message,wparam,lparam); 578 else 579 _dispatch(hWnd,message,wparam,lparam); 580} 581 582// Dispatch next even in the queue 583bool DispatchNext() 584{ 585 WinMessageQueue::Message msg; 586 if (!_MessageQueue.next(&msg)) 587 return false; 588 _dispatch(msg.hWnd,msg.message,msg.wparam,msg.lparam); 589 return true; 590} 591 592// Remove events from the queue 593void DispatchRemove(HWND hWnd) 594{ 595 _MessageQueue.remove(hWnd); 596} 597 598 599#endif 600