summaryrefslogtreecommitdiffstats
path: root/src/input_common/udp/client.h
blob: a523f612455f0daba42ce120a5ea029365ab83f2 (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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#pragma once

#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <thread>
#include <tuple>
#include "common/common_types.h"
#include "common/param_package.h"
#include "common/thread.h"
#include "common/threadsafe_queue.h"
#include "common/vector_math.h"
#include "core/frontend/input.h"
#include "input_common/motion_input.h"

namespace InputCommon::CemuhookUDP {

constexpr char DEFAULT_SRV[] = "127.0.0.1:26760";

class Socket;

namespace Response {
struct PadData;
struct PortInfo;
struct TouchPad;
struct Version;
} // namespace Response

enum class PadMotion {
    GyroX,
    GyroY,
    GyroZ,
    AccX,
    AccY,
    AccZ,
    Undefined,
};

enum class PadTouch {
    Click,
    Undefined,
};

struct UDPPadStatus {
    std::string host{"127.0.0.1"};
    u16 port{26760};
    std::size_t pad_index{};
    PadMotion motion{PadMotion::Undefined};
    f32 motion_value{0.0f};
};

struct DeviceStatus {
    std::mutex update_mutex;
    Input::MotionStatus motion_status;
    std::tuple<float, float, bool> touch_status;

    // calibration data for scaling the device's touch area to 3ds
    struct CalibrationData {
        u16 min_x{};
        u16 min_y{};
        u16 max_x{};
        u16 max_y{};
    };
    std::optional<CalibrationData> touch_calibration;
};

class Client {
public:
    // Initialize the UDP client capture and read sequence
    Client();

    // Close and release the client
    ~Client();

    // Used for polling
    void BeginConfiguration();
    void EndConfiguration();

    std::vector<Common::ParamPackage> GetInputDevices() const;

    bool DeviceConnected(std::size_t client) const;
    void ReloadSockets();

    Common::SPSCQueue<UDPPadStatus>& GetPadQueue();
    const Common::SPSCQueue<UDPPadStatus>& GetPadQueue() const;

    DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad);
    const DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad) const;

    Input::TouchStatus& GetTouchState();
    const Input::TouchStatus& GetTouchState() const;

private:
    struct ClientData {
        ClientData();
        ~ClientData();

        std::string host{"127.0.0.1"};
        u16 port{26760};
        std::size_t pad_index{};
        std::unique_ptr<Socket> socket;
        DeviceStatus status;
        std::thread thread;
        u64 packet_sequence{};
        s8 active{-1};

        // Realtime values
        // motion is initalized with PID values for drift correction on joycons
        InputCommon::MotionInput motion{0.3f, 0.005f, 0.0f};
        std::chrono::time_point<std::chrono::steady_clock> last_motion_update;
    };

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

    // Translates configuration to client number
    std::size_t GetClientNumber(std::string_view host, u16 port, std::size_t pad) const;

    void OnVersion(Response::Version);
    void OnPortInfo(Response::PortInfo);
    void OnPadData(Response::PadData, std::size_t client);
    void StartCommunication(std::size_t client, const std::string& host, u16 port,
                            std::size_t pad_index, u32 client_id);
    void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
                            const Common::Vec3<float>& gyro);

    // Returns an unused finger id, if there is no fingers available std::nullopt will be
    // returned
    std::optional<std::size_t> GetUnusedFingerID() const;

    // Merges and updates all touch inputs into the touch_status array
    void UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client, std::size_t id);

    bool configuring = false;

    // Allocate clients for 8 udp servers
    static constexpr std::size_t MAX_UDP_CLIENTS = 4 * 8;
    // Each client can have up 2 touch inputs
    static constexpr std::size_t MAX_TOUCH_FINGERS = MAX_UDP_CLIENTS * 2;
    std::array<ClientData, MAX_UDP_CLIENTS> clients{};
    Common::SPSCQueue<UDPPadStatus> pad_queue{};
    Input::TouchStatus touch_status{};
    std::array<std::size_t, MAX_TOUCH_FINGERS> finger_id{};
};

/// An async job allowing configuration of the touchpad calibration.
class CalibrationConfigurationJob {
public:
    enum class Status {
        Initialized,
        Ready,
        Stage1Completed,
        Completed,
    };
    /**
     * Constructs and starts the job with the specified parameter.
     *
     * @param status_callback Callback for job status updates
     * @param data_callback Called when calibration data is ready
     */
    explicit CalibrationConfigurationJob(const std::string& host, u16 port, std::size_t pad_index,
                                         u32 client_id, std::function<void(Status)> status_callback,
                                         std::function<void(u16, u16, u16, u16)> data_callback);
    ~CalibrationConfigurationJob();
    void Stop();

private:
    Common::Event complete_event;
};

void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, u32 client_id,
                       const std::function<void()>& success_callback,
                       const std::function<void()>& failure_callback);

} // namespace InputCommon::CemuhookUDP