| // Copyright 2016 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // We want to use assert in production environment |
| #undef NDEBUG |
| |
| #include <cassert> |
| #include <cstring> |
| |
| #include "userspace_touchpad/touch_emulator.h" |
| |
| // Touchpad size and resolution. |
| constexpr int kXMax = 1920; // points |
| constexpr int kYMax = 1080; // points |
| constexpr int kXRes = 16; // points/mm |
| constexpr int kYRes = 16; // points/mm |
| |
| TouchEmulator::TouchEmulator() |
| : touch_device_handler_(nullptr) { |
| CreateVirtualMultiTouchDevice(); |
| |
| // Clear slot usage. |
| for (size_t i = 0; i < kMaxFingers; ++i) |
| slot_tid_[i] = -1; |
| } |
| |
| void TouchEmulator::AbsInit(int code, int min_val, int max_val, int res) { |
| struct uinput_abs_setup abs_setup = {0}; |
| assert(touch_device_handler_.EnableAbsEvent(code)); |
| abs_setup.code = code; |
| abs_setup.absinfo.minimum = min_val; |
| abs_setup.absinfo.maximum = max_val; |
| abs_setup.absinfo.resolution = res; |
| assert(ioctl(touch_device_handler_.get_fd(), UI_ABS_SETUP, &abs_setup) == 0); |
| } |
| |
| void TouchEmulator::CreateVirtualMultiTouchDevice() { |
| // Setup virtual touchpad. |
| assert(touch_device_handler_.CreateUinputFD()); |
| |
| assert(touch_device_handler_.EnableEventType(EV_KEY)); |
| assert(touch_device_handler_.EnableKeyEvent(BTN_LEFT)); |
| assert(touch_device_handler_.EnableKeyEvent(BTN_TOOL_FINGER)); |
| assert(touch_device_handler_.EnableKeyEvent(BTN_TOUCH)); |
| assert(touch_device_handler_.EnableKeyEvent(BTN_TOOL_DOUBLETAP)); |
| assert(touch_device_handler_.EnableKeyEvent(BTN_TOOL_TRIPLETAP)); |
| assert(touch_device_handler_.EnableKeyEvent(BTN_TOOL_QUADTAP)); |
| |
| touch_device_handler_.EnableEventType(EV_ABS); |
| AbsInit(ABS_X, 0, kXMax, kXRes); |
| AbsInit(ABS_Y, 0, kYMax, kYRes); |
| AbsInit(ABS_PRESSURE, 0, 255, 0); |
| AbsInit(ABS_MT_SLOT, 0, kMaxFingers - 1, 0); |
| AbsInit(ABS_MT_TRACKING_ID, 0, 0xffff, 0); |
| AbsInit(ABS_MT_POSITION_X, 0, kXMax, kXRes); |
| AbsInit(ABS_MT_POSITION_Y, 0, kYMax, kYRes); |
| AbsInit(ABS_MT_PRESSURE, 0, 255, 0); |
| assert(touch_device_handler_.FinalizeUinputCreation("userspace-touchpad")); |
| } |
| |
| void TouchEmulator::FlushEvents(const std::vector<TouchEvent>& fingers, |
| const bool button_down) { |
| int min_active_slot = kMaxFingers; |
| int min_active_slot_finger_idx = -1; |
| int new_slot_tid[kMaxFingers]; |
| int current_finger_count = 0; |
| |
| for (size_t i = 0; i < kMaxFingers; i++) |
| new_slot_tid[i] = -1; |
| |
| // Process finger one-by-one. |
| for (size_t i = 0; i < fingers.size(); i++) { |
| int slot = -1; |
| size_t j; |
| const TouchEvent& finger = fingers[i]; |
| |
| // Skip if the finger is leaving. |
| if (finger.tracking_id == -1) |
| continue; |
| |
| // See if the finger is present in the previous frame. |
| for (j = 0; j < kMaxFingers; ++j) { |
| if (finger.tracking_id == slot_tid_[j]) |
| break; |
| // Get the first free slot if possible. |
| if (slot == -1 && slot_tid_[j] == -1) |
| slot = j; |
| } |
| |
| // Use the matched previous finger's slot if available. |
| if (j != kMaxFingers) |
| slot = j; |
| |
| // Record the finger with minimum slot index. This is used to identify the |
| // finger that we will use to send ABS_X and ABS_Y events. |
| if (slot < min_active_slot) { |
| min_active_slot = slot; |
| min_active_slot_finger_idx = i; |
| } |
| |
| // Update and send events for this finger. |
| current_finger_count++; |
| new_slot_tid[slot] = slot_tid_[slot] = finger.tracking_id; |
| WriteTouchEvent(slot, finger.tracking_id, |
| finger.x, finger.y, finger.pressure); |
| } |
| |
| // Check if any finger left, emit tracking id change if necessary. |
| for (size_t i = 0; i < kMaxFingers; i++) { |
| if (slot_tid_[i] != -1 && new_slot_tid[i] == -1) { |
| slot_tid_[i] = -1; |
| WriteTouchEvent(i, -1, 0, 0, 0); |
| } |
| } |
| |
| // Send multi-touch button events. |
| WriteTouchButtonEvent(current_finger_count); |
| |
| // Send single touch events for backward compatibility. |
| if (current_finger_count >= 1) { |
| assert(touch_device_handler_.SendEvent( |
| EV_ABS, ABS_X, fingers[min_active_slot_finger_idx].x)); |
| assert(touch_device_handler_.SendEvent( |
| EV_ABS, ABS_Y, fingers[min_active_slot_finger_idx].y)); |
| assert(touch_device_handler_.SendEvent( |
| EV_ABS, ABS_PRESSURE, fingers[min_active_slot_finger_idx].pressure)); |
| } |
| |
| // Send the physical button event. |
| assert(touch_device_handler_.SendEvent(EV_KEY, BTN_LEFT, |
| button_down ? 1 : 0)); |
| |
| // Conclude the input report. |
| assert(touch_device_handler_.SendEvent(EV_SYN, SYN_REPORT, 0)); |
| } |
| |
| void TouchEmulator::WriteTouchEvent(int slot, int id, int x, int y, |
| int pressure) { |
| assert(touch_device_handler_.SendEvent(EV_ABS, ABS_MT_SLOT, slot)); |
| assert(touch_device_handler_.SendEvent(EV_ABS, ABS_MT_TRACKING_ID, id)); |
| if (id != -1) { |
| assert(touch_device_handler_.SendEvent(EV_ABS, ABS_MT_POSITION_X, x)); |
| assert(touch_device_handler_.SendEvent(EV_ABS, ABS_MT_POSITION_Y, y)); |
| assert(touch_device_handler_.SendEvent(EV_ABS, ABS_MT_PRESSURE, pressure)); |
| } |
| } |
| |
| void TouchEmulator::WriteTouchButtonEvent(int finger_count) { |
| // Just send all events repetitively and let kernel's delta compression |
| // handles it. |
| assert(touch_device_handler_.SendEvent( |
| EV_KEY, BTN_TOUCH, finger_count > 0)); |
| assert(touch_device_handler_.SendEvent( |
| EV_KEY, BTN_TOOL_FINGER, finger_count == 1)); |
| assert(touch_device_handler_.SendEvent( |
| EV_KEY, BTN_TOOL_DOUBLETAP, finger_count == 2)); |
| assert(touch_device_handler_.SendEvent( |
| EV_KEY, BTN_TOOL_TRIPLETAP, finger_count == 3)); |
| assert(touch_device_handler_.SendEvent( |
| EV_KEY, BTN_TOOL_QUADTAP, finger_count == 4)); |
| assert(touch_device_handler_.SendEvent( |
| EV_KEY, BTN_TOOL_QUINTTAP, finger_count == 5)); |
| } |