521 lines
12 KiB
C++
521 lines
12 KiB
C++
//--------------------------------------------------------------------------------------
|
|
// 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) );
|
|
}
|