From ac72bbf4e46d6689070df09a25db2960a9036eb2 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Tue, 11 Aug 2015 18:29:28 -0700 Subject: [PATCH] inputflinger: Initial support for rotary encoders. This change introduces support for rotary encoder input devices. We also define a new input source (namely, AINPUT_SOURCE_ROTARY_ENCODER) and a new axis of input (namely, AXIS_SCROLL), since the rotary encoder motion doesn't necessarily tie to a horizontal or vertical scroll motion. A ROTARY_ENCODER input device class is also introduced, corresponding to the new input source. A new input source can be defined as producing rotary encoder motion events, if its corresponding .idc file contains the following declaration: device.type = rotaryEncoder Bug: 18707397 Change-Id: I8ccd540908311d1ff44fdfeba81b691895413641 Signed-off-by: Prashant Malani --- include/android/input.h | 9 +++ services/inputflinger/EventHub.cpp | 9 +++ services/inputflinger/EventHub.h | 3 + services/inputflinger/InputReader.cpp | 91 +++++++++++++++++++++++++++ services/inputflinger/InputReader.h | 20 ++++++ 5 files changed, 132 insertions(+) diff --git a/include/android/input.h b/include/android/input.h index 5ab4e2926..5eeb7fc89 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -643,6 +643,13 @@ enum { * against the surface. */ AMOTION_EVENT_AXIS_TILT = 25, + /** + * Axis constant: Generic scroll axis of a motion event. + * + * - This is used for scroll axis motion events that can't be classified as strictly + * vertical or horizontal. The movement of a rotating scroller is an example of this. + */ + AMOTION_EVENT_AXIS_SCROLL = 26, /** * Axis constant: Generic 1 axis of a motion event. * The interpretation of a generic axis is device-specific. @@ -817,6 +824,8 @@ enum { AINPUT_SOURCE_TOUCH_NAVIGATION = 0x00200000 | AINPUT_SOURCE_CLASS_NONE, /** joystick */ AINPUT_SOURCE_JOYSTICK = 0x01000000 | AINPUT_SOURCE_CLASS_JOYSTICK, + /** rotary encoder */ + AINPUT_SOURCE_ROTARY_ENCODER = 0x00400000 | AINPUT_SOURCE_CLASS_NONE, /** any */ AINPUT_SOURCE_ANY = 0xffffff00, diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/EventHub.cpp index 6b60c7cf7..98cfe2b54 100644 --- a/services/inputflinger/EventHub.cpp +++ b/services/inputflinger/EventHub.cpp @@ -1177,6 +1177,15 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { device->classes |= INPUT_DEVICE_CLASS_CURSOR; } + // See if this is a rotary encoder type device. + String8 deviceType = String8(); + if (device->configuration && + device->configuration->tryGetProperty(String8("device.type"), deviceType)) { + if (!deviceType.compare(String8("rotaryEncoder"))) { + device->classes |= INPUT_DEVICE_CLASS_ROTARY_ENCODER; + } + } + // See if this is a touch pad. // Is this a new modern multi-touch driver? if (test_bit(ABS_MT_POSITION_X, device->absBitmask) diff --git a/services/inputflinger/EventHub.h b/services/inputflinger/EventHub.h index 3ec49105c..7c3e11ec5 100644 --- a/services/inputflinger/EventHub.h +++ b/services/inputflinger/EventHub.h @@ -137,6 +137,9 @@ enum { /* The input device is an external stylus (has data we want to fuse with touch data). */ INPUT_DEVICE_CLASS_EXTERNAL_STYLUS = 0x00000800, + /* The input device has a rotary encoder */ + INPUT_DEVICE_CLASS_ROTARY_ENCODER = 0x00001000, + /* The input device is virtual (not a real device, not part of UI configuration). */ INPUT_DEVICE_CLASS_VIRTUAL = 0x40000000, diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index 36095bf1e..d7329d939 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -450,6 +450,11 @@ InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controlle device->addMapper(new SwitchInputMapper(device)); } + // Scroll wheel-like devices. + if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) { + device->addMapper(new RotaryEncoderInputMapper(device)); + } + // Vibrator-like devices. if (classes & INPUT_DEVICE_CLASS_VIBRATOR) { device->addMapper(new VibratorInputMapper(device)); @@ -2718,6 +2723,92 @@ void CursorInputMapper::fadePointer() { } } +// --- RotaryEncoderInputMapper --- + +RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDevice* device) : + InputMapper(device) { + mSource = AINPUT_SOURCE_ROTARY_ENCODER; +} + +RotaryEncoderInputMapper::~RotaryEncoderInputMapper() { +} + +uint32_t RotaryEncoderInputMapper::getSources() { + return mSource; +} + +void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) { + info->addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f); + } +} + +void RotaryEncoderInputMapper::dump(String8& dump) { + dump.append(INDENT2 "Rotary Encoder Input Mapper:\n"); + dump.appendFormat(INDENT3 "HaveWheel: %s\n", + toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel())); +} + +void RotaryEncoderInputMapper::configure(nsecs_t when, + const InputReaderConfiguration* config, uint32_t changes) { + InputMapper::configure(when, config, changes); + if (!changes) { + mRotaryEncoderScrollAccumulator.configure(getDevice()); + } +} + +void RotaryEncoderInputMapper::reset(nsecs_t when) { + mRotaryEncoderScrollAccumulator.reset(getDevice()); + + InputMapper::reset(when); +} + +void RotaryEncoderInputMapper::process(const RawEvent* rawEvent) { + mRotaryEncoderScrollAccumulator.process(rawEvent); + + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + sync(rawEvent->when); + } +} + +void RotaryEncoderInputMapper::sync(nsecs_t when) { + PointerCoords pointerCoords; + pointerCoords.clear(); + + PointerProperties pointerProperties; + pointerProperties.clear(); + pointerProperties.id = 0; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; + + float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel(); + bool scrolled = scroll != 0; + + // This is not a pointer, so it's not associated with a display. + int32_t displayId = ADISPLAY_ID_NONE; + + // Moving the rotary encoder should wake the device (if specified). + uint32_t policyFlags = 0; + if (scrolled && getDevice()->isExternal()) { + policyFlags |= POLICY_FLAG_WAKE; + } + + // Send motion event. + if (scrolled) { + int32_t metaState = mContext->getGlobalMetaState(); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll); + + NotifyMotionArgs scrollArgs(when, getDeviceId(), mSource, policyFlags, + AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, 0, + AMOTION_EVENT_EDGE_FLAG_NONE, + displayId, 1, &pointerProperties, &pointerCoords, + 0, 0, 0); + getListener()->notifyMotion(&scrollArgs); + } + + mRotaryEncoderScrollAccumulator.finishSync(); +} // --- TouchInputMapper --- diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h index 7cb4680ce..58db3dca6 100644 --- a/services/inputflinger/InputReader.h +++ b/services/inputflinger/InputReader.h @@ -1232,6 +1232,26 @@ private: }; +class RotaryEncoderInputMapper : public InputMapper { +public: + RotaryEncoderInputMapper(InputDevice* device); + virtual ~RotaryEncoderInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(String8& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + +private: + CursorScrollAccumulator mRotaryEncoderScrollAccumulator; + + int32_t mSource; + + void sync(nsecs_t when); +}; + class TouchInputMapper : public InputMapper { public: TouchInputMapper(InputDevice* device);