blob: 876d0911088b62757858a1871272b9c6d925c77f [file] [log] [blame]
// 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));
}