template project, first version
This commit is contained in:
557
DirectXTK/Src/Keyboard.cpp
Normal file
557
DirectXTK/Src/Keyboard.cpp
Normal file
@@ -0,0 +1,557 @@
|
||||
//--------------------------------------------------------------------------------------
|
||||
// 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 Platform (UWP) app implementation
|
||||
//======================================================================================
|
||||
|
||||
//
|
||||
// For a Windows Store app or Universal Windows Platform (UWP) app, add the following:
|
||||
//
|
||||
// void App::SetWindow(CoreWindow^ window )
|
||||
// {
|
||||
// m_keyboard->SetWindow(window);
|
||||
// }
|
||||
//
|
||||
|
||||
#include <Windows.Devices.Input.h>
|
||||
|
||||
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) );
|
||||
}
|
||||
|
||||
bool IsConnected() const
|
||||
{
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace Microsoft::WRL::Wrappers;
|
||||
using namespace ABI::Windows::Devices::Input;
|
||||
using namespace ABI::Windows::Foundation;
|
||||
|
||||
ComPtr<IKeyboardCapabilities> caps;
|
||||
HRESULT hr = RoActivateInstance(HStringReference(RuntimeClass_Windows_Devices_Input_KeyboardCapabilities).Get(), &caps);
|
||||
ThrowIfFailed(hr);
|
||||
|
||||
INT32 value;
|
||||
if (SUCCEEDED(caps->get_KeyboardPresent(&value)))
|
||||
{
|
||||
return value != 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
(void)mWindow->remove_Activated(mActivatedToken);
|
||||
mActivatedToken.value = 0;
|
||||
|
||||
ComPtr<ICoreAcceleratorKeys> keys;
|
||||
hr = dispatcher.As(&keys);
|
||||
ThrowIfFailed(hr);
|
||||
|
||||
(void)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()
|
||||
{
|
||||
}
|
||||
|
||||
bool IsConnected() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
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) );
|
||||
}
|
||||
|
||||
bool IsConnected() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
bool Keyboard::IsConnected() const
|
||||
{
|
||||
return pImpl->IsConnected();
|
||||
}
|
||||
|
||||
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) );
|
||||
}
|
||||
Reference in New Issue
Block a user