diff --git a/X1nput.sln b/X1nput.sln new file mode 100644 index 0000000..caea9a4 --- /dev/null +++ b/X1nput.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.168 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XInput1_3", "X1nput\XInput.vcxproj", "{C565022D-170F-427F-B2D9-D591D15562EA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C565022D-170F-427F-B2D9-D591D15562EA}.Debug|x64.ActiveCfg = Debug|x64 + {C565022D-170F-427F-B2D9-D591D15562EA}.Debug|x64.Build.0 = Debug|x64 + {C565022D-170F-427F-B2D9-D591D15562EA}.Debug|x86.ActiveCfg = Debug|Win32 + {C565022D-170F-427F-B2D9-D591D15562EA}.Debug|x86.Build.0 = Debug|Win32 + {C565022D-170F-427F-B2D9-D591D15562EA}.Release|x64.ActiveCfg = Release|x64 + {C565022D-170F-427F-B2D9-D591D15562EA}.Release|x64.Build.0 = Release|x64 + {C565022D-170F-427F-B2D9-D591D15562EA}.Release|x86.ActiveCfg = Release|Win32 + {C565022D-170F-427F-B2D9-D591D15562EA}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D3D39BE6-F6F3-4754-B735-9E32BD98ACDC} + EndGlobalSection +EndGlobal diff --git a/X1nput/GamePad.cpp b/X1nput/GamePad.cpp new file mode 100644 index 0000000..511e6fb --- /dev/null +++ b/X1nput/GamePad.cpp @@ -0,0 +1,1394 @@ +//-------------------------------------------------------------------------------------- +// File: GamePad.cpp +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#include "stdafx.h" + +#include "GamePad.h" +#include "PlatformHelpers.h" + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + + +namespace +{ + const float c_XboxOneThumbDeadZone = .24f; // Recommended Xbox One controller deadzone + + float ApplyLinearDeadZone(float value, float maxValue, float deadZoneSize) + { + if (value < -deadZoneSize) + { + // Increase negative values to remove the deadzone discontinuity. + value += deadZoneSize; + } + else if (value > deadZoneSize) + { + // Decrease positive values to remove the deadzone discontinuity. + value -= deadZoneSize; + } + else + { + // Values inside the deadzone come out zero. + return 0; + } + + // Scale into 0-1 range. + float scaledValue = value / (maxValue - deadZoneSize); + return std::max(-1.f, std::min(scaledValue, 1.f)); + } + + void ApplyStickDeadZone(float x, float y, GamePad::DeadZone deadZoneMode, float maxValue, float deadZoneSize, + _Out_ float& resultX, _Out_ float& resultY) + { + switch (deadZoneMode) + { + case GamePad::DEAD_ZONE_INDEPENDENT_AXES: + resultX = ApplyLinearDeadZone(x, maxValue, deadZoneSize); + resultY = ApplyLinearDeadZone(y, maxValue, deadZoneSize); + break; + + case GamePad::DEAD_ZONE_CIRCULAR: + { + float dist = sqrtf(x*x + y * y); + float wanted = ApplyLinearDeadZone(dist, maxValue, deadZoneSize); + + float scale = (wanted > 0.f) ? (wanted / dist) : 0.f; + + resultX = std::max(-1.f, std::min(x * scale, 1.f)); + resultY = std::max(-1.f, std::min(y * scale, 1.f)); + } + break; + + default: // GamePad::DEAD_ZONE_NONE + resultX = ApplyLinearDeadZone(x, maxValue, 0); + resultY = ApplyLinearDeadZone(y, maxValue, 0); + break; + } + } +} + + +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN10) + +//====================================================================================== +// Windows::Gaming::Input (Windows 10) +//====================================================================================== + +#pragma warning(push) +#pragma warning(disable : 4471) +#include +#pragma warning(pop) + +#include +#include +#include +#include +#include +#pragma comment(lib, "runtimeobject.lib") + + +class GamePad::Impl +{ +public: + Impl(GamePad* owner) : + mOwner(owner), + mCtrlChanged(INVALID_HANDLE_VALUE), + mUserChanged(INVALID_HANDLE_VALUE), + mMostRecentGamepad(0), + mStatics{}, + mGamePad{}, + mUserChangeToken{}, + mAddedToken{}, + mRemovedToken{}, + mChanged{} + { + using namespace Microsoft::WRL; + using namespace Microsoft::WRL::Wrappers; + using namespace ABI::Windows::Foundation; + + if (s_gamePad) + { + throw std::exception("GamePad is a singleton"); + } + + s_gamePad = this; + + mChanged.reset(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); + if (!mChanged) + { + throw std::exception("CreateEventEx"); + } + + ThrowIfFailed(GetActivationFactory(HStringReference(RuntimeClass_Windows_Gaming_Input_Gamepad).Get(), mStatics.GetAddressOf())); + + typedef __FIEventHandler_1_Windows__CGaming__CInput__CGamepad AddedHandler; + ThrowIfFailed(mStatics->add_GamepadAdded(Callback(GamepadAdded).Get(), &mAddedToken)); + + typedef __FIEventHandler_1_Windows__CGaming__CInput__CGamepad RemovedHandler; + ThrowIfFailed(mStatics->add_GamepadRemoved(Callback(GamepadRemoved).Get(), &mRemovedToken)); + + ScanGamePads(); + } + + ~Impl() + { + using namespace ABI::Windows::Gaming::Input; + + for (size_t j = 0; j < MAX_PLAYER_COUNT; ++j) + { + if (mGamePad[j]) + { + ComPtr ctrl; + HRESULT hr = mGamePad[j].As(&ctrl); + if (SUCCEEDED(hr) && ctrl) + { + (void)ctrl->remove_UserChanged(mUserChangeToken[j]); + mUserChangeToken[j].value = 0; + } + + mGamePad[j].Reset(); + } + } + + if (mStatics) + { + (void)mStatics->remove_GamepadAdded(mAddedToken); + mAddedToken.value = 0; + + (void)mStatics->remove_GamepadRemoved(mRemovedToken); + mRemovedToken.value = 0; + + mStatics.Reset(); + } + + s_gamePad = nullptr; + } + + void GetState(int player, _Out_ State& state, DeadZone deadZoneMode) + { + using namespace Microsoft::WRL; + using namespace ABI::Windows::Gaming::Input; + + if (WaitForSingleObjectEx(mChanged.get(), 0, FALSE) == WAIT_OBJECT_0) + { + ScanGamePads(); + } + + if (player == -1) + player = mMostRecentGamepad; + + if ((player >= 0) && (player < MAX_PLAYER_COUNT)) + { + if (mGamePad[player]) + { + GamepadReading reading; + HRESULT hr = mGamePad[player]->GetCurrentReading(&reading); + if (SUCCEEDED(hr)) + { + state.connected = true; + state.packet = reading.Timestamp; + + state.buttons.a = (reading.Buttons & GamepadButtons::GamepadButtons_A) != 0; + state.buttons.b = (reading.Buttons & GamepadButtons::GamepadButtons_B) != 0; + state.buttons.x = (reading.Buttons & GamepadButtons::GamepadButtons_X) != 0; + state.buttons.y = (reading.Buttons & GamepadButtons::GamepadButtons_Y) != 0; + + state.buttons.leftStick = (reading.Buttons & GamepadButtons::GamepadButtons_LeftThumbstick) != 0; + state.buttons.rightStick = (reading.Buttons & GamepadButtons::GamepadButtons_RightThumbstick) != 0; + + state.buttons.leftShoulder = (reading.Buttons & GamepadButtons::GamepadButtons_LeftShoulder) != 0; + state.buttons.rightShoulder = (reading.Buttons & GamepadButtons::GamepadButtons_RightShoulder) != 0; + + state.buttons.back = (reading.Buttons & GamepadButtons::GamepadButtons_View) != 0; + state.buttons.start = (reading.Buttons & GamepadButtons::GamepadButtons_Menu) != 0; + + state.dpad.up = (reading.Buttons & GamepadButtons::GamepadButtons_DPadUp) != 0; + state.dpad.down = (reading.Buttons & GamepadButtons::GamepadButtons_DPadDown) != 0; + state.dpad.right = (reading.Buttons & GamepadButtons::GamepadButtons_DPadRight) != 0; + state.dpad.left = (reading.Buttons & GamepadButtons::GamepadButtons_DPadLeft) != 0; + + state.thumbSticks.leftX = reading.LeftThumbstickX; + state.thumbSticks.leftY = reading.LeftThumbstickY; + state.thumbSticks.rightX = reading.RightThumbstickX; + state.thumbSticks.rightY = reading.RightThumbstickY; + + ApplyStickDeadZone(static_cast(reading.LeftThumbstickX), static_cast(reading.LeftThumbstickY), + deadZoneMode, 1.f, c_XboxOneThumbDeadZone, + state.thumbSticks.leftX, state.thumbSticks.leftY); + + ApplyStickDeadZone(static_cast(reading.RightThumbstickX), static_cast(reading.RightThumbstickY), + deadZoneMode, 1.f, c_XboxOneThumbDeadZone, + state.thumbSticks.rightX, state.thumbSticks.rightY); + + state.triggers.left = static_cast(reading.LeftTrigger); + state.triggers.right = static_cast(reading.RightTrigger); + + return; + } + } + } + + memset(&state, 0, sizeof(State)); + } + + void GetCapabilities(int player, Capabilities& caps) + { + using namespace Microsoft::WRL; + using namespace ABI::Windows::System; + using namespace ABI::Windows::Gaming::Input; + + if (WaitForSingleObjectEx(mChanged.get(), 0, FALSE) == WAIT_OBJECT_0) + { + ScanGamePads(); + } + + if (player == -1) + player = mMostRecentGamepad; + + if ((player >= 0) && (player < MAX_PLAYER_COUNT)) + { + if (mGamePad[player]) + { + caps.connected = true; + caps.gamepadType = Capabilities::GAMEPAD; + caps.id.clear(); + + ComPtr ctrl; + HRESULT hr = mGamePad[player].As(&ctrl); + if (SUCCEEDED(hr) && ctrl) + { + ComPtr user; + hr = ctrl->get_User(user.GetAddressOf()); + if (SUCCEEDED(hr) && user != nullptr) + { + Wrappers::HString str; + hr = user->get_NonRoamableId(str.GetAddressOf()); + if (SUCCEEDED(hr)) + { + caps.id = str.GetRawBuffer(nullptr); + } + } + } + return; + } + } + + caps.id.clear(); + caps = {}; + } + + bool SetVibration(int player, float leftMotor, float rightMotor, float leftTrigger, float rightTrigger) + { + using namespace ABI::Windows::Gaming::Input; + + if (player == -1) + player = mMostRecentGamepad; + + if ((player >= 0) && (player < MAX_PLAYER_COUNT)) + { + if (mGamePad[player]) + { + GamepadVibration vib; + vib.LeftMotor = leftMotor; + vib.RightMotor = rightMotor; + vib.LeftTrigger = leftTrigger; + vib.RightTrigger = rightTrigger; + HRESULT hr = mGamePad[player]->put_Vibration(vib); + + if (SUCCEEDED(hr)) + return true; + } + } + + return false; + } + + void Suspend() + { + for (size_t j = 0; j < MAX_PLAYER_COUNT; ++j) + { + mGamePad[j].Reset(); + } + } + + void Resume() + { + // Make sure we rescan gamepads + SetEvent(mChanged.get()); + } + + GamePad* mOwner; + + static GamePad::Impl* s_gamePad; + + HANDLE mCtrlChanged; + HANDLE mUserChanged; + +private: + int mMostRecentGamepad; + + void ScanGamePads() + { + using namespace Microsoft::WRL; + using namespace ABI::Windows::Foundation::Collections; + using namespace ABI::Windows::Gaming::Input; + + ComPtr> pads; + ThrowIfFailed(mStatics->get_Gamepads(pads.GetAddressOf())); + + unsigned int count = 0; + ThrowIfFailed(pads->get_Size(&count)); + + // Check for removed gamepads + for (size_t j = 0; j < MAX_PLAYER_COUNT; ++j) + { + if (mGamePad[j]) + { + unsigned int k = 0; + for (; k < count; ++k) + { + ComPtr pad; + HRESULT hr = pads->GetAt(k, pad.GetAddressOf()); + if (SUCCEEDED(hr) && (pad == mGamePad[j])) + { + break; + } + } + + if (k >= count) + { + ComPtr ctrl; + HRESULT hr = mGamePad[j].As(&ctrl); + if (SUCCEEDED(hr) && ctrl) + { + (void)ctrl->remove_UserChanged(mUserChangeToken[j]); + mUserChangeToken[j].value = 0; + } + + mGamePad[j].Reset(); + } + } + } + + // Check for added gamepads + for (unsigned int j = 0; j < count; ++j) + { + ComPtr pad; + HRESULT hr = pads->GetAt(j, pad.GetAddressOf()); + if (SUCCEEDED(hr)) + { + size_t empty = MAX_PLAYER_COUNT; + size_t k = 0; + for (; k < MAX_PLAYER_COUNT; ++k) + { + if (mGamePad[k] == pad) + { + if (j == (count - 1)) + mMostRecentGamepad = static_cast(k); + break; + } + else if (!mGamePad[k]) + { + if (empty >= MAX_PLAYER_COUNT) + empty = k; + } + } + + if (k >= MAX_PLAYER_COUNT) + { + // Silently ignore "extra" gamepads as there's no hard limit + if (empty < MAX_PLAYER_COUNT) + { + mGamePad[empty] = pad; + if (j == (count - 1)) + mMostRecentGamepad = static_cast(empty); + + ComPtr ctrl; + hr = pad.As(&ctrl); + if (SUCCEEDED(hr) && ctrl) + { + typedef __FITypedEventHandler_2_Windows__CGaming__CInput__CIGameController_Windows__CSystem__CUserChangedEventArgs UserHandler; + ThrowIfFailed(ctrl->add_UserChanged(Callback(UserChanged).Get(), &mUserChangeToken[empty])); + } + } + } + } + } + } + + ComPtr mStatics; + ComPtr mGamePad[MAX_PLAYER_COUNT]; + EventRegistrationToken mUserChangeToken[MAX_PLAYER_COUNT]; + + EventRegistrationToken mAddedToken; + EventRegistrationToken mRemovedToken; + + ScopedHandle mChanged; + + static HRESULT GamepadAdded(IInspectable *, ABI::Windows::Gaming::Input::IGamepad*) + { + if (s_gamePad) + { + SetEvent(s_gamePad->mChanged.get()); + + if (s_gamePad->mCtrlChanged != INVALID_HANDLE_VALUE) + { + SetEvent(s_gamePad->mCtrlChanged); + } + } + return S_OK; + } + + static HRESULT GamepadRemoved(IInspectable *, ABI::Windows::Gaming::Input::IGamepad*) + { + if (s_gamePad) + { + SetEvent(s_gamePad->mChanged.get()); + + if (s_gamePad->mCtrlChanged != INVALID_HANDLE_VALUE) + { + SetEvent(s_gamePad->mCtrlChanged); + } + } + return S_OK; + } + + static HRESULT UserChanged(ABI::Windows::Gaming::Input::IGameController*, ABI::Windows::System::IUserChangedEventArgs*) + { + if (s_gamePad) + { + if (s_gamePad->mUserChanged != INVALID_HANDLE_VALUE) + { + SetEvent(s_gamePad->mUserChanged); + } + } + return S_OK; + } +}; + +GamePad::Impl* GamePad::Impl::s_gamePad = nullptr; + + +#elif defined(_XBOX_ONE) + +//====================================================================================== +// Windows::Xbox::Input (Xbox One) +//====================================================================================== + +#include + +#include + +class GamePad::Impl +{ +public: + class GamepadAddedListener : public Microsoft::WRL::RuntimeClass, + ABI::Windows::Foundation::IEventHandler, + Microsoft::WRL::FtmBase> + { + public: + GamepadAddedListener(HANDLE event) : mEvent(event) {} + + STDMETHOD(Invoke)(_In_ IInspectable *, _In_ ABI::Windows::Xbox::Input::IGamepadAddedEventArgs *) override + { + SetEvent(mEvent); + + auto pad = GamePad::Impl::s_gamePad; + + if (pad && pad->mCtrlChanged != INVALID_HANDLE_VALUE) + { + SetEvent(pad->mCtrlChanged); + } + return S_OK; + } + + private: + HANDLE mEvent; + }; + + class GamepadRemovedListener : public Microsoft::WRL::RuntimeClass, + ABI::Windows::Foundation::IEventHandler, + Microsoft::WRL::FtmBase> + { + public: + GamepadRemovedListener(HANDLE event) : mEvent(event) {} + + STDMETHOD(Invoke)(_In_ IInspectable *, _In_ ABI::Windows::Xbox::Input::IGamepadRemovedEventArgs *) override + { + SetEvent(mEvent); + + auto pad = GamePad::Impl::s_gamePad; + + if (pad && pad->mCtrlChanged != INVALID_HANDLE_VALUE) + { + SetEvent(pad->mCtrlChanged); + } + return S_OK; + } + + private: + HANDLE mEvent; + }; + + class UserPairingListener : public Microsoft::WRL::RuntimeClass, + ABI::Windows::Foundation::IEventHandler, + Microsoft::WRL::FtmBase> + { + public: + UserPairingListener() noexcept {} + + STDMETHOD(Invoke)(_In_ IInspectable *, _In_ ABI::Windows::Xbox::Input::IControllerPairingChangedEventArgs *) override + { + auto pad = GamePad::Impl::s_gamePad; + + if (pad && pad->mUserChanged != INVALID_HANDLE_VALUE) + { + SetEvent(pad->mUserChanged); + } + return S_OK; + } + }; + + Impl(GamePad *owner) : + mOwner(owner), + mCtrlChanged(INVALID_HANDLE_VALUE), + mUserChanged(INVALID_HANDLE_VALUE), + mMostRecentGamepad(0), + mStatics{}, + mStaticsCtrl{}, + mGamePad{}, + mAddedToken{}, + mRemovedToken{}, + mUserParingToken{}, + mChanged{} + { + using namespace Microsoft::WRL; + using namespace Microsoft::WRL::Wrappers; + using namespace ABI::Windows::Foundation; + + if (s_gamePad) + { + throw std::exception("GamePad is a singleton"); + } + + s_gamePad = this; + + mChanged.reset(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); + if (!mChanged) + { + throw std::exception("CreateEventEx"); + } + + ThrowIfFailed(GetActivationFactory(HStringReference(RuntimeClass_Windows_Xbox_Input_Gamepad).Get(), mStatics.GetAddressOf())); + + ThrowIfFailed(GetActivationFactory(HStringReference(RuntimeClass_Windows_Xbox_Input_Controller).Get(), mStaticsCtrl.GetAddressOf())); + + ThrowIfFailed(mStatics->add_GamepadAdded(Make(mChanged.get()).Get(), &mAddedToken)); + + ThrowIfFailed(mStatics->add_GamepadRemoved(Make(mChanged.get()).Get(), &mRemovedToken)); + + ThrowIfFailed(mStaticsCtrl->add_ControllerPairingChanged(Make().Get(), &mUserParingToken)); + + ScanGamePads(); + } + + ~Impl() + { + if (mStatics) + { + (void)mStatics->remove_GamepadAdded(mAddedToken); + mAddedToken.value = 0; + + (void)mStatics->remove_GamepadRemoved(mRemovedToken); + mRemovedToken.value = 0; + + mStatics.Reset(); + } + + if (mStaticsCtrl) + { + (void)mStaticsCtrl->remove_ControllerPairingChanged(mUserParingToken); + mUserParingToken.value = 0; + + mStaticsCtrl.Reset(); + } + + s_gamePad = nullptr; + } + + void GetState(int player, _Out_ State& state, DeadZone deadZoneMode) + { + using namespace Microsoft::WRL; + using namespace ABI::Windows::Xbox::Input; + + if (WaitForSingleObjectEx(mChanged.get(), 0, FALSE) == WAIT_OBJECT_0) + { + ScanGamePads(); + } + + if (player == -1) + player = mMostRecentGamepad; + + if ((player >= 0) && (player < MAX_PLAYER_COUNT)) + { + if (mGamePad[player]) + { + RawGamepadReading reading; + HRESULT hr = mGamePad[player]->GetRawCurrentReading(&reading); + if (SUCCEEDED(hr)) + { + state.connected = true; + state.packet = reading.Timestamp; + + state.buttons.a = (reading.Buttons & GamepadButtons::GamepadButtons_A) != 0; + state.buttons.b = (reading.Buttons & GamepadButtons::GamepadButtons_B) != 0; + state.buttons.x = (reading.Buttons & GamepadButtons::GamepadButtons_X) != 0; + state.buttons.y = (reading.Buttons & GamepadButtons::GamepadButtons_Y) != 0; + + state.buttons.leftStick = (reading.Buttons & GamepadButtons::GamepadButtons_LeftThumbstick) != 0; + state.buttons.rightStick = (reading.Buttons & GamepadButtons::GamepadButtons_RightThumbstick) != 0; + + state.buttons.leftShoulder = (reading.Buttons & GamepadButtons::GamepadButtons_LeftShoulder) != 0; + state.buttons.rightShoulder = (reading.Buttons & GamepadButtons::GamepadButtons_RightShoulder) != 0; + + state.buttons.back = (reading.Buttons & GamepadButtons::GamepadButtons_View) != 0; + state.buttons.start = (reading.Buttons & GamepadButtons::GamepadButtons_Menu) != 0; + + state.dpad.up = (reading.Buttons & GamepadButtons::GamepadButtons_DPadUp) != 0; + state.dpad.down = (reading.Buttons & GamepadButtons::GamepadButtons_DPadDown) != 0; + state.dpad.right = (reading.Buttons & GamepadButtons::GamepadButtons_DPadRight) != 0; + state.dpad.left = (reading.Buttons & GamepadButtons::GamepadButtons_DPadLeft) != 0; + + ApplyStickDeadZone(reading.LeftThumbstickX, reading.LeftThumbstickY, + deadZoneMode, 1.f, c_XboxOneThumbDeadZone, + state.thumbSticks.leftX, state.thumbSticks.leftY); + + ApplyStickDeadZone(reading.RightThumbstickX, reading.RightThumbstickY, + deadZoneMode, 1.f, c_XboxOneThumbDeadZone, + state.thumbSticks.rightX, state.thumbSticks.rightY); + + state.triggers.left = reading.LeftTrigger; + state.triggers.right = reading.RightTrigger; + + return; + } + } + } + + memset(&state, 0, sizeof(State)); + } + + void GetCapabilities(int player, _Out_ Capabilities& caps) + { + using namespace Microsoft::WRL; + using namespace ABI::Windows::Xbox::Input; + + if (WaitForSingleObjectEx(mChanged.get(), 0, FALSE) == WAIT_OBJECT_0) + { + ScanGamePads(); + } + + if (player == -1) + player = mMostRecentGamepad; + + if ((player >= 0) && (player < MAX_PLAYER_COUNT)) + { + if (mGamePad[player]) + { + caps.connected = true; + caps.gamepadType = Capabilities::UNKNOWN; + + ComPtr ctrl; + HRESULT hr = mGamePad[player].As(&ctrl); + if (SUCCEEDED(hr) && ctrl) + { + hr = ctrl->get_Id(&caps.id); + if (FAILED(hr)) + caps.id = 0; + + Wrappers::HString str; + hr = ctrl->get_Type(str.GetAddressOf()); + if (SUCCEEDED(hr)) + { + const wchar_t* typeStr = str.GetRawBuffer(nullptr); + if (_wcsicmp(typeStr, L"Windows.Xbox.Input.Gamepad") == 0) + { + caps.gamepadType = Capabilities::GAMEPAD; + } + else if (_wcsicmp(typeStr, L"Microsoft.Xbox.Input.ArcadeStick") == 0) + { + caps.gamepadType = Capabilities::ARCADE_STICK; + } + else if (_wcsicmp(typeStr, L"Microsoft.Xbox.Input.Wheel") == 0) + { + caps.gamepadType = Capabilities::WHEEL; + } + } + } + else + caps.id = 0; + + return; + } + } + + memset(&caps, 0, sizeof(Capabilities)); + } + + bool SetVibration(int player, float leftMotor, float rightMotor, float leftTrigger, float rightTrigger) + { + using namespace ABI::Windows::Xbox::Input; + + if (player == -1) + player = mMostRecentGamepad; + + if ((player >= 0) && (player < MAX_PLAYER_COUNT)) + { + if (mGamePad[player]) + { + HRESULT hr; + try + { + GamepadVibration vib; + vib.LeftMotorLevel = leftMotor; + vib.RightMotorLevel = rightMotor; + vib.LeftTriggerLevel = leftTrigger; + vib.RightTriggerLevel = rightTrigger; + hr = mGamePad[player]->SetVibration(vib); + } + catch (...) + { + // Handle case where gamepad might be invalid + hr = E_FAIL; + } + + if (SUCCEEDED(hr)) + return true; + } + } + + return false; + } + + void Suspend() + { + for (size_t j = 0; j < MAX_PLAYER_COUNT; ++j) + { + mGamePad[j].Reset(); + } + } + + void Resume() + { + // Make sure we rescan gamepads + SetEvent(mChanged.get()); + } + + GamePad* mOwner; + + static GamePad::Impl* s_gamePad; + + HANDLE mCtrlChanged; + HANDLE mUserChanged; + +private: + int mMostRecentGamepad; + + void ScanGamePads() + { + using namespace ABI::Windows::Foundation::Collections; + using namespace ABI::Windows::Xbox::Input; + + ComPtr> pads; + ThrowIfFailed(mStatics->get_Gamepads(pads.GetAddressOf())); + + unsigned int count = 0; + ThrowIfFailed(pads->get_Size(&count)); + + // Check for removed gamepads + for (size_t j = 0; j < MAX_PLAYER_COUNT; ++j) + { + if (mGamePad[j]) + { + unsigned int k = 0; + for (; k < count; ++k) + { + ComPtr pad; + HRESULT hr = pads->GetAt(k, pad.GetAddressOf()); + if (SUCCEEDED(hr) && (pad == mGamePad[j])) + { + break; + } + } + + if (k >= count) + { + mGamePad[j].Reset(); + } + } + } + + // Check for added gamepads + for (unsigned int j = 0; j < count; ++j) + { + ComPtr pad; + HRESULT hr = pads->GetAt(j, pad.GetAddressOf()); + if (SUCCEEDED(hr)) + { + size_t empty = MAX_PLAYER_COUNT; + size_t k = 0; + for (; k < MAX_PLAYER_COUNT; ++k) + { + if (mGamePad[k] == pad) + { + if (!j) + mMostRecentGamepad = static_cast(k); + break; + } + else if (!mGamePad[k]) + { + if (empty >= MAX_PLAYER_COUNT) + empty = k; + } + } + + if (k >= MAX_PLAYER_COUNT) + { + if (empty >= MAX_PLAYER_COUNT) + { + throw std::exception("Too many gamepads found"); + } + + mGamePad[empty] = pad; + if (!j) + mMostRecentGamepad = static_cast(empty); + } + } + } + } + + ComPtr mStatics; + ComPtr mStaticsCtrl; + ComPtr mGamePad[MAX_PLAYER_COUNT]; + + EventRegistrationToken mAddedToken; + EventRegistrationToken mRemovedToken; + EventRegistrationToken mUserParingToken; + + ScopedHandle mChanged; +}; + +GamePad::Impl* GamePad::Impl::s_gamePad = nullptr; + +#else + +//====================================================================================== +// XInput +//====================================================================================== + +#include + +static_assert(GamePad::MAX_PLAYER_COUNT == XUSER_MAX_COUNT, "xinput.h mismatch"); + +class GamePad::Impl +{ +public: + Impl(GamePad* owner) : + mOwner(owner), + mConnected{}, + mLastReadTime{} + #if (_WIN32_WINNT < _WIN32_WINNT_WIN8) + , mLeftMotor{} + , mRightMotor{} + , mSuspended(false) + #endif + { + for (int j = 0; j < XUSER_MAX_COUNT; ++j) + { + ClearSlot(j, 0); + } + + if (s_gamePad) + { + throw std::exception("GamePad is a singleton"); + } + + s_gamePad = this; + } + + ~Impl() + { + s_gamePad = nullptr; + } + + void GetState(int player, _Out_ State& state, DeadZone deadZoneMode) + { + if (player == -1) + player = GetMostRecent(); + + ULONGLONG time = GetTickCount64(); + + if (!ThrottleRetry(player, time)) + { + #if (_WIN32_WINNT < _WIN32_WINNT_WIN8) + if (mSuspended) + { + memset(&state, 0, sizeof(State)); + state.connected = mConnected[player]; + return; + } + #endif + + XINPUT_STATE xstate; + DWORD result = XInputGetState(DWORD(player), &xstate); + if (result == ERROR_DEVICE_NOT_CONNECTED) + { + ClearSlot(player, time); + } + else + { + if (!mConnected[player]) + mLastReadTime[player] = time; + + mConnected[player] = true; + + state.connected = true; + state.packet = xstate.dwPacketNumber; + + WORD xbuttons = xstate.Gamepad.wButtons; + state.buttons.a = (xbuttons & XINPUT_GAMEPAD_A) != 0; + state.buttons.b = (xbuttons & XINPUT_GAMEPAD_B) != 0; + state.buttons.x = (xbuttons & XINPUT_GAMEPAD_X) != 0; + state.buttons.y = (xbuttons & XINPUT_GAMEPAD_Y) != 0; + state.buttons.leftStick = (xbuttons & XINPUT_GAMEPAD_LEFT_THUMB) != 0; + state.buttons.rightStick = (xbuttons & XINPUT_GAMEPAD_RIGHT_THUMB) != 0; + state.buttons.leftShoulder = (xbuttons & XINPUT_GAMEPAD_LEFT_SHOULDER) != 0; + state.buttons.rightShoulder = (xbuttons & XINPUT_GAMEPAD_RIGHT_SHOULDER) != 0; + state.buttons.back = (xbuttons & XINPUT_GAMEPAD_BACK) != 0; + state.buttons.start = (xbuttons & XINPUT_GAMEPAD_START) != 0; + + state.dpad.up = (xbuttons & XINPUT_GAMEPAD_DPAD_UP) != 0; + state.dpad.down = (xbuttons & XINPUT_GAMEPAD_DPAD_DOWN) != 0; + state.dpad.right = (xbuttons & XINPUT_GAMEPAD_DPAD_RIGHT) != 0; + state.dpad.left = (xbuttons & XINPUT_GAMEPAD_DPAD_LEFT) != 0; + + if (deadZoneMode == DEAD_ZONE_NONE) + { + state.triggers.left = ApplyLinearDeadZone(float(xstate.Gamepad.bLeftTrigger), 255.f, 0.f); + state.triggers.right = ApplyLinearDeadZone(float(xstate.Gamepad.bRightTrigger), 255.f, 0.f); + } + else + { + state.triggers.left = ApplyLinearDeadZone(float(xstate.Gamepad.bLeftTrigger), 255.f, float(XINPUT_GAMEPAD_TRIGGER_THRESHOLD)); + state.triggers.right = ApplyLinearDeadZone(float(xstate.Gamepad.bRightTrigger), 255.f, float(XINPUT_GAMEPAD_TRIGGER_THRESHOLD)); + } + + ApplyStickDeadZone(float(xstate.Gamepad.sThumbLX), float(xstate.Gamepad.sThumbLY), + deadZoneMode, 32767.f, float(XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE), + state.thumbSticks.leftX, state.thumbSticks.leftY); + + ApplyStickDeadZone(float(xstate.Gamepad.sThumbRX), float(xstate.Gamepad.sThumbRY), + deadZoneMode, 32767.f, float(XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE), + state.thumbSticks.rightX, state.thumbSticks.rightY); + + return; + } + } + + memset(&state, 0, sizeof(State)); + } + + void GetCapabilities(int player, _Out_ Capabilities& caps) + { + if (player == -1) + player = GetMostRecent(); + + ULONGLONG time = GetTickCount64(); + + if (!ThrottleRetry(player, time)) + { + XINPUT_CAPABILITIES xcaps; + DWORD result = XInputGetCapabilities(DWORD(player), 0, &xcaps); + if (result == ERROR_DEVICE_NOT_CONNECTED) + { + ClearSlot(player, time); + } + else + { + if (!mConnected[player]) + mLastReadTime[player] = time; + + mConnected[player] = true; + + caps.connected = true; + caps.id = uint64_t(player); + if (xcaps.Type == XINPUT_DEVTYPE_GAMEPAD) + { + static_assert(Capabilities::GAMEPAD == XINPUT_DEVSUBTYPE_GAMEPAD, "xinput.h mismatch"); + #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + static_assert(XINPUT_DEVSUBTYPE_WHEEL == Capabilities::WHEEL, "xinput.h mismatch"); + static_assert(XINPUT_DEVSUBTYPE_ARCADE_STICK == Capabilities::ARCADE_STICK, "xinput.h mismatch"); + static_assert(XINPUT_DEVSUBTYPE_FLIGHT_STICK == Capabilities::FLIGHT_STICK, "xinput.h mismatch"); + static_assert(XINPUT_DEVSUBTYPE_DANCE_PAD == Capabilities::DANCE_PAD, "xinput.h mismatch"); + static_assert(XINPUT_DEVSUBTYPE_GUITAR == Capabilities::GUITAR, "xinput.h mismatch"); + static_assert(XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE == Capabilities::GUITAR_ALTERNATE, "xinput.h mismatch"); + static_assert(XINPUT_DEVSUBTYPE_DRUM_KIT == Capabilities::DRUM_KIT, "xinput.h mismatch"); + static_assert(XINPUT_DEVSUBTYPE_GUITAR_BASS == Capabilities::GUITAR_BASS, "xinput.h mismatch"); + static_assert(XINPUT_DEVSUBTYPE_ARCADE_PAD == Capabilities::ARCADE_PAD, "xinput.h mismatch"); + #endif + + caps.gamepadType = Capabilities::Type(xcaps.SubType); + } + + return; + } + } + + memset(&caps, 0, sizeof(Capabilities)); + } + + bool SetVibration(int player, float leftMotor, float rightMotor, float leftTrigger, float rightTrigger) + { + if (player == -1) + player = GetMostRecent(); + + ULONGLONG time = GetTickCount64(); + + if (ThrottleRetry(player, time)) + { + return false; + } + + // XInput does not provide a way to set the left/right trigger impulse motors on the Xbox One Controller, + // and these motors are not present on the Xbox 360 Common Controller + UNREFERENCED_PARAMETER(leftTrigger); + UNREFERENCED_PARAMETER(rightTrigger); + + #if (_WIN32_WINNT < _WIN32_WINNT_WIN8) + mLeftMotor[player] = leftMotor; + mRightMotor[player] = rightMotor; + + if (mSuspended) + return mConnected[player]; + #endif + + XINPUT_VIBRATION xvibration; + xvibration.wLeftMotorSpeed = WORD(leftMotor * 0xFFFF); + xvibration.wRightMotorSpeed = WORD(rightMotor * 0xFFFF); + DWORD result = XInputSetState(DWORD(player), &xvibration); + if (result == ERROR_DEVICE_NOT_CONNECTED) + { + ClearSlot(player, time); + return false; + } + else + { + if (!mConnected[player]) + mLastReadTime[player] = time; + + mConnected[player] = true; + return (result == ERROR_SUCCESS); + } + } + + void Suspend() + { + #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + XInputEnable(FALSE); + #else + // For XInput 9.1.0, we have to emulate the behavior of XInputEnable( FALSE ) + if (!mSuspended) + { + for (size_t j = 0; j < XUSER_MAX_COUNT; ++j) + { + if (mConnected[j]) + { + XINPUT_VIBRATION xvibration; + xvibration.wLeftMotorSpeed = xvibration.wRightMotorSpeed = 0; + (void)XInputSetState(DWORD(j), &xvibration); + } + } + + mSuspended = true; + } + #endif + } + + void Resume() + { + #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + XInputEnable(TRUE); + #else + // For XInput 9.1.0, we have to emulate the behavior of XInputEnable( TRUE ) + if (mSuspended) + { + ULONGLONG time = GetTickCount64(); + + for (int j = 0; j < XUSER_MAX_COUNT; ++j) + { + if (mConnected[j]) + { + XINPUT_VIBRATION xvibration; + xvibration.wLeftMotorSpeed = WORD(mLeftMotor[j] * 0xFFFF); + xvibration.wRightMotorSpeed = WORD(mRightMotor[j] * 0xFFFF); + DWORD result = XInputSetState(DWORD(j), &xvibration); + if (result == ERROR_DEVICE_NOT_CONNECTED) + { + ClearSlot(j, time); + } + } + } + + mSuspended = false; + } + #endif + } + + GamePad* mOwner; + + static GamePad::Impl* s_gamePad; + +private: + bool mConnected[XUSER_MAX_COUNT]; + ULONGLONG mLastReadTime[XUSER_MAX_COUNT]; + +#if (_WIN32_WINNT < _WIN32_WINNT_WIN8) + // Variables for emulating XInputEnable on XInput 9.1.0 + float mLeftMotor[XUSER_MAX_COUNT]; + float mRightMotor[XUSER_MAX_COUNT]; + bool mSuspended; +#endif + + bool ThrottleRetry(int player, ULONGLONG time) + { + // This function minimizes a potential performance issue with XInput on Windows when + // checking a disconnected controller slot which requires device enumeration. + // This throttling keeps checks for newly connected gamepads to about once a second + + if ((player < 0) || (player >= XUSER_MAX_COUNT)) + return true; + + if (mConnected[player]) + return false; + + for (int j = 0; j < XUSER_MAX_COUNT; ++j) + { + if (!mConnected[j]) + { + LONGLONG delta = time - mLastReadTime[j]; + + LONGLONG interval = 1000; + if (j != player) + interval /= 4; + + if ((delta >= 0) && (delta < interval)) + return true; + } + } + + return false; + } + + void ClearSlot(int player, ULONGLONG time) + { + mConnected[player] = false; + mLastReadTime[player] = time; + #if (_WIN32_WINNT < _WIN32_WINNT_WIN8) + mLeftMotor[player] = mRightMotor[player] = 0.f; + #endif + } + + int GetMostRecent() + { + int player = -1; + ULONGLONG time = 0; + + for (size_t j = 0; j < XUSER_MAX_COUNT; ++j) + { + if (mConnected[j] && (mLastReadTime[j] > time)) + { + time = mLastReadTime[j]; + player = static_cast(j); + } + } + + return player; + } +}; + +GamePad::Impl* GamePad::Impl::s_gamePad = nullptr; + +#endif + +#pragma warning( disable : 4355 ) + +// Public constructor. +GamePad::GamePad() noexcept(false) + : pImpl(std::make_unique(this)) +{ +} + + +// Move constructor. +GamePad::GamePad(GamePad&& moveFrom) noexcept + : pImpl(std::move(moveFrom.pImpl)) +{ + pImpl->mOwner = this; +} + + +// Move assignment. +GamePad& GamePad::operator= (GamePad&& moveFrom) noexcept +{ + pImpl = std::move(moveFrom.pImpl); + pImpl->mOwner = this; + return *this; +} + + +// Public destructor. +GamePad::~GamePad() +{ +} + + +GamePad::State GamePad::GetState(int player, DeadZone deadZoneMode) +{ + State state; + pImpl->GetState(player, state, deadZoneMode); + return state; +} + + +GamePad::Capabilities GamePad::GetCapabilities(int player) +{ + Capabilities caps; + pImpl->GetCapabilities(player, caps); + return caps; +} + + +bool GamePad::SetVibration(int player, float leftMotor, float rightMotor, float leftTrigger, float rightTrigger) +{ + return pImpl->SetVibration(player, leftMotor, rightMotor, leftTrigger, rightTrigger); +} + + +void GamePad::Suspend() +{ + pImpl->Suspend(); +} + + +void GamePad::Resume() +{ + pImpl->Resume(); +} + + +#if (_WIN32_WINNT >= 0x0A00 /*_WIN32_WINNT_WIN10*/ ) || defined(_XBOX_ONE) +void GamePad::RegisterEvents(HANDLE ctrlChanged, HANDLE userChanged) +{ + pImpl->mCtrlChanged = (!ctrlChanged) ? INVALID_HANDLE_VALUE : ctrlChanged; + pImpl->mUserChanged = (!userChanged) ? INVALID_HANDLE_VALUE : userChanged; +} +#endif + + +GamePad& GamePad::Get() +{ + if (!Impl::s_gamePad || !Impl::s_gamePad->mOwner) + throw std::exception("GamePad is a singleton"); + + return *Impl::s_gamePad->mOwner; +} + + + +//====================================================================================== +// ButtonStateTracker +//====================================================================================== + +#define UPDATE_BUTTON_STATE(field) field = static_cast( ( !!state.buttons.field ) | ( ( !!state.buttons.field ^ !!lastState.buttons.field ) << 1 ) ); + +void GamePad::ButtonStateTracker::Update(const GamePad::State& state) +{ + UPDATE_BUTTON_STATE(a); + + assert((!state.buttons.a && !lastState.buttons.a) == (a == UP)); + assert((state.buttons.a && lastState.buttons.a) == (a == HELD)); + assert((!state.buttons.a && lastState.buttons.a) == (a == RELEASED)); + assert((state.buttons.a && !lastState.buttons.a) == (a == PRESSED)); + + UPDATE_BUTTON_STATE(b); + UPDATE_BUTTON_STATE(x); + UPDATE_BUTTON_STATE(y); + + UPDATE_BUTTON_STATE(leftStick); + UPDATE_BUTTON_STATE(rightStick); + + UPDATE_BUTTON_STATE(leftShoulder); + UPDATE_BUTTON_STATE(rightShoulder); + + UPDATE_BUTTON_STATE(back); + UPDATE_BUTTON_STATE(start); + + dpadUp = static_cast((!!state.dpad.up) | ((!!state.dpad.up ^ !!lastState.dpad.up) << 1)); + dpadDown = static_cast((!!state.dpad.down) | ((!!state.dpad.down ^ !!lastState.dpad.down) << 1)); + dpadLeft = static_cast((!!state.dpad.left) | ((!!state.dpad.left ^ !!lastState.dpad.left) << 1)); + dpadRight = static_cast((!!state.dpad.right) | ((!!state.dpad.right ^ !!lastState.dpad.right) << 1)); + + assert((!state.dpad.up && !lastState.dpad.up) == (dpadUp == UP)); + assert((state.dpad.up && lastState.dpad.up) == (dpadUp == HELD)); + assert((!state.dpad.up && lastState.dpad.up) == (dpadUp == RELEASED)); + assert((state.dpad.up && !lastState.dpad.up) == (dpadUp == PRESSED)); + + // Handle 'threshold' tests which emulate buttons + + bool threshold = state.IsLeftThumbStickUp(); + leftStickUp = static_cast((!!threshold) | ((!!threshold ^ !!lastState.IsLeftThumbStickUp()) << 1)); + + threshold = state.IsLeftThumbStickDown(); + leftStickDown = static_cast((!!threshold) | ((!!threshold ^ !!lastState.IsLeftThumbStickDown()) << 1)); + + threshold = state.IsLeftThumbStickLeft(); + leftStickLeft = static_cast((!!threshold) | ((!!threshold ^ !!lastState.IsLeftThumbStickLeft()) << 1)); + + threshold = state.IsLeftThumbStickRight(); + leftStickRight = static_cast((!!threshold) | ((!!threshold ^ !!lastState.IsLeftThumbStickRight()) << 1)); + + threshold = state.IsRightThumbStickUp(); + rightStickUp = static_cast((!!threshold) | ((!!threshold ^ !!lastState.IsRightThumbStickUp()) << 1)); + + threshold = state.IsRightThumbStickDown(); + rightStickDown = static_cast((!!threshold) | ((!!threshold ^ !!lastState.IsRightThumbStickDown()) << 1)); + + threshold = state.IsRightThumbStickLeft(); + rightStickLeft = static_cast((!!threshold) | ((!!threshold ^ !!lastState.IsRightThumbStickLeft()) << 1)); + + threshold = state.IsRightThumbStickRight(); + rightStickRight = static_cast((!!threshold) | ((!!threshold ^ !!lastState.IsRightThumbStickRight()) << 1)); + + threshold = state.IsLeftTriggerPressed(); + leftTrigger = static_cast((!!threshold) | ((!!threshold ^ !!lastState.IsLeftTriggerPressed()) << 1)); + + threshold = state.IsRightTriggerPressed(); + rightTrigger = static_cast((!!threshold) | ((!!threshold ^ !!lastState.IsRightTriggerPressed()) << 1)); + + lastState = state; +} + +#undef UPDATE_BUTTON_STATE + + +void GamePad::ButtonStateTracker::Reset() noexcept +{ + memset(this, 0, sizeof(ButtonStateTracker)); +} diff --git a/X1nput/GamePad.h b/X1nput/GamePad.h new file mode 100644 index 0000000..58c6e3b --- /dev/null +++ b/X1nput/GamePad.h @@ -0,0 +1,272 @@ +//-------------------------------------------------------------------------------------- +// File: GamePad.h +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#if (_WIN32_WINNT < 0x0A00 /*_WIN32_WINNT_WIN10*/) +#ifndef _XBOX_ONE +#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP) +#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/ ) +#pragma comment(lib,"xinput.lib") +#else +#pragma comment(lib,"xinput9_1_0.lib") +#endif +#endif +#endif +#endif + +#include +#include + +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN10) +#include +#endif + + +namespace DirectX +{ + class GamePad + { + public: + GamePad() noexcept(false); + GamePad(GamePad&& moveFrom) noexcept; + GamePad& operator= (GamePad&& moveFrom) noexcept; + + GamePad(GamePad const&) = delete; + GamePad& operator=(GamePad const&) = delete; + + virtual ~GamePad(); + + #if (_WIN32_WINNT >= 0x0A00 /*_WIN32_WINNT_WIN10*/ ) || defined(_XBOX_ONE) + static const int MAX_PLAYER_COUNT = 8; + #else + static const int MAX_PLAYER_COUNT = 4; + #endif + + enum DeadZone + { + DEAD_ZONE_INDEPENDENT_AXES = 0, + DEAD_ZONE_CIRCULAR, + DEAD_ZONE_NONE, + }; + + struct Buttons + { + bool a; + bool b; + bool x; + bool y; + bool leftStick; + bool rightStick; + bool leftShoulder; + bool rightShoulder; + union + { + bool back; + bool view; + }; + union + { + bool start; + bool menu; + }; + }; + + struct DPad + { + bool up; + bool down; + bool right; + bool left; + }; + + struct ThumbSticks + { + float leftX; + float leftY; + float rightX; + float rightY; + }; + + struct Triggers + { + float left; + float right; + }; + + struct State + { + bool connected; + uint64_t packet; + Buttons buttons; + DPad dpad; + ThumbSticks thumbSticks; + Triggers triggers; + + bool __cdecl IsConnected() const { return connected; } + + // Is the button pressed currently? + bool __cdecl IsAPressed() const { return buttons.a; } + bool __cdecl IsBPressed() const { return buttons.b; } + bool __cdecl IsXPressed() const { return buttons.x; } + bool __cdecl IsYPressed() const { return buttons.y; } + + bool __cdecl IsLeftStickPressed() const { return buttons.leftStick; } + bool __cdecl IsRightStickPressed() const { return buttons.rightStick; } + + bool __cdecl IsLeftShoulderPressed() const { return buttons.leftShoulder; } + bool __cdecl IsRightShoulderPressed() const { return buttons.rightShoulder; } + + bool __cdecl IsBackPressed() const { return buttons.back; } + bool __cdecl IsViewPressed() const { return buttons.view; } + bool __cdecl IsStartPressed() const { return buttons.start; } + bool __cdecl IsMenuPressed() const { return buttons.menu; } + + bool __cdecl IsDPadDownPressed() const { return dpad.down; } + bool __cdecl IsDPadUpPressed() const { return dpad.up; } + bool __cdecl IsDPadLeftPressed() const { return dpad.left; } + bool __cdecl IsDPadRightPressed() const { return dpad.right; } + + bool __cdecl IsLeftThumbStickUp() const { return (thumbSticks.leftY > 0.5f) != 0; } + bool __cdecl IsLeftThumbStickDown() const { return (thumbSticks.leftY < -0.5f) != 0; } + bool __cdecl IsLeftThumbStickLeft() const { return (thumbSticks.leftX < -0.5f) != 0; } + bool __cdecl IsLeftThumbStickRight() const { return (thumbSticks.leftX > 0.5f) != 0; } + + bool __cdecl IsRightThumbStickUp() const { return (thumbSticks.rightY > 0.5f) != 0; } + bool __cdecl IsRightThumbStickDown() const { return (thumbSticks.rightY < -0.5f) != 0; } + bool __cdecl IsRightThumbStickLeft() const { return (thumbSticks.rightX < -0.5f) != 0; } + bool __cdecl IsRightThumbStickRight() const { return (thumbSticks.rightX > 0.5f) != 0; } + + bool __cdecl IsLeftTriggerPressed() const { return (triggers.left > 0.5f) != 0; } + bool __cdecl IsRightTriggerPressed() const { return (triggers.right > 0.5f) != 0; } + }; + + struct Capabilities + { + enum Type + { + UNKNOWN = 0, + GAMEPAD, + WHEEL, + ARCADE_STICK, + FLIGHT_STICK, + DANCE_PAD, + GUITAR, + GUITAR_ALTERNATE, + DRUM_KIT, + GUITAR_BASS = 11, + ARCADE_PAD = 19, + }; + + bool connected; + Type gamepadType; + #if (_WIN32_WINNT >= 0x0A00 /*_WIN32_WINNT_WIN10*/) + std::wstring id; + + Capabilities() noexcept : connected(false), gamepadType(UNKNOWN) {} + #else + uint64_t id; + #endif + + bool __cdecl IsConnected() const { return connected; } + }; + + class ButtonStateTracker + { + public: + enum ButtonState + { + UP = 0, // Button is up + HELD = 1, // Button is held down + RELEASED = 2, // Button was just released + PRESSED = 3, // Buton was just pressed + }; + + ButtonState a; + ButtonState b; + ButtonState x; + ButtonState y; + + ButtonState leftStick; + ButtonState rightStick; + + ButtonState leftShoulder; + ButtonState rightShoulder; + + union + { + ButtonState back; + ButtonState view; + }; + + union + { + ButtonState start; + ButtonState menu; + }; + + ButtonState dpadUp; + ButtonState dpadDown; + ButtonState dpadLeft; + ButtonState dpadRight; + + ButtonState leftStickUp; + ButtonState leftStickDown; + ButtonState leftStickLeft; + ButtonState leftStickRight; + + ButtonState rightStickUp; + ButtonState rightStickDown; + ButtonState rightStickLeft; + ButtonState rightStickRight; + + ButtonState leftTrigger; + ButtonState rightTrigger; + + #pragma prefast(suppress: 26495, "Reset() performs the initialization") + ButtonStateTracker() noexcept { Reset(); } + + void __cdecl Update(const State& state); + + void __cdecl Reset() noexcept; + + State __cdecl GetLastState() const { return lastState; } + + private: + State lastState; + }; + + // Retrieve the current state of the gamepad of the associated player index + State __cdecl GetState(int player, DeadZone deadZoneMode = DEAD_ZONE_INDEPENDENT_AXES); + + // Retrieve the current capabilities of the gamepad of the associated player index + Capabilities __cdecl GetCapabilities(int player); + + // Set the vibration motor speeds of the gamepad + bool __cdecl SetVibration(int player, float leftMotor, float rightMotor, float leftTrigger = 0.f, float rightTrigger = 0.f); + + // Handle suspending/resuming + void __cdecl Suspend(); + void __cdecl Resume(); + + #if (_WIN32_WINNT >= 0x0A00 /*_WIN32_WINNT_WIN10*/ ) || defined(_XBOX_ONE) + void __cdecl RegisterEvents(void* ctrlChanged, void* userChanged); + #endif + + // Singleton + static GamePad& __cdecl Get(); + + private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + }; +} diff --git a/X1nput/PlatformHelpers.h b/X1nput/PlatformHelpers.h new file mode 100644 index 0000000..3640e33 --- /dev/null +++ b/X1nput/PlatformHelpers.h @@ -0,0 +1,84 @@ +//-------------------------------------------------------------------------------------- +// File: PlatformHelpers.h +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#pragma warning(disable : 4324) + +#include +#include + +#ifndef MAKEFOURCC + #define MAKEFOURCC(ch0, ch1, ch2, ch3) \ + (static_cast(static_cast(ch0)) \ + | (static_cast(static_cast(ch1)) << 8) \ + | (static_cast(static_cast(ch2)) << 16) \ + | (static_cast(static_cast(ch3)) << 24)) +#endif /* defined(MAKEFOURCC) */ + +namespace DirectX +{ + // Helper class for COM exceptions + class com_exception : public std::exception + { + public: + com_exception(HRESULT hr) noexcept : result(hr) {} + + virtual const char* what() const override + { + static char s_str[64] = {}; + sprintf_s(s_str, "Failure with HRESULT of %08X", static_cast(result)); + return s_str; + } + + private: + HRESULT result; + }; + + // Helper utility converts D3D API failures into exceptions. + inline void ThrowIfFailed(HRESULT hr) + { + if (FAILED(hr)) + { + throw com_exception(hr); + } + } + + + // Helper for output debug tracing + inline void DebugTrace(_In_z_ _Printf_format_string_ const char* format, ...) noexcept + { + #ifdef _DEBUG + va_list args; + va_start(args, format); + + char buff[1024] = {}; + vsprintf_s(buff, format, args); + OutputDebugStringA(buff); + va_end(args); + #else + UNREFERENCED_PARAMETER(format); + #endif + } + + + // Helper smart-pointers +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN10) || (defined(_XBOX_ONE) && defined(_TITLE)) || !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) + struct virtual_deleter { void operator()(void* p) noexcept { if (p) VirtualFree(p, 0, MEM_RELEASE); } }; +#endif + + struct aligned_deleter { void operator()(void* p) noexcept { _aligned_free(p); } }; + + struct handle_closer { void operator()(HANDLE h) noexcept { if (h) CloseHandle(h); } }; + + typedef std::unique_ptr ScopedHandle; + + inline HANDLE safe_handle(HANDLE h) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; } +} diff --git a/X1nput/XInput.cpp b/X1nput/XInput.cpp new file mode 100644 index 0000000..6575610 --- /dev/null +++ b/X1nput/XInput.cpp @@ -0,0 +1 @@ +#include "stdafx.h" \ No newline at end of file diff --git a/X1nput/XInput.def b/X1nput/XInput.def new file mode 100644 index 0000000..e09e5b2 --- /dev/null +++ b/X1nput/XInput.def @@ -0,0 +1,14 @@ +LIBRARY XInput +EXPORTS + DllMain @1 + XInputGetState @2 + XInputSetState @3 + XInputGetCapabilities @4 + XInputEnable @5 + XInputGetDSoundAudioDeviceGuids @6 + XInputGetBatteryInformation @7 + XInputGetKeystroke @8 + XInputGetStateEx @100 + XInputWaitForGuideButton @101 + XInputCancelGuideButtonWait @102 + XInputPowerOffController @103 \ No newline at end of file diff --git a/X1nput/XInput.vcxproj b/X1nput/XInput.vcxproj new file mode 100644 index 0000000..95adecf --- /dev/null +++ b/X1nput/XInput.vcxproj @@ -0,0 +1,182 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {C565022D-170F-427F-B2D9-D591D15562EA} + Win32Proj + XInput + 10.0.17134.0 + XInput1_3 + + + + DynamicLibrary + true + v141 + MultiByte + + + DynamicLibrary + false + v141 + true + MultiByte + + + DynamicLibrary + true + v141 + MultiByte + + + DynamicLibrary + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;XINPUT_EXPORTS;%(PreprocessorDefinitions) + + + Windows + XInput.def + + + + + Use + Level3 + Disabled + _DEBUG;_WINDOWS;_USRDLL;XINPUT_EXPORTS;%(PreprocessorDefinitions) + + + Windows + XInput.def + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;XINPUT_EXPORTS;%(PreprocessorDefinitions) + None + + + Windows + true + true + XInput.def + false + + + + + Level3 + Use + MaxSpeed + true + true + NDEBUG;_WINDOWS;_USRDLL;XINPUT_EXPORTS;%(PreprocessorDefinitions) + None + + + Windows + true + true + XInput.def + false + + + + + + + + + + + + + + false + + + false + + + false + + + false + + + + + + Create + Create + Create + Create + + + + + + + \ No newline at end of file diff --git a/X1nput/XInput.vcxproj.user b/X1nput/XInput.vcxproj.user new file mode 100644 index 0000000..a894860 --- /dev/null +++ b/X1nput/XInput.vcxproj.user @@ -0,0 +1,9 @@ + + + + false + + + WindowsLocalDebugger + + \ No newline at end of file diff --git a/X1nput/dllmain.cpp b/X1nput/dllmain.cpp new file mode 100644 index 0000000..c3ccdef --- /dev/null +++ b/X1nput/dllmain.cpp @@ -0,0 +1,317 @@ +/* + Thanks to r57zone for his Xinput emulation library + https://github.com/r57zone/XInput +*/ + +#include "stdafx.h" + +#define XINPUT_GAMEPAD_DPAD_UP 0x0001 +#define XINPUT_GAMEPAD_DPAD_DOWN 0x0002 +#define XINPUT_GAMEPAD_DPAD_LEFT 0x0004 +#define XINPUT_GAMEPAD_DPAD_RIGHT 0x0008 +#define XINPUT_GAMEPAD_START 0x0010 +#define XINPUT_GAMEPAD_BACK 0x0020 +#define XINPUT_GAMEPAD_LEFT_THUMB 0x0040 +#define XINPUT_GAMEPAD_RIGHT_THUMB 0x0080 +#define XINPUT_GAMEPAD_LEFT_SHOULDER 0x0100 +#define XINPUT_GAMEPAD_RIGHT_SHOULDER 0x0200 +#define XINPUT_GAMEPAD_A 0x1000 +#define XINPUT_GAMEPAD_B 0x2000 +#define XINPUT_GAMEPAD_X 0x4000 +#define XINPUT_GAMEPAD_Y 0x8000 + +#define XINPUT_CAPS_FFB_SUPPORTED 0x0001 +#define XINPUT_CAPS_WIRELESS 0x0002 +#define XINPUT_CAPS_PMD_SUPPORTED 0x0008 +#define XINPUT_CAPS_NO_NAVIGATION 0x0010 + +// +// Flags for battery status level +// +#define BATTERY_TYPE_DISCONNECTED 0x00 // This device is not connected +#define BATTERY_TYPE_WIRED 0x01 // Wired device, no battery +#define BATTERY_TYPE_ALKALINE 0x02 // Alkaline battery source +#define BATTERY_TYPE_NIMH 0x03 // Nickel Metal Hydride battery source +#define BATTERY_TYPE_UNKNOWN 0xFF // Cannot determine the battery type + +// These are only valid for wireless, connected devices, with known battery types +// The amount of use time remaining depends on the type of device. +#define BATTERY_LEVEL_EMPTY 0x00 +#define BATTERY_LEVEL_LOW 0x01 +#define BATTERY_LEVEL_MEDIUM 0x02 +#define BATTERY_LEVEL_FULL 0x03 + + +#define XINPUT_DEVTYPE_GAMEPAD 0x01 + +#define BATTERY_TYPE_DISCONNECTED 0x00 + +#define XUSER_MAX_COUNT 4 +#define XUSER_INDEX_ANY 0x000000FF + +#define ERROR_DEVICE_NOT_CONNECTED 1167 +#define ERROR_SUCCESS 0 + +std::unique_ptr m_gamePad; + +bool initialized = false; + +// +// Structures used by XInput APIs +// +typedef struct _XINPUT_GAMEPAD +{ + WORD wButtons; + BYTE bLeftTrigger; + BYTE bRightTrigger; + SHORT sThumbLX; + SHORT sThumbLY; + SHORT sThumbRX; + SHORT sThumbRY; +} XINPUT_GAMEPAD, *PXINPUT_GAMEPAD; + +typedef struct _XINPUT_STATE +{ + DWORD dwPacketNumber; + XINPUT_GAMEPAD Gamepad; +} XINPUT_STATE, *PXINPUT_STATE; + +typedef struct _XINPUT_VIBRATION +{ + WORD wLeftMotorSpeed; + WORD wRightMotorSpeed; +} XINPUT_VIBRATION, *PXINPUT_VIBRATION; + +typedef struct _XINPUT_CAPABILITIES +{ + BYTE Type; + BYTE SubType; + WORD Flags; + XINPUT_GAMEPAD Gamepad; + XINPUT_VIBRATION Vibration; +} XINPUT_CAPABILITIES, *PXINPUT_CAPABILITIES; + +typedef struct _XINPUT_BATTERY_INFORMATION +{ + BYTE BatteryType; + BYTE BatteryLevel; +} XINPUT_BATTERY_INFORMATION, *PXINPUT_BATTERY_INFORMATION; + +typedef struct _XINPUT_KEYSTROKE +{ + WORD VirtualKey; + WCHAR Unicode; + WORD Flags; + BYTE UserIndex; + BYTE HidCode; +} XINPUT_KEYSTROKE, *PXINPUT_KEYSTROKE; + +#define DLLEXPORT extern "C" __declspec(dllexport) + +DLLEXPORT BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + /*switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + }*/ + return TRUE; +} + +DLLEXPORT DWORD WINAPI XInputGetState(_In_ DWORD dwUserIndex, _Out_ XINPUT_STATE *pState) +{ + if (!initialized) { + m_gamePad = std::make_unique(); + initialized = true; + } + + auto state = m_gamePad->GetState(dwUserIndex); + + if (state.connected) { + + DWORD keys = 0; + + pState->Gamepad.bRightTrigger = 0; + pState->Gamepad.bLeftTrigger = 0; + pState->Gamepad.sThumbLX = 0; + pState->Gamepad.sThumbLY = 0; + pState->Gamepad.sThumbRX = 0; + pState->Gamepad.sThumbRY = 0; + + if (state.buttons.a) keys += XINPUT_GAMEPAD_A; + if (state.buttons.x) keys += XINPUT_GAMEPAD_X; //E + if (state.buttons.y) keys += XINPUT_GAMEPAD_Y; //Q + if (state.buttons.b) keys += XINPUT_GAMEPAD_B; //CTRL + + pState->Gamepad.bLeftTrigger = state.triggers.left * 255; + pState->Gamepad.bRightTrigger = state.triggers.right * 255; + + pState->Gamepad.sThumbLX = (state.thumbSticks.leftX >= 0) ? state.thumbSticks.leftX * 32767 : state.thumbSticks.leftX * 32768; + pState->Gamepad.sThumbLY = (state.thumbSticks.leftY >= 0) ? state.thumbSticks.leftY * 32767 : state.thumbSticks.leftY * 32768; + pState->Gamepad.sThumbRX = (state.thumbSticks.rightX >= 0) ? state.thumbSticks.rightX * 32767 : state.thumbSticks.rightX * 32768; + pState->Gamepad.sThumbRY = (state.thumbSticks.rightY >= 0) ? state.thumbSticks.rightY * 32767 : state.thumbSticks.rightY * 32768; + + if (state.buttons.rightStick) keys += XINPUT_GAMEPAD_RIGHT_THUMB; + if (state.buttons.leftStick) keys += XINPUT_GAMEPAD_LEFT_THUMB; + if (state.buttons.leftShoulder) keys += XINPUT_GAMEPAD_LEFT_SHOULDER; + if (state.buttons.rightShoulder) keys += XINPUT_GAMEPAD_RIGHT_SHOULDER; + + if (state.buttons.back) keys += XINPUT_GAMEPAD_BACK; + if (state.buttons.start) keys += XINPUT_GAMEPAD_START; + + if (state.dpad.up) keys += XINPUT_GAMEPAD_DPAD_UP; + if (state.dpad.down) keys += XINPUT_GAMEPAD_DPAD_DOWN; + if (state.dpad.left) keys += XINPUT_GAMEPAD_DPAD_LEFT; + if (state.dpad.right) keys += XINPUT_GAMEPAD_DPAD_RIGHT; + + pState->dwPacketNumber = state.packet; + pState->Gamepad.wButtons = keys; + + return ERROR_SUCCESS; + } + else + { + return ERROR_DEVICE_NOT_CONNECTED; + } + +} + +DLLEXPORT DWORD WINAPI XInputSetState(_In_ DWORD dwUserIndex, _In_ XINPUT_VIBRATION *pVibration) +{ + auto state = m_gamePad->GetCapabilities(dwUserIndex); + + if (state.connected) { + + m_gamePad->SetVibration(dwUserIndex, pVibration->wLeftMotorSpeed / 65535.0f, pVibration->wRightMotorSpeed / 65535.0f, pVibration->wLeftMotorSpeed / 65535.0f, pVibration->wRightMotorSpeed / 65535.0f); + + return ERROR_SUCCESS; + } + else + { + return ERROR_DEVICE_NOT_CONNECTED; + } +} + + +DLLEXPORT DWORD WINAPI XInputGetCapabilities(_In_ DWORD dwUserIndex, _In_ DWORD dwFlags, _Out_ XINPUT_CAPABILITIES *pCapabilities) +{ + auto state = m_gamePad->GetCapabilities(dwUserIndex); + + if (state.connected) { + // TODO: most likely not needed, but might be nice + + /* + XINPUT_CAPABILITIES x; + + x.Type = XINPUT_DEVTYPE_GAMEPAD; + + WORD flags = 0; + + if (state.gamepadType == state.GAMEPAD) { + flags += XINPUT_CAPS_FFB_SUPPORTED; + flags += XINPUT_CAPS_PMD_SUPPORTED; + } + + x.Flags = flags; + */ + return ERROR_SUCCESS; + } + else + { + return ERROR_DEVICE_NOT_CONNECTED; + } +} + +DLLEXPORT void WINAPI XInputEnable(_In_ BOOL enable) +{ +} + +DLLEXPORT DWORD WINAPI XInputGetDSoundAudioDeviceGuids(DWORD dwUserIndex, GUID* pDSoundRenderGuid, GUID* pDSoundCaptureGuid) +{ + auto state = m_gamePad->GetCapabilities(dwUserIndex); + if (state.connected) { + return ERROR_SUCCESS; + } + else + { + return ERROR_DEVICE_NOT_CONNECTED; + } +} + +DLLEXPORT DWORD WINAPI XInputGetBatteryInformation(_In_ DWORD dwUserIndex, _In_ BYTE devType, _Out_ XINPUT_BATTERY_INFORMATION *pBatteryInformation) +{ + auto state = m_gamePad->GetCapabilities(dwUserIndex); + if (state.connected) { + return ERROR_SUCCESS; + } + else + { + return ERROR_DEVICE_NOT_CONNECTED; + } +} + +DLLEXPORT DWORD WINAPI XInputGetKeystroke(DWORD dwUserIndex, DWORD dwReserved, PXINPUT_KEYSTROKE pKeystroke) +{ + auto state = m_gamePad->GetCapabilities(dwUserIndex); + if (state.connected) { + return ERROR_SUCCESS; + } + else + { + return ERROR_DEVICE_NOT_CONNECTED; + } +} + +DLLEXPORT DWORD WINAPI XInputGetStateEx(_In_ DWORD dwUserIndex, _Out_ XINPUT_STATE *pState) +{ + auto state = m_gamePad->GetCapabilities(dwUserIndex); + if (state.connected) { + return ERROR_SUCCESS; + } + else + { + return ERROR_DEVICE_NOT_CONNECTED; + } +} + +DLLEXPORT DWORD WINAPI XInputWaitForGuideButton(_In_ DWORD dwUserIndex, _In_ DWORD dwFlag, _In_ LPVOID pVoid) +{ + auto state = m_gamePad->GetCapabilities(dwUserIndex); + if (state.connected) { + return ERROR_SUCCESS; + } + else + { + return ERROR_DEVICE_NOT_CONNECTED; + } +} + +DLLEXPORT DWORD XInputCancelGuideButtonWait(_In_ DWORD dwUserIndex) +{ + auto state = m_gamePad->GetCapabilities(dwUserIndex); + if (state.connected) { + return ERROR_SUCCESS; + } + else + { + return ERROR_DEVICE_NOT_CONNECTED; + } +} + +DLLEXPORT DWORD XInputPowerOffController(_In_ DWORD dwUserIndex) +{ + auto state = m_gamePad->GetCapabilities(dwUserIndex); + if (state.connected) { + return ERROR_SUCCESS; + } + else + { + return ERROR_DEVICE_NOT_CONNECTED; + } +} \ No newline at end of file diff --git a/X1nput/stdafx.cpp b/X1nput/stdafx.cpp new file mode 100644 index 0000000..804d088 --- /dev/null +++ b/X1nput/stdafx.cpp @@ -0,0 +1,2 @@ +#include "stdafx.h" + diff --git a/X1nput/stdafx.h b/X1nput/stdafx.h new file mode 100644 index 0000000..d442ab7 --- /dev/null +++ b/X1nput/stdafx.h @@ -0,0 +1,156 @@ +//-------------------------------------------------------------------------------------- +// File: pch.h +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +// Off by default warnings +#pragma warning(disable : 4619 4061 4265 4355 4365 4571 4623 4625 4626 4628 4668 4710 4711 4746 4774 4820 4987 5026 5027 5031 5032 5039 5045) +// C4619 #pragma warning: there is no warning number 'X' +// C4061 enumerator 'X' in switch of enum 'X' is not explicitly handled by a case label +// C4265 class has virtual functions, but destructor is not virtual +// C4355 'this': used in base member initializer list +// C4365 signed/unsigned mismatch +// C4571 behavior change +// C4623 default constructor was implicitly defined as deleted +// C4625 copy constructor was implicitly defined as deleted +// C4626 assignment operator was implicitly defined as deleted +// C4628 digraphs not supported +// C4668 not defined as a preprocessor macro +// C4710 function not inlined +// C4711 selected for automatic inline expansion +// C4746 volatile access of '' is subject to /volatile: setting +// C4774 format string expected in argument 3 is not a string literal +// C4820 padding added after data member +// C4987 nonstandard extension used +// C5026 move constructor was implicitly defined as deleted +// C5027 move assignment operator was implicitly defined as deleted +// C5031/5032 push/pop mismatches in windows headers +// C5039 pointer or reference to potentially throwing function passed to extern C function under - EHc +// C5045 Spectre mitigation warning + +// XBox One XDK related Off by default warnings +#pragma warning(disable : 4471 4917 4986 5043) +// C4471 forward declaration of an unscoped enumeration must have an underlying type +// C4917 a GUID can only be associated with a class, interface or namespace +// C4986 exception specification does not match previous declaration +// C5043 exception specification does not match previous declaration + +// Xbox One XDK related Off by default warnings +#pragma warning(disable : 4643) +// C4643 Forward declaring in namespace std is not permitted by the C++ Standard + +#ifdef __INTEL_COMPILER +#pragma warning(disable : 161 2960 3280) +// warning #161: unrecognized #pragma +// message #2960: allocation may not satisfy the type's alignment; consider using header +// message #3280: declaration hides member +#endif + +#pragma warning(push) +#pragma warning(disable : 4005) +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#define NODRAWTEXT +#define NOGDI +#define NOBITMAP +#define NOMCX +#define NOSERVICE +#define NOHELP +#pragma warning(pop) + +#include "targetver.h" + +#include + +#ifndef _WIN32_WINNT_WIN10 +#define _WIN32_WINNT_WIN10 0x0A00 +#endif + +#if defined(_XBOX_ONE) && defined(_TITLE) +#include + +#if _XDK_VER < 0x295A044C /* XDK Edition 160200 */ +#error DirectX Tool Kit for Direct3D 12 requires the February 2016 XDK or later +#endif + +#include // core 12.x header +#include // utility 12.x header +#define DCOMMON_H_INCLUDED +#else +#include +#include + +#define D3DX12_NO_STATE_OBJECT_HELPERS +#endif + +#if (defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)) || (defined(_XBOX_ONE) && defined(_TITLE)) +#pragma warning(push) +#pragma warning(disable: 4471) +#include +#pragma warning(pop) +#endif + +#define _XM_NO_XMVECTOR_OVERLOADS_ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma warning(push) +#pragma warning(disable : 4702) +#include +#pragma warning(pop) + +#include +#include +#include + +#pragma warning(push) +#pragma warning(disable : 4467 5038) +#include +#pragma warning(pop) + +#include + +// DirectX Tool Kit for Audio is in all versions of DirectXTK12 +#include +#include + +#ifndef XAUDIO2_HELPER_FUNCTIONS +#define XAUDIO2_HELPER_FUNCTIONS +#endif + +#include +#include +#include +#include + +#if defined(_XBOX_ONE) && defined(_TITLE) +#include +#include +#include +#endif + +#include "gamepad.h" \ No newline at end of file diff --git a/X1nput/targetver.h b/X1nput/targetver.h new file mode 100644 index 0000000..05ba7d4 --- /dev/null +++ b/X1nput/targetver.h @@ -0,0 +1,3 @@ +#pragma once + +#include