From 4ae430b8134db0b6a6c246fec809bccdcd6778bc Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Wed, 20 Oct 2021 21:48:19 +0200 Subject: [PATCH] Hide gaming devices from all apps but Steam This means support for more than just the SteamController. --- GlosSITarget/HidHide.cpp | 488 +++++++++++++++++++++++++++++++ GlosSITarget/HidHide.h | 96 ++++++ GlosSITarget/InputRedirector.cpp | 1 - GlosSITarget/SteamTarget.cpp | 16 +- GlosSITarget/SteamTarget.h | 12 +- 5 files changed, 604 insertions(+), 9 deletions(-) create mode 100644 GlosSITarget/HidHide.cpp create mode 100644 GlosSITarget/HidHide.h diff --git a/GlosSITarget/HidHide.cpp b/GlosSITarget/HidHide.cpp new file mode 100644 index 0000000..e012593 --- /dev/null +++ b/GlosSITarget/HidHide.cpp @@ -0,0 +1,488 @@ +/* +Copyright 2021 Peter Repukat - FlatspotSoftware + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// parts of code adapted from https://github.com/ViGEm/HidHide/blob/HEAD/HidHideCLI/src/HID.cpp +// // (c) Eric Korff de Gidts +// SPDX-License-Identifier: MIT + +#include "HidHide.h" + +#include +#include +#include +#include + +// Device configuration related +#include + +#include +// +#include +#include +#include + +// {D61CA365-5AF4-4486-998B-9DB4734C6CA3}add the XUSB class GUID as it is missing in the public interfaces +DEFINE_GUID(GUID_DEVCLASS_XUSBCLASS, 0xD61CA365, 0x5AF4, 0x4486, 0x99, 0x8B, 0x9D, 0xB4, 0x73, 0x4C, 0x6C, 0xA3); +// {EC87F1E3-C13B-4100-B5F7-8B84D54260CB} add the XUSB interface class GUID as it is missing in the public interfaces +DEFINE_GUID(GUID_DEVINTERFACE_XUSB, 0xEC87F1E3, 0xC13B, 0x4100, 0xB5, 0xF7, 0x8B, 0x84, 0xD5, 0x42, 0x60, 0xCB); +// {00000000-0000-0000-FFFF-FFFFFFFFFFFF} the system container id +DEFINE_GUID(GUID_CONTAINER_ID_SYSTEM, 0x00000000, 0x0000, 0x0000, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); + +HidHide::HidHide() = default; + +void HidHide::openCtrlDevice() +{ + hidhide_handle = CreateFile( + L"\\\\.\\HidHide", + GENERIC_READ, + (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); +} + +void HidHide::closeCtrlDevice() +{ + if (hidhide_handle == nullptr) { + return; + } + CloseHandle(hidhide_handle); + hidhide_handle = nullptr; +} + +void HidHide::hideDevices(const std::filesystem::path& steam_path) +{ + openCtrlDevice(); + auto active = getActive(); + if (active) { + // disable hidhide so we can see devices ourselves + setActive(false); + } + auto whitelist = getAppWhiteList(); + // has anyone more than 4 keys to open overlay?! + std::wsmatch m; + const auto steam_path_string = steam_path.wstring(); + if (!std::regex_search(steam_path_string, m, std::wregex(L"(.:)(\\/|\\\\)")) || m.size() < 3) { + spdlog::warn("Couldn't detect steam drive letter; Device hiding may not function"); + return; + } + const auto dos_device = DosDeviceForVolume(m[1]); + if (dos_device.empty()) { + spdlog::warn("Couldn't detect steam drive letter DOS Path; Device hiding may not function"); + return; + } + + for (const auto& exe : whitelist_executeables_) { + auto path = std::regex_replace(steam_path_string, std::wregex(L"(.:)(\\/|\\\\)"), dos_device + L"\\"); + path = std::regex_replace(path, std::wregex(L"\\/"), L"\\") + L"\\" + std::wstring{exe}; + if (std::ranges::none_of(whitelist, [&path](auto ep) { // make copy! + auto p = path; // non-const(!) copy of path + std::ranges::transform(path, p.begin(), tolower); + std::ranges::transform(ep, ep.begin(), tolower); + return p == ep; + })) { + whitelist.push_back(path); + } + } + setAppWhiteList(whitelist); + + const auto device_list = GetHidDeviceList(); + auto blacklist = getBlackListDevices(); + + for (const auto& dev : device_list) { + if (std::ranges::none_of(blacklist, [&dev](const auto& blackdev) { + return blackdev == dev.device_instance_path || blackdev == dev.base_container_device_instance_path; + })) { + if (!dev.device_instance_path.empty()) { + blacklist.push_back(dev.device_instance_path); + } + if (!dev.device_instance_path.empty()) { + blacklist.push_back(dev.base_container_device_instance_path); + } + } + } + setBlacklistDevices(blacklist); + setActive(true); + closeCtrlDevice(); + spdlog::info("Hid Gaming Devices"); // TODO: add list of blacklisted devices +} + +void HidHide::disableHidHide() +{ + openCtrlDevice(); + setActive(false); + closeCtrlDevice(); + spdlog::info("Un-hid Gaming Devices"); // TODO: add list of blacklisted devices +} + +std::wstring HidHide::DosDeviceForVolume(const std::wstring& volume) +{ + std::vector buffer(UNICODE_STRING_MAX_CHARS); + QueryDosDeviceW(volume.c_str(), buffer.data(), static_cast(buffer.size())); + return {buffer.data()}; +} + +std::vector HidHide::getAppWhiteList() const +{ + DWORD bytes_needed = getRequiredOutputBufferSize(IOCTL_TYPE::GET_WHITELIST); + if (bytes_needed == 0) { + return std::vector{}; + } + std::vector buffer(bytes_needed); + if (!DeviceIoControl( + hidhide_handle, static_cast(IOCTL_TYPE::GET_WHITELIST), nullptr, 0, buffer.data(), static_cast(buffer.size() * sizeof(WCHAR)), &bytes_needed, nullptr)) { + spdlog::error("Couldn't retrieve HidHide Whitelist"); + return std::vector{}; + } + return BufferToStringVec(buffer); +} + +std::vector HidHide::getBlackListDevices() const +{ + DWORD bytes_needed = getRequiredOutputBufferSize(IOCTL_TYPE::GET_BLACKLIST); + if (bytes_needed == 0) { + return std::vector{}; + } + std::vector buffer(bytes_needed); + if (!DeviceIoControl( + hidhide_handle, static_cast(IOCTL_TYPE::GET_BLACKLIST), nullptr, 0, buffer.data(), static_cast(buffer.size() * sizeof(WCHAR)), &bytes_needed, nullptr)) { + spdlog::error("Couldn't retrieve HidHide Blacklist"); + return std::vector{}; + } + return BufferToStringVec(buffer); +} + +bool HidHide::getActive() const +{ + DWORD bytes_needed; + BOOLEAN res; + if (!DeviceIoControl( + hidhide_handle, static_cast(IOCTL_TYPE::GET_ACTIVE), nullptr, 0, &res, sizeof(BOOLEAN), &bytes_needed, nullptr)) { + spdlog::error("Couldn't retrieve HidHide State"); + return false; + } + return res; +} + +void HidHide::setAppWhiteList(const std::vector& whitelist) const +{ + DWORD bytes_needed; + auto buffer = StringListToMultiString(whitelist); + if (!DeviceIoControl( + hidhide_handle, static_cast(IOCTL_TYPE::SET_WHITELIST), buffer.data(), static_cast(buffer.size() * sizeof(WCHAR)), nullptr, 0, &bytes_needed, nullptr)) { + spdlog::error("Couldn't set HidHide WhiteList"); + } +} + +void HidHide::setBlacklistDevices(const std::vector& blacklist) const +{ + DWORD bytes_needed; + auto buffer = StringListToMultiString(blacklist); + if (!DeviceIoControl( + hidhide_handle, static_cast(IOCTL_TYPE::SET_BLACKLIST), buffer.data(), static_cast(buffer.size() * sizeof(WCHAR)), nullptr, 0, &bytes_needed, nullptr)) { + spdlog::error("Couldn't set HidHide BlackList"); + } +} + +void HidHide::setActive(bool active) const +{ + DWORD bytes_needed; + if (!DeviceIoControl( + hidhide_handle, static_cast(IOCTL_TYPE::SET_ACTIVE), &active, sizeof(BOOLEAN), nullptr, 0, &bytes_needed, nullptr)) { + spdlog::error("Couldn't set HidHide State"); + } +} + +DWORD HidHide::getRequiredOutputBufferSize(IOCTL_TYPE type) const +{ + DWORD bytes_needed; + if (!DeviceIoControl(hidhide_handle, static_cast(type), nullptr, 0, nullptr, 0, &bytes_needed, nullptr)) { + spdlog::error("Couldn't determine required HidHide output buffer size; type: {}", type); + return 0; + } + return bytes_needed; +} + +std::vector HidHide::BufferToStringVec(const auto& buffer) +{ + std::vector res; + if (buffer[0] != L'\0') { + res.emplace_back(); + for (const auto& ch : buffer) { + if (ch == L'\0') { + if ((res.end() - 1)->length() == 0) { + res.erase(res.end() - 1); + break; + } + res.emplace_back(); + continue; + } + (res.end() - 1)->push_back(ch); + } + } + return res; +} + +std::vector HidHide::StringListToMultiString(const std::vector& stringlist) +{ + auto res = std::accumulate(stringlist.begin(), stringlist.end(), std::vector{}, [](auto acc, const auto& curr) { + acc.insert(acc.end(), curr.begin(), curr.end()); + acc.push_back(L'\0'); + return acc; + }); + res.push_back(L'\0'); + return res; +} + +std::vector HidHide::GetHidDeviceList() +{ + std::wstring hid_class_guid_string; + hid_class_guid_string.resize(39); + if (!StringFromGUID2(GUID_DEVCLASS_HIDCLASS, hid_class_guid_string.data(), static_cast(hid_class_guid_string.size()))) { + spdlog::error("couldn't convert GUID to string"); + } + + ULONG needed{}; + if (const auto result = CM_Get_Device_ID_List_SizeW(&needed, hid_class_guid_string.c_str(), CM_GETIDLIST_FILTER_CLASS); + (CR_SUCCESS != result)) { + spdlog::error("Couldn't get device id list size; code: {}", result); + } + std::vector buffer(needed); + if (const auto result = CM_Get_Device_ID_ListW(hid_class_guid_string.c_str(), buffer.data(), needed, CM_GETIDLIST_FILTER_CLASS); + (CR_SUCCESS != result)) { + spdlog::error("Couldn't get device id list; code: {}", result); + } + + auto device_instance_paths = BufferToStringVec(buffer); + device_instance_paths.erase( + std::ranges::remove_if( + device_instance_paths, + [](const auto& dev) { return !DevicePresent(dev); }) + .begin(), + device_instance_paths.end()); + + GUID hid_device_interface_guid{}; + HidD_GetHidGuid(&hid_device_interface_guid); + std::vector res; + for (auto& instance_path : device_instance_paths) { + auto symlink = SymbolicLink(hid_device_interface_guid, instance_path); + if (!symlink.empty()) { + res.push_back(GetDeviceInfo(instance_path, symlink)); + } + } + + res.erase( + std::ranges::remove_if( + res, + [](const auto& dev) { return !dev.gaming_device; }) + .begin(), + res.end()); + + return res; +} + +HidHide::SmallHidInfo HidHide::GetDeviceInfo(const DeviceInstancePath& instance_path, const std::filesystem::path& symlink) +{ + SmallHidInfo res; + res.device_instance_path = instance_path; + + // Open a handle to communicate with the HID device + const CloseHandlePtr device_object( + CreateFileW( + symlink.c_str(), + GENERIC_READ, + (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr), + &CloseHandle); + if (INVALID_HANDLE_VALUE == device_object.get()) { + const auto err = GetLastError(); + switch (err) { + case ERROR_ACCESS_DENIED: + // The device is opened exclusively and in use hence we can't interact with it + __fallthrough; + case ERROR_SHARING_VIOLATION: + // The device is (most-likely) cloaked by Hid Hide itself while its client application isn't on the white-list + __fallthrough; + case ERROR_FILE_NOT_FOUND: + // The device is currently not present hence we can't query its details + return res; + default: + spdlog::error(L"Couldn't open device {}; code: {}", instance_path, err); + return res; + } + } + + PHIDP_PREPARSED_DATA pre_parsed_data; + if (!HidD_GetPreparsedData(device_object.get(), &pre_parsed_data)) { + spdlog::error(L"Couldn't get PreParsed data; Device: {}", instance_path); + return {}; + } + const HidD_FreePreparsedDataPtr free_preparsed_data_ptr(pre_parsed_data, &HidD_FreePreparsedData); + + HIDP_CAPS capabilities; + if (HIDP_STATUS_SUCCESS != HidP_GetCaps(pre_parsed_data, &capabilities)) { + spdlog::error(L"Could get Hid capabilities; Device: {}", instance_path); + return {}; + } + + HIDD_ATTRIBUTES attributes; + if (!HidD_GetAttributes(device_object.get(), &attributes)) { + spdlog::error(L"Could get Hid attributes; Device: {}", instance_path); + return {}; + } + std::wstring buffer; + buffer.resize(127 * sizeof WCHAR); + res.name = (HidD_GetProductString(device_object.get(), buffer.data(), static_cast(sizeof(WCHAR) * buffer.size())) + ? buffer + : L""); + res.base_container_device_instance_path = BaseContainerDeviceInstancePath(instance_path); + res.gaming_device = IsGamingDevice(attributes, capabilities); + + return res; +} + +bool HidHide::DevicePresent(const DeviceInstancePath& dev) +{ + DEVINST dev_inst{}; + if ( + const auto result = CM_Locate_DevNodeW(&dev_inst, const_cast(dev.c_str()), CM_LOCATE_DEVNODE_NORMAL); + (CR_NO_SUCH_DEVNODE == result) || (CR_SUCCESS == result)) { + return (CR_SUCCESS == result); + } + spdlog::error(L"Couldn't determine if device \"{}\" is present", dev); + return false; +} + +std::filesystem::path HidHide::SymbolicLink(GUID const& interface_guid, DeviceInstancePath const& instance_path) +{ + // Ask the device for the device interface + // Note that this call will succeed, whether or not the interface is present, but the iterator will have no entries, when the device interface isn't supported + const SetupDiDestroyDeviceInfoListPtr handle(SetupDiGetClassDevsW(&interface_guid, instance_path.c_str(), nullptr, DIGCF_DEVICEINTERFACE), &SetupDiDestroyDeviceInfoList); + if (INVALID_HANDLE_VALUE == handle.get()) { + spdlog::error(L"Device Handle invalid; device: {}", instance_path); + return {}; + } + + // Is the interface supported ? + SP_DEVICE_INTERFACE_DATA device_interface_data; + device_interface_data.cbSize = sizeof(device_interface_data); + if (!SetupDiEnumDeviceInterfaces(handle.get(), nullptr, &interface_guid, 0, &device_interface_data)) { + if (ERROR_NO_MORE_ITEMS != GetLastError()) { + spdlog::error(L"Couldn't get Device interfaces; device: {}", instance_path); + return {}; + } + return {}; + } + + // Determine the buffer length needed + DWORD needed{}; + if (!SetupDiGetDeviceInterfaceDetailW(handle.get(), &device_interface_data, nullptr, 0, &needed, nullptr) && ERROR_INSUFFICIENT_BUFFER != GetLastError()) { + spdlog::error(L"Couldn't get Device interface details; device: {}", instance_path); + return {}; + } + std::vector buffer(needed); + + // Acquire the detailed data containing the symbolic link (aka. device path) + auto& [cbSize, DevicePath]{*reinterpret_cast(buffer.data())}; + cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W); + if (!SetupDiGetDeviceInterfaceDetailW(handle.get(), &device_interface_data, reinterpret_cast(buffer.data()), static_cast(buffer.size()), nullptr, nullptr)) { + spdlog::error(L"Couldn't get Device interface details; device: {}", instance_path); + return {}; + } + return {std::wstring(DevicePath)}; +} + +HidHide::DeviceInstancePath HidHide::BaseContainerDeviceInstancePath(DeviceInstancePath const& device_instance_path) +{ + const GUID base_container_id(BaseContainerId(device_instance_path)); + if ((GUID_NULL == base_container_id) || (GUID_CONTAINER_ID_SYSTEM == base_container_id)) + return (std::wstring{}); + for (auto it{device_instance_path};;) { + if (const auto device_instance_path_parent = DeviceInstancePathParent(it); (base_container_id == BaseContainerId(device_instance_path_parent))) + it = device_instance_path_parent; + else + return (it); + } +} + +GUID HidHide::BaseContainerId(DeviceInstancePath const& device_instance_path) +{ + // Bail out when the device instance path is empty + if (device_instance_path.empty()) + return (GUID_NULL); + + DEVINST devInst{}; + DEVPROPTYPE devPropType{}; + GUID buffer{}; + ULONG needed{sizeof(buffer)}; + if (const auto result = CM_Locate_DevNodeW(&devInst, const_cast(device_instance_path.c_str()), CM_LOCATE_DEVNODE_PHANTOM); (CR_SUCCESS != result)) { + spdlog::error(L"Couldn't locate device DevNode; Device {}; Code: {}", device_instance_path, result); + return {}; + } + if (const auto result = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_Device_ContainerId, &devPropType, reinterpret_cast(&buffer), &needed, 0); (CR_SUCCESS != result)) { + // Bail out when the container id property isn't present + if (CR_NO_SUCH_VALUE == result) { + return (GUID_NULL); + } + spdlog::error(L"Couldn't locate device DevNode Property; Device {}; Code: {}", device_instance_path, result); + return {}; + } + if (DEVPROP_TYPE_GUID != devPropType) { + spdlog::error(L"Device Prop is not GUID; Device {}", device_instance_path); + return {}; + } + return (buffer); +} + +HidHide::DeviceInstancePath HidHide::DeviceInstancePathParent(DeviceInstancePath const& device_instance_path) +{ + DEVINST dev_inst{}; + DEVPROPTYPE dev_prop_type{}; + DEVINST dev_inst_parent{}; + std::wstring res; + res.resize(UNICODE_STRING_MAX_CHARS); + ULONG needed{static_cast(res.size())}; + if (const auto result = CM_Locate_DevNodeW(&dev_inst, const_cast(device_instance_path.c_str()), CM_LOCATE_DEVNODE_PHANTOM); (CR_SUCCESS != result)) { + spdlog::error(L"Couldn't locate device DevNode; Device {}; Code: {}", device_instance_path, result); + return {}; + } + if (const auto result = CM_Get_Parent(&dev_inst_parent, dev_inst, 0); (CR_SUCCESS != result)) { + spdlog::error(L"Couldn't get device Parent; Device {}; Code: {}", device_instance_path, result); + return {}; + } + if (const auto result = CM_Get_DevNode_PropertyW(dev_inst_parent, &DEVPKEY_Device_InstanceId, &dev_prop_type, reinterpret_cast(res.data()), &needed, 0); (CR_SUCCESS != result)) { + spdlog::error(L"Couldn't locate device DevNode Property; Device {}; Code: {}", device_instance_path, result); + return {}; + } + if (DEVPROP_TYPE_STRING != dev_prop_type) { + spdlog::error(L"Device Prop is not STRING; Device {}", device_instance_path); + return {}; + } + return res; +} + +bool HidHide::IsGamingDevice(const HIDD_ATTRIBUTES& attributes, const HIDP_CAPS& capabilities) +{ + return ( + // 0x28DE 0x1142 = Valve Corporation Steam Controller + // keep them for now + /* ((attributes.VendorID == 0x28DE) && (attributes.ProductID == 0x1142)) || */ + (0x05 == capabilities.UsagePage) || (0x01 == capabilities.UsagePage) && ((0x04 == capabilities.Usage) || (0x05 == capabilities.Usage))); +} diff --git a/GlosSITarget/HidHide.h b/GlosSITarget/HidHide.h new file mode 100644 index 0000000..bd46a65 --- /dev/null +++ b/GlosSITarget/HidHide.h @@ -0,0 +1,96 @@ +/* +Copyright 2021 Peter Repukat - FlatspotSoftware + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#pragma once +#define NOMINMAX +#include + +#include + +#include +#include +#include +#include +#include + +class HidHide { + private: + using DeviceInstancePath = std::wstring; + using SetupDiDestroyDeviceInfoListPtr = std::unique_ptr, decltype(&SetupDiDestroyDeviceInfoList)>; + using CloseHandlePtr = std::unique_ptr, decltype(&CloseHandle)>; + using HidD_FreePreparsedDataPtr = std::unique_ptr, decltype(&HidD_FreePreparsedData)>; + + // The Hid Hide I/O control custom device type (range 32768 .. 65535) + static constexpr DWORD IoControlDeviceType = 32769; + // The Hid Hide I/O control codes + enum class IOCTL_TYPE : DWORD { + GET_WHITELIST = CTL_CODE(IoControlDeviceType, 2048, METHOD_BUFFERED, FILE_READ_DATA), + SET_WHITELIST = CTL_CODE(IoControlDeviceType, 2049, METHOD_BUFFERED, FILE_READ_DATA), + GET_BLACKLIST = CTL_CODE(IoControlDeviceType, 2050, METHOD_BUFFERED, FILE_READ_DATA), + SET_BLACKLIST = CTL_CODE(IoControlDeviceType, 2051, METHOD_BUFFERED, FILE_READ_DATA), + GET_ACTIVE = CTL_CODE(IoControlDeviceType, 2052, METHOD_BUFFERED, FILE_READ_DATA), + SET_ACTIVE = CTL_CODE(IoControlDeviceType, 2053, METHOD_BUFFERED, FILE_READ_DATA) + }; + + struct SmallHidInfo { + std::wstring name; + DeviceInstancePath device_instance_path; + DeviceInstancePath base_container_device_instance_path; + std::filesystem::path symlink; + bool gaming_device = false; + }; + + public: + HidHide(); + + void openCtrlDevice(); + void closeCtrlDevice(); + + void hideDevices(const std::filesystem::path& steam_path); + void disableHidHide(); + // TODO: MAYBE: restore hidhide state/lists when app closes. not only disable device_hiding + + private: + HANDLE hidhide_handle = nullptr; + + static inline constexpr std::array whitelist_executeables_{ + L"GameOverlayUI.exe", + L"steam.exe", + L"streaming_client.exe"}; + + static [[nodiscard]] std::wstring DosDeviceForVolume(const std::wstring& volume); + + [[nodiscard]] std::vector getAppWhiteList() const; + [[nodiscard]] std::vector getBlackListDevices() const; + [[nodiscard]] bool getActive() const; + void setAppWhiteList(const std::vector& whitelist) const; + void setBlacklistDevices(const std::vector& blacklist) const; + void setActive(bool active) const; + + [[nodiscard]] DWORD getRequiredOutputBufferSize(IOCTL_TYPE type) const; + + static [[nodiscard]] std::vector BufferToStringVec(const auto& buffer); + static [[nodiscard]] std::vector StringListToMultiString(const std::vector& stringlist); + + static [[nodiscard]] std::vector GetHidDeviceList(); + static [[nodiscard]] SmallHidInfo GetDeviceInfo(const DeviceInstancePath& instance_path, const std::filesystem::path& symlink); + static [[nodiscard]] bool DevicePresent(const DeviceInstancePath& dev); + static [[nodiscard]] std::filesystem::path SymbolicLink(GUID const& interface_guid, DeviceInstancePath const& instance_path); + static [[nodiscard]] DeviceInstancePath BaseContainerDeviceInstancePath(DeviceInstancePath const& device_instance_path); + static [[nodiscard]] GUID BaseContainerId(DeviceInstancePath const& device_instance_path); + static [[nodiscard]] DeviceInstancePath DeviceInstancePathParent(DeviceInstancePath const& device_instance_path); + + static [[nodiscard]] bool IsGamingDevice(const HIDD_ATTRIBUTES& attributes, const HIDP_CAPS& capabilities); +}; diff --git a/GlosSITarget/InputRedirector.cpp b/GlosSITarget/InputRedirector.cpp index 0a09626..0dc339a 100644 --- a/GlosSITarget/InputRedirector.cpp +++ b/GlosSITarget/InputRedirector.cpp @@ -41,7 +41,6 @@ InputRedirector::~InputRedirector() controller_thread_.join(); vigem_disconnect(driver_); vigem_free(driver_); - spdlog::debug("ViGEm Disconnected"); #endif } diff --git a/GlosSITarget/SteamTarget.cpp b/GlosSITarget/SteamTarget.cpp index 267a64c..cffe1e4 100644 --- a/GlosSITarget/SteamTarget.cpp +++ b/GlosSITarget/SteamTarget.cpp @@ -49,14 +49,20 @@ SteamTarget::SteamTarget(int argc, char* argv[]) int SteamTarget::run() { run_ = true; - keepControllerConfig(true); +#ifdef _WIN32 + hidhide_.hideDevices(steam_path_); input_redirector_.run(); +#endif + keepControllerConfig(true); while (run_) { detector_.update(); window_.update(); overlayHotkeyWorkaround(); } +#ifdef _WIN32 input_redirector_.stop(); + hidhide_.disableHidHide(); +#endif return 1; } @@ -109,7 +115,7 @@ void SteamTarget::focusWindow(WindowHandle hndl) #endif } -std::wstring SteamTarget::getSteamPath() +std::filesystem::path SteamTarget::getSteamPath() const { #ifdef _WIN32 // TODO: check if keys/value exist @@ -123,7 +129,7 @@ std::wstring SteamTarget::getSteamPath() #endif } -std::wstring SteamTarget::getSteamUserId() + std::wstring SteamTarget::getSteamUserId() const { #ifdef _WIN32 // TODO: check if keys/value exist @@ -139,7 +145,7 @@ std::wstring SteamTarget::getSteamUserId() std::vector SteamTarget::getOverlayHotkey() { - const auto config_path = steam_path_ + std::wstring(user_data_path_) + steam_user_id_ + std::wstring(config_file_name_); + const auto config_path = std::wstring(steam_path_) + std::wstring(user_data_path_) + steam_user_id_ + std::wstring(config_file_name_); std::ifstream config_file(config_path); // TODO: check if file exists auto root = tyti::vdf::read(config_file); @@ -173,7 +179,7 @@ std::vector SteamTarget::getOverlayHotkey() std::vector SteamTarget::getScreenshotHotkey() { - const auto config_path = steam_path_ + std::wstring(user_data_path_) + steam_user_id_ + std::wstring(config_file_name_); + const auto config_path = std::wstring(steam_path_) + std::wstring(user_data_path_) + steam_user_id_ + std::wstring(config_file_name_); std::ifstream config_file(config_path); // TODO: check if file exists auto root = tyti::vdf::read(config_file); diff --git a/GlosSITarget/SteamTarget.h b/GlosSITarget/SteamTarget.h index 0270450..78db9a8 100644 --- a/GlosSITarget/SteamTarget.h +++ b/GlosSITarget/SteamTarget.h @@ -20,9 +20,12 @@ limitations under the License. #include "TargetWindow.h" #ifdef _WIN32 +#include "HidHide.h" #include "InputRedirector.h" #endif +#include + class SteamTarget { public: explicit SteamTarget(int argc, char* argv[]); @@ -31,10 +34,10 @@ class SteamTarget { private: void onOverlayChanged(bool overlay_open); void focusWindow(WindowHandle hndl); - std::wstring getSteamPath(); - std::wstring getSteamUserId(); + std::filesystem::path getSteamPath() const; + std::wstring getSteamUserId() const; - std::wstring steam_path_ = getSteamPath(); + std::filesystem::path steam_path_ = getSteamPath(); std::wstring steam_user_id_ = getSteamUserId(); std::vector getOverlayHotkey(); @@ -54,7 +57,10 @@ class SteamTarget { bool run_ = false; std::vector overlay_hotkey_ = getOverlayHotkey(); +#ifdef _WIN32 + HidHide hidhide_; InputRedirector input_redirector_; +#endif TargetWindow window_; OverlayDetector detector_; WindowHandle last_foreground_window_ = nullptr;