mirror of
https://github.com/cemu-project/WinGamingInput.git
synced 2025-01-09 19:27:30 -03:00
initial project commit
This commit is contained in:
parent
b5684baec6
commit
17c1fdbee6
4 changed files with 1107 additions and 0 deletions
15
CMakeLists.txt
Normal file
15
CMakeLists.txt
Normal 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
25
exports.def
Normal 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
|
||||||
|
|
||||||
|
|
225
include/WindowsGamingInput.h
Normal file
225
include/WindowsGamingInput.h
Normal 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
842
src/WindowsGamingInput.cpp
Normal 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, ×tamp);
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue