Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 43b5b06c authored by Kevin Schoedel's avatar Kevin Schoedel Committed by Alex Vakulenko
Browse files

Add synthetic back button to virtual touchpad/stylus.

Also add a bit more error checking and documentation.

Test: added to VirtualTouchpad_test.cpp
Bug: 34673438
Change-Id: I3851a2ad79c5338cdd1db0c7d460aecfff082cc3
parent 6890d95e
Loading
Loading
Loading
Loading
+44 −4
Original line number Diff line number Diff line
#include "VirtualTouchpad.h"

#include <android/input.h>
#include <cutils/log.h>
#include <inttypes.h>
#include <linux/input.h>

// References:
//  [0] Multi-touch (MT) Protocol,
//      https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt

namespace android {
namespace dvr {

namespace {

// Virtual evdev device properties.
// Virtual evdev device properties. The name is arbitrary, but Android can
// use it to look up device configuration, so it must be unique. Vendor and
// product values must be 0 to indicate an internal device and prevent a
// similar lookup that could conflict with a physical device.
static const char* const kDeviceName = "vr window manager virtual touchpad";
static constexpr int16_t kDeviceBusType = BUS_VIRTUAL;
static constexpr int16_t kDeviceVendor = 0x18D1;   // Google USB vendor ID.
static constexpr int16_t kDeviceProduct = 0x5652;  // 'VR'
static constexpr int16_t kDeviceVendor = 0;
static constexpr int16_t kDeviceProduct = 0;
static constexpr int16_t kDeviceVersion = 0x0001;

static constexpr int32_t kWidth = 0x10000;
static constexpr int32_t kHeight = 0x10000;
static constexpr int32_t kSlots = 2;
@@ -32,18 +41,24 @@ int VirtualTouchpad::Initialize() {
  injector_->ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1);
  injector_->ConfigureAbsSlots(kSlots);
  injector_->ConfigureKey(BTN_TOUCH);
  injector_->ConfigureKey(BTN_BACK);
  injector_->ConfigureEnd();
  return injector_->GetError();
}

int VirtualTouchpad::Touch(float x, float y, float pressure) {
  int error = 0;
  if ((x < 0.0f) || (x >= 1.0f) || (y < 0.0f) || (y >= 1.0f)) {
    return EINVAL;
  }
  int32_t device_x = x * kWidth;
  int32_t device_y = y * kHeight;
  touches_ = ((touches_ & 1) << 1) | (pressure > 0);
  ALOGV("(%f,%f) %f -> (%" PRId32 ",%" PRId32 ") %d",
        x, y, pressure, device_x, device_y, touches_);

  if (!injector_) {
    return EvdevInjector::ERROR_SEQUENCING;
  }
  injector_->ResetError();
  switch (touches_) {
    case 0b00:  // Hover continues.
@@ -76,5 +91,30 @@ int VirtualTouchpad::Touch(float x, float y, float pressure) {
  return injector_->GetError();
}

int VirtualTouchpad::ButtonState(int buttons) {
  const int changes = last_motion_event_buttons_ ^ buttons;
  if (!changes) {
    return 0;
  }
  if (buttons & ~AMOTION_EVENT_BUTTON_BACK) {
    return ENOTSUP;
  }
  ALOGV("change %X from %X to %X", changes, last_motion_event_buttons_,
        buttons);

  if (!injector_) {
    return EvdevInjector::ERROR_SEQUENCING;
  }
  injector_->ResetError();
  if (changes & AMOTION_EVENT_BUTTON_BACK) {
    injector_->SendKey(BTN_BACK,
                       (buttons & AMOTION_EVENT_BUTTON_BACK)
                           ? EvdevInjector::KEY_PRESS
                           : EvdevInjector::KEY_RELEASE);
  }
  last_motion_event_buttons_ = buttons;
  return injector_->GetError();
}

}  // namespace dvr
}  // namespace android
+36 −3
Original line number Diff line number Diff line
@@ -10,12 +10,39 @@ namespace dvr {

class EvdevInjector;

// Provides a virtual touchpad for injecting events into the input system.
//
class VirtualTouchpad {
 public:
  VirtualTouchpad() {}
  ~VirtualTouchpad() {}

  // |Intialize()| must be called once on a VirtualTouchpad before
  // and other public method. Returns zero on success.
  int Initialize();

  // Generate a simulated touch event.
  //
  // @param x Horizontal touch position.
  // @param y Vertical touch position.
  //            Values must be in the range [0.0, 1.0).
  // @param pressure Touch pressure.
  //            Positive values represent contact; use 1.0f if contact
  //            is binary. Use 0.0f for no contact.
  // @returns Zero on success.
  //
  int Touch(float x, float y, float pressure);

  // Generate a simulated touchpad button state.
  //
  // @param buttons A union of MotionEvent BUTTON_* values.
  // @returns Zero on success.
  //
  // Currently only BUTTON_BACK is supported, as the implementation
  // restricts itself to operations actually required by VrWindowManager.
  //
  int ButtonState(int buttons);

 protected:
  // Must be called only between construction and Initialize().
  inline void SetEvdevInjectorForTesting(EvdevInjector* injector) {
@@ -23,17 +50,23 @@ class VirtualTouchpad {
  }

 private:
  // Except for testing, the |EvdevInjector| used to inject evdev events.
  std::unique_ptr<EvdevInjector> owned_injector_;

  // Active pointer to |owned_injector_| or to a testing injector.
  EvdevInjector* injector_ = nullptr;
  std::unique_ptr<EvdevInjector> owned_injector_;

  // Previous (x,y) position to suppress redundant events.
  // Previous (x, y) position in device space, to suppress redundant events.
  int32_t last_device_x_ = INT32_MIN;
  int32_t last_device_y_ = INT32_MIN;

  // Records current touch state in bit 0 and previous state in bit 1.
  // Records current touch state (0=up 1=down) in bit 0, and previous state
  // in bit 1, to track transitions.
  int touches_ = 0;

  // Previous injected button state, to detect changes.
  int32_t last_motion_event_buttons_ = 0;

  VirtualTouchpad(const VirtualTouchpad&) = delete;
  void operator=(const VirtualTouchpad&) = delete;
};
+6 −1
Original line number Diff line number Diff line
@@ -13,11 +13,16 @@ int VirtualTouchpadService::Initialize() {
}

binder::Status VirtualTouchpadService::touch(float x, float y, float pressure) {
  // Permissions check added and removed here :^)
  const int error = touchpad_.Touch(x, y, pressure);
  return error ? binder::Status::fromServiceSpecificError(error)
               : binder::Status::ok();
}

binder::Status VirtualTouchpadService::buttonState(int buttons) {
  const int error = touchpad_.ButtonState(buttons);
  return error ? binder::Status::fromServiceSpecificError(error)
               : binder::Status::ok();
}

}  // namespace dvr
}  // namespace android
+4 −0
Original line number Diff line number Diff line
@@ -8,6 +8,9 @@
namespace android {
namespace dvr {

// VirtualTouchpadService implements the service side of
// the Binder interface defined in VirtualTouchpadService.aidl.
//
class VirtualTouchpadService : public BnVirtualTouchpadService {
 public:
  VirtualTouchpadService(VirtualTouchpad& touchpad)
@@ -22,6 +25,7 @@ class VirtualTouchpadService : public BnVirtualTouchpadService {
 protected:
  // Implements IVirtualTouchpadService.
  ::android::binder::Status touch(float x, float y, float pressure) override;
  ::android::binder::Status buttonState(int buttons) override;

 private:
  VirtualTouchpad& touchpad_;
+7 −0
Original line number Diff line number Diff line
@@ -13,4 +13,11 @@ interface VirtualTouchpadService
   * Position values in the range [0.0, 1.0) map to the screen.
   */
  void touch(float x, float y, float pressure);

  /**
   * Generate a simulated touchpad button state event.
   *
   * @param buttons A union of MotionEvent BUTTON_* values.
   */
  void buttonState(int buttons);
}
Loading