Add Controller emulation (needs hidhide)

I can escape the cloak from hidhide??? wtf?
pull/130/head
Peter Repukat 3 years ago
parent bd0a83ab42
commit 0232b73d19

@ -80,15 +80,15 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<ExternalIncludePath>..\deps\SFML\include;..\deps\WinReg;..\deps\spdlog\include;..\deps\ValveFileVDF;..\deps\subhook;$(ExternalIncludePath)</ExternalIncludePath>
<LibraryPath>..\deps\SFML\out\build\x64-Debug\lib;$(LibraryPath)</LibraryPath>
<ExternalIncludePath>..\deps\SFML\include;..\deps\WinReg;..\deps\spdlog\include;..\deps\ValveFileVDF;..\deps\subhook;..\deps\ViGEmClient\include;$(ExternalIncludePath)</ExternalIncludePath>
<LibraryPath>..\deps\SFML\out\build\x64-Debug\lib;..\deps\ViGEmClient\lib\debug\x64;$(LibraryPath)</LibraryPath>
<CopyLocalProjectReference>false</CopyLocalProjectReference>
<CopyLocalDeploymentContent>true</CopyLocalDeploymentContent>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<ExternalIncludePath>..\deps\SFML\include;..\deps\WinReg;..\deps\spdlog\include;..\deps\ValveFileVDF;..\deps\subhook;$(ExternalIncludePath)</ExternalIncludePath>
<LibraryPath>..\deps\SFML\out\build\x64-Release\lib;$(LibraryPath)</LibraryPath>
<ExternalIncludePath>..\deps\SFML\include;..\deps\WinReg;..\deps\spdlog\include;..\deps\ValveFileVDF;..\deps\subhook;..\deps\ViGEmClient\include;$(ExternalIncludePath)</ExternalIncludePath>
<LibraryPath>..\deps\SFML\out\build\x64-Release\lib;..\deps\ViGEmClient\lib\release\x64;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
@ -129,7 +129,7 @@
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>sfml-window-d.lib;sfml-system-d.lib;sfml-graphics-d.lib;dwmapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>sfml-window-d.lib;sfml-system-d.lib;sfml-graphics-d.lib;dwmapi.lib;xinput9_1_0.lib;setupapi.lib;ViGEmClient.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -147,10 +147,12 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>xinput9_1_0.lib;setupapi.lib;ViGEmClient.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\deps\subhook\subhook.c" />
<ClCompile Include="InputRedirector.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="OverlayDetector.cpp" />
<ClCompile Include="SteamTarget.cpp" />
@ -158,6 +160,7 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\deps\subhook\subhook.h" />
<ClInclude Include="InputRedirector.h" />
<ClInclude Include="OverlayDetector.h" />
<ClInclude Include="SteamTarget.h" />
<ClInclude Include="steam_sf_keymap.h" />

@ -30,6 +30,9 @@
<ClCompile Include="..\deps\subhook\subhook.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="InputRedirector.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="SteamTarget.h">
@ -47,6 +50,9 @@
<ClInclude Include="..\deps\subhook\subhook.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="InputRedirector.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="..\deps\SFML\out\build\x64-Debug\lib\sfml-system-d-2.dll" />

