//-------------------------------------------------------------------------------------- // File: Keyboard.cpp // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved. // // http://go.microsoft.com/fwlink/?LinkId=248929 //-------------------------------------------------------------------------------------- #include "pch.h" #include "Keyboard.h" #include "PlatformHelpers.h" using namespace DirectX; using Microsoft::WRL::ComPtr; static_assert(sizeof(Keyboard::State) == (256 / 8), "Size mismatch for State"); namespace { void KeyDown(int key, Keyboard::State& state) { if (key < 0 || key > 0xfe) return; auto ptr = reinterpret_cast(&state); unsigned int bf = 1u << (key & 0x1f); ptr[(key >> 5)] |= bf; } void KeyUp(int key, Keyboard::State& state) { if (key < 0 || key > 0xfe) return; auto ptr = reinterpret_cast(&state); unsigned int bf = 1u << (key & 0x1f); ptr[(key >> 5)] &= ~bf; } } #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) //====================================================================================== // Windows Store or universal Windows app implementation //====================================================================================== // // For a Windows Store app or universal Windows app, add the following: // // void App::SetWindow(CoreWindow^ window ) // { // m_keyboard->SetWindow(window); // } // class Keyboard::Impl { public: Impl(Keyboard* owner) : mOwner(owner) { mAcceleratorKeyToken.value = 0; mActivatedToken.value = 0; if ( s_keyboard ) { throw std::exception( "Keyboard is a singleton" ); } s_keyboard = this; memset( &mState, 0, sizeof(State) ); } ~Impl() { s_keyboard = nullptr; RemoveHandlers(); } void GetState(State& state) const { memcpy( &state, &mState, sizeof(State) ); } void Reset() { memset( &mState, 0, sizeof(State) ); } void SetWindow(ABI::Windows::UI::Core::ICoreWindow* window) { using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; using namespace ABI::Windows::UI::Core; if (mWindow.Get() == window) return; RemoveHandlers(); mWindow = window; if (!window) return; typedef __FITypedEventHandler_2_Windows__CUI__CCore__CCoreWindow_Windows__CUI__CCore__CWindowActivatedEventArgs ActivatedHandler; HRESULT hr = window->add_Activated(Callback(Activated).Get(), &mActivatedToken); ThrowIfFailed(hr); ComPtr dispatcher; hr = window->get_Dispatcher( dispatcher.GetAddressOf() ); ThrowIfFailed(hr); ComPtr keys; hr = dispatcher.As(&keys); ThrowIfFailed(hr); typedef __FITypedEventHandler_2_Windows__CUI__CCore__CCoreDispatcher_Windows__CUI__CCore__CAcceleratorKeyEventArgs AcceleratorKeyHandler; hr = keys->add_AcceleratorKeyActivated( Callback(AcceleratorKeyEvent).Get(), &mAcceleratorKeyToken); ThrowIfFailed(hr); } State mState; Keyboard* mOwner; static Keyboard::Impl* s_keyboard; private: ComPtr mWindow; EventRegistrationToken mAcceleratorKeyToken; EventRegistrationToken mActivatedToken; void RemoveHandlers() { if (mWindow) { using namespace ABI::Windows::UI::Core; ComPtr dispatcher; HRESULT hr = mWindow->get_Dispatcher( dispatcher.GetAddressOf() ); ThrowIfFailed(hr); mWindow->remove_Activated(mActivatedToken); mActivatedToken.value = 0; ComPtr keys; hr = dispatcher.As(&keys); ThrowIfFailed(hr); keys->remove_AcceleratorKeyActivated(mAcceleratorKeyToken); mAcceleratorKeyToken.value = 0; } } static HRESULT Activated( IInspectable *, ABI::Windows::UI::Core::IWindowActivatedEventArgs* ) { auto pImpl = Impl::s_keyboard; if (!pImpl) return S_OK; pImpl->Reset(); return S_OK; } static HRESULT AcceleratorKeyEvent( IInspectable *, ABI::Windows::UI::Core::IAcceleratorKeyEventArgs* args ) { using namespace ABI::Windows::System; using namespace ABI::Windows::UI::Core; auto pImpl = Impl::s_keyboard; if (!pImpl) return S_OK; CoreAcceleratorKeyEventType evtType; HRESULT hr = args->get_EventType(&evtType); ThrowIfFailed(hr); bool down = false; switch (evtType) { case CoreAcceleratorKeyEventType_KeyDown: case CoreAcceleratorKeyEventType_SystemKeyDown: down = true; break; case CoreAcceleratorKeyEventType_KeyUp: case CoreAcceleratorKeyEventType_SystemKeyUp: break; default: return S_OK; } CorePhysicalKeyStatus status; hr = args->get_KeyStatus(&status); ThrowIfFailed(hr); VirtualKey virtualKey; hr = args->get_VirtualKey(&virtualKey); ThrowIfFailed(hr); int vk = static_cast( virtualKey ); switch (vk) { case VK_SHIFT: vk = (status.ScanCode == 0x36) ? VK_RSHIFT : VK_LSHIFT; if ( !down ) { // Workaround to ensure left vs. right shift get cleared when both were pressed at same time KeyUp(VK_LSHIFT, pImpl->mState); KeyUp(VK_RSHIFT, pImpl->mState); } break; case VK_CONTROL: vk = (status.IsExtendedKey) ? VK_RCONTROL : VK_LCONTROL; break; case VK_MENU: vk = (status.IsExtendedKey) ? VK_RMENU : VK_LMENU; break; } if (down) { KeyDown(vk, pImpl->mState); } else { KeyUp(vk, pImpl->mState); } return S_OK; } }; Keyboard::Impl* Keyboard::Impl::s_keyboard = nullptr; void Keyboard::SetWindow(ABI::Windows::UI::Core::ICoreWindow* window) { pImpl->SetWindow(window); } #elif defined(_XBOX_ONE) || ( defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) ) //====================================================================================== // Null device for Windows Phone and Xbox One //====================================================================================== class Keyboard::Impl { public: Impl(Keyboard* owner) : mOwner(owner) { if ( s_keyboard ) { throw std::exception( "Keyboard is a singleton" ); } s_keyboard = this; } ~Impl() { s_keyboard = nullptr; } void GetState(State& state) const { memset( &state, 0, sizeof(State) ); } void Reset() { } Keyboard* mOwner; static Keyboard::Impl* s_keyboard; }; Keyboard::Impl* Keyboard::Impl::s_keyboard = nullptr; #else //====================================================================================== // Win32 desktop implementation //====================================================================================== // // For a Win32 desktop application, call this function from your Window Message Procedure // // LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // { // switch (message) // { // // case WM_ACTIVATEAPP: // Keyboard::ProcessMessage(message, wParam, lParam); // break; // // case WM_KEYDOWN: // case WM_SYSKEYDOWN: // case WM_KEYUP: // case WM_SYSKEYUP: // Keyboard::ProcessMessage(message, wParam, lParam); // break; // // } // } // class Keyboard::Impl { public: Impl(Keyboard* owner) : mOwner(owner) { if ( s_keyboard ) { throw std::exception( "Keyboard is a singleton" ); } s_keyboard = this; memset( &mState, 0, sizeof(State) ); } ~Impl() { s_keyboard = nullptr; } void GetState(State& state) const { memcpy( &state, &mState, sizeof(State) ); } void Reset() { memset( &mState, 0, sizeof(State) ); } State mState; Keyboard* mOwner; static Keyboard::Impl* s_keyboard; }; Keyboard::Impl* Keyboard::Impl::s_keyboard = nullptr; void Keyboard::ProcessMessage(UINT message, WPARAM wParam, LPARAM lParam) { auto pImpl = Impl::s_keyboard; if (!pImpl) return; bool down = false; switch (message) { case WM_ACTIVATEAPP: pImpl->Reset(); return; case WM_KEYDOWN: case WM_SYSKEYDOWN: down = true; break; case WM_KEYUP: case WM_SYSKEYUP: break; default: return; } int vk = static_cast( wParam ); switch (vk) { case VK_SHIFT: vk = MapVirtualKey((lParam & 0x00ff0000) >> 16, MAPVK_VSC_TO_VK_EX); if ( !down ) { // Workaround to ensure left vs. right shift get cleared when both were pressed at same time KeyUp(VK_LSHIFT, pImpl->mState); KeyUp(VK_RSHIFT, pImpl->mState); } break; case VK_CONTROL: vk = (lParam & 0x01000000) ? VK_RCONTROL : VK_LCONTROL; break; case VK_MENU: vk = (lParam & 0x01000000) ? VK_RMENU : VK_LMENU; break; } if (down) { KeyDown(vk, pImpl->mState); } else { KeyUp(vk, pImpl->mState); } } #endif #pragma warning( disable : 4355 ) // Public constructor. Keyboard::Keyboard() : pImpl( new Impl(this) ) { } // Move constructor. Keyboard::Keyboard(Keyboard&& moveFrom) : pImpl(std::move(moveFrom.pImpl)) { pImpl->mOwner = this; } // Move assignment. Keyboard& Keyboard::operator= (Keyboard&& moveFrom) { pImpl = std::move(moveFrom.pImpl); pImpl->mOwner = this; return *this; } // Public destructor. Keyboard::~Keyboard() { } Keyboard::State Keyboard::GetState() const { State state; pImpl->GetState(state); return state; } void Keyboard::Reset() { pImpl->Reset(); } Keyboard& Keyboard::Get() { if ( !Impl::s_keyboard || !Impl::s_keyboard->mOwner ) throw std::exception( "Keyboard is a singleton" ); return *Impl::s_keyboard->mOwner; } //====================================================================================== // KeyboardStateTracker //====================================================================================== void Keyboard::KeyboardStateTracker::Update( const State& state ) { auto currPtr = reinterpret_cast(&state); auto prevPtr = reinterpret_cast(&lastState); auto releasedPtr = reinterpret_cast(&released); auto pressedPtr = reinterpret_cast(&pressed); for (size_t j = 0; j < (256 / 32); ++j) { *pressedPtr = *currPtr & ~(*prevPtr); *releasedPtr = ~(*currPtr) & *prevPtr; ++currPtr; ++prevPtr; ++releasedPtr; ++pressedPtr; } lastState = state; } void Keyboard::KeyboardStateTracker::Reset() { memset( this, 0, sizeof(KeyboardStateTracker) ); }