blob: 51a1d31b202ff1b886d439579eb0189c6275f145 [file] [log] [blame]
/*
* Copyright (C) 2016 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 "common.h"
#include "wear_touch.h"
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <linux/input.h>
#define DEVICE_PATH "/dev/input"
WearSwipeDetector::WearSwipeDetector(int low, int high, OnSwipeCallback callback, void* cookie):
mLowThreshold(low),
mHighThreshold(high),
mCallback(callback),
mCookie(cookie),
mCurrentSlot(-1) {
pthread_create(&mThread, NULL, touch_thread, this);
}
WearSwipeDetector::~WearSwipeDetector() {
}
void WearSwipeDetector::detect(int dx, int dy) {
enum SwipeDirection direction;
if (abs(dy) < mLowThreshold && abs(dx) > mHighThreshold) {
direction = dx < 0 ? LEFT : RIGHT;
} else if (abs(dx) < mLowThreshold && abs(dy) > mHighThreshold) {
direction = dy < 0 ? UP : DOWN;
} else {
LOGD("Ignore %d %d\n", dx, dy);
return;
}
LOGD("Swipe direction=%d\n", direction);
mCallback(mCookie, direction);
}
void WearSwipeDetector::process(struct input_event *event) {
if (mCurrentSlot < 0) {
mCallback(mCookie, UP);
mCurrentSlot = 0;
}
if (event->type == EV_ABS) {
if (event->code == ABS_MT_SLOT)
mCurrentSlot = event->value;
// Ignore other fingers
if (mCurrentSlot > 0) {
return;
}
switch (event->code) {
case ABS_MT_POSITION_X:
mX = event->value;
mFingerDown = true;
break;
case ABS_MT_POSITION_Y:
mY = event->value;
mFingerDown = true;
break;
case ABS_MT_TRACKING_ID:
if (event->value < 0)
mFingerDown = false;
break;
}
} else if (event->type == EV_SYN) {
if (event->code == SYN_REPORT) {
if (mFingerDown && !mSwiping) {
mStartX = mX;
mStartY = mY;
mSwiping = true;
} else if (!mFingerDown && mSwiping) {
mSwiping = false;
detect(mX - mStartX, mY - mStartY);
}
}
}
}
void WearSwipeDetector::run() {
int fd = findDevice(DEVICE_PATH);
if (fd < 0) {
LOGE("no input devices found\n");
return;
}
struct input_event event;
while (read(fd, &event, sizeof(event)) == sizeof(event)) {
process(&event);
}
close(fd);
}
void* WearSwipeDetector::touch_thread(void* cookie) {
((WearSwipeDetector*)cookie)->run();
return NULL;
}
#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)%8)))
int WearSwipeDetector::openDevice(const char *device) {
int fd = open(device, O_RDONLY);
if (fd < 0) {
LOGE("could not open %s, %s\n", device, strerror(errno));
return false;
}
char name[80];
name[sizeof(name) - 1] = '\0';
if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
LOGE("could not get device name for %s, %s\n", device, strerror(errno));
name[0] = '\0';
}
uint8_t bits[512];
memset(bits, 0, sizeof(bits));
int ret = ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(bits)), bits);
if (ret > 0) {
if (test_bit(ABS_MT_POSITION_X, bits) && test_bit(ABS_MT_POSITION_Y, bits)) {
LOGD("Found %s %s\n", device, name);
return fd;
}
}
close(fd);
return -1;
}
int WearSwipeDetector::findDevice(const char* path) {
DIR* dir = opendir(path);
if (dir == NULL) {
LOGE("Could not open directory %s", path);
return false;
}
struct dirent* entry;
int ret = -1;
while (ret < 0 && (entry = readdir(dir)) != NULL) {
if (entry->d_name[0] == '.') continue;
char device[PATH_MAX];
device[PATH_MAX-1] = '\0';
snprintf(device, PATH_MAX-1, "%s/%s", path, entry->d_name);
ret = openDevice(device);
}
closedir(dir);
return ret;
}