mirror of
https://github.com/ddeverill/SteamlessController.git
synced 2026-10-23 07:02:16 -05:00
vigem_connect returns VIGEM_ERROR_BUS_NOT_FOUND both when no bus driver is installed and when one is present and did not answer, and the client never records which product's bus it tried. The app inferred "driver missing" from that error, so a driverMissing=1 that happened to be right was indistinguishable from one that was wrong — which is most of why #65 took as long as it did to pin down. ViGEmBusInfo enumerates the interface GUID directly. The failure line is now followed by what the machine actually has, and driverMissing is set from an empty enumeration rather than inferred from an error code with several causes. The GUID comes from the vendored client's own header rather than a copy, so updating third_party cannot leave a stale value behind here. That matters because the header instructs forks to change it, and several products ship one: measured on the #68 reporter's machine, which carries both the Oculus and Virtual Desktop forks, neither publishes this GUID and only the Nefarius bus appears. An empty result therefore does mean no usable bus, which is what makes it safe to drive the "go install the driver" advice from. It also retires the theory that the client had been connecting to a fork. A disabled bus reads the same as an absent one — the devnode stays present and reports a problem, but its interface is withdrawn entirely — so that case belongs in the empty-list wording rather than in a state of its own. ViGEmBusProbe prints the same inventory, since "vigem_connect failed" is not a question a reporter's log can answer on its own. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
80 lines
3.0 KiB
C++
80 lines
3.0 KiB
C++
#pragma once
|
|
#include <cstdint>
|
|
#include <cstddef>
|
|
#include <atomic>
|
|
#include <functional>
|
|
#include <string>
|
|
#include <thread>
|
|
#include "BackButtonConfig.h"
|
|
#include "ControllerPlatform.h"
|
|
#include "TrackpadConfig.h"
|
|
|
|
class VirtualController {
|
|
public:
|
|
using RumbleCallback = std::function<void(uint8_t largeMotor, uint8_t smallMotor)>;
|
|
|
|
explicit VirtualController(ControllerPlatform platform, RumbleCallback rumbleCallback = {});
|
|
~VirtualController();
|
|
VirtualController(const VirtualController&) = delete;
|
|
VirtualController& operator=(const VirtualController&) = delete;
|
|
|
|
bool IsValid() const { return m_valid; }
|
|
// Established by asking the system what buses exist, not inferred from an
|
|
// error code — VIGEM_ERROR_BUS_NOT_FOUND is returned for causes other than
|
|
// an absent driver, and reporting those as "driver missing" is what sent
|
|
// the reporter in #65 looking for a driver they already had.
|
|
bool IsDriverMissing() const { return m_driverMissing; }
|
|
|
|
// Why construction failed. Only meaningful when !IsValid(); FailStage() names
|
|
// the ViGEm call that failed and LastError() carries its VIGEM_ERROR code
|
|
// (0 when the failing call returns no code, e.g. the allocators).
|
|
const char* FailStage() const { return m_failStage; }
|
|
uint32_t LastError() const { return m_lastError; }
|
|
|
|
// What buses were on the machine when construction failed, for the log and
|
|
// for choosing what to tell the user. Empty unless a connect failed.
|
|
const std::wstring& BusReport() const { return m_busReport; }
|
|
|
|
void Update(const uint8_t* buf, size_t n, const ControllerProfile& profile);
|
|
void OnRumble(uint8_t largeMotor, uint8_t smallMotor);
|
|
|
|
// DS4-only: update battery level shown in the DS4 report.
|
|
void SetBatteryState(uint8_t levelPercent, uint8_t chargeState);
|
|
|
|
private:
|
|
void StartDs4OutputThread();
|
|
void StopDs4OutputThread();
|
|
void Ds4OutputLoop();
|
|
|
|
ControllerPlatform m_platform;
|
|
void* m_client = nullptr;
|
|
void* m_target = nullptr;
|
|
RumbleCallback m_rumbleCallback;
|
|
bool m_valid = false;
|
|
bool m_driverMissing = false;
|
|
const char* m_failStage = "none";
|
|
uint32_t m_lastError = 0;
|
|
std::wstring m_busReport;
|
|
|
|
// DS4 touchpad tracking. Which pads feed it is read from the profile
|
|
// handed to Update every frame, so none of that is duplicated here.
|
|
uint8_t m_touchPacketCounter = 0;
|
|
uint8_t m_rightTracking = 0;
|
|
uint8_t m_leftTracking = 0;
|
|
bool m_wasRightTouching = false;
|
|
bool m_wasLeftTouching = false;
|
|
|
|
// DS4 IMU timestamp tracking
|
|
uint16_t m_ds4Timestamp = 0;
|
|
uint32_t m_lastImuTimestamp = 0;
|
|
bool m_hasLastImuTimestamp = false;
|
|
|
|
// DS4 battery
|
|
uint8_t m_ds4BatteryLevel = 0x0B; // full / unknown
|
|
uint8_t m_ds4BatterySpecial = 0x1B;
|
|
|
|
// DS4 output thread (polls for rumble/lightbar output reports)
|
|
std::atomic<bool> m_ds4OutputRunning{false};
|
|
std::thread m_ds4OutputThread;
|
|
};
|