Implement receiver HID++ connect/disconnect events

Many changes were made here but that was the biggest one.

There's currently a bug where std::system_error: Broken pipe is thrown
after launching the daemon with a receiver connector.

A workaround for this bug is to simply shake the mouse while starting
the daemon. I will investigate this soon.
This commit is contained in:
pixl
2020-06-21 05:33:33 -04:00
parent b05e525bbc
commit e40da5f0c0
24 changed files with 689 additions and 181 deletions

View File

@@ -24,8 +24,8 @@ using namespace std::chrono;
bool RawDevice::supportedReportID(uint8_t id)
{
return (hidpp::ReportType::Short == id) || (hidpp::ReportType::Long == id) ||
(dj::ReportType::Short == id) || (dj::ReportType::Long == id);
return (hidpp::ReportType::Short == id) || (hidpp::ReportType::Long == id)
|| (dj::ReportType::Short == id) || (dj::ReportType::Long == id);
}
RawDevice::RawDevice(std::string path) : path (path), continue_listen (false)
@@ -34,14 +34,16 @@ RawDevice::RawDevice(std::string path) : path (path), continue_listen (false)
fd = ::open(path.c_str(), O_RDWR);
if (fd == -1)
throw std::system_error(errno, std::system_category(), "RawDevice open failed");
throw std::system_error(errno, std::system_category(),
"RawDevice open failed");
hidraw_devinfo devinfo{};
if (-1 == ::ioctl(fd, HIDIOCGRAWINFO, &devinfo))
{
int err = errno;
::close(fd);
throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRAWINFO failed");
throw std::system_error(err, std::system_category(),
"RawDevice HIDIOCGRAWINFO failed");
}
vid = devinfo.vendor;
pid = devinfo.product;
@@ -51,22 +53,25 @@ RawDevice::RawDevice(std::string path) : path (path), continue_listen (false)
{
int err = errno;
::close(fd);
throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRAWNAME failed");
throw std::system_error(err, std::system_category(),
"RawDevice HIDIOCGRAWNAME failed");
}
name.assign(name_buf, ret - 1);
_name.assign(name_buf, ret - 1);
hidraw_report_descriptor _rdesc{};
if (-1 == ::ioctl(fd, HIDIOCGRDESCSIZE, &_rdesc.size))
{
int err = errno;
::close(fd);
throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRDESCSIZE failed");
throw std::system_error(err, std::system_category(),
"RawDevice HIDIOCGRDESCSIZE failed");
}
if (-1 == ::ioctl(fd, HIDIOCGRDESC, &_rdesc))
{
int err = errno;
::close(fd);
throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRDESC failed");
throw std::system_error(err, std::system_category(),
"RawDevice HIDIOCGRDESC failed");
}
rdesc = std::vector<uint8_t>(_rdesc.value, _rdesc.value + _rdesc.size);
@@ -74,7 +79,8 @@ RawDevice::RawDevice(std::string path) : path (path), continue_listen (false)
{
int err = errno;
close(fd);
throw std::system_error(err, std::system_category(), "RawDevice pipe open failed");
throw std::system_error(err, std::system_category(),
"RawDevice pipe open failed");
}
continue_listen = false;
@@ -92,8 +98,6 @@ RawDevice::~RawDevice()
std::vector<uint8_t> RawDevice::sendReport(const std::vector<uint8_t>& report)
{
assert(supportedReportID(report[0]));
/* If the listener will stop, handle I/O manually.
* Otherwise, push to queue and wait for result. */
if(continue_listen)
@@ -189,11 +193,6 @@ std::vector<uint8_t> RawDevice::_respondToReport
int RawDevice::_sendReport(const std::vector<uint8_t>& report)
{
std::lock_guard<std::mutex> lock(dev_io);
int ret = ::write(fd, report.data(), report.size());
if(ret == -1)
throw std::system_error(errno, std::system_category(), "_sendReport write failed");
if(logid::global_verbosity == LogLevel::RAWREPORT) {
printf("[RAWREPORT] %s OUT: ", path.c_str());
for(auto &i : report)
@@ -201,6 +200,14 @@ int RawDevice::_sendReport(const std::vector<uint8_t>& report)
printf("\n");
}
assert(supportedReportID(report[0]));
std::lock_guard<std::mutex> lock(dev_io);
int ret = ::write(fd, report.data(), report.size());
if(ret == -1)
throw std::system_error(errno, std::system_category(),
"_sendReport write failed");
return ret;
}
@@ -225,13 +232,15 @@ int RawDevice::_readReport(std::vector<uint8_t>& report, std::size_t maxDataLeng
} while(ret == -1 && errno == EINTR);
if(ret == -1)
throw std::system_error(errno, std::system_category(), "_readReport select failed");
throw std::system_error(errno, std::system_category(),
"_readReport select failed");
if(FD_ISSET(fd, &fds))
{
ret = read(fd, report.data(), report.size());
if(ret == -1)
throw std::system_error(errno, std::system_category(), "_readReport read failed");
throw std::system_error(errno, std::system_category(),
"_readReport read failed");
report.resize(ret);
}
@@ -240,7 +249,8 @@ int RawDevice::_readReport(std::vector<uint8_t>& report, std::size_t maxDataLeng
char c;
ret = read(dev_pipe[0], &c, sizeof(char));
if(ret == -1)
throw std::system_error(errno, std::system_category(), "_readReport read pipe failed");
throw std::system_error(errno, std::system_category(),
"_readReport read pipe failed");
}
if(0 == ret)
@@ -260,7 +270,8 @@ void RawDevice::interruptRead()
{
char c = 0;
if(-1 == write(dev_pipe[1], &c, sizeof(char)))
throw std::system_error(errno, std::system_category(), "interruptRead write pipe failed");
throw std::system_error(errno, std::system_category(),
"interruptRead write pipe failed");
// Ensure I/O has halted
std::lock_guard<std::mutex> lock(dev_io);
@@ -302,7 +313,8 @@ void RawDevice::stopListener()
interruptRead();
}
void RawDevice::addEventHandler(const std::string& nickname, RawEventHandler& handler)
void RawDevice::addEventHandler(const std::string& nickname,
const std::shared_ptr<RawEventHandler>& handler)
{
auto it = event_handlers.find(nickname);
assert(it == event_handlers.end());
@@ -314,11 +326,17 @@ void RawDevice::removeEventHandler(const std::string &nickname)
event_handlers.erase(nickname);
}
const std::map<std::string, std::shared_ptr<RawEventHandler>>&
RawDevice::eventHandlers()
{
return event_handlers;
}
void RawDevice::handleEvent(std::vector<uint8_t> &report)
{
for(auto& handler : event_handlers)
if(handler.second.condition(report))
handler.second.callback(report);
if(handler.second->condition(report))
handler.second->callback(report);
}
bool RawDevice::isListening()
@@ -328,5 +346,5 @@ bool RawDevice::isListening()
if(ret)
listening.unlock();
return ret;
return !ret;
}

