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() {}
 };