lowpoly-walking-simulator/directx11_hellovr/DirectXTK/Src/Keyboard.cpp

521 lines
12 KiB
C++
Raw Permalink Normal View History

2024-11-14 11:54:38 +00:00
//--------------------------------------------------------------------------------------
// 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<uint32_t*>(&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<uint32_t*>(&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<ActivatedHandler>(Activated).Get(), &mActivatedToken);
ThrowIfFailed(hr);
ComPtr<ICoreDispatcher> dispatcher;
hr = window->get_Dispatcher( dispatcher.GetAddressOf() );
ThrowIfFailed(hr);
ComPtr<ICoreAcceleratorKeys> keys;
hr = dispatcher.As(&keys);
ThrowIfFailed(hr);
typedef __FITypedEventHandler_2_Windows__CUI__CCore__CCoreDispatcher_Windows__CUI__CCore__CAcceleratorKeyEventArgs AcceleratorKeyHandler;
hr = keys->add_AcceleratorKeyActivated( Callback<AcceleratorKeyHandler>(AcceleratorKeyEvent).Get(), &mAcceleratorKeyToken);
ThrowIfFailed(hr);
}
State mState;
Keyboard* mOwner;
static Keyboard::Impl* s_keyboard;
private:
ComPtr<ABI::Windows::UI::Core::ICoreWindow> mWindow;
EventRegistrationToken mAcceleratorKeyToken;
EventRegistrationToken mActivatedToken;
void RemoveHandlers()
{
if (mWindow)
{
using namespace ABI::Windows::UI::Core;
ComPtr<ICoreDispatcher> dispatcher;
HRESULT hr = mWindow->get_Dispatcher( dispatcher.GetAddressOf() );
ThrowIfFailed(hr);
mWindow->remove_Activated(mActivatedToken);
mActivatedToken.value = 0;
ComPtr<ICoreAcceleratorKeys> 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<int>( 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<int>( 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<const uint32_t*>(&state);
auto prevPtr = reinterpret_cast<const uint32_t*>(&lastState);
auto releasedPtr = reinterpret_cast<uint32_t*>(&released);
auto pressedPtr = reinterpret_cast<uint32_t*>(&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) );
}