summaryrefslogtreecommitdiffstats
path: root/src/input_common/drivers/gc_adapter.h
blob: c0bf1ed7ae8178dd968354f9658df4c1f629d513 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <mutex>
#include <stop_token>
#include <thread>

#include "input_common/input_engine.h"

struct libusb_context;
struct libusb_device;
struct libusb_device_handle;

namespace InputCommon {

class LibUSBContext;
class LibUSBDeviceHandle;

class GCAdapter : public InputCommon::InputEngine {
public:
    explicit GCAdapter(const std::string input_engine_);
    ~GCAdapter();

    bool SetRumble(const PadIdentifier& identifier,
                   const Input::VibrationStatus vibration) override;

    /// Used for automapping features
    std::vector<Common::ParamPackage> GetInputDevices() const override;
    ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
    AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
    std::string GetUIName(const Common::ParamPackage& params) const override;

private:
    enum class PadButton {
        Undefined = 0x0000,
        ButtonLeft = 0x0001,
        ButtonRight = 0x0002,
        ButtonDown = 0x0004,
        ButtonUp = 0x0008,
        TriggerZ = 0x0010,
        TriggerR = 0x0020,
        TriggerL = 0x0040,
        ButtonA = 0x0100,
        ButtonB = 0x0200,
        ButtonX = 0x0400,
        ButtonY = 0x0800,
        ButtonStart = 0x1000,
    };

    enum class PadAxes : u8 {
        StickX,
        StickY,
        SubstickX,
        SubstickY,
        TriggerLeft,
        TriggerRight,
        Undefined,
    };

    enum class ControllerTypes {
        None,
        Wired,
        Wireless,
    };

    struct GCController {
        ControllerTypes type = ControllerTypes::None;
        PadIdentifier identifier{};
        bool enable_vibration = false;
        u8 rumble_amplitude{};
        std::array<u8, 6> axis_origin{};
        u8 reset_origin_counter{};
    };

    using AdapterPayload = std::array<u8, 37>;

    void UpdatePadType(std::size_t port, ControllerTypes pad_type);
    void UpdateControllers(const AdapterPayload& adapter_payload);
    void UpdateStateButtons(std::size_t port, u8 b1, u8 b2);
    void UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload);

    void AdapterInputThread(std::stop_token stop_token);

    void AdapterScanThread(std::stop_token stop_token);

    bool IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size);

    /// For use in initialization, querying devices to find the adapter
    bool Setup();

    /// Returns true if we successfully gain access to GC Adapter
    bool CheckDeviceAccess();

    /// Captures GC Adapter endpoint address
    /// Returns true if the endpoint was set correctly
    bool GetGCEndpoint(libusb_device* device);

    /// Returns true if there is a device connected to port
    bool DeviceConnected(std::size_t port) const;

    /// For shutting down, clear all data, join all threads, release usb
    void Reset();

    void UpdateVibrations();
    // Updates vibration state of all controllers
    void SendVibrations();
    std::unique_ptr<LibUSBDeviceHandle> usb_adapter_handle;
    std::array<GCController, 4> pads;

    std::jthread adapter_input_thread;
    std::jthread adapter_scan_thread;
    bool restart_scan_thread{};

    std::unique_ptr<LibUSBContext> libusb_ctx;

    u8 input_endpoint{0};
    u8 output_endpoint{0};
    u8 input_error_counter{0};
    u8 output_error_counter{0};
    int vibration_counter{0};

    bool rumble_enabled{true};
    bool vibration_changed{true};
};
} // namespace InputCommon