diff --git a/Source/WindowsDualsense_ds5w/Private/Core/DeviceRegistry.cpp b/Source/WindowsDualsense_ds5w/Private/Core/DeviceRegistry.cpp index 251efbf..7d3cefe 100644 --- a/Source/WindowsDualsense_ds5w/Private/Core/DeviceRegistry.cpp +++ b/Source/WindowsDualsense_ds5w/Private/Core/DeviceRegistry.cpp @@ -5,7 +5,6 @@ #include "Core/DeviceRegistry.h" #include "Async/Async.h" #include "Async/TaskGraphInterfaces.h" -#include "Core/HIDPollingRunnable.h" #include "Core/HIDDeviceInfo.h" #include "Windows/WindowsApplication.h" #include "Core/DualSense/DualSenseLibrary.h" @@ -21,7 +20,6 @@ TSharedPtr FDeviceRegistry::Instance; TMap FDeviceRegistry::KnownDevicePaths; TMap FDeviceRegistry::HistoryDevices; TMap FDeviceRegistry::LibraryInstances; -TMap> FDeviceRegistry::ActiveConnectionWatchers; bool PrimaryTick = true; float AccumulateSecurity = 0; @@ -80,7 +78,7 @@ void FDeviceRegistry::DetectedChangeConnections(float DeltaTime) { IPlatformInputDeviceMapper::Get().Internal_SetInputDeviceConnectionState(DeviceId, EInputDeviceConnectionState::Disconnected); - Manager->RemoveLibraryInstance(DeviceId.GetId()); + Manager->RemoveLibraryInstance(DeviceId); DisconnectedPaths.Add(Path); } } @@ -127,10 +125,10 @@ TSharedPtr FDeviceRegistry::Get() FDeviceRegistry::~FDeviceRegistry() { - TArray WatcherKeys; - ActiveConnectionWatchers.GetKeys(WatcherKeys); + TArray WatcherKeys; + LibraryInstances.GetKeys(WatcherKeys); - for (const int32 ControllerId : WatcherKeys) + for (const FInputDeviceId& ControllerId : WatcherKeys) { RemoveLibraryInstance(ControllerId); } @@ -150,9 +148,8 @@ ISonyGamepadInterface* FDeviceRegistry::GetLibraryInstance(const FInputDeviceId& return LibraryInstances[DeviceId]; } -void FDeviceRegistry::RemoveLibraryInstance(int32 ControllerId) +void FDeviceRegistry::RemoveLibraryInstance(const FInputDeviceId& GamepadId) { - FInputDeviceId GamepadId = FInputDeviceId::CreateFromInternalId(ControllerId); if ( IPlatformInputDeviceMapper::Get().GetInputDeviceConnectionState(GamepadId) != EInputDeviceConnectionState::Disconnected) @@ -168,8 +165,6 @@ void FDeviceRegistry::RemoveLibraryInstance(int32 ControllerId) LibraryInstances[GamepadId]->ShutdownLibrary(); LibraryInstances.Remove(GamepadId); - - const int32 NumRemoved = ActiveConnectionWatchers.Remove(ControllerId); } void FDeviceRegistry::CreateLibraryInstance(FDeviceContext& Context) @@ -243,34 +238,8 @@ void FDeviceRegistry::CreateLibraryInstance(FDeviceContext& Context) UserId, EInputDeviceConnectionState::Connected); } - - - if (ActiveConnectionWatchers.Contains(Context.UniqueInputDeviceId.GetId())) - { - ActiveConnectionWatchers.Remove(Context.UniqueInputDeviceId.GetId()); - } - - auto WatcherRunnable = MakeUnique( - MoveTemp(Context.Handle), - std::chrono::milliseconds(150) - ); - if (WatcherRunnable) - { - WatcherRunnable->StartThread(); - ActiveConnectionWatchers.Add(Context.UniqueInputDeviceId.GetId(), MoveTemp(WatcherRunnable)); - } } -void FDeviceRegistry::RemoveAllLibraryInstance() -{ - for (const auto& LibraryInstance : LibraryInstances) - { - RemoveLibraryInstance(LibraryInstance.Key.GetId()); - } - LibraryInstances.Reset(); -} - - int32 FDeviceRegistry::GetAllocatedDevices() { return LibraryInstances.Num(); diff --git a/Source/WindowsDualsense_ds5w/Private/Core/HIDPollingRunnable.cpp b/Source/WindowsDualsense_ds5w/Private/Core/HIDPollingRunnable.cpp deleted file mode 100644 index caa6ef9..0000000 --- a/Source/WindowsDualsense_ds5w/Private/Core/HIDPollingRunnable.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) 2025 Rafael Valoto/Publisher. All rights reserved. -// Created for: WindowsDualsense_ds5w - Plugin to support DualSense controller on Windows. -// Planned Release Year: 2025 - -#include "Core/HIDPollingRunnable.h" -#include "Async/TaskGraphInterfaces.h" -#include "HAL/RunnableThread.h" -#include "HAL/PlatformProcess.h" -#include "Core/HIDDeviceInfo.h" -#include - -FHIDPollingRunnable::FHIDPollingRunnable(HANDLE InDeviceHandle, const std::chrono::milliseconds InInterval): - DeviceHandle(InDeviceHandle), - Thread(nullptr), - Interval(InInterval) -{ -} - -FHIDPollingRunnable::~FHIDPollingRunnable() -{ - Stop(); - - if (Thread) - { - Thread->WaitForCompletion(); - - delete Thread; - Thread = nullptr; - } -} - -bool FHIDPollingRunnable::Init() -{ - return DeviceHandle != INVALID_HANDLE_VALUE; -} - -uint32 FHIDPollingRunnable::Run() -{ - using FClock = std::chrono::steady_clock; - auto NextPingTime = FClock::now() + Interval; - - while (!bStopRequested.load(std::memory_order_relaxed)) - { - DWORD LastError = ERROR_SUCCESS; - if (!FHIDDeviceInfo::PingOnce(DeviceHandle, &LastError) && FHIDDeviceInfo::ShouldTreatAsDisconnected(LastError)) - { - UE_LOG(LogTemp, Log, TEXT("Ping failed: device is no longer connected. Shutting down the ping")); - return 1; - } - - std::this_thread::sleep_until(NextPingTime); - - do - { - NextPingTime += Interval; - } while (NextPingTime <= FClock::now()); - } - - return 0; -} - -void FHIDPollingRunnable::Stop() -{ - bStopRequested.store(true, std::memory_order_relaxed); -} - -void FHIDPollingRunnable::StartThread() -{ - const FString ThreadName = FString::Printf(TEXT("FHIDPollingRunnable_%p"), this); - Thread = FRunnableThread::Create(this, *ThreadName); -} - -void FHIDPollingRunnable::Exit() -{ - UE_LOG(LogTemp, Log, TEXT("Thread is exiting, cleaning up buffer.")); -} diff --git a/Source/WindowsDualsense_ds5w/Public/Core/DeviceRegistry.h b/Source/WindowsDualsense_ds5w/Public/Core/DeviceRegistry.h index 11a34e1..e8c7ddf 100644 --- a/Source/WindowsDualsense_ds5w/Public/Core/DeviceRegistry.h +++ b/Source/WindowsDualsense_ds5w/Public/Core/DeviceRegistry.h @@ -7,7 +7,6 @@ #include "CoreMinimal.h" #include "Windows/WindowsApplication.h" #include "Async/TaskGraphInterfaces.h" -#include "Core/HIDPollingRunnable.h" #include "HAL/PlatformProcess.h" #include "HAL/RunnableThread.h" #include "Interfaces/SonyGamepadInterface.h" @@ -31,8 +30,7 @@ class WINDOWSDUALSENSE_DS5W_API FDeviceRegistry : public TSharedFromThis Get(); /** * Destructor for the FDeviceRegistry class. Responsible for cleaning up resources associated with the device - * library instances. Ensures that all active connection watchers are properly removed and their corresponding - * controller instances are cleaned up to prevent resource leakage. + * library instances. Ensures that all controller instances are cleaned up to prevent resource leakage. */ virtual ~FDeviceRegistry(); /** @@ -73,9 +71,9 @@ class WINDOWSDUALSENSE_DS5W_API FDeviceRegistry : public TSharedFromThis HistoryDevices; - /** - * A static map that maintains active connections by associating unique integer identifiers with their - * corresponding HID polling runnable instances. This map is used to manage and monitor ongoing input - * device connection activities and ensures proper lifecycle control of the associated polling threads. - */ - static TMap> ActiveConnectionWatchers; }; diff --git a/Source/WindowsDualsense_ds5w/Public/Core/HIDPollingRunnable.h b/Source/WindowsDualsense_ds5w/Public/Core/HIDPollingRunnable.h deleted file mode 100644 index db04e33..0000000 --- a/Source/WindowsDualsense_ds5w/Public/Core/HIDPollingRunnable.h +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (c) 2025 Rafael Valoto/Publisher. All rights reserved. -// Created for: WindowsDualsense_ds5w - Plugin to support DualSense controller on Windows. -// Planned Release Year: 2025 - -#pragma once - -#include "CoreMinimal.h" -#include "HAL/Runnable.h" -#include -#include -#include - - -/** - * A dedicated runnable class for managing periodic HID device polling. - * - * The `FHIDPollingRunnable` class provides an abstraction for executing - * periodic tasks, such as pinging a Human Interface Device (HID) to ensure - * its availability or to perform other periodic maintenance operations. It - * is designed to run in its own thread, executing its logic at a configurable - * time interval. - * - * This class inherits from `FRunnable`, enabling it to integrate seamlessly - * with Unreal's threading constructs. It handles initialization of resources, - * execution of polling logic, and cleanup upon termination. It also provides - * functionality for gracefully stopping the thread. - * - * The primary responsibilities of this class include setting up the device handle, - * maintaining regular intervals for polling operations, and managing the thread's - * lifecycle to ensure stability and resource management. - */ -class FHIDPollingRunnable final : public FRunnable -{ - /** - * Constructs an instance of the `FHIDPollingRunnable` class, initializing it with the given device handle and polling interval. - * - * This constructor sets up the required resources for the polling operation, including - * the target device handle and the interval at which polling tasks will be executed. - * - * @param InDeviceHandle The handle to the HID device that this polling runnable will manage. - * @param InInterval The time interval, in milliseconds, defining how frequently the polling operation should occur. - * @return A newly constructed `FHIDPollingRunnable` instance prepared for polling operations. - */ -public: - FHIDPollingRunnable(HANDLE InDeviceHandle, const std::chrono::milliseconds InInterval); - /** - * Destructor for the `FHIDPollingRunnable` class. - * - * Responsible for performing cleanup operations when an instance of - * `FHIDPollingRunnable` is destroyed. This involves releasing resources, - * terminating any ongoing operations, and ensuring that the class instance - * is properly disposed of to prevent memory leaks or dangling pointers. - * - * The destructor ensures that the HID polling thread is stopped and all - * associated resources are safely released before the object is destroyed. - */ - virtual ~FHIDPollingRunnable() override; - - /** - * Initializes the runnable before execution begins. - * - * This method is called to perform any necessary setup required for the - * `FHIDPollingRunnable` instance. By default, it delegates to the base - * class `FRunnable::Init` for initialization. Override this if additional - * setup logic specific to `FHIDPollingRunnable` is required. - * - * @return True if the initialization succeeds, otherwise false. - */ - virtual bool Init() override; - /** - * Executes the main logic of the runnable in a dedicated thread. - * - * This method contains the core operations to be performed in the thread, - * managing periodic pinging of a connected HID device. It ensures efficient - * timing using `std::this_thread::sleep_until`, handles device communication - * via `FHIDDeviceInfo::PingOnce`, and gracefully terminates the loop when the - * device is disconnected or a stop request is issued. - * - * The method computes the next scheduled operation time and ensures stable - * intervals are maintained, even under system slowdowns. If the device is - * considered disconnected based on the result of pinging or other criteria, - * the method logs the event and terminates the thread. - * - * @return An exit code of `0` indicating the thread completed its execution. - */ - virtual uint32 Run() override; - /** - * Signals the runnable to stop execution. - * - * This method is called to request termination of the `FHIDPollingRunnable` instance. - * It ensures that the thread managing the periodic polling operations is safely notified - * to stop. The method delegates to the base class `FRunnable::Stop` for the core stop - * handling logic. Override this if specific stop behavior for `FHIDPollingRunnable` - * is needed. - * - * Once invoked, the runnable will complete any in-progress operation and terminate - * gracefully at the next appropriate checkpoint. - */ - virtual void Stop() override; - /** - * Cleans up resources and performs shutdown tasks after execution ends. - * - * This method is invoked to handle any necessary cleanup for the - * `FHIDPollingRunnable` instance once the execution has finished. By default, - * it delegates cleanup operations to the base class `FRunnable::Exit`. - * Override this method to implement additional teardown logic specific to - * `FHIDPollingRunnable`, if needed. - */ - virtual void Exit() override; - - /** - * - */ - void StartThread(); - - /** - * A handle to a device used for communication or management in HID operations. - * - * This member represents a system-specific HANDLE, typically used to interface - * with a connected device, such as opening, reading from, or writing to it. - * In the context of the `FHIDPollingRunnable` class, it is utilized to perform - * actions like sending periodic pings or ensuring the device connection remains valid. - * - * Proper initialization and cleanup of this handle are critical to avoid resource - * leaks or undefined behavior when interacting with hardware devices. - */ -private: - HANDLE DeviceHandle; - - /** - * Pointer to the thread managing the execution of the runnable instance. - * - * This member represents the dedicated thread created to run the `FHIDPollingRunnable` instance. - * It is responsible for initializing, executing, and managing the lifecycle of the runnable's - * `Run` method. The thread ensures that the polling logic operates independently and does not - * block the main execution flow. - * - * Proper management of this thread, including starting, stopping, and cleanup, is crucial to - * prevent resource leaks and ensure smooth operation of the system. - */ - FRunnableThread* Thread; - - /** - * Specifies the time interval for periodic operations or polling. - * - * This member indicates the duration between consecutive actions, such as pings - * or other scheduled tasks, executed within the context of the `FHIDPollingRunnable` class. - * It is represented in milliseconds and guarantees a consistent timing mechanism - * for maintaining regular intervals when performing operations like communication - * with a device. - * - * Proper configuration ensures optimal timing and prevents excessive or insufficient - * resource utilization during operations. - */ - std::chrono::milliseconds Interval; - - /** - * A thread-safe flag indicating whether a stop request has been issued. - * - * This atomic boolean is primarily used to signal a running thread to terminate - * its execution in a safe and controlled manner. For example, in the context of the - * `FHIDPollingRunnable` class, it indicates if the polling thread should stop its - * periodic execution and terminate. - * - * By using `std::atomic_bool`, it ensures that the flag is updated and read across - * threads without introducing race conditions. - */ - std::atomic_bool bStopRequested; - -};