initial project commit

This commit is contained in:
TeamCemu 2021-03-20 00:30:31 +01:00
parent b5684baec6
commit 17c1fdbee6
4 changed files with 1107 additions and 0 deletions

15
CMakeLists.txt Normal file
View File

@ -0,0 +1,15 @@
cmake_minimum_required (VERSION 3.1)
project ("WinGamingInput" LANGUAGES CXX)
# require c++20
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS ON)
add_library (WinGamingInput SHARED "src/WindowsGamingInput.cpp" "include/WindowsGamingInput.h" "exports.def")
# use static runtime lib for msvc
set_target_properties(WinGamingInput PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
target_link_libraries(WinGamingInput PRIVATE runtimeobject)

25
exports.def Normal file
View File

@ -0,0 +1,25 @@
EXPORTS
AddControllerChanged=?AddControllerChanged@WindowsGamingInput@@YAXP6AXW4EventType@1@W4ControllerType@1@V?$variant@_KV?$basic_string_view@_WU?$char_traits@_W@std@@@std@@@std@@@Z@Z
RemoveControllerChanged=?RemoveControllerChanged@WindowsGamingInput@@YAXP6AXW4EventType@1@W4ControllerType@1@V?$variant@_KV?$basic_string_view@_WU?$char_traits@_W@std@@@std@@@std@@@Z@Z
Gamepad_GetCount=?GetCount@Gamepad@WindowsGamingInput@@YA_KXZ
Gamepad_IsConnected=?IsConnected@Gamepad@WindowsGamingInput@@YA_N_K@Z
Gamepad_IsWireless=?IsWireless@Gamepad@WindowsGamingInput@@YA_N_KAEA_N@Z
Gamepad_GetBatteryStatus=?GetBatteryStatus@Gamepad@WindowsGamingInput@@YA_N_KAEAW4BatteryStatus@2@AEAN@Z
Gamepad_GetState=?GetState@Gamepad@WindowsGamingInput@@YA_N_KAEAUGamepadState@2@@Z
Gamepad_GetVibration=?GetVibration@Gamepad@WindowsGamingInput@@YA_N_KAEAUVibration@2@@Z
Gamepad_SetVibration=?SetVibration@Gamepad@WindowsGamingInput@@YA_N_KAEBUVibration@2@@Z
RawGameController_GetCount=?GetCount@RawGameController@WindowsGamingInput@@YA_KXZ
RawGameController_GetControllers=?GetControllers@RawGameController@WindowsGamingInput@@YA_KPEAUDescription@RawController@2@_K@Z
RawGameController_GetController=?GetController@RawGameController@WindowsGamingInput@@YA_NV?$basic_string_view@_WU?$char_traits@_W@std@@@std@@AEAUDescription@RawController@2@@Z
RawGameController_GetButtonLabel=?GetButtonLabel@RawGameController@WindowsGamingInput@@YA_NV?$basic_string_view@_WU?$char_traits@_W@std@@@std@@_KAEAW4ButtonLabel@2@@Z
RawGameController_IsConnected=?IsConnected@RawGameController@WindowsGamingInput@@YA_NV?$basic_string_view@_WU?$char_traits@_W@std@@@std@@@Z
RawGameController_IsWireless=?IsWireless@RawGameController@WindowsGamingInput@@YA_NV?$basic_string_view@_WU?$char_traits@_W@std@@@std@@AEA_N@Z
RawGameController_GetBatteryStatus=?GetBatteryStatus@RawGameController@WindowsGamingInput@@YA_NV?$basic_string_view@_WU?$char_traits@_W@std@@@std@@AEAW4BatteryStatus@2@AEAN@Z
RawGameController_GetState=?GetState@RawGameController@WindowsGamingInput@@YA_NV?$basic_string_view@_WU?$char_traits@_W@std@@@std@@PEA_N_KPEAW4SwitchPosition@2@2PEAN2AEA_K@Z
RawGameController_IsVibrating=?IsVibrating@RawGameController@WindowsGamingInput@@YA_NV?$basic_string_view@_WU?$char_traits@_W@std@@@std@@@Z
RawGameController_SetVibration=?SetVibration@RawGameController@WindowsGamingInput@@YA_NV?$basic_string_view@_WU?$char_traits@_W@std@@@std@@N@Z
RawGameController_HasVibration=?HasVibration@RawGameController@WindowsGamingInput@@YA_NV?$basic_string_view@_WU?$char_traits@_W@std@@@std@@@Z

View File

@ -0,0 +1,225 @@
#pragma once
#include <cstdint>
#include <string>
#include <vector>
#include <variant>
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NO_MIN_MAX
#define NO_MIN_MAX
#endif
#include <Windows.h>
#ifndef DLLEXPORT
#define DLLEXPORT
#endif
namespace WindowsGamingInput
{
// == ABI::Windows::Gaming::Input::GamepadButtons
enum class GamepadButtons : unsigned int
{
None = 0,
Menu = 0x1,
View = 0x2,
A = 0x4,
B = 0x8,
X = 0x10,
Y = 0x20,
DPadUp = 0x40,
DPadDown = 0x80,
DPadLeft = 0x100,
DPadRight = 0x200,
LeftShoulder = 0x400,
RightShoulder = 0x800,
LeftThumbstick = 0x1000,
RightThumbstick = 0x2000,
Paddle1 = 0x4000,
Paddle2 = 0x8000,
Paddle3 = 0x10000,
Paddle4 = 0x20000,
};
DEFINE_ENUM_FLAG_OPERATORS(GamepadButtons)
// == ABI::Windows::Gaming::Input::GamepadReading
struct GamepadState
{
uint64_t Timestamp;
GamepadButtons Buttons;
double LeftTrigger;
double RightTrigger;
double LeftThumbstickX;
double LeftThumbstickY;
double RightThumbstickX;
double RightThumbstickY;
};
// == ABI::Windows::Gaming::Input::GamepadVibration
struct Vibration
{
double LeftMotor = 0;
double RightMotor = 0;
double LeftTrigger = 0;
double RightTrigger = 0;
};
// == ABI::Windows::System::Power::BatteryStatus
enum class BatteryStatus
{
NotPresent = 0,
Discharging = 1,
Idle = 2,
Charging = 3,
};
// == ABI::Windows::Gaming::Input::GameControllerSwitchPosition
enum class SwitchPosition
{
Center = 0,
Up = 1,
UpRight = 2,
Right = 3,
DownRight = 4,
Down = 5,
DownLeft = 6,
Left = 7,
UpLeft = 8,
};
enum class ButtonLabel : int // == ABI::Windows::Gaming::Input::GameControllerButtonLabel
{
None = 0,
XboxBack = 1,
XboxStart = 2,
XboxMenu = 3,
XboxView = 4,
XboxUp = 5,
XboxDown = 6,
XboxLeft = 7,
XboxRight = 8,
XboxA = 9,
XboxB = 10,
XboxX = 11,
XboxY = 12,
XboxLeftBumper = 13,
XboxLeftTrigger = 14,
XboxLeftStickButton = 15,
XboxRightBumper = 16,
XboxRightTrigger = 17,
XboxRightStickButton = 18,
XboxPaddle1 = 19,
XboxPaddle2 = 20,
XboxPaddle3 = 21,
XboxPaddle4 = 22,
Mode = 23,
Select = 24,
Menu = 25,
View = 26,
Back = 27,
Start = 28,
Options = 29,
Share = 30,
Up = 31,
Down = 32,
Left = 33,
Right = 34,
LetterA = 35,
LetterB = 36,
LetterC = 37,
LetterL = 38,
LetterR = 39,
LetterX = 40,
LetterY = 41,
LetterZ = 42,
Cross = 43,
Circle = 44,
Square = 45,
Triangle = 46,
LeftBumper = 47,
LeftTrigger = 48,
LeftStickButton = 49,
Left1 = 50,
Left2 = 51,
Left3 = 52,
RightBumper = 53,
RightTrigger = 54,
RightStickButton = 55,
Right1 = 56,
Right2 = 57,
Right3 = 58,
Paddle1 = 59,
Paddle2 = 60,
Paddle3 = 61,
Paddle4 = 62,
Plus = 63,
Minus = 64,
DownLeftArrow = 65,
DialLeft = 66,
DialRight = 67,
Suspension = 68,
};
enum class ControllerType
{
RawController,
Gamepad,
};
enum class EventType
{
ControllerAdded,
ControllerRemoved,
};
using ControllerChanged_t = void (*)(EventType type, ControllerType controller, std::variant<size_t, std::wstring_view> uid);
DLLEXPORT void AddControllerChanged(ControllerChanged_t cb);
DLLEXPORT void RemoveControllerChanged(ControllerChanged_t cb);
namespace Gamepad
{
DLLEXPORT size_t GetCount();
DLLEXPORT bool IsConnected(size_t index);
DLLEXPORT bool GetState(size_t index, GamepadState& state);
DLLEXPORT bool SetVibration(size_t index, const Vibration& vibration);
DLLEXPORT bool GetVibration(size_t index, Vibration& vibration);
DLLEXPORT bool IsWireless(size_t index, bool& wireless);
DLLEXPORT bool GetBatteryStatus(size_t index, BatteryStatus& status, double& battery);
}
namespace RawController
{
struct Description
{
wchar_t uid[256];
wchar_t display_name[256];
size_t button_count;
size_t switches_count;
size_t axis_count;
};
// <uid, display_name>
DLLEXPORT size_t GetCount();
DLLEXPORT size_t GetControllers(Description* controllers, size_t count);
DLLEXPORT bool GetController(std::wstring_view uid, Description& description);
DLLEXPORT bool GetButtonLabel(std::wstring_view uid, size_t button, ButtonLabel& label);
DLLEXPORT bool IsConnected(std::wstring_view uid);
DLLEXPORT bool GetState(std::wstring_view uid, bool* buttons, size_t button_count, SwitchPosition* switches, size_t switch_count, double* axis, size_t axis_count, uint64_t& timestamp);
DLLEXPORT bool SetVibration(std::wstring_view uid, double vibration);
DLLEXPORT bool IsVibrating(std::wstring_view uid);
DLLEXPORT bool HasVibration(std::wstring_view uid);
DLLEXPORT bool IsWireless(std::wstring_view uid, bool& wireless);
DLLEXPORT bool GetBatteryStatus(std::wstring_view uid, BatteryStatus& status, double& battery);
}
}

842
src/WindowsGamingInput.cpp Normal file
View File

@ -0,0 +1,842 @@
#define DLLEXPORT __declspec(dllexport)
#include "../include/WindowsGamingInput.h"
#include <cassert>
#include <cstdint>
#include <iostream>
#include <shared_mutex>
#include <string>
#include <unordered_map>
#include <vector>
#include <queue>
#include <roapi.h>
#include <wrl.h>
#include <windows.gaming.input.h>
using namespace ABI::Windows::Foundation::Collections;
using namespace ABI::Windows::Gaming::Input;
using namespace ABI::Windows::Devices::Haptics;
using namespace Microsoft::WRL;
using namespace Wrappers;
RoInitializeWrapper g_ro{RO_INIT_MULTITHREADED};
std::mutex g_cb_mutex;
std::vector<WindowsGamingInput::ControllerChanged_t> g_callbacks;
#pragma region Gamepad
IGamepadStatics* g_gamepad_statics = nullptr;
using GamepadPtr = ComPtr<IGamepad>;
std::vector<GamepadPtr> g_gamepads;
std::shared_mutex g_gamepad_mutex;
void ScanGamepads()
{
ComPtr<IVectorView<Gamepad*>> gamepads;
auto hr = g_gamepad_statics->get_Gamepads(&gamepads);
assert(SUCCEEDED(hr));
uint32_t count = 0;
hr = gamepads->get_Size(&count);
assert(SUCCEEDED(hr));
#ifdef _DEBUG
std::cout << count << " gamepads are connected" << std::endl;
#endif
for (uint32_t i = 0; i < count; ++i)
{
ComPtr<IGamepad> gamepad;
hr = gamepads->GetAt(i, &gamepad);
assert(SUCCEEDED(hr));
std::unique_lock lock(g_gamepad_mutex);
const auto it = std::ranges::find(std::as_const(g_gamepads), gamepad);
if (it == g_gamepads.cend())
{
g_gamepads.emplace_back(gamepad);
#ifdef _DEBUG
std::cout << "inserted new gamepad" << std::endl;
#endif
lock.unlock();
std::scoped_lock cb_lock(g_cb_mutex);
for (const auto& cb : g_callbacks)
{
cb(WindowsGamingInput::EventType::ControllerAdded, WindowsGamingInput::ControllerType::Gamepad, g_gamepads.size() - 1);
}
}
}
}
EventRegistrationToken g_add_gamepad_token;
HRESULT OnGamepadAdded(IInspectable* sender, IGamepad* gamepad)
{
#ifdef _DEBUG
std::cout << "OnGamepadAdded" << std::endl;
#endif
const ComPtr<IGamepad> ptr{ gamepad };
std::unique_lock lock(g_gamepad_mutex);
const auto it = std::ranges::find(std::as_const(g_gamepads), ptr);
if (it != g_gamepads.cend())
return S_OK;
size_t index = (size_t)-1;
// check if we still got a free index in our internal list
for(size_t i = 0; i < g_gamepads.size(); ++i)
{
if (!g_gamepads[i])
{
g_gamepads[i] = ptr;
#ifdef _DEBUG
std::cout << "OnGamepadAdded:inserted new gamepad at empty index" << std::endl;
#endif
index = i;
}
}
// no free index
if (index == (size_t)-1)
{
g_gamepads.emplace_back(gamepad);
index = g_gamepads.size() - 1;
#ifdef _DEBUG
std::cout << "inserted new gamepad at the end" << std::endl;
#endif
}
lock.unlock();
std::scoped_lock cb_lock(g_cb_mutex);
for (const auto& cb : g_callbacks)
{
cb(WindowsGamingInput::EventType::ControllerAdded, WindowsGamingInput::ControllerType::Gamepad, index);
}
return S_OK;
}
EventRegistrationToken g_remove_gamepad_token;
HRESULT OnGamepadRemoved(IInspectable* sender, IGamepad* gamepad)
{
#ifdef _DEBUG
std::cout << "OnGamepadRemoved" << std::endl;
#endif
std::unique_lock lock(g_gamepad_mutex);
for (size_t i = 0; i < g_gamepads.size(); ++i)
{
if(g_gamepads[i].Get() == gamepad)
{
g_gamepads[i].Reset();
#ifdef _DEBUG
std::cout << "OnGamepadRemoved: removed known gamepad from internal list" << std::endl;
#endif
lock.unlock();
std::scoped_lock cb_lock(g_cb_mutex);
for (const auto& cb : g_callbacks)
{
cb(WindowsGamingInput::EventType::ControllerRemoved, WindowsGamingInput::ControllerType::Gamepad, i);
}
break;
}
}
return S_OK;
}
#pragma endregion
#pragma region RawGameController
IRawGameControllerStatics* g_rcontroller_statics = nullptr;
using RControllerPtr = ComPtr<IRawGameController>;
struct wstring_hash
{
using is_transparent = void;
using key_equal = std::equal_to<>;
using hash_type = std::hash<std::wstring_view>;
size_t operator()(std::wstring_view str) const { return hash_type{}(str); }
size_t operator()(const std::wstring& str) const { return hash_type{}(str); }
size_t operator()(const wchar_t* str) const { return hash_type{}(str); }
};
std::unordered_map<std::wstring, RControllerPtr, wstring_hash, wstring_hash::key_equal> g_rcontrollers;
std::shared_mutex g_rcontroller_mutex;
// https://docs.microsoft.com/en-us/uwp/api/windows.devices.haptics.knownsimplehapticscontrollerwaveforms
// ABI::Windows::Devices::Haptics::IKnownSimpleHapticsControllerWaveformsStatics::get_RumbleContinuous()
constexpr uint16_t kRumbleContinuous = 0x1005;
void ScanRawGameControllers()
{
ComPtr<IVectorView<RawGameController*>> controllers;
auto hr = g_rcontroller_statics->get_RawGameControllers(&controllers);
assert(SUCCEEDED(hr));
uint32_t count;
hr = controllers->get_Size(&count);
assert(SUCCEEDED(hr));
#ifdef _DEBUG
std::cout << count << " controllers are connected" << std::endl;
#endif
// check for all connected controllers
for (uint32_t i = 0; i < count; ++i)
{
ComPtr<IRawGameController> controller;
hr = controllers->GetAt(i, &controller);
assert(SUCCEEDED(hr));
ComPtr<IRawGameController2> controller2;
hr = controller.As(&controller2);
if (FAILED(hr)) // I guess shouldn't fail, idk (?)
continue;
HSTRING tmp_name;
hr = controller2->get_NonRoamableId(&tmp_name);
if (FAILED(hr))
continue;
std::wstring name = WindowsGetStringRawBuffer(tmp_name, nullptr);
if (name.empty())
continue;
std::unique_lock lock(g_rcontroller_mutex);
if (!g_rcontrollers.contains(name))
{
g_rcontrollers.emplace(name, controller);
lock.unlock();
#ifdef _DEBUG
std::wcout << L"inserted new controller with uid: " << name << std::endl;
#endif
std::scoped_lock cb_lock(g_cb_mutex);
for (const auto& cb : g_callbacks)
{
cb(WindowsGamingInput::EventType::ControllerAdded, WindowsGamingInput::ControllerType::RawController, name);
}
}
}
}
EventRegistrationToken g_add_rcontroller_token;
HRESULT OnRawGameControllerAdded(IInspectable* sender, IRawGameController* controller)
{
#ifdef _DEBUG
std::cout << "OnRawGameControllerAdded" << std::endl;
#endif
ComPtr<IRawGameController2> controller2;
HRESULT hr = controller->QueryInterface(__uuidof(IRawGameController2), &controller2);
if (SUCCEEDED(hr))
{
HSTRING tmp_name;
hr = controller2->get_NonRoamableId(&tmp_name);
if (SUCCEEDED(hr))
{
const std::wstring name = WindowsGetStringRawBuffer(tmp_name, nullptr);
std::unique_lock lock(g_rcontroller_mutex);
if (!g_rcontrollers.contains(name))
{
g_rcontrollers.emplace(name, controller);
#ifdef _DEBUG
std::wcout << L"OnRawGameControllerAdded: added new controller with uid: " << name << std::endl;
#endif
lock.unlock();
std::scoped_lock cb_lock(g_cb_mutex);
for (const auto& cb : g_callbacks)
{
cb(WindowsGamingInput::EventType::ControllerAdded, WindowsGamingInput::ControllerType::RawController, name);
}
}
}
}
return S_OK;
}
EventRegistrationToken g_remove_rcontroller_token;
HRESULT OnRawGameControllerRemoved(IInspectable* sender, IRawGameController* controller)
{
#ifdef _DEBUG
std::cout << "OnRawGameControllerRemoved" << std::endl;
#endif
ComPtr<IRawGameController2> controller2;
HRESULT hr = controller->QueryInterface(IID_PPV_ARGS(&controller2));
if (SUCCEEDED(hr))
{
HSTRING tmp_name;
hr = controller2->get_NonRoamableId(&tmp_name);
if (SUCCEEDED(hr))
{
const std::wstring name = WindowsGetStringRawBuffer(tmp_name, nullptr);
std::unique_lock lock(g_rcontroller_mutex);
const auto erased = g_rcontrollers.erase(name) == 1;
lock.unlock();
#ifdef _DEBUG
std::cout << "OnRawGameControllerRemoved: removed known controller: " << erased << std::endl;
#endif
std::scoped_lock cb_lock(g_cb_mutex);
for (const auto& cb : g_callbacks)
{
cb(WindowsGamingInput::EventType::ControllerRemoved, WindowsGamingInput::ControllerType::RawController, name);
}
}
}
return S_OK;
}
#pragma endregion
BOOL WINAPI DllMain(HINSTANCE hinstance, DWORD reason, LPVOID reserved)
{
if (reason == DLL_PROCESS_ATTACH)
{
std::thread([]()
{
if (!g_gamepad_statics)
{
auto hr = RoGetActivationFactory(HStringReference(L"Windows.Gaming.Input.Gamepad").Get(),
__uuidof(IGamepadStatics), (void**)&g_gamepad_statics);
assert(SUCCEEDED(hr));
hr = g_gamepad_statics->add_GamepadAdded(
Callback<__FIEventHandler_1_Windows__CGaming__CInput__CGamepad>(OnGamepadAdded).Get(),
&g_add_gamepad_token);
assert(SUCCEEDED(hr));
hr = g_gamepad_statics->add_GamepadRemoved(
Callback<__FIEventHandler_1_Windows__CGaming__CInput__CGamepad>(OnGamepadRemoved).Get(),
&g_remove_gamepad_token);
assert(SUCCEEDED(hr));
#ifdef _DEBUG
std::cout << "Windows.Gaming.Input.Gamepad initialized" << std::endl;
#endif
}
ScanGamepads();
if (!g_rcontroller_statics)
{
auto hr = RoGetActivationFactory(HStringReference(L"Windows.Gaming.Input.RawGameController").Get(),
__uuidof(IRawGameControllerStatics), (void**)&g_rcontroller_statics);
assert(SUCCEEDED(hr));
hr = g_rcontroller_statics->add_RawGameControllerAdded(
Callback<__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController>(OnRawGameControllerAdded).
Get(),
&g_add_rcontroller_token);
assert(SUCCEEDED(hr));
hr = g_rcontroller_statics->add_RawGameControllerRemoved(
Callback<__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController>(
OnRawGameControllerRemoved).Get(),
&g_remove_rcontroller_token);
assert(SUCCEEDED(hr));
#ifdef _DEBUG
std::cout << "Windows.Gaming.Input.RawGameController initialized" << std::endl;
#endif
}
ScanRawGameControllers();
}).detach();
}
else if (reason == DLL_PROCESS_DETACH)
{
// callbacks detach
{
std::scoped_lock lock(g_cb_mutex);
g_callbacks.clear();
}
// gamepad detach
{
std::scoped_lock lock(g_gamepad_mutex);
g_gamepads.clear();
if (g_gamepad_statics)
{
g_gamepad_statics->remove_GamepadAdded(g_add_rcontroller_token);
g_gamepad_statics->remove_GamepadRemoved(g_remove_rcontroller_token);
}
}
// raw game controller detach
{
std::scoped_lock lock(g_rcontroller_mutex);
g_rcontrollers.clear();
if (g_rcontroller_statics)
{
g_rcontroller_statics->remove_RawGameControllerAdded(g_add_rcontroller_token);
g_rcontroller_statics->remove_RawGameControllerRemoved(g_remove_rcontroller_token);
}
}
}
return TRUE;
}
namespace WindowsGamingInput
{
void AddControllerChanged(ControllerChanged_t cb)
{
std::scoped_lock lock(g_cb_mutex);
if(std::ranges::find(std::as_const(g_callbacks), cb) == g_callbacks.cend())
g_callbacks.emplace_back(cb);
}
void RemoveControllerChanged(ControllerChanged_t cb)
{
std::scoped_lock lock(g_cb_mutex);
const auto rm = std::ranges::remove(g_callbacks, cb);
g_callbacks.erase(rm.begin(), rm.end());
}
bool GetBatteryInfo(ComPtr<IGameControllerBatteryInfo> battery_info, BatteryStatus& status, double& battery)
{
ComPtr<ABI::Windows::Devices::Power::IBatteryReport> report;
HRESULT hr = battery_info->TryGetBatteryReport(&report);
if (FAILED(hr) || !report)
return false;
static_assert(sizeof(BatteryStatus) == sizeof(ABI::Windows::System::Power::BatteryStatus));
hr = report->get_Status((ABI::Windows::System::Power::BatteryStatus*)&status);
assert(SUCCEEDED(hr));
ComPtr<__FIReference_1_int> remaining_ptr, full_ptr;
report->get_RemainingCapacityInMilliwattHours(&remaining_ptr);
report->get_FullChargeCapacityInMilliwattHours(&full_ptr);
int remaining = 0, full = 0;
if (remaining_ptr)
{
hr = remaining_ptr->get_Value(&remaining);
assert(SUCCEEDED(hr));
}
if (full_ptr)
{
hr = full_ptr->get_Value(&full);
assert(SUCCEEDED(hr));
}
// remaining is always 100 when connected and status discharching (?!) -> check for IsWireless before
battery = full <= 0 ? 0 : static_cast<double>(remaining) / static_cast<double>(full);
return true;
}
namespace Gamepad
{
size_t GetCount()
{
std::shared_lock lock(g_gamepad_mutex);
return g_gamepads.size();
}
bool IsConnected(size_t index)
{
GamepadState tmp;
return GetState(index, tmp);
}
bool GetState(size_t index, GamepadState& state)
{
std::shared_lock lock(g_gamepad_mutex);
if (index >= g_gamepads.size())
return false;
const auto gamepad = g_gamepads[index];
lock.unlock();
if (!gamepad)
return false;
return SUCCEEDED(gamepad->GetCurrentReading((GamepadReading*)&state));
}
bool SetVibration(size_t index, const Vibration& vibration)
{
std::shared_lock lock(g_gamepad_mutex);
if (index >= g_gamepads.size())
return false;
const auto gamepad = g_gamepads[index];
lock.unlock();
if (!gamepad)
return false;
static_assert(sizeof(Vibration) == sizeof(GamepadVibration));
GamepadVibration tmp;
memcpy(&tmp, &vibration, sizeof(GamepadVibration));
return SUCCEEDED(gamepad->put_Vibration(tmp));
}
bool GetVibration(size_t index, Vibration& vibration)
{
std::shared_lock lock(g_gamepad_mutex);
if (index >= g_gamepads.size())
return false;
const auto gamepad = g_gamepads[index];
lock.unlock();
if (!gamepad)
return false;
static_assert(sizeof(Vibration) == sizeof(GamepadVibration));
return SUCCEEDED(gamepad->get_Vibration((GamepadVibration*)&vibration));
}
bool IsWireless(size_t index, bool& wireless)
{
std::shared_lock lock(g_gamepad_mutex);
if (index >= g_gamepads.size())
return false;
const auto gamepad = g_gamepads[index];
lock.unlock();
if (!gamepad)
return false;
ComPtr<IGameController> controller;
auto hr = gamepad.As(&controller);
assert(SUCCEEDED(hr));
static_assert(sizeof(bool) == sizeof(boolean));
return SUCCEEDED(controller->get_IsWireless((boolean*)&wireless));
}
bool GetBatteryStatus(size_t index, BatteryStatus& status, double& battery)
{
std::shared_lock lock(g_gamepad_mutex);
if (index >= g_gamepads.size())
return false;
const auto gamepad = g_gamepads[index];
lock.unlock();
if (!gamepad)
return false;
ComPtr<IGameControllerBatteryInfo> battery_info;
auto hr = gamepad.As(&battery_info);
assert(SUCCEEDED(hr));
return GetBatteryInfo(battery_info, status, battery);
}
}
namespace RawGameController
{
size_t GetCount()
{
std::shared_lock lock(g_rcontroller_mutex);
return g_rcontrollers.size();
}
size_t GetControllers(RawController::Description* controllers, size_t count)
{
std::shared_lock lock(g_rcontroller_mutex);
if (controllers == nullptr)
return g_rcontrollers.size(); // return size if no buffer have been given
size_t result = 0;
for (const auto& kv : g_rcontrollers)
{
if (result >= count)
break;
ComPtr<IRawGameController2> controller2;
kv.second.As(&controller2);
HSTRING tmp_name;
controller2->get_DisplayName(&tmp_name);
const std::wstring name = WindowsGetStringRawBuffer(tmp_name, nullptr);
wcscpy_s(controllers[result].uid, kv.first.c_str());
wcscpy_s(controllers[result].display_name, name.c_str());
controllers[result].axis_count = 0;
kv.second->get_AxisCount((int*)&controllers[result].axis_count);
controllers[result].button_count = 0;
kv.second->get_ButtonCount((int*)&controllers[result].button_count);
controllers[result].switches_count = 0;
kv.second->get_SwitchCount((int*)&controllers[result].switches_count);
++result;
}
return result;
}
bool GetController(std::wstring_view uid, RawController::Description& description)
{
std::shared_lock lock(g_rcontroller_mutex);
const auto it = g_rcontrollers.find(uid);
if (it == g_rcontrollers.cend())
return false;
auto controller = it->second;
lock.unlock();
description.axis_count = 0;
controller->get_AxisCount((int*)&description.axis_count);
description.button_count = 0;
controller->get_ButtonCount((int*)&description.button_count);
description.switches_count = 0;
controller->get_SwitchCount((int*)&description.switches_count);
return true;
}
bool IsConnected(std::wstring_view uid)
{
std::shared_lock lock(g_rcontroller_mutex);
const auto it = g_rcontrollers.find(uid);
return it != g_rcontrollers.cend();
}
bool GetState(std::wstring_view uid, bool* buttons, size_t button_count, SwitchPosition* switches, size_t switch_count, double* axis, size_t axis_count, uint64_t& timestamp)
{
std::shared_lock lock(g_rcontroller_mutex);
const auto it = g_rcontrollers.find(uid);
if (it == g_rcontrollers.cend())
return false;
ComPtr<IRawGameController> controller;
auto hr = it->second.As(&controller);
assert(SUCCEEDED(hr));
lock.unlock();
static_assert(sizeof(bool) == sizeof(boolean));
hr = controller->GetCurrentReading((uint32_t)button_count, (boolean*)buttons, (uint32_t)switch_count, (GameControllerSwitchPosition*)switches, (uint32_t)axis_count, (double*)axis, &timestamp);
return SUCCEEDED(hr);
}
bool HasVibration(std::wstring_view uid)
{
std::shared_lock lock(g_rcontroller_mutex);
const auto it = g_rcontrollers.find(uid);
if (it == g_rcontrollers.cend())
return false;
ComPtr<IRawGameController2> controller;
auto hr = it->second.As(&controller);
assert(SUCCEEDED(hr));
lock.unlock();
ComPtr<IVectorView<SimpleHapticsController*>> haptics;
hr = controller->get_SimpleHapticsControllers(&haptics);
if (FAILED(hr))
return false;
uint32_t count = 0;
haptics->get_Size(&count); // motor_count (?)
for (uint32_t i = 0; i < count; ++i)
{
ISimpleHapticsController* haptic;
haptics->GetAt(i, &haptic);
ComPtr<IVectorView<SimpleHapticsControllerFeedback*>> feedbacks;
hr = haptic->get_SupportedFeedback(&feedbacks);
if (FAILED(hr))
return false;
uint32_t feedback_count = 0;
feedbacks->get_Size(&feedback_count);
for (uint32_t j = 0; j < feedback_count; ++j)
{
ISimpleHapticsControllerFeedback* feedback;
feedbacks->GetAt(j, &feedback);
uint16_t waveform = 0;
feedback->get_Waveform(&waveform);
if (waveform == kRumbleContinuous)
return true;
}
}
return false;
}
bool SetVibration(std::wstring_view uid, double vibration)
{
std::shared_lock lock(g_rcontroller_mutex);
const auto it = g_rcontrollers.find(uid);
if (it == g_rcontrollers.cend())
return false;
ComPtr<IRawGameController2> controller;
auto hr = it->second.As(&controller);
assert(SUCCEEDED(hr));
lock.unlock();
ComPtr<IVectorView<SimpleHapticsController*>> haptics;
hr = controller->get_SimpleHapticsControllers(&haptics);
assert(SUCCEEDED(hr));
bool result = false;
uint32_t count = 0;
haptics->get_Size(&count);
for (uint32_t i = 0; i < count; ++i)
{
ISimpleHapticsController* haptic;
haptics->GetAt(i, &haptic);
if (vibration <= 0.000001)
{
haptic->StopFeedback();
continue;
}
ComPtr<IVectorView<SimpleHapticsControllerFeedback*>> feedbacks;
hr = haptic->get_SupportedFeedback(&feedbacks);
if (FAILED(hr))
return false;
uint32_t feedback_count = 0;
feedbacks->get_Size(&feedback_count);
for (uint32_t j = 0; j < feedback_count; ++j)
{
ISimpleHapticsControllerFeedback* feedback;
feedbacks->GetAt(j, &feedback);
uint16_t waveform = 0;
feedback->get_Waveform(&waveform);
if (waveform == kRumbleContinuous)
{
haptic->SendHapticFeedbackWithIntensity(feedback, vibration);
result = true;
break;
}
}
}
return result;
}
bool IsVibrating(std::wstring_view uid)
{
std::shared_lock lock(g_rcontroller_mutex);
const auto it = g_rcontrollers.find(uid);
if (it == g_rcontrollers.cend())
return false;
ComPtr<IRawGameController2> controller;
auto hr = it->second.As(&controller);
assert(SUCCEEDED(hr));
lock.unlock();
ComPtr<IVectorView<SimpleHapticsController*>> haptics;
hr = controller->get_SimpleHapticsControllers(&haptics);
assert(SUCCEEDED(hr));
uint32_t count = 0;
haptics->get_Size(&count);
for (uint32_t i = 0; i < count; ++i)
{
ISimpleHapticsController* haptic;
haptics->GetAt(i, &haptic);
ComPtr<IVectorView<SimpleHapticsControllerFeedback*>> feedbacks;
hr = haptic->get_SupportedFeedback(&feedbacks);
if (FAILED(hr))
return false;
uint32_t feedback_count = 0;
feedbacks->get_Size(&feedback_count);
for (uint32_t j = 0; j < feedback_count; ++j)
{
ISimpleHapticsControllerFeedback* feedback;
feedbacks->GetAt(j, &feedback);
uint16_t waveform = 0;
feedback->get_Waveform(&waveform);
if (waveform == kRumbleContinuous)
{
ABI::Windows::Foundation::TimeSpan ts{};
feedback->get_Duration(&ts);
if (ts.Duration != 0)
return true;
}
}
}
return false;
}
bool IsWireless(std::wstring_view uid, bool& wireless)
{
std::shared_lock lock(g_rcontroller_mutex);
const auto it = g_rcontrollers.find(uid);
if (it == g_rcontrollers.cend())
return false;
ComPtr<IGameController> controller;
auto hr = it->second.As(&controller);
assert(SUCCEEDED(hr));
lock.unlock();
static_assert(sizeof(bool) == sizeof(boolean));
return SUCCEEDED(controller->get_IsWireless((boolean*)&wireless));
}
bool GetBatteryStatus(std::wstring_view uid, BatteryStatus& status, double& battery)
{
std::shared_lock lock(g_rcontroller_mutex);
const auto it = g_rcontrollers.find(uid);
if (it == g_rcontrollers.cend())
return false;
ComPtr<IGameController> controller;
auto hr = it->second.As(&controller);
assert(SUCCEEDED(hr));
lock.unlock();
ComPtr<IGameControllerBatteryInfo> battery_info;
hr = controller.As(&battery_info);
if(FAILED(hr) || !battery_info)
return false;
return GetBatteryInfo(battery_info, status, battery);
}
bool GetButtonLabel(std::wstring_view uid, size_t button, ButtonLabel& label)
{
std::shared_lock lock(g_rcontroller_mutex);
const auto it = g_rcontrollers.find(uid);
if (it == g_rcontrollers.cend())
return false;
auto controller = it->second;
lock.unlock();
int max_count = 0;
controller->get_ButtonCount(&max_count);
if ((int)button >= max_count)
return false;
static_assert(sizeof(ButtonLabel) == sizeof(GameControllerButtonLabel));
return SUCCEEDED(controller->GetButtonLabel((int)button, (GameControllerButtonLabel*)&label));
}
}
}