@ -0,0 +1,118 @@
#include "InputRedirector.h"
#include <SFML/System/Clock.hpp>
#include <SFML/System/Sleep.hpp>
#include <spdlog/spdlog.h>
InputRedirector::InputRedirector()
{
#ifdef _WIN32
driver_ = vigem_alloc();
vigem_connected_ = VIGEM_SUCCESS(vigem_connect(driver_));
if (vigem_connected_) {
spdlog::debug("Connected to ViGEm");
} else {
spdlog::error("Error initializing ViGEm");
// TODO: setup some mechanic to draw to window...
}
#endif
}
InputRedirector::~InputRedirector()
{
#ifdef _WIN32
if (controller_thread_.joinable())
controller_thread_.join();
vigem_disconnect(driver_);
vigem_free(driver_);
spdlog::debug("ViGEm Disconnected");
#endif
}
void InputRedirector::run()
{
run_ = vigem_connected_;
controller_thread_ = std::thread(&InputRedirector::runLoop, this);
}
void InputRedirector::stop()
{
run_ = false;
if (vigem_connected_) {
for (const auto &target : vt_x360_) {
vigem_target_remove(driver_, target);
}
}
}
void InputRedirector::runLoop()
{
// wait for steam to do all of it's hooking
const sf::Clock clock;
while (clock.getElapsedTime().asMilliseconds() < start_delay_ms_) {
sf::sleep(sf::milliseconds(20));
}
while (run_) {
#ifdef _WIN32
for (int i = 0; i < XUSER_MAX_COUNT; i++) {
XINPUT_STATE state{};
if (XInputGetState(i, &state) == ERROR_SUCCESS) {
if (vt_x360_[i] != nullptr) {
vigem_target_x360_update(driver_, vt_x360_[i], *reinterpret_cast<XUSB_REPORT *>(&state.Gamepad));
} else {
vt_x360_[i] = vigem_target_x360_alloc();
// By using VID and PID of Valve's SteamController, Steam doesn't give us ANOTHER "fake" XInput device
// -> Leading to endless pain and suffering.
// Or really, leading to plugging in one virtual controller after another and mirroring inputs
// Also annoying the shit out of the user when they open the overlay as steam prompts to setup new XInput devices
// Also avoiding any fake inputs from Valve's default controller profile
// -> Leading to endless pain and suffering
//
// this surprisingly works, even with no SteamController connected
vigem_target_set_vid(vt_x360_[i], 0x28de); //Valve SteamController VID
vigem_target_set_pid(vt_x360_[i], 0x1102); //Valve SteamController PID
const int target_add_res = vigem_target_add(driver_, vt_x360_[i]);
if (target_add_res == VIGEM_ERROR_TARGET_UNINITIALIZED) {
vt_x360_[i] = vigem_target_x360_alloc();
}
if (target_add_res == VIGEM_ERROR_NONE) {
spdlog::info("Plugged in controller {}", vigem_target_get_index(vt_x360_[i]));
const auto callback_register_res = vigem_target_x360_register_notification(
driver_,
vt_x360_[i],
&InputRedirector::controllerCallback,
nullptr);
if (!VIGEM_SUCCESS(callback_register_res)) {
spdlog::error("Registering controller {} for notification failed with error code: {1:#x}", vigem_target_get_index(vt_x360_[i]), callback_register_res);
}
}
}
} else {
if (vt_x360_[i] != nullptr) {
if (VIGEM_SUCCESS(vigem_target_remove(driver_, vt_x360_[i]))) {
spdlog::info("Unplugged controller {}", vigem_target_get_index(vt_x360_[i]));
vt_x360_[i] = nullptr;
}
}
}
}
sf::sleep(sf::milliseconds(1));
#endif
}
}
#ifdef _WIN32
void InputRedirector::controllerCallback(PVIGEM_CLIENT client, PVIGEM_TARGET Target, UCHAR LargeMotor, UCHAR SmallMotor, UCHAR LedNumber, LPVOID UserData)
{
XINPUT_VIBRATION vibration;
ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION));
vibration.wLeftMotorSpeed = LargeMotor * 0xff; //Controllers only use 1 byte, XInput-API uses two, ViGEm also only uses one, like the hardware does, so we have to multiply
vibration.wRightMotorSpeed = SmallMotor * 0xff; //Yeah yeah I do know about bitshifting and the multiplication not being 100% correct...
XInputSetState(vigem_target_get_index(Target) - 1, &vibration);
}
#endif

@ -0,0 +1,32 @@
#pragma once
#include <thread>
#ifdef _WIN32
#define NOMINMAX
#include <Windows.h>
#include <ViGEm/Client.h>
#include <Xinput.h>
#endif
class InputRedirector {
public:
InputRedirector();
~InputRedirector();
void run();
void stop();
private:
void runLoop();
static constexpr int start_delay_ms_ = 2000;
bool run_ = false;
int controller_count_ = 0;
#ifdef _WIN32
PVIGEM_CLIENT driver_;
PVIGEM_TARGET vt_x360_[XUSER_MAX_COUNT]{};
bool vigem_connected_;
static void CALLBACK controllerCallback(PVIGEM_CLIENT client, PVIGEM_TARGET Target, UCHAR LargeMotor, UCHAR SmallMotor, UCHAR LedNumber, LPVOID UserData);
#endif
std::thread controller_thread_;
};

@ -50,11 +50,13 @@ int SteamTarget::run()
{
run_ = true;
keepControllerConfig(true);
input_redirector_.run();
while (run_) {
detector_.update();
window_.update();
overlayHotkeyWorkaround();
}
input_redirector_.stop();
return 1;
}

@ -15,6 +15,7 @@ limitations under the License.
*/
#pragma once
#include "InputRedirector.h"
#include "OverlayDetector.h"
#include "TargetWindow.h"
@ -50,6 +51,7 @@ class SteamTarget {
bool run_ = false;
std::vector<std::string> overlay_hotkey_ = getOverlayHotkey();
InputRedirector input_redirector_;
TargetWindow window_;
OverlayDetector detector_;
WindowHandle last_foreground_window_ = nullptr;

Loading…
Cancel
Save