132 lines
4.0 KiB
C
132 lines
4.0 KiB
C
|
#ifndef SMXDevice_H
|
||
|
#define SMXDevice_H
|
||
|
|
||
|
#include <windows.h>
|
||
|
#include <vector>
|
||
|
#include <memory>
|
||
|
#include <string>
|
||
|
#include <list>
|
||
|
#include <functional>
|
||
|
using namespace std;
|
||
|
|
||
|
#include "Helpers.h"
|
||
|
|
||
|
namespace SMX
|
||
|
{
|
||
|
|
||
|
struct SMXDeviceInfo
|
||
|
{
|
||
|
// If true, this controller is set to player 2.
|
||
|
bool m_bP2 = false;
|
||
|
|
||
|
// This device's serial number.
|
||
|
char m_Serial[33];
|
||
|
|
||
|
// This device's firmware version (normally 1).
|
||
|
uint16_t m_iFirmwareVersion;
|
||
|
};
|
||
|
|
||
|
// Low-level SMX device handling.
|
||
|
class SMXDeviceConnection
|
||
|
{
|
||
|
public:
|
||
|
static shared_ptr<SMXDeviceConnection> Create();
|
||
|
SMXDeviceConnection(shared_ptr<SMXDeviceConnection> &pSelf);
|
||
|
~SMXDeviceConnection();
|
||
|
|
||
|
bool Open(shared_ptr<AutoCloseHandle> DeviceHandle, wstring &error);
|
||
|
|
||
|
void Close();
|
||
|
|
||
|
// Get the device handle opened by Open(), or NULL if we're not open.
|
||
|
shared_ptr<AutoCloseHandle> GetDeviceHandle() const { return m_hDevice; }
|
||
|
|
||
|
void Update(wstring &sError);
|
||
|
|
||
|
// Devices are inactive by default, and will just read device info and then idle. We'll
|
||
|
// process input state packets, but we won't send any commands to the device or process
|
||
|
// any commands from it. It's safe to have a device open but inactive if it's being used
|
||
|
// by another application.
|
||
|
void SetActive(bool bActive);
|
||
|
bool GetActive() const { return m_bActive; }
|
||
|
|
||
|
bool IsConnected() const { return m_hDevice != nullptr; }
|
||
|
bool IsConnectedWithDeviceInfo() const { return m_hDevice != nullptr && m_bGotInfo; }
|
||
|
SMXDeviceInfo GetDeviceInfo() const { return m_DeviceInfo; }
|
||
|
|
||
|
// Read from the read buffer. This only returns data that we've already read, so there aren't
|
||
|
// any errors to report here.
|
||
|
bool ReadPacket(string &out);
|
||
|
|
||
|
// Send a command. This must be a single complete command: partial writes and multiple
|
||
|
// commands in a call aren't allowed.
|
||
|
void SendCommand(const string &cmd, function<void(string response)> pComplete=nullptr);
|
||
|
|
||
|
uint16_t GetInputState() const { return m_iInputState; }
|
||
|
|
||
|
private:
|
||
|
void RequestDeviceInfo(function<void(string response)> pComplete = nullptr);
|
||
|
|
||
|
void CheckReads(wstring &error);
|
||
|
void BeginAsyncRead(wstring &error);
|
||
|
void CheckWrites(wstring &error);
|
||
|
void HandleUsbPacket(const string &buf);
|
||
|
|
||
|
weak_ptr<SMXDeviceConnection> m_pSelf;
|
||
|
shared_ptr<AutoCloseHandle> m_hDevice;
|
||
|
|
||
|
bool m_bActive = false;
|
||
|
|
||
|
// After we open a device, we request basic info. Once we get it, this is set to true.
|
||
|
bool m_bGotInfo = false;
|
||
|
|
||
|
list<string> m_sReadBuffers;
|
||
|
string m_sCurrentReadBuffer;
|
||
|
|
||
|
struct PendingCommandPacket {
|
||
|
PendingCommandPacket();
|
||
|
|
||
|
string sData;
|
||
|
};
|
||
|
|
||
|
// Commands that are waiting to be sent:
|
||
|
struct PendingCommand {
|
||
|
PendingCommand();
|
||
|
|
||
|
list<shared_ptr<PendingCommandPacket>> m_Packets;
|
||
|
|
||
|
// The overlapped struct for writing this command's packets. m_bWriting is true
|
||
|
// if we're waiting for the write to complete.
|
||
|
OVERLAPPED m_Overlapped;
|
||
|
bool m_bWriting = false;
|
||
|
|
||
|
// This is only called if m_bWaitForResponse if true. Otherwise, we send the command
|
||
|
// and forget about it. If the command has a response, it'll be in buf.
|
||
|
function<void(string response)> m_pComplete;
|
||
|
|
||
|
// If true, once we send this command we won't send any other commands until we get
|
||
|
// a response.
|
||
|
bool m_bIsDeviceInfoCommand = false;
|
||
|
|
||
|
// The SMX::GetMonotonicTime when we started sending this command.
|
||
|
double m_fSentAt = 0;
|
||
|
};
|
||
|
list<shared_ptr<PendingCommand>> m_aPendingCommands;
|
||
|
|
||
|
// If set, we've sent a command out of m_aPendingCommands and we're waiting for a response. We
|
||
|
// can't send another command until the previous one has completed.
|
||
|
shared_ptr<PendingCommand> m_pCurrentCommand = nullptr;
|
||
|
|
||
|
// We always have a read in progress.
|
||
|
OVERLAPPED overlapped_read;
|
||
|
char overlapped_read_buffer[64];
|
||
|
|
||
|
uint16_t m_iInputState = 0;
|
||
|
|
||
|
// The current device info. We retrieve this when we connect.
|
||
|
SMXDeviceInfo m_DeviceInfo;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
#endif
|