Monitor all HID++ reports on wireless device 1
Again, many things were done in this commit such as implementing an I/O queue, a mutex_queue, and implementing the hidpp::Report class. I'm expecting commits to be like this until I can get a clean codebase for the backend.
This commit is contained in:
parent
1de722b935
commit
6b895b3015
|
@ -12,11 +12,13 @@ add_executable(logid
|
||||||
logid.cpp
|
logid.cpp
|
||||||
util.cpp
|
util.cpp
|
||||||
DeviceMonitor.cpp
|
DeviceMonitor.cpp
|
||||||
|
backend/Error.cpp
|
||||||
backend/raw/DeviceMonitor.cpp
|
backend/raw/DeviceMonitor.cpp
|
||||||
backend/raw/RawDevice.cpp
|
backend/raw/RawDevice.cpp
|
||||||
backend/hidpp/Device.cpp
|
backend/hidpp/Device.cpp
|
||||||
backend/hidpp/Report.cpp
|
backend/hidpp/Report.cpp
|
||||||
backend/dj/Report.cpp)
|
backend/dj/Report.cpp
|
||||||
|
util/mutex_queue.h)
|
||||||
|
|
||||||
set_target_properties(logid PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
set_target_properties(logid PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||||
|
|
||||||
|
|
|
@ -102,9 +102,29 @@ void DeviceMonitor::stopAndDeleteDevice (const std::string &path, HIDPP::DeviceI
|
||||||
void DeviceMonitor::addDevice(std::string path)
|
void DeviceMonitor::addDevice(std::string path)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
backend::hidpp::Device device(path, hidpp::DeviceIndex::DefaultDevice);
|
backend::hidpp::Device device(path, hidpp::DeviceIndex::WirelessDevice1);
|
||||||
|
|
||||||
log_printf(DEBUG, "Detected HID++ device at %s", path.c_str());
|
log_printf(DEBUG, "Detected HID++ device at %s", path.c_str());
|
||||||
|
|
||||||
|
backend::hidpp::EventHandler eventHandler;
|
||||||
|
eventHandler.condition = [device](backend::hidpp::Report& report)->bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
eventHandler.callback = [device](backend::hidpp::Report& report)->void
|
||||||
|
{
|
||||||
|
log_printf(DEBUG, "Event on %s:%d", device.devicePath().c_str(),
|
||||||
|
device.deviceIndex());
|
||||||
|
for(auto& i : report.rawReport())
|
||||||
|
printf("%02x ", i);
|
||||||
|
printf("\n");
|
||||||
|
};
|
||||||
|
|
||||||
|
device.addEventHandler("MONITOR_ALL", eventHandler);
|
||||||
|
|
||||||
|
std::thread([](backend::hidpp::Device device) { device.listen(); }, device).detach();
|
||||||
|
|
||||||
|
/* This is a temporary solution to avoid std::bad_function_call */
|
||||||
|
while(true) {}
|
||||||
}
|
}
|
||||||
catch(backend::hidpp::Device::InvalidDevice &e)
|
catch(backend::hidpp::Device::InvalidDevice &e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -38,6 +38,7 @@ namespace logid
|
||||||
private:
|
private:
|
||||||
std::mutex devices_mutex;
|
std::mutex devices_mutex;
|
||||||
std::map<std::string, std::map<backend::hidpp::DeviceIndex, ConnectedDevice>> devices;
|
std::map<std::string, std::map<backend::hidpp::DeviceIndex, ConnectedDevice>> devices;
|
||||||
|
backend::hidpp::EventHandler eventHandler;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern DeviceMonitor* finder;
|
extern DeviceMonitor* finder;
|
||||||
|
|
6
src/logid/backend/Error.cpp
Normal file
6
src/logid/backend/Error.cpp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#include "Error.h"
|
||||||
|
|
||||||
|
const char *logid::backend::TimeoutError::what() noexcept
|
||||||
|
{
|
||||||
|
return "Device timed out";
|
||||||
|
}
|
16
src/logid/backend/Error.h
Normal file
16
src/logid/backend/Error.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef LOGID_BACKEND_ERROR_H
|
||||||
|
#define LOGID_BACKEND_ERROR_H
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace logid {
|
||||||
|
namespace backend {
|
||||||
|
class TimeoutError: public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TimeoutError() = default;
|
||||||
|
virtual const char* what() noexcept;
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif //LOGID_BACKEND_ERROR_H
|
15
src/logid/backend/defs.h
Normal file
15
src/logid/backend/defs.h
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef LOGID_BACKEND_DEFS_H
|
||||||
|
#define LOGID_BACKEND_DEFS_H
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace logid::backend
|
||||||
|
{
|
||||||
|
struct RawEventHandler
|
||||||
|
{
|
||||||
|
std::function<bool(std::vector<uint8_t>& )> condition;
|
||||||
|
std::function<void(std::vector<uint8_t>& )> callback;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //LOGID_BACKEND_DEFS_H
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <assert.h>
|
||||||
#include "Device.h"
|
#include "Device.h"
|
||||||
#include "Report.h"
|
#include "Report.h"
|
||||||
|
|
||||||
|
@ -21,10 +22,67 @@ Device::InvalidDevice::Reason Device::InvalidDevice::code() const noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO: Initialize a single RawDevice for each path.
|
/// TODO: Initialize a single RawDevice for each path.
|
||||||
Device::Device(std::string path, DeviceIndex index):
|
Device::Device(const std::string& path, DeviceIndex index):
|
||||||
raw_device (std::make_shared<raw::RawDevice>(path)), path (path), index (index)
|
raw_device (std::make_shared<raw::RawDevice>(path)), path (path), index (index)
|
||||||
{
|
{
|
||||||
supported_reports = getSupportedReports(raw_device->reportDescriptor());
|
supported_reports = getSupportedReports(raw_device->reportDescriptor());
|
||||||
if(!supported_reports)
|
if(!supported_reports)
|
||||||
throw InvalidDevice(InvalidDevice::NoHIDPPReport);
|
throw InvalidDevice(InvalidDevice::NoHIDPPReport);
|
||||||
}
|
|
||||||
|
// Pass all HID++ events with device index to this device.
|
||||||
|
RawEventHandler rawEventHandler;
|
||||||
|
rawEventHandler.condition = [index](std::vector<uint8_t>& report)->bool
|
||||||
|
{
|
||||||
|
return (report[Offset::Type] == Report::Short ||
|
||||||
|
report[Offset::Type] == Report::Long) && (report[Offset::DeviceIndex] == index);
|
||||||
|
};
|
||||||
|
rawEventHandler.callback = [this](std::vector<uint8_t>& report)->void
|
||||||
|
{
|
||||||
|
Report _report(report);
|
||||||
|
this->handleEvent(_report);
|
||||||
|
};
|
||||||
|
|
||||||
|
raw_device->addEventHandler("DEV_" + std::to_string(index), rawEventHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::addEventHandler(const std::string& nickname, EventHandler& handler)
|
||||||
|
{
|
||||||
|
auto it = event_handlers.find(nickname);
|
||||||
|
assert(it == event_handlers.end());
|
||||||
|
|
||||||
|
event_handlers.emplace(nickname, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::removeEventHandler(const std::string& nickname)
|
||||||
|
{
|
||||||
|
event_handlers.erase(nickname);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::handleEvent(Report& report)
|
||||||
|
{
|
||||||
|
for(auto& handler : event_handlers)
|
||||||
|
if(handler.second.condition(report))
|
||||||
|
handler.second.callback(report);
|
||||||
|
}
|
||||||
|
|
||||||
|
Report Device::sendReport(Report& report)
|
||||||
|
{
|
||||||
|
switch(report.type())
|
||||||
|
{
|
||||||
|
case Report::Short:
|
||||||
|
if(!(supported_reports & HIDPP_REPORT_SHORT_SUPPORTED))
|
||||||
|
report.setType(Report::Long);
|
||||||
|
break;
|
||||||
|
case Report::Long:
|
||||||
|
/* Report can be truncated, but that isn't a good idea. */
|
||||||
|
assert(supported_reports & HIDPP_REPORT_LONG_SUPPORTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto raw_response = raw_device->sendReport(report.rawReport());
|
||||||
|
return Report(raw_response);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::listen()
|
||||||
|
{
|
||||||
|
raw_device->listen();
|
||||||
|
}
|
||||||
|
|
|
@ -3,22 +3,21 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
#include "../raw/RawDevice.h"
|
#include "../raw/RawDevice.h"
|
||||||
|
#include "Report.h"
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
namespace logid::backend::hidpp
|
namespace logid {
|
||||||
|
namespace backend {
|
||||||
|
namespace hidpp
|
||||||
{
|
{
|
||||||
enum DeviceIndex: uint8_t
|
struct EventHandler
|
||||||
{
|
{
|
||||||
DefaultDevice = 0,
|
std::function<bool(Report&)> condition;
|
||||||
WirelessDevice1 = 1,
|
std::function<void(Report&)> callback;
|
||||||
WirelessDevice2 = 2,
|
|
||||||
WirelessDevice3 = 3,
|
|
||||||
WirelessDevice4 = 4,
|
|
||||||
WirelessDevice5 = 5,
|
|
||||||
WirelessDevice6 = 6,
|
|
||||||
CordedDevice = 0xff
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Device
|
class Device
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -35,17 +34,30 @@ namespace logid::backend::hidpp
|
||||||
virtual Reason code() const noexcept;
|
virtual Reason code() const noexcept;
|
||||||
private:
|
private:
|
||||||
Reason _reason;
|
Reason _reason;
|
||||||
|
|
||||||
};
|
};
|
||||||
Device(std::string path, DeviceIndex index);
|
|
||||||
|
Device(const std::string& path, DeviceIndex index);
|
||||||
|
|
||||||
std::string devicePath() const { return path; }
|
std::string devicePath() const { return path; }
|
||||||
DeviceIndex deviceIndex() const { return index; }
|
DeviceIndex deviceIndex() const { return index; }
|
||||||
|
|
||||||
|
void listen(); // Runs asynchronously
|
||||||
|
void stopListening();
|
||||||
|
|
||||||
|
void addEventHandler(const std::string& nickname, EventHandler& handler);
|
||||||
|
void removeEventHandler(const std::string& nickname);
|
||||||
|
|
||||||
|
Report sendReport(Report& report);
|
||||||
|
|
||||||
|
void handleEvent(Report& report);
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<logid::backend::raw::RawDevice> raw_device;
|
std::shared_ptr<raw::RawDevice> raw_device;
|
||||||
std::string path;
|
std::string path;
|
||||||
DeviceIndex index;
|
DeviceIndex index;
|
||||||
uint8_t supported_reports;
|
uint8_t supported_reports;
|
||||||
|
|
||||||
|
std::map<std::string, EventHandler> event_handlers;
|
||||||
};
|
};
|
||||||
}
|
} } }
|
||||||
|
|
||||||
#endif //LOGID_HIDPP_DEVICE_H
|
#endif //LOGID_HIDPP_DEVICE_H
|
|
@ -1,5 +1,6 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
#include "Report.h"
|
#include "Report.h"
|
||||||
|
|
||||||
using namespace logid::backend::hidpp;
|
using namespace logid::backend::hidpp;
|
||||||
|
@ -80,4 +81,80 @@ uint8_t hidpp::getSupportedReports(std::vector<uint8_t>&& rdesc)
|
||||||
ret |= HIDPP_REPORT_LONG_SUPPORTED;
|
ret |= HIDPP_REPORT_LONG_SUPPORTED;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *Report::InvalidReportID::what() const noexcept
|
||||||
|
{
|
||||||
|
return "Invalid report ID";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *Report::InvalidReportLength::what() const noexcept
|
||||||
|
{
|
||||||
|
return "Invalid report length";
|
||||||
|
}
|
||||||
|
|
||||||
|
Report::Report(Report::Type type, DeviceIndex device_index,
|
||||||
|
uint8_t feature_index, uint8_t function, uint8_t sw_id)
|
||||||
|
{
|
||||||
|
assert(!(function & functionMask));
|
||||||
|
assert(!(sw_id & swIdMask));
|
||||||
|
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case Short:
|
||||||
|
_data.resize(HeaderLength + ShortParamLength);
|
||||||
|
break;
|
||||||
|
case Long:
|
||||||
|
_data.resize(HeaderLength + LongParamLength);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw InvalidReportID();
|
||||||
|
}
|
||||||
|
|
||||||
|
_data[Offset::Type] = type;
|
||||||
|
_data[Offset::DeviceIndex] = device_index;
|
||||||
|
_data[Offset::Feature] = feature_index;
|
||||||
|
_data[Offset::Function] = (function & functionMask) << 4 | (sw_id & swIdMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
Report::Report(const std::vector<uint8_t>& data)
|
||||||
|
{
|
||||||
|
_data = data;
|
||||||
|
|
||||||
|
switch(_data[Offset::Type])
|
||||||
|
{
|
||||||
|
case Short:
|
||||||
|
_data.resize(HeaderLength + ShortParamLength);
|
||||||
|
break;
|
||||||
|
case Long:
|
||||||
|
_data.resize(HeaderLength + LongParamLength);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw InvalidReportID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Report::setType(Report::Type type)
|
||||||
|
{
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case Short:
|
||||||
|
_data.resize(HeaderLength + ShortParamLength);
|
||||||
|
break;
|
||||||
|
case Long:
|
||||||
|
_data.resize(HeaderLength + LongParamLength);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw InvalidReportID();
|
||||||
|
}
|
||||||
|
|
||||||
|
_data[Offset::Type] = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Report::setParams(const std::vector<uint8_t>& _params)
|
||||||
|
{
|
||||||
|
assert(_params.size() <= _data.size()-HeaderLength);
|
||||||
|
|
||||||
|
for(std::size_t i = 0; i < _params.size(); i++)
|
||||||
|
_data[Offset::Parameters + i] = _params[i];
|
||||||
|
}
|
||||||
|
|
|
@ -3,9 +3,7 @@
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include "../raw/RawDevice.h"
|
#include "../raw/RawDevice.h"
|
||||||
#include "Device.h"
|
#include "defs.h"
|
||||||
|
|
||||||
#define LOGID_HIDPP_SW_ID 0x0f
|
|
||||||
|
|
||||||
/* Some devices only support a subset of these reports */
|
/* Some devices only support a subset of these reports */
|
||||||
#define HIDPP_REPORT_SHORT_SUPPORTED 1U
|
#define HIDPP_REPORT_SHORT_SUPPORTED 1U
|
||||||
|
@ -15,6 +13,16 @@
|
||||||
namespace logid::backend::hidpp
|
namespace logid::backend::hidpp
|
||||||
{
|
{
|
||||||
uint8_t getSupportedReports(std::vector<uint8_t>&& rdesc);
|
uint8_t getSupportedReports(std::vector<uint8_t>&& rdesc);
|
||||||
|
|
||||||
|
namespace Offset
|
||||||
|
{
|
||||||
|
static constexpr uint8_t Type = 0;
|
||||||
|
static constexpr uint8_t DeviceIndex = 1;
|
||||||
|
static constexpr uint8_t Feature = 2;
|
||||||
|
static constexpr uint8_t Function = 3;
|
||||||
|
static constexpr uint8_t Parameters = 4;
|
||||||
|
}
|
||||||
|
|
||||||
class Report
|
class Report
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -24,30 +32,43 @@ namespace logid::backend::hidpp
|
||||||
Long = 0x11
|
Long = 0x11
|
||||||
};
|
};
|
||||||
|
|
||||||
class InvalidReportID: std::exception
|
class InvalidReportID: public std::exception
|
||||||
{
|
{
|
||||||
InvalidReportID();
|
public:
|
||||||
|
InvalidReportID() = default;
|
||||||
virtual const char* what() const noexcept;
|
virtual const char* what() const noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
class InvalidReportLength: std::exception
|
class InvalidReportLength: public std::exception
|
||||||
{
|
{
|
||||||
InvalidReportLength();
|
public:
|
||||||
|
InvalidReportLength() = default;;
|
||||||
virtual const char* what() const noexcept;
|
virtual const char* what() const noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr std::size_t MaxDataLength = 32;
|
static constexpr std::size_t MaxDataLength = 20;
|
||||||
|
static constexpr uint8_t swIdMask = 0x0f;
|
||||||
|
static constexpr uint8_t functionMask = 0x0f;
|
||||||
|
|
||||||
Report(uint8_t report_id, const uint8_t* data, std::size_t length);
|
Report(Type type, DeviceIndex device_index,
|
||||||
Report(std::vector<uint8_t> data);
|
uint8_t feature_index,
|
||||||
|
uint8_t function,
|
||||||
|
uint8_t sw_id);
|
||||||
|
explicit Report(const std::vector<uint8_t>& data);
|
||||||
|
|
||||||
Type type() const;
|
Type type() const { return static_cast<Type>(_data[Offset::Type]); };
|
||||||
void setType(Report::Type type);
|
void setType(Report::Type type);
|
||||||
|
|
||||||
logid::backend::hidpp::DeviceIndex deviceIndex();
|
std::vector<uint8_t>::const_iterator paramBegin() const { return _data.begin() + Offset::Parameters; }
|
||||||
|
std::vector<uint8_t>::const_iterator paramEnd() const { return _data.end(); }
|
||||||
|
void setParams(const std::vector<uint8_t>& _params);
|
||||||
|
|
||||||
|
logid::backend::hidpp::DeviceIndex deviceIndex()
|
||||||
|
{
|
||||||
|
return static_cast<DeviceIndex>(_data[Offset::DeviceIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> rawReport () const { return _data; }
|
std::vector<uint8_t> rawReport () const { return _data; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr std::size_t HeaderLength = 4;
|
static constexpr std::size_t HeaderLength = 4;
|
||||||
std::vector<uint8_t> _data;
|
std::vector<uint8_t> _data;
|
||||||
|
|
24
src/logid/backend/hidpp/defs.h
Normal file
24
src/logid/backend/hidpp/defs.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef LOGID_HIDPP_DEFS_H
|
||||||
|
#define LOGID_HIDPP_DEFS_H
|
||||||
|
|
||||||
|
#define LOGID_HIDPP_SOFTWARE_ID 1
|
||||||
|
|
||||||
|
namespace logid::backend::hidpp
|
||||||
|
{
|
||||||
|
enum DeviceIndex: uint8_t
|
||||||
|
{
|
||||||
|
DefaultDevice = 0,
|
||||||
|
WirelessDevice1 = 1,
|
||||||
|
WirelessDevice2 = 2,
|
||||||
|
WirelessDevice3 = 3,
|
||||||
|
WirelessDevice4 = 4,
|
||||||
|
WirelessDevice5 = 5,
|
||||||
|
WirelessDevice6 = 6,
|
||||||
|
CordedDevice = 0xff
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr std::size_t ShortParamLength = 3;
|
||||||
|
static constexpr std::size_t LongParamLength = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //LOGID_HIDPP_DEFS_H
|
|
@ -1,9 +1,11 @@
|
||||||
#include "RawDevice.h"
|
#include "RawDevice.h"
|
||||||
|
#include "../Error.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
#include <utility>
|
#include <cassert>
|
||||||
|
|
||||||
|
#define MAX_DATA_LENGTH 32
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
|
@ -76,29 +78,43 @@ RawDevice::~RawDevice()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RawDevice::sendReport(std::vector<uint8_t> report)
|
std::vector<uint8_t> RawDevice::sendReport(const std::vector<uint8_t>& report)
|
||||||
{
|
{
|
||||||
_sendReport(std::move(report));
|
std::packaged_task<std::vector<uint8_t>()> task(
|
||||||
|
[=]() {
|
||||||
|
_sendReport(report);
|
||||||
|
std::vector<uint8_t> response;
|
||||||
|
_readReport(response, MAX_DATA_LENGTH);
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
|
||||||
|
/* If the listener will stop, handle I/O manually.
|
||||||
|
* Otherwise, push to queue and wait for result. */
|
||||||
|
if(continue_listen)
|
||||||
|
{
|
||||||
|
auto f = task.get_future();
|
||||||
|
write_queue.push(&task);
|
||||||
|
return f.get();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return task.get_future().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> RawDevice::readReport(std::size_t maxDataLength)
|
int RawDevice::_sendReport(const std::vector<uint8_t>& report)
|
||||||
{
|
|
||||||
return _readReport(maxDataLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RawDevice::_sendReport(std::vector<uint8_t> report)
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(dev_io);
|
std::lock_guard<std::mutex> lock(dev_io);
|
||||||
int ret = ::write(fd, report.data(), report.size());
|
int ret = ::write(fd, report.data(), report.size());
|
||||||
if(ret == -1)
|
if(ret == -1)
|
||||||
throw std::system_error(errno, std::system_category(), "_sendReport write failed");
|
throw std::system_error(errno, std::system_category(), "_sendReport write failed");
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> RawDevice::_readReport(std::size_t maxDataLength)
|
int RawDevice::_readReport(std::vector<uint8_t>& report, std::size_t maxDataLength)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(dev_io);
|
std::lock_guard<std::mutex> lock(dev_io);
|
||||||
int ret;
|
int ret;
|
||||||
std::vector<uint8_t> report(maxDataLength);
|
report.resize(maxDataLength);
|
||||||
|
|
||||||
timeval timeout = { duration_cast<milliseconds>(HIDPP_IO_TIMEOUT).count(),
|
timeval timeout = { duration_cast<milliseconds>(HIDPP_IO_TIMEOUT).count(),
|
||||||
duration_cast<microseconds>(HIDPP_IO_TIMEOUT).count() };
|
duration_cast<microseconds>(HIDPP_IO_TIMEOUT).count() };
|
||||||
|
@ -133,7 +149,10 @@ std::vector<uint8_t> RawDevice::_readReport(std::size_t maxDataLength)
|
||||||
throw std::system_error(errno, std::system_category(), "_readReport read pipe failed");
|
throw std::system_error(errno, std::system_category(), "_readReport read pipe failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
return report;
|
if(0 == ret)
|
||||||
|
throw backend::TimeoutError();
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RawDevice::interruptRead()
|
void RawDevice::interruptRead()
|
||||||
|
@ -144,4 +163,62 @@ void RawDevice::interruptRead()
|
||||||
|
|
||||||
// Ensure I/O has halted
|
// Ensure I/O has halted
|
||||||
std::lock_guard<std::mutex> lock(dev_io);
|
std::lock_guard<std::mutex> lock(dev_io);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RawDevice::listen()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(listening);
|
||||||
|
|
||||||
|
continue_listen = true;
|
||||||
|
while(continue_listen)
|
||||||
|
{
|
||||||
|
while(!write_queue.empty())
|
||||||
|
{
|
||||||
|
auto task = write_queue.front();
|
||||||
|
(*task)();
|
||||||
|
write_queue.pop();
|
||||||
|
}
|
||||||
|
std::vector<uint8_t> report;
|
||||||
|
_readReport(report, MAX_DATA_LENGTH);
|
||||||
|
std::thread([this](std::vector<uint8_t> report) {
|
||||||
|
this->handleEvent(report);
|
||||||
|
}, report).detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
continue_listen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RawDevice::stopListener()
|
||||||
|
{
|
||||||
|
continue_listen = false;
|
||||||
|
interruptRead();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RawDevice::addEventHandler(const std::string &nickname, RawEventHandler &handler)
|
||||||
|
{
|
||||||
|
auto it = event_handlers.find(nickname);
|
||||||
|
assert(it == event_handlers.end());
|
||||||
|
event_handlers.emplace(nickname, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RawDevice::removeEventHandler(const std::string &nickname)
|
||||||
|
{
|
||||||
|
event_handlers.erase(nickname);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RawDevice::handleEvent(std::vector<uint8_t> &report)
|
||||||
|
{
|
||||||
|
for(auto& handler : event_handlers)
|
||||||
|
if(handler.second.condition(report))
|
||||||
|
handler.second.callback(report);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RawDevice::isListening()
|
||||||
|
{
|
||||||
|
bool ret = listening.try_lock();
|
||||||
|
|
||||||
|
if(ret)
|
||||||
|
listening.unlock();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
|
@ -4,10 +4,18 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <map>
|
||||||
|
#include <atomic>
|
||||||
|
#include <future>
|
||||||
|
|
||||||
|
#include "../defs.h"
|
||||||
|
#include "../../util/mutex_queue.h"
|
||||||
|
|
||||||
#define HIDPP_IO_TIMEOUT std::chrono::seconds(2)
|
#define HIDPP_IO_TIMEOUT std::chrono::seconds(2)
|
||||||
|
|
||||||
namespace logid::backend::raw
|
namespace logid {
|
||||||
|
namespace backend {
|
||||||
|
namespace raw
|
||||||
{
|
{
|
||||||
class RawDevice
|
class RawDevice
|
||||||
{
|
{
|
||||||
|
@ -18,12 +26,18 @@ namespace logid::backend::raw
|
||||||
std::vector<uint8_t> reportDescriptor() const { return rdesc; }
|
std::vector<uint8_t> reportDescriptor() const { return rdesc; }
|
||||||
|
|
||||||
/// TODO: Process reports in a queue.
|
/// TODO: Process reports in a queue.
|
||||||
void sendReport(std::vector<uint8_t> report);
|
std::vector<uint8_t> sendReport(const std::vector<uint8_t>& report);
|
||||||
std::vector<uint8_t> readReport(std::size_t maxDataLength);
|
|
||||||
|
|
||||||
void interruptRead();
|
void interruptRead();
|
||||||
|
|
||||||
|
void listen();
|
||||||
|
void stopListener();
|
||||||
|
bool isListening();
|
||||||
|
|
||||||
|
void addEventHandler(const std::string& nickname, RawEventHandler& handler);
|
||||||
|
void removeEventHandler(const std::string& nickname);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::mutex dev_io;
|
std::mutex dev_io, listening;
|
||||||
std::string path;
|
std::string path;
|
||||||
int fd;
|
int fd;
|
||||||
int dev_pipe[2];
|
int dev_pipe[2];
|
||||||
|
@ -32,10 +46,17 @@ namespace logid::backend::raw
|
||||||
std::string name;
|
std::string name;
|
||||||
std::vector<uint8_t> rdesc;
|
std::vector<uint8_t> rdesc;
|
||||||
|
|
||||||
|
std::atomic<bool> continue_listen;
|
||||||
|
|
||||||
|
std::map<std::string, backend::RawEventHandler> event_handlers;
|
||||||
|
void handleEvent(std::vector<uint8_t>& report);
|
||||||
|
|
||||||
/* These will only be used internally and processed with a queue */
|
/* These will only be used internally and processed with a queue */
|
||||||
void _sendReport(std::vector<uint8_t> report);
|
int _sendReport(const std::vector<uint8_t>& report);
|
||||||
std::vector<uint8_t> _readReport(std::size_t maxDataLength);
|
int _readReport(std::vector<uint8_t>& report, std::size_t maxDataLength);
|
||||||
|
|
||||||
|
mutex_queue<std::packaged_task<std::vector<uint8_t>()>*> write_queue;
|
||||||
};
|
};
|
||||||
}
|
}}}
|
||||||
|
|
||||||
#endif //LOGID_BACKEND_RAWDEVICE_H
|
#endif //LOGID_BACKEND_RAWDEVICE_H
|
37
src/logid/util/mutex_queue.h
Normal file
37
src/logid/util/mutex_queue.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef MUTEX_QUEUE_H
|
||||||
|
#define MUTEX_QUEUE_H
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
template<typename data>
|
||||||
|
class mutex_queue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
mutex_queue<data>() = default;
|
||||||
|
bool empty()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
return _queue.empty();
|
||||||
|
}
|
||||||
|
data& front()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
return _queue.front();
|
||||||
|
}
|
||||||
|
void push(const data& _data)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
_queue.push(_data);
|
||||||
|
}
|
||||||
|
void pop()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
_queue.pop();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::queue<data> _queue;
|
||||||
|
std::mutex _mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //MUTEX_QUEUE_H
|
Loading…
Reference in New Issue
Block a user