View File

@@ -23,9 +23,14 @@ namespace raw
public:
static bool supportedReportID(uint8_t id);
RawDevice(std::string path);
explicit RawDevice(std::string path);
~RawDevice();
std::string hidrawPath() const { return path; }
std::string name() const { return _name; }
uint16_t vendorId() const { return vid; }
uint16_t productId() const { return pid; }
std::vector<uint8_t> reportDescriptor() const { return rdesc; }
std::vector<uint8_t> sendReport(const std::vector<uint8_t>& report);
@@ -36,8 +41,11 @@ namespace raw
void stopListener();
bool isListening();
void addEventHandler(const std::string& nickname, RawEventHandler& handler);
void addEventHandler(const std::string& nickname,
const std::shared_ptr<backend::RawEventHandler>& handler);
void removeEventHandler(const std::string& nickname);
const std::map<std::string, std::shared_ptr<backend::RawEventHandler>>&
eventHandlers();
private:
std::mutex dev_io, listening;
@@ -46,12 +54,13 @@ namespace raw
int dev_pipe[2];
uint16_t vid;
uint16_t pid;
std::string name;
std::string _name;
std::vector<uint8_t> rdesc;
std::atomic<bool> continue_listen;
std::map<std::string, backend::RawEventHandler> event_handlers;
std::map<std::string, std::shared_ptr<backend::RawEventHandler>>
event_handlers;
void handleEvent(std::vector<uint8_t>& report);
/* These will only be used internally and processed with a queue */