summaryrefslogtreecommitdiffstats
path: root/src/controllers/hid/hidcontroller.h
blob: d735fc8db6ac04b793161ca6e150e28c4247f547 (plain)
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
#pragma once

#include <QThread>

#include "controllers/controller.h"
#include "controllers/hid/hiddevice.h"
#include "controllers/hid/hidiothread.h"
#include "controllers/hid/legacyhidcontrollermapping.h"
#include "util/duration.h"

/// HID controller backend
class HidController final : public Controller {
    Q_OBJECT
  public:
    explicit HidController(
            mixxx::hid::DeviceInfo&& deviceInfo);
    ~HidController() override;

    ControllerJSProxy* jsProxy() override;

    QString mappingExtension() override;

    virtual std::shared_ptr<LegacyControllerMapping> cloneMapping() override;
    void setMapping(std::shared_ptr<LegacyControllerMapping> pMapping) override;

    bool isMappable() const override {
        if (!m_pMapping) {
            return false;
        }
        return m_pMapping->isMappable();
    }

    bool matchMapping(const MappingInfo& mapping) override;

  private slots:
    int open() override;
    int close() override;

  private:
    // For devices which only support a single report, reportID must be set to
    // 0x0.
    void sendBytes(const QByteArray& data) override;

    const mixxx::hid::DeviceInfo m_deviceInfo;

    std::unique_ptr<HidIoThread> m_pHidIoThread;
    std::shared_ptr<LegacyHidControllerMapping> m_pMapping;

    friend class HidControllerJSProxy;
};

class HidControllerJSProxy : public ControllerJSProxy {
    Q_OBJECT
  public:
    HidControllerJSProxy(HidController* m_pController)
            : ControllerJSProxy(m_pController),
              m_pHidController(m_pController) {
    }

    /// @brief Sends HID OutputReport with hard coded ReportID 0 to HID device
    ///        This function only works with HID devices, which don't use ReportIDs
    /// @param dataList Data to send as list of bytes
    /// @param length Unused optional argument
    Q_INVOKABLE void send(const QList<int>& dataList, unsigned int length = 0) override {
        // This function is only for class compatibility with the (midi)controller
        Q_UNUSED(length);
        send(dataList, 0, 0);
    }

    /// @brief Sends HID OutputReport to HID device
    /// @param dataList Data to send as list of bytes
    /// @param length Unused but mandatory argument
    /// @param reportID 1...255 for HID devices that uses ReportIDs - or 0 for devices, which don't use ReportIDs
    /// @param resendUnchangedReport If set, the report will also be send, if the data are unchanged since last sending
    Q_INVOKABLE void send(const QList<int>& dataList,
            unsigned int length,
            quint8 reportID,
            bool resendUnchangedReport = false) {
        Q_UNUSED(length);
        QByteArray dataArray;
        dataArray.reserve(dataList.size());
        for (int datum : dataList) {
            dataArray.append(datum);
        }
        sendOutputReport(reportID, dataArray, resendUnchangedReport);
    }

    /// @brief Sends an OutputReport to HID device
    /// @param reportID 1...255 for HID devices that uses ReportIDs - or 0 for devices, which don't use ReportIDs
    /// @param dataArray Data to send as byte array (Javascript type Uint8Array)
    /// @param resendUnchangedReport If set, the report will also be send, if the data are unchanged since last sending
    Q_INVOKABLE void sendOutputReport(quint8 reportID,
            const QByteArray& dataArray,
            bool resendUnchangedReport = false) {
        VERIFY_OR_DEBUG_ASSERT(m_pHidController->m_pHidIoThread) {
            return;
        }
        m_pHidController->m_pHidIoThread->updateCachedOutputReportData(
                reportID, dataArray, resendUnchangedReport);
    }

    /// @brief getInputReport receives an InputReport from the HID device on request.
    /// @details This can be used on startup to initialize the knob positions in Mixxx
    ///          to the physical position of the hardware knobs on the controller.
    ///          This is an optional command in the HID standard - not all devices support it.
    /// @param reportID 1...255 for HID devices that uses ReportIDs - or 0 for devices, which don't use
    /// @return Returns report data with ReportID byte as prefix
    Q_INVOKABLE QByteArray getInputReport(
            quint8 reportID) {
        VERIFY_OR_DEBUG_ASSERT(m_pHidController->m_pHidIoThread) {
            return {};
        }
        return m_pHidController->m_pHidIoThread->getInputReport(reportID);
    }

    /// @brief Sends a FeatureReport to HID device
    /// @param reportID 1...255 for HID devices that uses ReportIDs - or 0 for devices, which don't use
    /// @param reportData Data to send as byte array (Javascript type Uint8Array)
    Q_INVOKABLE void sendFeatureReport(
            quint8 reportID, const QByteArray& reportData) {
        VERIFY_OR_DEBUG_ASSERT(m_pHidController->m_pHidIoThread) {
            return;
        }
        m_pHidController->m_pHidIoThread->sendFeatureReport(reportID, reportData);
    }

    /// @brief getFeatureReport receives a FeatureReport from the HID device on request.
    /// @param reportID 1...255 for HID devices that uses ReportIDs - or 0 for devices, which don't use
    /// @return The returned array matches the input format of sendFeatureReport (Javascript type Uint8Array),
    ///         allowing it to be read, modified and sent it back to the controller.
    Q_INVOKABLE QByteArray getFeatureReport(
            quint8 reportID) {
        VERIFY_OR_DEBUG_ASSERT(m_pHidController->m_pHidIoThread) {
            return {};
        }
        return m_pHidController->m_pHidIoThread->getFeatureReport(reportID);
    }

  private:
    HidController* m_pHidController;
};