|  | /* | 
|  | * Copyright (C) 2019 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | // clang-format off | 
|  | #include "../Macros.h" | 
|  | // clang-format on | 
|  |  | 
|  | #include "RotaryEncoderInputMapper.h" | 
|  |  | 
|  | #include "CursorScrollAccumulator.h" | 
|  |  | 
|  | namespace android { | 
|  |  | 
|  | RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDeviceContext& deviceContext) | 
|  | : InputMapper(deviceContext), mOrientation(DISPLAY_ORIENTATION_0) { | 
|  | mSource = AINPUT_SOURCE_ROTARY_ENCODER; | 
|  | } | 
|  |  | 
|  | RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {} | 
|  |  | 
|  | uint32_t RotaryEncoderInputMapper::getSources() { | 
|  | return mSource; | 
|  | } | 
|  |  | 
|  | void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo* info) { | 
|  | InputMapper::populateDeviceInfo(info); | 
|  |  | 
|  | if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) { | 
|  | float res = 0.0f; | 
|  | if (!getDeviceContext().getConfiguration().tryGetProperty(String8("device.res"), res)) { | 
|  | ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n"); | 
|  | } | 
|  | if (!getDeviceContext().getConfiguration().tryGetProperty(String8("device.scalingFactor"), | 
|  | mScalingFactor)) { | 
|  | ALOGW("Rotary Encoder device configuration file didn't specify scaling factor," | 
|  | "default to 1.0!\n"); | 
|  | mScalingFactor = 1.0f; | 
|  | } | 
|  | info->addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, | 
|  | res * mScalingFactor); | 
|  | } | 
|  | } | 
|  |  | 
|  | void RotaryEncoderInputMapper::dump(std::string& dump) { | 
|  | dump += INDENT2 "Rotary Encoder Input Mapper:\n"; | 
|  | dump += StringPrintf(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(getDeviceContext()); | 
|  | } | 
|  | if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { | 
|  | std::optional<DisplayViewport> internalViewport = | 
|  | config->getDisplayViewportByType(ViewportType::INTERNAL); | 
|  | if (internalViewport) { | 
|  | mOrientation = internalViewport->orientation; | 
|  | } else { | 
|  | mOrientation = DISPLAY_ORIENTATION_0; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void RotaryEncoderInputMapper::reset(nsecs_t when) { | 
|  | mRotaryEncoderScrollAccumulator.reset(getDeviceContext()); | 
|  |  | 
|  | InputMapper::reset(when); | 
|  | } | 
|  |  | 
|  | void RotaryEncoderInputMapper::process(const RawEvent* rawEvent) { | 
|  | mRotaryEncoderScrollAccumulator.process(rawEvent); | 
|  |  | 
|  | if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { | 
|  | sync(rawEvent->when, rawEvent->readTime); | 
|  | } | 
|  | } | 
|  |  | 
|  | void RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readTime) { | 
|  | 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 && getDeviceContext().isExternal()) { | 
|  | policyFlags |= POLICY_FLAG_WAKE; | 
|  | } | 
|  |  | 
|  | if (mOrientation == DISPLAY_ORIENTATION_180) { | 
|  | scroll = -scroll; | 
|  | } | 
|  |  | 
|  | // Send motion event. | 
|  | if (scrolled) { | 
|  | int32_t metaState = getContext()->getGlobalMetaState(); | 
|  | pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor); | 
|  |  | 
|  | NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(), | 
|  | mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, | 
|  | 0, metaState, /* buttonState */ 0, MotionClassification::NONE, | 
|  | AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, | 
|  | &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, | 
|  | AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); | 
|  | getListener()->notifyMotion(&scrollArgs); | 
|  | } | 
|  |  | 
|  | mRotaryEncoderScrollAccumulator.finishSync(); | 
|  | } | 
|  |  | 
|  | } // namespace android |