| Prabir Pradhan | baa5c82 | 2019-08-30 15:27:05 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (C) 2019 The Android Open Source Project | 
 | 3 |  * | 
 | 4 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 | 5 |  * you may not use this file except in compliance with the License. | 
 | 6 |  * You may obtain a copy of the License at | 
 | 7 |  * | 
 | 8 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 | 9 |  * | 
 | 10 |  * Unless required by applicable law or agreed to in writing, software | 
 | 11 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 | 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | 13 |  * See the License for the specific language governing permissions and | 
 | 14 |  * limitations under the License. | 
 | 15 |  */ | 
 | 16 |  | 
 | 17 | #include "Macros.h" | 
 | 18 |  | 
 | 19 | #include "MultiTouchInputMapper.h" | 
 | 20 |  | 
 | 21 | namespace android { | 
 | 22 |  | 
 | 23 | // --- Constants --- | 
 | 24 |  | 
 | 25 | // Maximum number of slots supported when using the slot-based Multitouch Protocol B. | 
 | 26 | static constexpr size_t MAX_SLOTS = 32; | 
 | 27 |  | 
 | 28 | // --- MultiTouchMotionAccumulator --- | 
 | 29 |  | 
 | 30 | MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() | 
 | 31 |       : mCurrentSlot(-1), | 
 | 32 |         mSlots(nullptr), | 
 | 33 |         mSlotCount(0), | 
 | 34 |         mUsingSlotsProtocol(false), | 
 | 35 |         mHaveStylus(false) {} | 
 | 36 |  | 
 | 37 | MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() { | 
 | 38 |     delete[] mSlots; | 
 | 39 | } | 
 | 40 |  | 
 | 41 | void MultiTouchMotionAccumulator::configure(InputDevice* device, size_t slotCount, | 
 | 42 |                                             bool usingSlotsProtocol) { | 
 | 43 |     mSlotCount = slotCount; | 
 | 44 |     mUsingSlotsProtocol = usingSlotsProtocol; | 
 | 45 |     mHaveStylus = device->hasAbsoluteAxis(ABS_MT_TOOL_TYPE); | 
 | 46 |  | 
 | 47 |     delete[] mSlots; | 
 | 48 |     mSlots = new Slot[slotCount]; | 
 | 49 | } | 
 | 50 |  | 
 | 51 | void MultiTouchMotionAccumulator::reset(InputDevice* device) { | 
 | 52 |     // Unfortunately there is no way to read the initial contents of the slots. | 
 | 53 |     // So when we reset the accumulator, we must assume they are all zeroes. | 
 | 54 |     if (mUsingSlotsProtocol) { | 
 | 55 |         // Query the driver for the current slot index and use it as the initial slot | 
 | 56 |         // before we start reading events from the device.  It is possible that the | 
 | 57 |         // current slot index will not be the same as it was when the first event was | 
 | 58 |         // written into the evdev buffer, which means the input mapper could start | 
 | 59 |         // out of sync with the initial state of the events in the evdev buffer. | 
 | 60 |         // In the extremely unlikely case that this happens, the data from | 
 | 61 |         // two slots will be confused until the next ABS_MT_SLOT event is received. | 
 | 62 |         // This can cause the touch point to "jump", but at least there will be | 
 | 63 |         // no stuck touches. | 
 | 64 |         int32_t initialSlot; | 
 | 65 |         status_t status = device->getEventHub()->getAbsoluteAxisValue(device->getId(), ABS_MT_SLOT, | 
 | 66 |                                                                       &initialSlot); | 
 | 67 |         if (status) { | 
 | 68 |             ALOGD("Could not retrieve current multitouch slot index.  status=%d", status); | 
 | 69 |             initialSlot = -1; | 
 | 70 |         } | 
 | 71 |         clearSlots(initialSlot); | 
 | 72 |     } else { | 
 | 73 |         clearSlots(-1); | 
 | 74 |     } | 
 | 75 | } | 
 | 76 |  | 
 | 77 | void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) { | 
 | 78 |     if (mSlots) { | 
 | 79 |         for (size_t i = 0; i < mSlotCount; i++) { | 
 | 80 |             mSlots[i].clear(); | 
 | 81 |         } | 
 | 82 |     } | 
 | 83 |     mCurrentSlot = initialSlot; | 
 | 84 | } | 
 | 85 |  | 
 | 86 | void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { | 
 | 87 |     if (rawEvent->type == EV_ABS) { | 
 | 88 |         bool newSlot = false; | 
 | 89 |         if (mUsingSlotsProtocol) { | 
 | 90 |             if (rawEvent->code == ABS_MT_SLOT) { | 
 | 91 |                 mCurrentSlot = rawEvent->value; | 
 | 92 |                 newSlot = true; | 
 | 93 |             } | 
 | 94 |         } else if (mCurrentSlot < 0) { | 
 | 95 |             mCurrentSlot = 0; | 
 | 96 |         } | 
 | 97 |  | 
 | 98 |         if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) { | 
 | 99 | #if DEBUG_POINTERS | 
 | 100 |             if (newSlot) { | 
 | 101 |                 ALOGW("MultiTouch device emitted invalid slot index %d but it " | 
 | 102 |                       "should be between 0 and %zd; ignoring this slot.", | 
 | 103 |                       mCurrentSlot, mSlotCount - 1); | 
 | 104 |             } | 
 | 105 | #endif | 
 | 106 |         } else { | 
 | 107 |             Slot* slot = &mSlots[mCurrentSlot]; | 
 | 108 |  | 
 | 109 |             switch (rawEvent->code) { | 
 | 110 |                 case ABS_MT_POSITION_X: | 
 | 111 |                     slot->mInUse = true; | 
 | 112 |                     slot->mAbsMTPositionX = rawEvent->value; | 
 | 113 |                     break; | 
 | 114 |                 case ABS_MT_POSITION_Y: | 
 | 115 |                     slot->mInUse = true; | 
 | 116 |                     slot->mAbsMTPositionY = rawEvent->value; | 
 | 117 |                     break; | 
 | 118 |                 case ABS_MT_TOUCH_MAJOR: | 
 | 119 |                     slot->mInUse = true; | 
 | 120 |                     slot->mAbsMTTouchMajor = rawEvent->value; | 
 | 121 |                     break; | 
 | 122 |                 case ABS_MT_TOUCH_MINOR: | 
 | 123 |                     slot->mInUse = true; | 
 | 124 |                     slot->mAbsMTTouchMinor = rawEvent->value; | 
 | 125 |                     slot->mHaveAbsMTTouchMinor = true; | 
 | 126 |                     break; | 
 | 127 |                 case ABS_MT_WIDTH_MAJOR: | 
 | 128 |                     slot->mInUse = true; | 
 | 129 |                     slot->mAbsMTWidthMajor = rawEvent->value; | 
 | 130 |                     break; | 
 | 131 |                 case ABS_MT_WIDTH_MINOR: | 
 | 132 |                     slot->mInUse = true; | 
 | 133 |                     slot->mAbsMTWidthMinor = rawEvent->value; | 
 | 134 |                     slot->mHaveAbsMTWidthMinor = true; | 
 | 135 |                     break; | 
 | 136 |                 case ABS_MT_ORIENTATION: | 
 | 137 |                     slot->mInUse = true; | 
 | 138 |                     slot->mAbsMTOrientation = rawEvent->value; | 
 | 139 |                     break; | 
 | 140 |                 case ABS_MT_TRACKING_ID: | 
 | 141 |                     if (mUsingSlotsProtocol && rawEvent->value < 0) { | 
 | 142 |                         // The slot is no longer in use but it retains its previous contents, | 
 | 143 |                         // which may be reused for subsequent touches. | 
 | 144 |                         slot->mInUse = false; | 
 | 145 |                     } else { | 
 | 146 |                         slot->mInUse = true; | 
 | 147 |                         slot->mAbsMTTrackingId = rawEvent->value; | 
 | 148 |                     } | 
 | 149 |                     break; | 
 | 150 |                 case ABS_MT_PRESSURE: | 
 | 151 |                     slot->mInUse = true; | 
 | 152 |                     slot->mAbsMTPressure = rawEvent->value; | 
 | 153 |                     break; | 
 | 154 |                 case ABS_MT_DISTANCE: | 
 | 155 |                     slot->mInUse = true; | 
 | 156 |                     slot->mAbsMTDistance = rawEvent->value; | 
 | 157 |                     break; | 
 | 158 |                 case ABS_MT_TOOL_TYPE: | 
 | 159 |                     slot->mInUse = true; | 
 | 160 |                     slot->mAbsMTToolType = rawEvent->value; | 
 | 161 |                     slot->mHaveAbsMTToolType = true; | 
 | 162 |                     break; | 
 | 163 |             } | 
 | 164 |         } | 
 | 165 |     } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) { | 
 | 166 |         // MultiTouch Sync: The driver has returned all data for *one* of the pointers. | 
 | 167 |         mCurrentSlot += 1; | 
 | 168 |     } | 
 | 169 | } | 
 | 170 |  | 
 | 171 | void MultiTouchMotionAccumulator::finishSync() { | 
 | 172 |     if (!mUsingSlotsProtocol) { | 
 | 173 |         clearSlots(-1); | 
 | 174 |     } | 
 | 175 | } | 
 | 176 |  | 
 | 177 | bool MultiTouchMotionAccumulator::hasStylus() const { | 
 | 178 |     return mHaveStylus; | 
 | 179 | } | 
 | 180 |  | 
 | 181 | // --- MultiTouchMotionAccumulator::Slot --- | 
 | 182 |  | 
 | 183 | MultiTouchMotionAccumulator::Slot::Slot() { | 
 | 184 |     clear(); | 
 | 185 | } | 
 | 186 |  | 
 | 187 | void MultiTouchMotionAccumulator::Slot::clear() { | 
 | 188 |     mInUse = false; | 
 | 189 |     mHaveAbsMTTouchMinor = false; | 
 | 190 |     mHaveAbsMTWidthMinor = false; | 
 | 191 |     mHaveAbsMTToolType = false; | 
 | 192 |     mAbsMTPositionX = 0; | 
 | 193 |     mAbsMTPositionY = 0; | 
 | 194 |     mAbsMTTouchMajor = 0; | 
 | 195 |     mAbsMTTouchMinor = 0; | 
 | 196 |     mAbsMTWidthMajor = 0; | 
 | 197 |     mAbsMTWidthMinor = 0; | 
 | 198 |     mAbsMTOrientation = 0; | 
 | 199 |     mAbsMTTrackingId = -1; | 
 | 200 |     mAbsMTPressure = 0; | 
 | 201 |     mAbsMTDistance = 0; | 
 | 202 |     mAbsMTToolType = 0; | 
 | 203 | } | 
 | 204 |  | 
 | 205 | int32_t MultiTouchMotionAccumulator::Slot::getToolType() const { | 
 | 206 |     if (mHaveAbsMTToolType) { | 
 | 207 |         switch (mAbsMTToolType) { | 
 | 208 |             case MT_TOOL_FINGER: | 
 | 209 |                 return AMOTION_EVENT_TOOL_TYPE_FINGER; | 
 | 210 |             case MT_TOOL_PEN: | 
 | 211 |                 return AMOTION_EVENT_TOOL_TYPE_STYLUS; | 
 | 212 |         } | 
 | 213 |     } | 
 | 214 |     return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; | 
 | 215 | } | 
 | 216 |  | 
 | 217 | // --- MultiTouchInputMapper --- | 
 | 218 |  | 
 | 219 | MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {} | 
 | 220 |  | 
 | 221 | MultiTouchInputMapper::~MultiTouchInputMapper() {} | 
 | 222 |  | 
 | 223 | void MultiTouchInputMapper::reset(nsecs_t when) { | 
 | 224 |     mMultiTouchMotionAccumulator.reset(getDevice()); | 
 | 225 |  | 
 | 226 |     mPointerIdBits.clear(); | 
 | 227 |  | 
 | 228 |     TouchInputMapper::reset(when); | 
 | 229 | } | 
 | 230 |  | 
 | 231 | void MultiTouchInputMapper::process(const RawEvent* rawEvent) { | 
 | 232 |     TouchInputMapper::process(rawEvent); | 
 | 233 |  | 
 | 234 |     mMultiTouchMotionAccumulator.process(rawEvent); | 
 | 235 | } | 
 | 236 |  | 
 | 237 | void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { | 
 | 238 |     size_t inCount = mMultiTouchMotionAccumulator.getSlotCount(); | 
 | 239 |     size_t outCount = 0; | 
 | 240 |     BitSet32 newPointerIdBits; | 
 | 241 |     mHavePointerIds = true; | 
 | 242 |  | 
 | 243 |     for (size_t inIndex = 0; inIndex < inCount; inIndex++) { | 
 | 244 |         const MultiTouchMotionAccumulator::Slot* inSlot = | 
 | 245 |                 mMultiTouchMotionAccumulator.getSlot(inIndex); | 
 | 246 |         if (!inSlot->isInUse()) { | 
 | 247 |             continue; | 
 | 248 |         } | 
 | 249 |  | 
 | 250 |         if (outCount >= MAX_POINTERS) { | 
 | 251 | #if DEBUG_POINTERS | 
 | 252 |             ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; " | 
 | 253 |                   "ignoring the rest.", | 
 | 254 |                   getDeviceName().c_str(), MAX_POINTERS); | 
 | 255 | #endif | 
 | 256 |             break; // too many fingers! | 
 | 257 |         } | 
 | 258 |  | 
 | 259 |         RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount]; | 
 | 260 |         outPointer.x = inSlot->getX(); | 
 | 261 |         outPointer.y = inSlot->getY(); | 
 | 262 |         outPointer.pressure = inSlot->getPressure(); | 
 | 263 |         outPointer.touchMajor = inSlot->getTouchMajor(); | 
 | 264 |         outPointer.touchMinor = inSlot->getTouchMinor(); | 
 | 265 |         outPointer.toolMajor = inSlot->getToolMajor(); | 
 | 266 |         outPointer.toolMinor = inSlot->getToolMinor(); | 
 | 267 |         outPointer.orientation = inSlot->getOrientation(); | 
 | 268 |         outPointer.distance = inSlot->getDistance(); | 
 | 269 |         outPointer.tiltX = 0; | 
 | 270 |         outPointer.tiltY = 0; | 
 | 271 |  | 
 | 272 |         outPointer.toolType = inSlot->getToolType(); | 
 | 273 |         if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { | 
 | 274 |             outPointer.toolType = mTouchButtonAccumulator.getToolType(); | 
 | 275 |             if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { | 
 | 276 |                 outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; | 
 | 277 |             } | 
 | 278 |         } | 
 | 279 |  | 
 | 280 |         bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE && | 
 | 281 |                 (mTouchButtonAccumulator.isHovering() || | 
 | 282 |                  (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0)); | 
 | 283 |         outPointer.isHovering = isHovering; | 
 | 284 |  | 
 | 285 |         // Assign pointer id using tracking id if available. | 
 | 286 |         if (mHavePointerIds) { | 
 | 287 |             int32_t trackingId = inSlot->getTrackingId(); | 
 | 288 |             int32_t id = -1; | 
 | 289 |             if (trackingId >= 0) { | 
 | 290 |                 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) { | 
 | 291 |                     uint32_t n = idBits.clearFirstMarkedBit(); | 
 | 292 |                     if (mPointerTrackingIdMap[n] == trackingId) { | 
 | 293 |                         id = n; | 
 | 294 |                     } | 
 | 295 |                 } | 
 | 296 |  | 
 | 297 |                 if (id < 0 && !mPointerIdBits.isFull()) { | 
 | 298 |                     id = mPointerIdBits.markFirstUnmarkedBit(); | 
 | 299 |                     mPointerTrackingIdMap[id] = trackingId; | 
 | 300 |                 } | 
 | 301 |             } | 
 | 302 |             if (id < 0) { | 
 | 303 |                 mHavePointerIds = false; | 
 | 304 |                 outState->rawPointerData.clearIdBits(); | 
 | 305 |                 newPointerIdBits.clear(); | 
 | 306 |             } else { | 
 | 307 |                 outPointer.id = id; | 
 | 308 |                 outState->rawPointerData.idToIndex[id] = outCount; | 
 | 309 |                 outState->rawPointerData.markIdBit(id, isHovering); | 
 | 310 |                 newPointerIdBits.markBit(id); | 
 | 311 |             } | 
 | 312 |         } | 
 | 313 |         outCount += 1; | 
 | 314 |     } | 
 | 315 |  | 
 | 316 |     outState->rawPointerData.pointerCount = outCount; | 
 | 317 |     mPointerIdBits = newPointerIdBits; | 
 | 318 |  | 
 | 319 |     mMultiTouchMotionAccumulator.finishSync(); | 
 | 320 | } | 
 | 321 |  | 
 | 322 | void MultiTouchInputMapper::configureRawPointerAxes() { | 
 | 323 |     TouchInputMapper::configureRawPointerAxes(); | 
 | 324 |  | 
 | 325 |     getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x); | 
 | 326 |     getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y); | 
 | 327 |     getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor); | 
 | 328 |     getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor); | 
 | 329 |     getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor); | 
 | 330 |     getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor); | 
 | 331 |     getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation); | 
 | 332 |     getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure); | 
 | 333 |     getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance); | 
 | 334 |     getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId); | 
 | 335 |     getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot); | 
 | 336 |  | 
 | 337 |     if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid && | 
 | 338 |         mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) { | 
 | 339 |         size_t slotCount = mRawPointerAxes.slot.maxValue + 1; | 
 | 340 |         if (slotCount > MAX_SLOTS) { | 
 | 341 |             ALOGW("MultiTouch Device %s reported %zu slots but the framework " | 
 | 342 |                   "only supports a maximum of %zu slots at this time.", | 
 | 343 |                   getDeviceName().c_str(), slotCount, MAX_SLOTS); | 
 | 344 |             slotCount = MAX_SLOTS; | 
 | 345 |         } | 
 | 346 |         mMultiTouchMotionAccumulator.configure(getDevice(), slotCount, true /*usingSlotsProtocol*/); | 
 | 347 |     } else { | 
 | 348 |         mMultiTouchMotionAccumulator.configure(getDevice(), MAX_POINTERS, | 
 | 349 |                                                false /*usingSlotsProtocol*/); | 
 | 350 |     } | 
 | 351 | } | 
 | 352 |  | 
 | 353 | bool MultiTouchInputMapper::hasStylus() const { | 
 | 354 |     return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus(); | 
 | 355 | } | 
 | 356 |  | 
 | 357 | } // namespace android |