diff options
| author | 2020-01-13 18:08:23 +0000 | |
|---|---|---|
| committer | 2020-01-13 18:08:23 +0000 | |
| commit | 44825c34dfa1c2ae2d4714dd72d57f49b69c4740 (patch) | |
| tree | d20c91316010967eb44ed859bd683e480b3753d0 | |
| parent | e8c770d5d3bfdb06a8561847f08857f718f84d49 (diff) | |
| parent | 97b8eec72bbb08f966487fa60f3e1e7c34b25938 (diff) | |
Merge "Handle different scale and offset for pointers in InputTarget."
| -rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.cpp | 144 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/InputTarget.cpp | 59 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/InputTarget.h | 47 | ||||
| -rw-r--r-- | services/inputflinger/tests/InputDispatcher_test.cpp | 103 |
4 files changed, 305 insertions, 48 deletions
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index a3cb4f8f68..6157d997fd 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -256,6 +256,67 @@ static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) { return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT; } +static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget, + EventEntry* eventEntry, + int32_t inputTargetFlags) { + if (inputTarget.useDefaultPointerInfo()) { + const PointerInfo& pointerInfo = inputTarget.getDefaultPointerInfo(); + return std::make_unique<DispatchEntry>(eventEntry, // increments ref + inputTargetFlags, pointerInfo.xOffset, + pointerInfo.yOffset, inputTarget.globalScaleFactor, + pointerInfo.windowXScale, pointerInfo.windowYScale); + } + + ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION); + const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry); + + PointerCoords pointerCoords[MAX_POINTERS]; + + // Use the first pointer information to normalize all other pointers. This could be any pointer + // as long as all other pointers are normalized to the same value and the final DispatchEntry + // uses the offset and scale for the normalized pointer. + const PointerInfo& firstPointerInfo = + inputTarget.pointerInfos[inputTarget.pointerIds.firstMarkedBit()]; + + // Iterate through all pointers in the event to normalize against the first. + for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount; pointerIndex++) { + const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex]; + uint32_t pointerId = uint32_t(pointerProperties.id); + const PointerInfo& currPointerInfo = inputTarget.pointerInfos[pointerId]; + + // The scale factor is the ratio of the current pointers scale to the normalized scale. + float scaleXDiff = currPointerInfo.windowXScale / firstPointerInfo.windowXScale; + float scaleYDiff = currPointerInfo.windowYScale / firstPointerInfo.windowYScale; + + pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]); + // First apply the current pointers offset to set the window at 0,0 + pointerCoords[pointerIndex].applyOffset(currPointerInfo.xOffset, currPointerInfo.yOffset); + // Next scale the coordinates. + pointerCoords[pointerIndex].scale(1, scaleXDiff, scaleYDiff); + // Lastly, offset the coordinates so they're in the normalized pointer's frame. + pointerCoords[pointerIndex].applyOffset(-firstPointerInfo.xOffset, + -firstPointerInfo.yOffset); + } + + MotionEntry* combinedMotionEntry = + new MotionEntry(motionEntry.sequenceNum, motionEntry.eventTime, motionEntry.deviceId, + motionEntry.source, motionEntry.displayId, motionEntry.policyFlags, + motionEntry.action, motionEntry.actionButton, motionEntry.flags, + motionEntry.metaState, motionEntry.buttonState, + motionEntry.classification, motionEntry.edgeFlags, + motionEntry.xPrecision, motionEntry.yPrecision, + motionEntry.xCursorPosition, motionEntry.yCursorPosition, + motionEntry.downTime, motionEntry.pointerCount, + motionEntry.pointerProperties, pointerCoords, 0 /* xOffset */, + 0 /* yOffset */); + + return std::make_unique<DispatchEntry>(combinedMotionEntry, // increments ref + inputTargetFlags, firstPointerInfo.xOffset, + firstPointerInfo.yOffset, inputTarget.globalScaleFactor, + firstPointerInfo.windowXScale, + firstPointerInfo.windowYScale); +} + // --- InputDispatcherThread --- class InputDispatcher::InputDispatcherThread : public Thread { @@ -1785,23 +1846,34 @@ Unresponsive: void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags, BitSet32 pointerIds, std::vector<InputTarget>& inputTargets) { - sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken()); - if (inputChannel == nullptr) { - ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str()); - return; - } + std::vector<InputTarget>::iterator it = + std::find_if(inputTargets.begin(), inputTargets.end(), + [&windowHandle](const InputTarget& inputTarget) { + return inputTarget.inputChannel->getConnectionToken() == + windowHandle->getToken(); + }); const InputWindowInfo* windowInfo = windowHandle->getInfo(); - InputTarget target; - target.inputChannel = inputChannel; - target.flags = targetFlags; - target.xOffset = -windowInfo->frameLeft; - target.yOffset = -windowInfo->frameTop; - target.globalScaleFactor = windowInfo->globalScaleFactor; - target.windowXScale = windowInfo->windowXScale; - target.windowYScale = windowInfo->windowYScale; - target.pointerIds = pointerIds; - inputTargets.push_back(target); + + if (it == inputTargets.end()) { + InputTarget inputTarget; + sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken()); + if (inputChannel == nullptr) { + ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str()); + return; + } + inputTarget.inputChannel = inputChannel; + inputTarget.flags = targetFlags; + inputTarget.globalScaleFactor = windowInfo->globalScaleFactor; + inputTargets.push_back(inputTarget); + it = inputTargets.end() - 1; + } + + ALOG_ASSERT(it->flags == targetFlags); + ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor); + + it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop, + windowInfo->windowXScale, windowInfo->windowYScale); } void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, @@ -1824,10 +1896,7 @@ void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xO InputTarget target; target.inputChannel = monitor.inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; - target.xOffset = xOffset; - target.yOffset = yOffset; - target.pointerIds.clear(); - target.globalScaleFactor = 1.0f; + target.setDefaultPointerInfo(xOffset, yOffset, 1 /* windowXScale */, 1 /* windowYScale */); inputTargets.push_back(target); } @@ -2054,11 +2123,10 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, } #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, " - "xOffset=%f, yOffset=%f, globalScaleFactor=%f, " - "windowScaleFactor=(%f, %f), pointerIds=0x%x", - connection->getInputChannelName().c_str(), inputTarget.flags, inputTarget.xOffset, - inputTarget.yOffset, inputTarget.globalScaleFactor, inputTarget.windowXScale, - inputTarget.windowYScale, inputTarget.pointerIds.value); + "globalScaleFactor=%f, pointerIds=0x%x %s", + connection->getInputChannelName().c_str(), inputTarget.flags, + inputTarget.globalScaleFactor, inputTarget.pointerIds.value, + inputTarget.getPointerInfoString().c_str()); #endif // Skip this event if the connection status is not normal. @@ -2152,15 +2220,15 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. std::unique_ptr<DispatchEntry> dispatchEntry = - std::make_unique<DispatchEntry>(eventEntry, // increments ref - inputTargetFlags, inputTarget.xOffset, - inputTarget.yOffset, inputTarget.globalScaleFactor, - inputTarget.windowXScale, inputTarget.windowYScale); + createDispatchEntry(inputTarget, eventEntry, inputTargetFlags); + // Use the eventEntry from dispatchEntry since the entry may have changed and can now be a + // different EventEntry than what was passed in. + EventEntry* newEntry = dispatchEntry->eventEntry; // Apply target flags and update the connection's input state. - switch (eventEntry->type) { + switch (newEntry->type) { case EventEntry::Type::KEY: { - const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*eventEntry); + const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*newEntry); dispatchEntry->resolvedAction = keyEntry.action; dispatchEntry->resolvedFlags = keyEntry.flags; @@ -2176,7 +2244,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio } case EventEntry::Type::MOTION: { - const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry); + const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*newEntry); if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE; } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) { @@ -2227,14 +2295,14 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { LOG_ALWAYS_FATAL("%s events should not go to apps", - EventEntry::typeToString(eventEntry->type)); + EventEntry::typeToString(newEntry->type)); break; } } // Remember that we are waiting for this dispatch to complete. if (dispatchEntry->hasForegroundTarget()) { - incrementPendingForegroundDispatches(eventEntry); + incrementPendingForegroundDispatches(newEntry); } // Enqueue the dispatch entry. @@ -2611,15 +2679,9 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( getWindowHandleLocked(connection->inputChannel->getConnectionToken()); if (windowHandle != nullptr) { const InputWindowInfo* windowInfo = windowHandle->getInfo(); - target.xOffset = -windowInfo->frameLeft; - target.yOffset = -windowInfo->frameTop; + target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop, + windowInfo->windowXScale, windowInfo->windowYScale); target.globalScaleFactor = windowInfo->globalScaleFactor; - target.windowXScale = windowInfo->windowXScale; - target.windowYScale = windowInfo->windowYScale; - } else { - target.xOffset = 0; - target.yOffset = 0; - target.globalScaleFactor = 1.0f; } target.inputChannel = connection->inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp index 80fa2cb995..0588374292 100644 --- a/services/inputflinger/dispatcher/InputTarget.cpp +++ b/services/inputflinger/dispatcher/InputTarget.cpp @@ -42,4 +42,63 @@ std::string dispatchModeToString(int32_t dispatchMode) { return StringPrintf("%" PRId32, dispatchMode); } +void InputTarget::addPointers(BitSet32 newPointerIds, float xOffset, float yOffset, + float windowXScale, float windowYScale) { + // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors + // and non splittable windows since we will just use all the pointers from the input event. + if (newPointerIds.isEmpty()) { + setDefaultPointerInfo(xOffset, yOffset, windowXScale, windowYScale); + return; + } + + // Ensure that the new set of pointers doesn't overlap with the current set of pointers. + ALOG_ASSERT((pointerIds & newPointerIds) == 0); + + pointerIds |= newPointerIds; + while (!newPointerIds.isEmpty()) { + int32_t pointerId = newPointerIds.clearFirstMarkedBit(); + pointerInfos[pointerId].xOffset = xOffset; + pointerInfos[pointerId].yOffset = yOffset; + pointerInfos[pointerId].windowXScale = windowXScale; + pointerInfos[pointerId].windowYScale = windowYScale; + } +} + +void InputTarget::setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale, + float windowYScale) { + pointerIds.clear(); + pointerInfos[0].xOffset = xOffset; + pointerInfos[0].yOffset = yOffset; + pointerInfos[0].windowXScale = windowXScale; + pointerInfos[0].windowYScale = windowYScale; +} + +bool InputTarget::useDefaultPointerInfo() const { + return pointerIds.isEmpty(); +} + +const PointerInfo& InputTarget::getDefaultPointerInfo() const { + return pointerInfos[0]; +} + +std::string InputTarget::getPointerInfoString() const { + if (useDefaultPointerInfo()) { + const PointerInfo& pointerInfo = getDefaultPointerInfo(); + return StringPrintf("xOffset=%.1f, yOffset=%.1f windowScaleFactor=(%.1f, %.1f)", + pointerInfo.xOffset, pointerInfo.yOffset, pointerInfo.windowXScale, + pointerInfo.windowYScale); + } + + std::string out; + for (uint32_t i = pointerIds.firstMarkedBit(); i <= pointerIds.lastMarkedBit(); i++) { + if (!pointerIds.hasBit(i)) { + continue; + } + out += StringPrintf("\n pointerId %d: xOffset=%.1f, yOffset=%.1f " + "windowScaleFactor=(%.1f, %.1f)", + i, pointerInfos[i].xOffset, pointerInfos[i].yOffset, + pointerInfos[i].windowXScale, pointerInfos[i].windowYScale); + } + return out; +} } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h index 1ba5effada..499a75fdac 100644 --- a/services/inputflinger/dispatcher/InputTarget.h +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -24,6 +24,22 @@ namespace android::inputdispatcher { /* + * Information about each pointer for an InputTarget. This includes offset and scale so + * all pointers can be normalized to a single offset and scale. + * + * These values are ignored for KeyEvents + */ +struct PointerInfo { + // The x and y offset to add to a MotionEvent as it is delivered. + float xOffset = 0.0f; + float yOffset = 0.0f; + + // Scaling factor to apply to MotionEvent as it is delivered. + float windowXScale = 1.0f; + float windowYScale = 1.0f; +}; + +/* * An input target specifies how an input event is to be dispatched to a particular window * including the window's input channel, control flags, a timeout, and an X / Y offset to * be added to input event coordinates to compensate for the absolute position of the @@ -95,20 +111,37 @@ struct InputTarget { // Flags for the input target. int32_t flags = 0; - // The x and y offset to add to a MotionEvent as it is delivered. - // (ignored for KeyEvents) - float xOffset = 0.0f; - float yOffset = 0.0f; - // Scaling factor to apply to MotionEvent as it is delivered. // (ignored for KeyEvents) float globalScaleFactor = 1.0f; - float windowXScale = 1.0f; - float windowYScale = 1.0f; // The subset of pointer ids to include in motion events dispatched to this input target // if FLAG_SPLIT is set. BitSet32 pointerIds; + // The data is stored by the pointerId. Use the bit position of pointerIds to look up + // PointerInfo per pointerId. + PointerInfo pointerInfos[MAX_POINTERS]; + + void addPointers(BitSet32 pointerIds, float xOffset, float yOffset, float windowXScale, + float windowYScale); + void setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale, + float windowYScale); + + /** + * Returns whether the default pointer information should be used. This will be true when the + * InputTarget doesn't have any bits set in the pointerIds bitset. This can happen for monitors + * and non splittable windows since we want all pointers for the EventEntry to go to this + * target. + */ + bool useDefaultPointerInfo() const; + + /** + * Returns the default PointerInfo object. This should be used when useDefaultPointerInfo is + * true. + */ + const PointerInfo& getDefaultPointerInfo() const; + + std::string getPointerInfoString() const; }; std::string dispatchModeToString(int32_t dispatchMode); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 5ffc89d210..bd8d2a4d39 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -1439,4 +1439,107 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) { consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); } +TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentScale) { + mWindow2->setWindowScale(0.5f, 0.5f); + + // Touch Window 1 + std::vector<PointF> touchedPoints = {PointF{10, 10}}; + std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); + + // Touch Window 2 + int32_t actionPointerDown = + AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + touchedPoints.emplace_back(PointF{150, 150}); + expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); + + motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + + // Consuming from window1 since it's the window that has the InputReceiver + consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); +} + +TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentScale) { + mWindow2->setWindowScale(0.5f, 0.5f); + + // Touch Window 1 + std::vector<PointF> touchedPoints = {PointF{10, 10}}; + std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); + + // Touch Window 2 + int32_t actionPointerDown = + AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + touchedPoints.emplace_back(PointF{150, 150}); + expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); + + motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + + // Consuming from window1 since it's the window that has the InputReceiver + consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); + + // Move both windows + touchedPoints = {{20, 20}, {175, 175}}; + expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), + getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); +} + +TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) { + mWindow1->setWindowScale(0.5f, 0.5f); + + // Touch Window 1 + std::vector<PointF> touchedPoints = {PointF{10, 10}}; + std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); + + // Touch Window 2 + int32_t actionPointerDown = + AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + touchedPoints.emplace_back(PointF{150, 150}); + expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); + + motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + + // Consuming from window1 since it's the window that has the InputReceiver + consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); + + // Move both windows + touchedPoints = {{20, 20}, {175, 175}}; + expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), + getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + + consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); +} + } // namespace android::inputdispatcher |