#include <errno.h>
#include <utils/Log.h>
+#include <algorithm>
+
// Headers from libdvr
#include <dvr/dvr_buffer.h>
#include <dvr/dvr_buffer_queue.h>
ALOGI("dvrGetApi: api=%p struct_size=%zu version=%d", api, struct_size,
version);
if (version == 1) {
- if (struct_size != sizeof(DvrApi_v1)) {
- ALOGE("dvrGetApi: Size mismatch: expected %zu; actual %zu",
- sizeof(DvrApi_v1), struct_size);
- return -EINVAL;
- }
+ // New entry points are added at the end. If the caller's struct and
+ // this library have different sizes, we define the entry points in common.
+ // The caller is expected to handle unset entry points if necessary.
+ size_t clamped_struct_size = std::min(struct_size, sizeof(DvrApi_v1));
DvrApi_v1* dvr_api = static_cast<DvrApi_v1*>(api);
// Defines an API entry for V1 (no version suffix).
-#define DVR_V1_API_ENTRY(name) dvr_api->name = dvr##name
+#define DVR_V1_API_ENTRY(name) \
+ do { \
+ if ((offsetof(DvrApi_v1, name) + sizeof(dvr_api->name)) <= \
+ clamped_struct_size) { \
+ dvr_api->name = dvr##name; \
+ } \
+ } while (0)
#include "include/dvr/dvr_api_entries.h"
float pressure);
typedef int (*DvrVirtualTouchpadButtonStatePtr)(DvrVirtualTouchpad* client,
int touchpad, int buttons);
+typedef int (*DvrVirtualTouchpadScrollPtr)(DvrVirtualTouchpad* client,
+ int touchpad, float x, float y);
// dvr_hardware_composer_client.h
typedef struct DvrHwcClient DvrHwcClient;
DVR_V1_API_ENTRY(HwcFrameGetLayerVisibleRegion);
DVR_V1_API_ENTRY(HwcFrameGetLayerNumDamagedRegions);
DVR_V1_API_ENTRY(HwcFrameGetLayerDamagedRegion);
+
+// New entries added at the end to allow the DVR platform library API
+// to be updated before updating VrCore.
+
+// Virtual touchpad client
+DVR_V1_API_ENTRY(VirtualTouchpadScroll);
return FromC(client)->ButtonState(touchpad, buttons);
}
+int dvrVirtualTouchpadScroll(DvrVirtualTouchpad* client, int touchpad, float x,
+ float y) {
+ return FromC(client)->Scroll(touchpad, x, y);
+}
+
#ifdef __cplusplus
} // extern "C"
#endif
return ConfigureAbs(ABS_MT_SLOT, 0, slots, 0, 0);
}
+int EvdevInjector::ConfigureRel(uint16_t rel_type) {
+ ALOGV("ConfigureRel 0x%" PRIX16 "", rel_type);
+ if (rel_type < 0 || rel_type >= REL_CNT) {
+ ALOGE("EV_REL type 0x%" PRIX16 " out of range [0,0x%X)", rel_type, REL_CNT);
+ return Error(ERROR_REL_RANGE);
+ }
+ if (const int status = RequireState(State::CONFIGURING)) {
+ return status;
+ }
+ if (const int status = EnableEventType(EV_REL)) {
+ return status;
+ }
+ if (const int status = uinput_->IoctlSetInt(UI_SET_RELBIT, rel_type)) {
+ ALOGE("failed to enable EV_REL 0x%" PRIX16 "", rel_type);
+ return Error(status);
+ }
+ return 0;
+}
+
int EvdevInjector::ConfigureEnd() {
ALOGV("ConfigureEnd:");
ALOGV(" name=\"%s\"", uidev_.name);
return Send(EV_ABS, code, value);
}
+int EvdevInjector::SendRel(uint16_t code, int32_t value) {
+ return Send(EV_REL, code, value);
+}
+
int EvdevInjector::SendMultiTouchSlot(int32_t slot) {
if (latest_slot_ != slot) {
if (const int status = SendAbs(ABS_MT_SLOT, slot)) {
ERROR_KEY_RANGE = -3, // |KEY_*|/|BTN_*| code out of range.
ERROR_ABS_RANGE = -4, // |ABS_*| code out of range.
ERROR_SEQUENCING = -5, // Configure/Send out of order.
+ ERROR_REL_RANGE = -6, // |REL_*| code out of range.
};
// Key event |value| is not defined in <linux/input.h>.
// Configure multitouch coordinate range.
int ConfigureMultiTouchXY(int32_t x0, int32_t y0, int32_t x1, int32_t y1);
+ // Configure a relative axis.
+ // @param rel_type One of the |REL_*| constants from <linux/input.h>.
+ int ConfigureRel(uint16_t rel_type);
+
// Complete configuration and create the input device.
int ConfigureEnd();
int SendSynReport();
int SendKey(uint16_t code, int32_t value);
int SendAbs(uint16_t code, int32_t value);
+ int SendRel(uint16_t code, int32_t value);
int SendMultiTouchSlot(int32_t slot);
int SendMultiTouchXY(int32_t slot, int32_t id, int32_t x, int32_t y);
int SendMultiTouchLift(int32_t slot);
return service_->buttonState(touchpad, buttons).transactionError();
}
+ status_t Scroll(int touchpad, float x, float y) override {
+ if (service_ == nullptr) {
+ return NO_INIT;
+ }
+ return service_->scroll(touchpad, x, y).transactionError();
+ }
+
void dumpInternal(String8& result) override {
result.append("[virtual touchpad]\n");
result.appendFormat("connected = %s\n\n",
static constexpr int32_t kHeight = 0x10000;
static constexpr int32_t kSlots = 2;
+int32_t scale_relative_scroll(float x) {
+ // Guilty with an explanation, your honor.
+ // Ideally we should be able to communicate the full incoming precision
+ // to InputFlinger, through the evdev int32_t value, by scaling by a
+ // large factor, i.e. 2²³ for IEEE single precision floating point.
+ // However, although InputFlinger has |wheelVelocityControlParameters|,
+ // those parameters are currently hard coded, with a scale factor of 1.0.
+ // The observed evdev value for a physical mouse scroll wheel is usually
+ // ±1, with higher values up to ±4 for a very fast spin. So we imitate
+ // that. If the incoming value is not actually 0, the resulting magnitude
+ // should be at least 1, so that small movements are not lost.
+ // Adding IDC configurability of |VelocityControlParameters| may be
+ // desirable in the future.
+ return copysignf(ceilf(fabs(4.0f * x)), x);
+}
+
} // anonymous namespace
std::unique_ptr<VirtualTouchpad> VirtualTouchpadEvdev::Create() {
return touchpad.injector->GetError();
}
+int VirtualTouchpadEvdev::Scroll(int touchpad_id, float x, float y) {
+ if (touchpad_id < 0 || touchpad_id >= kTouchpads) {
+ return EINVAL;
+ }
+ if ((x < -1.0f) || (x > 1.0f) || (y < -1.0f) || (y > 1.0f)) {
+ return EINVAL;
+ }
+ Touchpad& touchpad = touchpad_[touchpad_id];
+ if (!touchpad.injector) {
+ return EvdevInjector::ERROR_SEQUENCING;
+ }
+ touchpad.injector->ResetError();
+ const int32_t scaled_x = scale_relative_scroll(x);
+ const int32_t scaled_y = scale_relative_scroll(y);
+ if (scaled_x) {
+ touchpad.injector->SendRel(REL_HWHEEL, scaled_x);
+ }
+ if (scaled_y) {
+ touchpad.injector->SendRel(REL_WHEEL, scaled_y);
+ }
+ if (scaled_x || scaled_y) {
+ touchpad.injector->SendSynReport();
+ }
+ return touchpad.injector->GetError();
+}
+
void VirtualTouchpadEvdev::dumpInternal(String8& result) {
for (int i = 0; i < kTouchpads; ++i) {
const auto& touchpad = touchpad_[i];
status_t Detach() override;
status_t Touch(int touchpad, float x, float y, float pressure) override;
status_t ButtonState(int touchpad, int buttons) override;
+ status_t Scroll(int touchpad, float x, float y) override;
void dumpInternal(String8& result) override;
protected:
return binder::Status::ok();
}
+binder::Status VirtualTouchpadService::scroll(int touchpad, float x, float y) {
+ if (!CheckPermissions()) {
+ return binder::Status::fromStatusT(PERMISSION_DENIED);
+ }
+ if (const status_t error = touchpad_->Scroll(touchpad, x, y)) {
+ return binder::Status::fromStatusT(error);
+ }
+ return binder::Status::ok();
+}
+
status_t VirtualTouchpadService::dump(
int fd, const Vector<String16>& args[[gnu::unused]]) {
String8 result;
binder::Status detach() override;
binder::Status touch(int touchpad, float x, float y, float pressure) override;
binder::Status buttonState(int touchpad, int buttons) override;
+ binder::Status scroll(int touchpad, float x, float y) override;
// Implements BBinder::dump().
status_t dump(int fd, const Vector<String16>& args) override;
* @param buttons A union of MotionEvent BUTTON_* values.
*/
void buttonState(int touchpad, int buttons) = 3;
+
+ /**
+ * Generate a simulated scroll event.
+ *
+ * @param touchpad Selects touchpad.
+ * @param x Horizontal scroll increment.
+ * @param y Vertical scroll increment.
+ *
+ * Scroll values are in the range [-1.0, 1.0].
+ */
+ void scroll(int touchpad, float x, float y) = 4;
}
//
virtual status_t ButtonState(int touchpad, int buttons) = 0;
+ // Generate a simulated scroll event.
+ //
+ // @param touchpad Touchpad selector index.
+ // @param x Horizontal scroll increment.
+ // @param y Vertical scroll increment.
+ // Values must be in the range [-1.0, 1.0].
+ // @returns OK on success.
+ //
+ virtual status_t Scroll(int touchpad, float x, float y) = 0;
+
// Report state for 'dumpsys'.
virtual void dumpInternal(String8& result) = 0;
status_t Detach() override;
status_t Touch(int touchpad, float x, float y, float pressure) override;
status_t ButtonState(int touchpad, int buttons) override;
+ status_t Scroll(int touchpad, float x, float y) override;
void dumpInternal(String8& result) override;
protected:
int dvrVirtualTouchpadButtonState(DvrVirtualTouchpad* client, int touchpad,
int buttons);
+// Generate a simulated scroll event.
+//
+// @param client Pointer to the virtual touchpad client.
+// @param touchpad Selects touchpad.
+// @param x Horizontal scroll increment.
+// @param y Vertical scroll increment.
+// @return Zero on success, status_t-style error code on failure.
+//
+int dvrVirtualTouchpadScroll(DvrVirtualTouchpad* client, int touchpad, float x,
+ float y);
+
#ifdef __cplusplus
} // extern "C"
#endif