| /* |
| * 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 <ddk/protocol/input.h> |
| #include <dirent.h> |
| #include <fcntl.h> |
| #include <hid/hid.h> |
| #include <hid/usages.h> |
| #include <magenta/device/device.h> |
| #include <magenta/syscalls.h> |
| #include <magenta/types.h> |
| #include <mxio/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() |
| { |
| mx_handle_t handle; |
| ssize_t ret = mxio_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 = mxio_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 = mxio_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 = mxio_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 = mxio_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<mx_wait_item_t> items; |
| for (const auto& oneReportHandler : reportHandlers) { |
| mx_wait_item_t item = { oneReportHandler->handle(), DEVICE_SIGNAL_READABLE, 0 }; |
| items.push_back(item); |
| } |
| mx_status_t result = mx_handle_wait_many(&items[0], items.size(), |
| MX_TIME_INFINITE); |
| if (result == NO_ERROR) { |
| 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); |
| } |
| |
| } |