Import Cobalt 12.88774 Change-Id: Id685e20e1b1f8fec13607b39f552fad3cc76ebae
diff --git a/src/starboard/shared/linux/dev_input/dev_input.cc b/src/starboard/shared/linux/dev_input/dev_input.cc index bc3202e..d0bd7fe 100644 --- a/src/starboard/shared/linux/dev_input/dev_input.cc +++ b/src/starboard/shared/linux/dev_input/dev_input.cc
@@ -24,6 +24,8 @@ #include <vector> #include <algorithm> +#include <cmath> +#include <map> #include <string> #include "starboard/configuration.h" @@ -44,15 +46,44 @@ using ::starboard::shared::starboard::Application; typedef int FileDescriptor; -const FileDescriptor kInvalidFd = -1; -const int kKeyboardDeviceId = 1; +const FileDescriptor kInvalidFd = -ENODEV; + +enum InputDeviceIds { + kKeyboardDeviceId = 1, + kGamepadDeviceId, +}; + +enum TouchPadPositionState { + kTouchPadPositionNone = 0, + kTouchPadPositionX = 1, + kTouchPadPositionY = 2, + kTouchPadPositionAll = kTouchPadPositionX | kTouchPadPositionY +}; + +struct InputDeviceInfo { + InputDeviceInfo() : fd(-1), touchpad_position_state(kTouchPadPositionNone) {} + + // File descriptor open for the device + FileDescriptor fd; + // Absolute Axis info. + std::map<int, struct input_absinfo> axis_info; + std::map<int, float> axis_value; + int touchpad_position_state; +}; + +bool IsTouchpadPositionKnown(InputDeviceInfo* device_info) { + return device_info->touchpad_position_state == kTouchPadPositionAll; +} // Private implementation of DevInput. class DevInputImpl : public DevInput { public: explicit DevInputImpl(SbWindow window); + DevInputImpl(SbWindow window, FileDescriptor wake_up_fd); ~DevInputImpl() SB_OVERRIDE; + void InitDevInputImpl(SbWindow window); + Event* PollNextSystemEvent() SB_OVERRIDE; Event* WaitForSystemEventWithTimeout(SbTime time) SB_OVERRIDE; void WakeSystemEventWait() SB_OVERRIDE; @@ -61,13 +92,27 @@ // Converts an input_event into a kSbEventInput Application::Event. The caller // is responsible for deleting the returned event. Event* InputToApplicationEvent(const struct input_event& event, + InputDeviceInfo* device_info, int modifiers); + // Converts an input_event containing a key input into a kSbEventInput + // Application::Event. The caller is responsible for deleting the returned + // event. + Event* KeyInputToApplicationEvent(const struct input_event& event, + int modifiers); + + // Converts an input_event containing an axis event into a kSbEventInput + // Application::Event. The caller is responsible for deleting the returned + // event. + Event* AxisInputToApplicationEvent(const struct input_event& event, + InputDeviceInfo* device_info, + int modifiers); + // The window to attribute /dev/input events to. SbWindow window_; // A set of read-only file descriptor of keyboard input devices. - std::vector<FileDescriptor> keyboard_fds_; + std::vector<InputDeviceInfo> input_devices_; // A file descriptor of the write end of a pipe that can be written to from // any thread to wake up this waiter in a thread-safe manner. @@ -76,6 +121,8 @@ // A file descriptor of the read end of a pipe that this waiter will wait on // to allow it to be signalled safely from other threads. FileDescriptor wakeup_read_fd_; + + FileDescriptor wake_up_fd_; }; // Helper class to manage a file descriptor set. @@ -126,16 +173,12 @@ case KEY_PAGEDOWN: return kSbKeyNext; case KEY_LEFT: - case BTN_DPAD_LEFT: return kSbKeyLeft; case KEY_RIGHT: - case BTN_DPAD_RIGHT: return kSbKeyRight; case KEY_DOWN: - case BTN_DPAD_DOWN: return kSbKeyDown; case KEY_UP: - case BTN_DPAD_UP: return kSbKeyUp; case KEY_ESC: return kSbKeyEscape; @@ -374,6 +417,49 @@ return kSbKeyBrightnessDown; case KEY_BRIGHTNESSUP: return kSbKeyBrightnessUp; + + // Gamepad buttons. + // https://www.kernel.org/doc/Documentation/input/gamepad.txt + case BTN_TL: + return kSbKeyGamepadLeftTrigger; + case BTN_TR: + return kSbKeyGamepadRightTrigger; + case BTN_DPAD_DOWN: + return kSbKeyGamepadDPadDown; + case BTN_DPAD_UP: + return kSbKeyGamepadDPadUp; + case BTN_DPAD_LEFT: + return kSbKeyGamepadDPadLeft; + case BTN_DPAD_RIGHT: + return kSbKeyGamepadDPadRight; + // The mapping for the buttons below can vary from controller to controller. + // TODO: Include button mapping for controllers with different layout. + case BTN_B: + return kSbKeyGamepad1; + case BTN_C: + return kSbKeyGamepad2; + case BTN_A: + return kSbKeyGamepad3; + case BTN_X: + return kSbKeyGamepad4; + case BTN_Y: + return kSbKeyGamepadLeftBumper; + case BTN_Z: + return kSbKeyGamepadRightBumper; + case BTN_TL2: + return kSbKeyGamepad5; + case BTN_TR2: + return kSbKeyGamepad6; + case BTN_SELECT: + return kSbKeyGamepadLeftStick; + case BTN_START: + return kSbKeyGamepadRightStick; + case BTN_MODE: + return kSbKeyGamepadSystem; + case BTN_THUMBL: + return kSbKeyGamepad1; + case BTN_THUMBR: + return kSbKeyGamepad1; } SB_DLOG(WARNING) << "Unknown code: 0x" << std::hex << code; return kSbKeyUnknown; @@ -408,21 +494,129 @@ return !!(bitset.at(bit / 8) & (1 << (bit % 8))); } -// Searches for the keyboard /dev/input devices, opens them and returns the file -// descriptors that report keyboard events. -std::vector<FileDescriptor> GetKeyboardFds() { - const char kDevicePath[] = "/dev/input"; - SbDirectory directory = SbDirectoryOpen(kDevicePath, NULL); - std::vector<FileDescriptor> fds; - if (!SbDirectoryIsValid(directory)) { - SB_DLOG(ERROR) << __FUNCTION__ << ": No /dev/input support, " - << "unable to open: " << kDevicePath; - return fds; +bool IsAxisFlat(float median, const struct input_absinfo& axis_info) { + SB_DCHECK((axis_info.flat * 2) <= (axis_info.maximum - axis_info.minimum)); + return (axis_info.flat != 0) && (axis_info.value > median - axis_info.flat) && + (axis_info.value < median + axis_info.flat); +} + +float GetAxisValue(const struct input_absinfo& axis_info) { + float median = + static_cast<float>(axis_info.maximum + axis_info.minimum) / 2.0f; + if (IsAxisFlat(median, axis_info)) + return 0; + float range = static_cast<float>(axis_info.maximum - axis_info.minimum); + float radius = range / 2.0f; + // Scale the axis value to [-1, 1]. + float axis_value = (static_cast<float>(axis_info.value) - median) / radius; + + if (axis_info.flat != 0) { + // Calculate the flat value scaled to [0, 1]. + float flat = static_cast<float>(axis_info.flat) / range; + + int sign = axis_value < 0.0f ? -1 : 1; + // Rescale the range: + // [-1.0f, -flat] to [-1.0f, 0.0f] and [flat, 1.0f] to [0.0f, 1.0f]. + axis_value = (axis_value - sign * flat) / (1 - flat); + } + return axis_value; +} + +void GetInputDeviceAbsoluteAxisInfo(int axis, + const std::vector<uint8_t>& bits, + InputDeviceInfo* info) { + if (IsBitSet(bits, axis)) { + struct input_absinfo axis_info; + int result = ioctl(info->fd, EVIOCGABS(axis), &axis_info); + if (result < 0) { + return; + } + info->axis_info.insert(std::make_pair(axis, axis_info)); + info->axis_value.insert(std::make_pair(axis, GetAxisValue(axis_info))); + } +} + +void GetInputDeviceInfo(InputDeviceInfo* info) { + std::vector<uint8_t> axis_bits(BytesNeededForBitSet(KEY_MAX)); + int result = + ioctl(info->fd, EVIOCGBIT(EV_ABS, axis_bits.size()), axis_bits.data()); + if (result < 0) { + return; + } + + GetInputDeviceAbsoluteAxisInfo(ABS_X, axis_bits, info); + GetInputDeviceAbsoluteAxisInfo(ABS_Y, axis_bits, info); + GetInputDeviceAbsoluteAxisInfo(ABS_Z, axis_bits, info); + GetInputDeviceAbsoluteAxisInfo(ABS_RZ, axis_bits, info); + GetInputDeviceAbsoluteAxisInfo(ABS_RX, axis_bits, info); + GetInputDeviceAbsoluteAxisInfo(ABS_RY, axis_bits, info); + GetInputDeviceAbsoluteAxisInfo(ABS_HAT0X, axis_bits, info); + GetInputDeviceAbsoluteAxisInfo(ABS_HAT0Y, axis_bits, info); + GetInputDeviceAbsoluteAxisInfo(ABS_MT_POSITION_X, axis_bits, info); + GetInputDeviceAbsoluteAxisInfo(ABS_MT_POSITION_Y, axis_bits, info); + GetInputDeviceAbsoluteAxisInfo(ABS_MT_TRACKING_ID, axis_bits, info); + // TODO: Handle multi-touch using ABS_MT_SLOT. +} + +FileDescriptor OpenDeviceIfKeyboardOrGamepad(const char* path) { + FileDescriptor fd = open(path, O_RDONLY | O_NONBLOCK); + if (fd < 0) { + // Open can fail if the application doesn't have permission to access + // the input device directly. + return kInvalidFd; } std::vector<uint8_t> ev_bits(BytesNeededForBitSet(EV_CNT)); std::vector<uint8_t> key_bits(BytesNeededForBitSet(KEY_MAX)); + int result = ioctl(fd, EVIOCGBIT(0, ev_bits.size()), ev_bits.data()); + if (result < 0) { + close(fd); + return kInvalidFd; + } + + bool has_ev_key = IsBitSet(ev_bits, EV_KEY); + if (!has_ev_key) { + close(fd); + return kInvalidFd; + } + + result = ioctl(fd, EVIOCGBIT(EV_KEY, key_bits.size()), key_bits.data()); + if (result < 0) { + close(fd); + return kInvalidFd; + } + + bool has_key_space = IsBitSet(key_bits, KEY_SPACE); + bool has_gamepad_button = IsBitSet(key_bits, BTN_GAMEPAD); + if (!has_key_space && !has_gamepad_button) { + // If it doesn't have a space key or gamepad button, it may be a mouse. + close(fd); + return kInvalidFd; + } + + result = ioctl(fd, EVIOCGRAB, 1); + if (result != 0) { + SB_DLOG(ERROR) << __FUNCTION__ << ": " + << "Unable to get exclusive access to \"" << path << "\"."; + close(fd); + return kInvalidFd; + } + return fd; +} + +// Searches for the keyboard and game controller /dev/input devices, opens them +// and returns the device info with a file descriptor and absolute axis details. +std::vector<InputDeviceInfo> GetInputDevices() { + const char kDevicePath[] = "/dev/input"; + SbDirectory directory = SbDirectoryOpen(kDevicePath, NULL); + std::vector<InputDeviceInfo> input_devices; + if (!SbDirectoryIsValid(directory)) { + SB_DLOG(ERROR) << __FUNCTION__ << ": No /dev/input support, " + << "unable to open: " << kDevicePath; + return input_devices; + } + while (true) { SbDirectoryEntry entry; if (!SbDirectoryGetNext(directory, &entry)) { @@ -438,60 +632,25 @@ continue; } - FileDescriptor fd = open(path.c_str(), O_RDONLY | O_NONBLOCK); - if (fd < 0) { - SB_DLOG(ERROR) << __FUNCTION__ << ": Unable to open \"" << path << "\"."; + FileDescriptor fd = OpenDeviceIfKeyboardOrGamepad(path.c_str()); + if (fd == kInvalidFd) { continue; } + InputDeviceInfo info; + info.fd = fd; + GetInputDeviceInfo(&info); - int result = ioctl(fd, EVIOCGBIT(0, ev_bits.size()), ev_bits.data()); - - if (result < 0) { - close(fd); - continue; - } - - bool has_ev_key = IsBitSet(ev_bits, EV_KEY); - - if (!has_ev_key) { - close(fd); - continue; - } - - result = ioctl(fd, EVIOCGBIT(EV_KEY, key_bits.size()), key_bits.data()); - - if (result < 0) { - close(fd); - continue; - } - - bool has_key_space = IsBitSet(key_bits, KEY_SPACE); - - if (!has_key_space) { - // If it doesn't have a space key, it may be a mouse - close(fd); - continue; - } - - result = ioctl(fd, EVIOCGRAB, 1); - if (result != 0) { - SB_DLOG(ERROR) << __FUNCTION__ << ": " - << "Unable to get exclusive access to \"" << path << "\"."; - close(fd); - continue; - } - - SB_DCHECK(fd != kInvalidFd); - fds.push_back(fd); + SB_DCHECK(info.fd != kInvalidFd); + input_devices.push_back(info); } - if (fds.empty()) { + if (input_devices.empty()) { SB_DLOG(ERROR) << __FUNCTION__ << ": No /dev/input support. " - << "No keyboards available."; + << "No keyboards or game controllers available."; } SbDirectoryClose(directory); - return fds; + return input_devices; } // Returns whether |key_code|'s bit is set in the bitmap |map|, assuming @@ -513,14 +672,14 @@ return 0; } -// Polls the given keyboard file descriptor for an input_event. If there are no +// Polls the given input file descriptor for an input_event. If there are no // bytes available, assumes that there is no input event to read. If it gets a // partial event, it will assume that it will be completed, and spins until it // receives an entire event. -bool PollKeyboardEvent(FileDescriptor fd, - struct input_event* out_event, - int* out_modifiers) { - if (fd == kInvalidFd) { +bool PollInputEvent(InputDeviceInfo* device_info, + struct input_event* out_event, + int* out_modifiers) { + if (device_info->fd == kInvalidFd) { return false; } @@ -531,7 +690,7 @@ size_t remaining = kEventSize; char* buffer = reinterpret_cast<char*>(out_event); while (remaining > 0) { - int bytes_read = read(fd, buffer, remaining); + int bytes_read = read(device_info->fd, buffer, remaining); if (bytes_read <= 0) { if (errno == EAGAIN || bytes_read == 0) { if (remaining == kEventSize) { @@ -544,18 +703,18 @@ } // Some unexpected type of read error occured. - SB_DLOG(ERROR) << __FUNCTION__ << ": Error reading keyboard: " << errno + SB_DLOG(ERROR) << __FUNCTION__ << ": Error reading input: " << errno << " - " << strerror(errno); return false; } - SB_DCHECK(bytes_read <= remaining) << "bytes_read=" << bytes_read - << ", remaining=" << remaining; + SB_DCHECK(bytes_read <= remaining) + << "bytes_read=" << bytes_read << ", remaining=" << remaining; remaining -= bytes_read; buffer += bytes_read; } - if (out_event->type != EV_KEY) { + if ((out_event->type != EV_KEY) && (out_event->type != EV_ABS)) { return false; } @@ -563,7 +722,7 @@ int modifiers = 0; char map[(KEY_MAX / 8) + 1] = {0}; errno = 0; - int result = ioctl(fd, EVIOCGKEY(sizeof(map)), map); + int result = ioctl(device_info->fd, EVIOCGKEY(sizeof(map)), map); if (result != -1) { modifiers |= GetModifier(KEY_LEFTSHIFT, KEY_RIGHTSHIFT, kSbKeyModifiersShift, map); @@ -593,20 +752,35 @@ // Also in starboard/shared/libevent/socket_waiter_internal.cc // TODO: Consider consolidating. -int SetNonBlocking(int fd) { +int SetNonBlocking(FileDescriptor fd) { int flags = fcntl(fd, F_GETFL, 0); - if (flags == -1) + if (flags == -1) { flags = 0; + } return fcntl(fd, F_SETFL, flags | O_NONBLOCK); } DevInputImpl::DevInputImpl(SbWindow window) : window_(window), - keyboard_fds_(GetKeyboardFds()), + input_devices_(GetInputDevices()), wakeup_write_fd_(kInvalidFd), - wakeup_read_fd_(kInvalidFd) { + wakeup_read_fd_(kInvalidFd), + wake_up_fd_(kInvalidFd) { + InitDevInputImpl(window); +} + +DevInputImpl::DevInputImpl(SbWindow window, FileDescriptor wake_up_fd) + : window_(window), + input_devices_(GetInputDevices()), + wakeup_write_fd_(kInvalidFd), + wakeup_read_fd_(kInvalidFd), + wake_up_fd_(wake_up_fd) { + InitDevInputImpl(window); +} + +void DevInputImpl::InitDevInputImpl(SbWindow window) { // Initialize wakeup pipe. - int fds[2] = {kInvalidFd, kInvalidFd}; + FileDescriptor fds[2] = {kInvalidFd, kInvalidFd}; int result = pipe(fds); SB_DCHECK(result == 0) << "result=" << result; @@ -622,9 +796,8 @@ } DevInputImpl::~DevInputImpl() { - for (std::vector<FileDescriptor>::const_iterator it = keyboard_fds_.begin(); - it != keyboard_fds_.end(); ++it) { - close(*it); + for (const auto& device : input_devices_) { + close(device.fd); } CloseFdSafely(&wakeup_write_fd_); CloseFdSafely(&wakeup_read_fd_); @@ -633,13 +806,12 @@ DevInput::Event* DevInputImpl::PollNextSystemEvent() { struct input_event event; int modifiers = 0; - for (std::vector<FileDescriptor>::const_iterator it = keyboard_fds_.begin(); - it != keyboard_fds_.end(); ++it) { - if (!PollKeyboardEvent(*it, &event, &modifiers)) { + for (auto& device : input_devices_) { + if (!PollInputEvent(&device, &event, &modifiers)) { continue; } - return InputToApplicationEvent(event, modifiers); + return InputToApplicationEvent(event, &device, modifiers); } return NULL; } @@ -651,9 +823,13 @@ } FdSet read_set; - for (std::vector<FileDescriptor>::const_iterator it = keyboard_fds_.begin(); - it != keyboard_fds_.end(); ++it) { - read_set.Set(*it); + if (wake_up_fd_ != kInvalidFd) { + read_set.Set(wake_up_fd_); + } + + for (std::vector<InputDeviceInfo>::const_iterator it = input_devices_.begin(); + it != input_devices_.end(); ++it) { + read_set.Set(it->fd); } read_set.Set(wakeup_read_fd_); @@ -693,13 +869,256 @@ } } -DevInput::Event* DevInputImpl::InputToApplicationEvent( - const struct input_event& event, - int modifiers) { - if (event.type != EV_KEY) { +namespace { + +// Creates a key event for an analog button input. +DevInput::Event* CreateAnalogButtonKeyEvent(SbWindow window, + float axis_value, + float previous_axis_value, + SbKey key, + SbKeyLocation location, + int modifiers, + const struct input_event& event) { + SbInputEventType previous_type = + (std::abs(previous_axis_value) > 0.5 ? kSbInputEventTypePress + : kSbInputEventTypeUnpress); + SbInputEventType type = + (std::abs(axis_value) > 0.5 ? kSbInputEventTypePress + : kSbInputEventTypeUnpress); + if (previous_type == type) { + // Key press/unpress state did not change. return NULL; } + SbInputData* data = new SbInputData(); + SbMemorySet(data, 0, sizeof(*data)); + data->window = window; + data->type = type; + data->device_type = kSbInputDeviceTypeGamepad; + data->device_id = kGamepadDeviceId; + data->key = key; + data->key_location = location; + data->key_modifiers = modifiers; + return new DevInput::Event(kSbEventTypeInput, data, + &Application::DeleteDestructor<SbInputData>); +} + +// Creates a move event with key for a stick input. +DevInput::Event* CreateMoveEventWithKey(SbWindow window, + SbKey key, + SbKeyLocation location, + int modifiers, + const SbInputVector& input_vector) { + SbInputData* data = new SbInputData(); + SbMemorySet(data, 0, sizeof(*data)); + + data->window = window; + data->type = kSbInputEventTypeMove; + data->device_type = kSbInputDeviceTypeGamepad; + data->device_id = kGamepadDeviceId; + + data->key = key; + data->key_location = location; + data->key_modifiers = modifiers; + data->position = input_vector; +#if SB_API_VERSION >= SB_POINTER_INPUT_API_VERSION + data->pressure = NAN; + data->size = {NAN, NAN}; + data->tilt = {NAN, NAN}; +#endif + + return new DevInput::Event(kSbEventTypeInput, data, + &Application::DeleteDestructor<SbInputData>); +} + +DevInput::Event* CreateTouchPadEvent(SbWindow window, + SbInputEventType type, + SbKey key, + SbKeyLocation location, + int modifiers, + const SbInputVector& input_vector) { + SbInputData* data = new SbInputData(); + SbMemorySet(data, 0, sizeof(*data)); + + data->window = window; + data->type = type; + data->device_type = kSbInputDeviceTypeTouchPad; + data->device_id = kGamepadDeviceId; + + data->key = key; + data->key_location = location; + data->key_modifiers = modifiers; + data->position = input_vector; +#if SB_API_VERSION >= SB_POINTER_INPUT_API_VERSION + data->pressure = NAN; + data->size = {NAN, NAN}; + data->tilt = {NAN, NAN}; +#endif + + return new DevInput::Event(kSbEventTypeInput, data, + &Application::DeleteDestructor<SbInputData>); +} + +} // namespace + +DevInput::Event* DevInputImpl::AxisInputToApplicationEvent( + const struct input_event& event, + InputDeviceInfo* device_info, + int modifiers) { + SB_DCHECK(event.type == EV_ABS); + SbKey key = kSbKeyUnknown; + float axis_value = 0; + float previous_axis_value = 0; + auto axis_info_it = device_info->axis_info.find(event.code); + if (axis_info_it != device_info->axis_info.end()) { + struct input_absinfo& axis_info = axis_info_it->second; + axis_info.value = event.value; + axis_value = GetAxisValue(axis_info); + float& stored_axis_value = device_info->axis_value[event.code]; + previous_axis_value = stored_axis_value; + if (previous_axis_value == axis_value) { + // If the value is unchanged, don't do anything. + return NULL; + } + stored_axis_value = axis_value; + } + + SbKeyLocation location = kSbKeyLocationUnspecified; + SbInputVector input_vector; + // The mapping for the axis codes can vary from controller to controller. + // TODO: Include axis mapping for controllers with different layout. + switch (event.code) { + case ABS_X: + // Report up and left as positive values. + input_vector.x = -axis_value; + input_vector.y = -device_info->axis_value[ABS_Y]; + key = kSbKeyGamepadLeftStickLeft; + location = kSbKeyLocationLeft; + return CreateMoveEventWithKey(window_, key, location, modifiers, + input_vector); + case ABS_Y: { + // Report up and left as positive values. + input_vector.x = -device_info->axis_value[ABS_X]; + input_vector.y = -axis_value; + key = kSbKeyGamepadLeftStickUp; + location = kSbKeyLocationLeft; + return CreateMoveEventWithKey(window_, key, location, modifiers, + input_vector); + } + case ABS_Z: + // Report up and left as positive values. + input_vector.x = -axis_value; + input_vector.y = -device_info->axis_value[ABS_RZ]; + key = kSbKeyGamepadRightStickLeft; + location = kSbKeyLocationRight; + return CreateMoveEventWithKey(window_, key, location, modifiers, + input_vector); + case ABS_RZ: + // Report up and left as positive values. + input_vector.x = -device_info->axis_value[ABS_Z]; + input_vector.y = -axis_value; + key = kSbKeyGamepadRightStickUp; + location = kSbKeyLocationRight; + return CreateMoveEventWithKey(window_, key, location, modifiers, + input_vector); + case ABS_RX: { + key = kSbKeyGamepadLeftTrigger; + location = kSbKeyLocationLeft; + // For trigger buttons, the range is [0..1]. + float trigger_value = (axis_value + 1) / 2; + float previous_trigger_value = (previous_axis_value + 1) / 2; + return CreateAnalogButtonKeyEvent(window_, trigger_value, + previous_trigger_value, key, location, + modifiers, event); + } + case ABS_RY: { + key = kSbKeyGamepadRightTrigger; + location = kSbKeyLocationRight; + // For trigger buttons, the range is [0..1]. + float trigger_value = (axis_value + 1) / 2; + float previous_trigger_value = (previous_axis_value + 1) / 2; + return CreateAnalogButtonKeyEvent(window_, trigger_value, + previous_trigger_value, key, location, + modifiers, event); + } + case ABS_HAT0X: { + float axis_value_for_key = + std::abs(axis_value) > 0.5f ? axis_value : previous_axis_value; + key = (axis_value_for_key < 0) ? kSbKeyGamepadDPadLeft + : kSbKeyGamepadDPadRight; + return CreateAnalogButtonKeyEvent(window_, axis_value, + previous_axis_value, key, location, + modifiers, event); + } + case ABS_HAT0Y: { + float axis_value_for_key = + std::abs(axis_value) > 0.5f ? axis_value : previous_axis_value; + key = (axis_value_for_key < 0) ? kSbKeyGamepadDPadUp + : kSbKeyGamepadDPadDown; + return CreateAnalogButtonKeyEvent(window_, axis_value, + previous_axis_value, key, location, + modifiers, event); + } + case ABS_MT_TRACKING_ID: + if (event.value == -1) { + bool touchpad_position_is_known = IsTouchpadPositionKnown(device_info); + device_info->touchpad_position_state = kTouchPadPositionNone; + if (touchpad_position_is_known) { + // Touch point is released, report last known position as unpress. + input_vector.x = (device_info->axis_value[ABS_MT_POSITION_X] + 1 / 2); + input_vector.y = (device_info->axis_value[ABS_MT_POSITION_Y] + 1 / 2); + return CreateTouchPadEvent(window_, kSbInputEventTypeUnpress, key, + location, modifiers, input_vector); + } + } + return NULL; + case ABS_MT_POSITION_X: { + // If all positions were known before this event, then this event is a + // move. + SbInputEventType type = IsTouchpadPositionKnown(device_info) + ? kSbInputEventTypeMove + : kSbInputEventTypePress; + device_info->touchpad_position_state |= kTouchPadPositionX; + if (IsTouchpadPositionKnown(device_info)) { + // For touchpads, the range is [0..1]. + input_vector.x = (axis_value + 1) / 2; + input_vector.y = (device_info->axis_value[ABS_MT_POSITION_Y] + 1) / 2; + return CreateTouchPadEvent(window_, type, key, location, modifiers, + input_vector); + } + // Not all axis positions are known yet. + return NULL; + } + case ABS_MT_POSITION_Y: { + // If all positions were known before this event, then this event is a + // move. + SbInputEventType type = IsTouchpadPositionKnown(device_info) + ? kSbInputEventTypeMove + : kSbInputEventTypePress; + device_info->touchpad_position_state |= kTouchPadPositionY; + if (IsTouchpadPositionKnown(device_info)) { + // For touchpads, the range is [0..1]. + input_vector.x = (device_info->axis_value[ABS_MT_POSITION_X] + 1) / 2; + input_vector.y = (axis_value + 1) / 2; + return CreateTouchPadEvent(window_, type, key, location, modifiers, + input_vector); + } + // Not all axis positions are known yet. + return NULL; + } + default: + // Ignored event codes. + return NULL; + } + + SB_NOTREACHED(); + return NULL; +} + +DevInput::Event* DevInputImpl::KeyInputToApplicationEvent( + const struct input_event& event, + int modifiers) { + SB_DCHECK(event.type == EV_KEY); SB_DCHECK(event.value <= 2); SbInputData* data = new SbInputData(); SbMemorySet(data, 0, sizeof(*data)); @@ -715,6 +1134,21 @@ &Application::DeleteDestructor<SbInputData>); } +DevInput::Event* DevInputImpl::InputToApplicationEvent( + const struct input_event& event, + InputDeviceInfo* device_info, + int modifiers) { + // EV_ABS events are axis values: Sticks, dpad, and touchpad. + // https://www.kernel.org/doc/Documentation/input/event-codes.txt + switch (event.type) { + case EV_ABS: + return AxisInputToApplicationEvent(event, device_info, modifiers); + case EV_KEY: + return KeyInputToApplicationEvent(event, modifiers); + } + return NULL; +} + } // namespace // static @@ -722,6 +1156,11 @@ return new DevInputImpl(window); } +// static +DevInput* DevInput::Create(SbWindow window, int wake_up_fd) { + return new DevInputImpl(window, wake_up_fd); +} + } // namespace dev_input } // namespace shared } // namespace starboard
diff --git a/src/starboard/shared/linux/dev_input/dev_input.h b/src/starboard/shared/linux/dev_input/dev_input.h index aa8577a..11b94c4 100644 --- a/src/starboard/shared/linux/dev_input/dev_input.h +++ b/src/starboard/shared/linux/dev_input/dev_input.h
@@ -48,6 +48,11 @@ // Creates an instance of DevInput for the given window. static DevInput* Create(SbWindow window); + // Creates an instance of DevInput for the given window. + // The wake_up_fd will be used in WaitForSystemEventWithTimeout() to return + // early when input is available on it. + static DevInput* Create(SbWindow window, int wake_up_fd); + protected: DevInput() {} };