blob: 08c52ca002e640188a0fcba0d7ab679f25f4be5f [file] [log] [blame]
/*
* Copyright 2016 The Fuchsia Authors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "FuchsiaInputHandler.h"
#include <wtf/Assertions.h>
#include <dirent.h>
#include <fcntl.h>
#include <hid/hid.h>
#include <hid/usages.h>
#include <zircon/device/device.h>
#include <zircon/device/input.h>
#include <zircon/syscalls.h>
#include <zircon/types.h>
#include <lib/fdio/io.h>
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <thread>
const char* kDevInput = "/dev/class/input";
namespace Fuchsia {
using namespace std;
#pragma mark - InputReportHandler
InputReportHandler::InputReportHandler(int descriptor, const std::string& deviceName)
: fDescriptor(descriptor)
, fName(deviceName)
{
}
InputReportHandler::~InputReportHandler()
{
}
ssize_t InputReportHandler::openHandle()
{
zx_handle_t handle;
ssize_t ret = fdio_ioctl(fDescriptor, IOCTL_DEVICE_GET_EVENT_HANDLE, nullptr, 0,
&handle, sizeof(handle));
if (ret >= 0) {
fHandle = handle;
}
return ret;
}
void InputReportHandler::checkForEvents(EventList& events)
{
size_t max_report_len = 0;
int rc = fdio_ioctl(fDescriptor, IOCTL_INPUT_GET_MAX_REPORTSIZE, NULL, 0, &max_report_len, sizeof(max_report_len));
if (rc < 0) {
WTFLogAlways("hid: could not get max report size from %d: %d", fDescriptor, rc);
}
vector<char> buffer(max_report_len);
int r = read(fDescriptor, &buffer[0], max_report_len);
handleReport(r, &buffer[0], events);
}
#pragma mark - MouseReportHandler
MouseReportHandler::MouseReportHandler(int descriptor, const std::string& deviceName)
: InputReportHandler(descriptor, deviceName)
{
}
void MouseReportHandler::handleReport(size_t byteLength, const char* reportData, EventList& events)
{
const boot_mouse_report_t* mouseReport = reinterpret_cast<const boot_mouse_report_t*>(reportData);
while (byteLength > 0) {
EventPtr eventP(new MouseEvent(fDescriptor, mouseReport->rel_x, mouseReport->rel_y, mouseReport->buttons));
events.push_back(eventP);
byteLength -= 3;
mouseReport += 1;
}
}
#pragma mark - KeyboardReportHandler
KeyboardReportHandler::KeyboardReportHandler(int descriptor, const std::string& deviceName)
: InputReportHandler(descriptor, deviceName)
{
}
void KeyboardReportHandler::handleReport(size_t byteLength, const char* reportData, EventList& events)
{
const uint8_t* keyboardReport = reinterpret_cast<const uint8_t*>(reportData);
uint8_t* keyboardReportWriteableSigh = const_cast<uint8_t*>(keyboardReport);
uint8_t keycode;
hid_keys_t newKeyState;
hid_keys_t keyDelta;
hid_kbd_parse_report(keyboardReportWriteableSigh, &newKeyState);
hid_kbd_pressed_keys(&fKeyState, &newKeyState, &keyDelta);
hid_for_every_key(&keyDelta, keycode) {
EventPtr eventP(new KeyEvent(fDescriptor, keycode, true));
events.push_back(eventP);
}
hid_kbd_released_keys(&fKeyState, &newKeyState, &keyDelta);
hid_for_every_key(&keyDelta, keycode) {
EventPtr eventP(new KeyEvent(fDescriptor, keycode, false));
events.push_back(eventP);
}
fKeyState = newKeyState;
}
#pragma mark - InputHandler
InputHandler::InputHandler()
{
}
static int get_hid_protocol(int fd, const char* name)
{
int proto = 0;
int rc = fdio_ioctl(fd, IOCTL_INPUT_GET_PROTOCOL, NULL, 0, &proto, sizeof(proto));
if (rc < 0) {
WTFLogAlways("hid: could not get protocol from %s (status=%d)\n", name, rc);
} else {
WTFLogAlways("hid: %s proto=%d\n", name, proto);
}
return proto;
}
static void checkForEvents(int fd, EventList& events)
{
size_t max_report_len = 0;
int rc = fdio_ioctl(fd, IOCTL_INPUT_GET_MAX_REPORTSIZE, NULL, 0, &max_report_len, sizeof(max_report_len));
if (rc < 0) {
WTFLogAlways("hid: could not get max report size from %d: %d", fd, rc);
return;
}
vector<char> buffer(max_report_len);
int proto = 0;
rc = fdio_ioctl(fd, IOCTL_INPUT_GET_PROTOCOL, NULL, 0, &proto, sizeof(proto));
if (rc < 0) {
WTFLogAlways("hid: could not get protocol (status=%d)", rc);
return;
}
int r = read(fd, &buffer[0], max_report_len);
if (proto == INPUT_PROTO_MOUSE) {
boot_mouse_report_t* mouseReport = reinterpret_cast<boot_mouse_report_t*>(&buffer[0]);
while (r > 0) {
EventPtr eventP(new MouseEvent(fd, mouseReport->rel_x, mouseReport->rel_y, mouseReport->buttons));
events.push_back(eventP);
r -= 3;
mouseReport += 1;
}
} else if (proto == INPUT_PROTO_KBD) {
boot_kbd_report* kbdReport = reinterpret_cast<boot_kbd_report*>(&buffer[0]);
while (r > 0) {
WTFLogAlways("keyboard action");
r -= sizeof(boot_kbd_report);
kbdReport += 1;
}
}
}
static EventList waitForEvents(vector<InputReportHandlerPtr> reportHandlers)
{
EventList events;
vector<zx_wait_item_t> items;
for (const auto& oneReportHandler : reportHandlers) {
zx_wait_item_t item = { oneReportHandler->handle(), DEVICE_SIGNAL_READABLE, 0 };
items.push_back(item);
}
zx_status_t result = zx_object_wait_many(&items[0], items.size(),
ZX_TIME_INFINITE);
if (result == ZX_OK) {
int index = 0;
for (const auto& oneItem : items) {
if (oneItem.pending & DEVICE_SIGNAL_READABLE) {
auto& oneReportHandler(reportHandlers[index]);
oneReportHandler->checkForEvents(events);
}
index += 1;
}
}
return events;
}
void InputHandler::openDevices()
{
struct dirent* de;
DIR* dir = opendir(kDevInput);
if (!dir) {
WTFLogAlways("hid: error opening %s", kDevInput);
return;
}
while ((de = readdir(dir)) != NULL) {
ostringstream oss;
oss << kDevInput << "/" << de->d_name;
string dname(oss.str());
int fd = open(dname.c_str(), O_RDONLY);
if (fd >= 0) {
int proto = get_hid_protocol(fd, dname.c_str());
InputReportHandlerPtr p;
if (proto == INPUT_PROTO_MOUSE) {
p.reset(new MouseReportHandler(fd, dname));
} else if (proto == INPUT_PROTO_KBD) {
p.reset(new KeyboardReportHandler(fd, dname));
} else {
close(fd);
}
if (p) {
if (p->openHandle() >= 0) {
fReportHandlers.push_back(p);
}
}
}
}
startReaderThread();
}
bool InputHandler::hasEvents()
{
std::lock_guard<std::mutex> lock(fEventsMutex);
bool v = !fEvents.empty();
return v;
}
EventList InputHandler::getPendingEvents()
{
std::lock_guard<std::mutex> lock(fEventsMutex);
EventList pending(fEvents);
fEvents.clear();
return pending;
}
void InputHandler::addEvents(const EventList& newEvents)
{
std::lock_guard<std::mutex> lock(fEventsMutex);
fEvents.insert(std::end(fEvents), std::begin(newEvents), std::end(newEvents));
}
void InputHandler::readEvents()
{
while(1) {
EventList newEvents = waitForEvents(fReportHandlers);
addEvents(newEvents);
}
}
void InputHandler::startReaderThread()
{
fEventReaderThread = std::thread(&InputHandler::readEvents,this);
}
}