225 lines
9.4 KiB
C++
225 lines
9.4 KiB
C++
|
|
/*
|
||
|
|
* 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.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include "../Macros.h"
|
||
|
|
|
||
|
|
#include <android/sysprop/InputProperties.sysprop.h>
|
||
|
|
#include "MultiTouchInputMapper.h"
|
||
|
|
|
||
|
|
namespace android {
|
||
|
|
|
||
|
|
// --- Constants ---
|
||
|
|
|
||
|
|
// Maximum number of slots supported when using the slot-based Multitouch Protocol B.
|
||
|
|
static constexpr size_t MAX_SLOTS = 32;
|
||
|
|
|
||
|
|
// --- MultiTouchInputMapper ---
|
||
|
|
|
||
|
|
MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext,
|
||
|
|
const InputReaderConfiguration& readerConfig)
|
||
|
|
: TouchInputMapper(deviceContext, readerConfig) {}
|
||
|
|
|
||
|
|
MultiTouchInputMapper::~MultiTouchInputMapper() {}
|
||
|
|
|
||
|
|
std::list<NotifyArgs> MultiTouchInputMapper::reset(nsecs_t when) {
|
||
|
|
// The evdev multi-touch protocol does not allow userspace applications to query the initial or
|
||
|
|
// current state of the pointers at any time. This means if we clear our accumulated state when
|
||
|
|
// resetting the input mapper, there's no way to rebuild the full initial state of the pointers.
|
||
|
|
// We can only wait for updates to all the pointers and axes. Rather than clearing the state and
|
||
|
|
// rebuilding the state from scratch, we work around this kernel API limitation by never
|
||
|
|
// fully clearing any state specific to the multi-touch protocol.
|
||
|
|
return TouchInputMapper::reset(when);
|
||
|
|
}
|
||
|
|
|
||
|
|
std::list<NotifyArgs> MultiTouchInputMapper::process(const RawEvent* rawEvent) {
|
||
|
|
std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent);
|
||
|
|
|
||
|
|
mMultiTouchMotionAccumulator.process(rawEvent);
|
||
|
|
return out;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::optional<int32_t> MultiTouchInputMapper::getActiveBitId(
|
||
|
|
const MultiTouchMotionAccumulator::Slot& inSlot) {
|
||
|
|
if (mHavePointerIds) {
|
||
|
|
int32_t trackingId = inSlot.getTrackingId();
|
||
|
|
for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
|
||
|
|
int32_t n = idBits.clearFirstMarkedBit();
|
||
|
|
if (mPointerTrackingIdMap[n] == trackingId) {
|
||
|
|
return std::make_optional(n);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return std::nullopt;
|
||
|
|
}
|
||
|
|
|
||
|
|
void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
|
||
|
|
size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
|
||
|
|
size_t outCount = 0;
|
||
|
|
BitSet32 newPointerIdBits;
|
||
|
|
mHavePointerIds = true;
|
||
|
|
|
||
|
|
for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
|
||
|
|
const MultiTouchMotionAccumulator::Slot& inSlot =
|
||
|
|
mMultiTouchMotionAccumulator.getSlot(inIndex);
|
||
|
|
if (!inSlot.isInUse()) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (inSlot.getToolType() == ToolType::PALM) {
|
||
|
|
std::optional<int32_t> id = getActiveBitId(inSlot);
|
||
|
|
if (id) {
|
||
|
|
outState->rawPointerData.canceledIdBits.markBit(id.value());
|
||
|
|
}
|
||
|
|
if (DEBUG_POINTERS) {
|
||
|
|
ALOGI("Stop processing slot %zu for it received a palm event from device %s",
|
||
|
|
inIndex, getDeviceName().c_str());
|
||
|
|
}
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (outCount >= MAX_POINTERS) {
|
||
|
|
if (DEBUG_POINTERS) {
|
||
|
|
ALOGD("MultiTouch device %s emitted more than maximum of %zu pointers; "
|
||
|
|
"ignoring the rest.",
|
||
|
|
getDeviceName().c_str(), MAX_POINTERS);
|
||
|
|
}
|
||
|
|
break; // too many fingers!
|
||
|
|
}
|
||
|
|
|
||
|
|
RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
|
||
|
|
outPointer.x = inSlot.getX();
|
||
|
|
outPointer.y = inSlot.getY();
|
||
|
|
outPointer.pressure = inSlot.getPressure();
|
||
|
|
outPointer.touchMajor = inSlot.getTouchMajor();
|
||
|
|
outPointer.touchMinor = inSlot.getTouchMinor();
|
||
|
|
outPointer.toolMajor = inSlot.getToolMajor();
|
||
|
|
outPointer.toolMinor = inSlot.getToolMinor();
|
||
|
|
outPointer.orientation = inSlot.getOrientation();
|
||
|
|
outPointer.distance = inSlot.getDistance();
|
||
|
|
outPointer.tiltX = 0;
|
||
|
|
outPointer.tiltY = 0;
|
||
|
|
|
||
|
|
outPointer.toolType = inSlot.getToolType();
|
||
|
|
if (outPointer.toolType == ToolType::UNKNOWN) {
|
||
|
|
outPointer.toolType = mTouchButtonAccumulator.getToolType();
|
||
|
|
if (outPointer.toolType == ToolType::UNKNOWN) {
|
||
|
|
outPointer.toolType = ToolType::FINGER;
|
||
|
|
}
|
||
|
|
} else if (outPointer.toolType == ToolType::STYLUS && !mStylusMtToolSeen) {
|
||
|
|
mStylusMtToolSeen = true;
|
||
|
|
// The multi-touch device produced a stylus event with MT_TOOL_PEN. Dynamically
|
||
|
|
// re-configure this input device so that we add SOURCE_STYLUS if we haven't already.
|
||
|
|
// This is to cover the case where we cannot reliably detect whether a multi-touch
|
||
|
|
// device will ever produce stylus events when it is initially being configured.
|
||
|
|
if (!isFromSource(mSource, AINPUT_SOURCE_STYLUS)) {
|
||
|
|
// Add the stylus source immediately so that it is included in any events generated
|
||
|
|
// before we have a chance to re-configure the device.
|
||
|
|
mSource |= AINPUT_SOURCE_STYLUS;
|
||
|
|
bumpGeneration();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (shouldSimulateStylusWithTouch() && outPointer.toolType == ToolType::FINGER) {
|
||
|
|
outPointer.toolType = ToolType::STYLUS;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool isHovering = mTouchButtonAccumulator.getToolType() != ToolType::MOUSE &&
|
||
|
|
(mTouchButtonAccumulator.isHovering() ||
|
||
|
|
(mRawPointerAxes.pressure.valid && inSlot.getPressure() <= 0));
|
||
|
|
outPointer.isHovering = isHovering;
|
||
|
|
|
||
|
|
// Assign pointer id using tracking id if available.
|
||
|
|
if (mHavePointerIds) {
|
||
|
|
int32_t trackingId = inSlot.getTrackingId();
|
||
|
|
int32_t id = -1;
|
||
|
|
if (trackingId >= 0) {
|
||
|
|
for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
|
||
|
|
uint32_t n = idBits.clearFirstMarkedBit();
|
||
|
|
if (mPointerTrackingIdMap[n] == trackingId) {
|
||
|
|
id = n;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (id < 0 && !mPointerIdBits.isFull()) {
|
||
|
|
id = mPointerIdBits.markFirstUnmarkedBit();
|
||
|
|
mPointerTrackingIdMap[id] = trackingId;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (id < 0) {
|
||
|
|
mHavePointerIds = false;
|
||
|
|
outState->rawPointerData.clearIdBits();
|
||
|
|
newPointerIdBits.clear();
|
||
|
|
} else {
|
||
|
|
outPointer.id = id;
|
||
|
|
outState->rawPointerData.idToIndex[id] = outCount;
|
||
|
|
outState->rawPointerData.markIdBit(id, isHovering);
|
||
|
|
newPointerIdBits.markBit(id);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
outCount += 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
outState->rawPointerData.pointerCount = outCount;
|
||
|
|
mPointerIdBits = newPointerIdBits;
|
||
|
|
|
||
|
|
mMultiTouchMotionAccumulator.finishSync();
|
||
|
|
}
|
||
|
|
|
||
|
|
void MultiTouchInputMapper::configureRawPointerAxes() {
|
||
|
|
TouchInputMapper::configureRawPointerAxes();
|
||
|
|
|
||
|
|
getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
|
||
|
|
getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
|
||
|
|
getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
|
||
|
|
getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
|
||
|
|
getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
|
||
|
|
getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
|
||
|
|
getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
|
||
|
|
getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
|
||
|
|
getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
|
||
|
|
getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
|
||
|
|
getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
|
||
|
|
|
||
|
|
if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid &&
|
||
|
|
mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
|
||
|
|
size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
|
||
|
|
if (slotCount > MAX_SLOTS) {
|
||
|
|
ALOGW("MultiTouch Device %s reported %zu slots but the framework "
|
||
|
|
"only supports a maximum of %zu slots at this time.",
|
||
|
|
getDeviceName().c_str(), slotCount, MAX_SLOTS);
|
||
|
|
slotCount = MAX_SLOTS;
|
||
|
|
}
|
||
|
|
mMultiTouchMotionAccumulator.configure(getDeviceContext(), slotCount,
|
||
|
|
/*usingSlotsProtocol=*/true);
|
||
|
|
} else {
|
||
|
|
mMultiTouchMotionAccumulator.configure(getDeviceContext(), MAX_POINTERS,
|
||
|
|
/*usingSlotsProtocol=*/false);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool MultiTouchInputMapper::hasStylus() const {
|
||
|
|
return mStylusMtToolSeen || mTouchButtonAccumulator.hasStylus() ||
|
||
|
|
shouldSimulateStylusWithTouch();
|
||
|
|
}
|
||
|
|
|
||
|
|
bool MultiTouchInputMapper::shouldSimulateStylusWithTouch() const {
|
||
|
|
static const bool SIMULATE_STYLUS_WITH_TOUCH =
|
||
|
|
sysprop::InputProperties::simulate_stylus_with_touch().value_or(false);
|
||
|
|
return SIMULATE_STYLUS_WITH_TOUCH &&
|
||
|
|
mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace android
|