421 lines
12 KiB
C++
421 lines
12 KiB
C++
// SPDX-License-Identifier: Apache2.0
|
|
/*
|
|
* Copyright (C) 2024 Amlogic Inc.
|
|
*/
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdint.h>
|
|
#include <linux/input.h>
|
|
#include <pthread.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
#include "events.h"
|
|
#include "events_process.h"
|
|
|
|
#define WAIT_KEY_TIMEOUT_SEC 120
|
|
#define nullptr NULL
|
|
#define KEY_EVENT_TIME_INTERVAL 20
|
|
|
|
EventsProcess:: KeyMapItem_t g_default_keymap[] = {
|
|
{ "power", KEY_POWER },
|
|
{ "down", KEY_DOWN },
|
|
{ "up", KEY_UP },
|
|
};
|
|
|
|
EventsProcess::EventsProcess()
|
|
: key_queue_len(0),
|
|
key_last_down(-1),
|
|
last_key(-1),
|
|
key_down_count(0),
|
|
report_longpress_flag(false),
|
|
num_keys(0),
|
|
keys_map(NULL) {
|
|
pthread_mutex_init(&key_queue_mutex, nullptr);
|
|
pthread_cond_init(&key_queue_cond, nullptr);
|
|
memset(key_pressed, 0, sizeof(key_pressed));
|
|
memset(&last_queue_time, 0, sizeof(last_queue_time));
|
|
load_key_map();
|
|
}
|
|
|
|
EventsProcess::~EventsProcess() {
|
|
|
|
if ( (keys_map != g_default_keymap) && (NULL != keys_map) ) {
|
|
free(keys_map);
|
|
}
|
|
pthread_mutex_destroy(&key_queue_mutex);
|
|
pthread_cond_destroy(&key_queue_cond);
|
|
}
|
|
|
|
int EventsProcess::InputCallback(int fd, uint32_t epevents, void* data) {
|
|
return reinterpret_cast<EventsProcess*>(data)->OnInputEvent(fd, epevents);
|
|
}
|
|
|
|
static void* InputThreadLoop(void*) {
|
|
while (true) {
|
|
if (!ev_wait(-1)) {
|
|
ev_dispatch();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void EventsProcess::Init() {
|
|
ev_init(InputCallback, this);
|
|
|
|
pthread_create(&input_thread_, nullptr, InputThreadLoop, nullptr);
|
|
}
|
|
|
|
int EventsProcess::OnInputEvent(int fd, uint32_t epevents) {
|
|
struct input_event ev;
|
|
|
|
if (ev_get_input(fd, epevents, &ev) == -1) {
|
|
return -1;
|
|
}
|
|
|
|
if (ev.type == EV_SYN) {
|
|
return 0;
|
|
}
|
|
|
|
if (ev.type == EV_KEY && ev.code <= KEY_MAX) {
|
|
|
|
int code = getMapKey(ev.code);
|
|
if (code > 0) {
|
|
ProcessKey(code, ev.value);
|
|
} else {
|
|
ProcessKey(ev.code, ev.value);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* use CLOCK_MONOTONIC clock, gettimeofday will overlap if ntp changes */
|
|
uint64_t EventsProcess::getSystemTimeUs() {
|
|
struct timespec ts;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
return (ts.tv_sec)*1000000LL + ts.tv_nsec/1000LL;
|
|
}
|
|
|
|
void EventsProcess::ProcessKey(int key_code, int value) {
|
|
bool register_key = false;
|
|
bool flag = false;
|
|
pthread_mutex_lock(&key_queue_mutex);
|
|
key_pressed[key_code] = value;
|
|
if (value == 1) {/*1:key down*/
|
|
KeyEvent *ke = new KeyEvent;
|
|
ke->down_ts_us = getSystemTimeUs();
|
|
ke->keyCode = key_code;
|
|
ke->keyState = value; /*key down*/
|
|
ke->longPress = false;
|
|
ke->curlongPress = false;
|
|
mKeyEventVec.push_back(ke);
|
|
|
|
++key_down_count;
|
|
key_last_down = key_code;
|
|
key_timer_t* info = new key_timer_t;
|
|
info->ep = this;
|
|
info->key_code = key_code;
|
|
info->count = key_down_count;
|
|
info->ke = ke;
|
|
pthread_t thread;
|
|
pthread_create(&thread, nullptr, &EventsProcess::time_key_helper, info);
|
|
pthread_detach(thread);
|
|
} else if(value == 2){/*2:key repeat*/
|
|
} else {/*0:key down*/
|
|
for (std::vector<KeyEvent *>::iterator iter = mKeyEventVec.begin();
|
|
iter != mKeyEventVec.end(); ++iter) {
|
|
if ((*iter)->keyCode == key_code && (*iter)->keyState == 1) {
|
|
(*iter)->up_ts_us = getSystemTimeUs();
|
|
(*iter)->keyState = value; /*key up*/
|
|
if ((*iter)->longPress || (*iter)->curlongPress) {
|
|
(*iter)->curlongPress = false;
|
|
flag = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (key_last_down == key_code) {
|
|
register_key = true;
|
|
}
|
|
key_last_down = -1;
|
|
}
|
|
pthread_mutex_unlock(&key_queue_mutex);
|
|
last_key = key_code;
|
|
if (register_key) {
|
|
EnqueueKey(key_code, flag);
|
|
}
|
|
}
|
|
|
|
void* EventsProcess::time_key_helper(void* cookie) {
|
|
key_timer_t* info = (key_timer_t*) cookie;
|
|
info->ep->time_key(info);
|
|
delete info;
|
|
return nullptr;
|
|
}
|
|
|
|
void EventsProcess::time_key(key_timer_t *info) {
|
|
int key_code = info->key_code;
|
|
int count = info->count;
|
|
int keymode = getKeyMode(key_code);
|
|
usleep(750000); // 750 ms == "long"
|
|
pthread_mutex_lock(&key_queue_mutex);
|
|
if (key_last_down == key_code && key_down_count == count) {
|
|
if (info->ke) {
|
|
info->ke->longPress = true;
|
|
if (keymode != 0)
|
|
info->ke->curlongPress = true;
|
|
}
|
|
else {
|
|
printf("##recive key but no events##\n");
|
|
pthread_mutex_unlock(&key_queue_mutex);
|
|
return;
|
|
}
|
|
}
|
|
if (info->ke->longPress && info->ke->curlongPress) {
|
|
pthread_mutex_unlock(&key_queue_mutex);
|
|
while (key_last_down == key_code && key_down_count == count) {
|
|
EnqueueKey(key_code, false);
|
|
}
|
|
} else {
|
|
pthread_mutex_unlock(&key_queue_mutex);
|
|
}
|
|
}
|
|
|
|
|
|
const char* EventsProcess::getKeyType(int key) {
|
|
int i;
|
|
for (i = 0; i < num_keys; i++) {
|
|
KeyMapItem_t* v = &keys_map[i];
|
|
if (v->value == key)
|
|
return v->type;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int EventsProcess::getKeyMode(int key) {
|
|
int i;
|
|
for (i = 0; i < num_keys; i++) {
|
|
KeyMapItem_t* v = &keys_map[i];
|
|
if (v->value == key)
|
|
return v->mode;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void EventsProcess::load_key_map() {
|
|
FILE* fstab = fopen("/etc/gpio_key.kl", "r");
|
|
if (fstab != NULL) {
|
|
printf("loaded /etc/gpio_key.kl\n");
|
|
int alloc = 2;
|
|
keys_map = (KeyMapItem_t*)malloc(alloc * sizeof(KeyMapItem_t));
|
|
num_keys = 0;
|
|
|
|
char buffer[1024];
|
|
int i;
|
|
int value = -1;
|
|
while (fgets(buffer, sizeof(buffer)-1, fstab)) {
|
|
for (i = 0; buffer[i] && isspace(buffer[i]); ++i);
|
|
|
|
if (buffer[i] == '\0' || buffer[i] == '#') continue;
|
|
|
|
char* original = strdup(buffer);
|
|
char* type = strtok(original+i, " \t\n");
|
|
char* key = strtok(NULL, " \t\n");
|
|
char* mode = strtok(NULL, " \t\n");
|
|
value = atoi (key);
|
|
if (type && (value > 0)) {
|
|
while (num_keys >= alloc) {
|
|
alloc *= 2;
|
|
keys_map = (KeyMapItem_t*)realloc(keys_map, alloc*sizeof(KeyMapItem_t));
|
|
}
|
|
keys_map[num_keys].type = strdup(type);
|
|
keys_map[num_keys].value = value;
|
|
if (!mode)
|
|
keys_map[num_keys].mode = 0;
|
|
else
|
|
keys_map[num_keys].mode = atoi (mode);
|
|
|
|
++num_keys;
|
|
} else {
|
|
printf("error: skipping malformed keyboard.lk line: %s\n", original);
|
|
}
|
|
free(original);
|
|
}
|
|
|
|
fclose(fstab);
|
|
|
|
} else {
|
|
printf("error: failed to open /etc/gpio_key.kl, use default map\n");
|
|
num_keys = DEFAULT_KEY_NUM;
|
|
keys_map = g_default_keymap;
|
|
}
|
|
printf("keyboard key map table:\n");
|
|
int i;
|
|
for (i = 0; i < num_keys; ++i) {
|
|
KeyMapItem_t* v = &keys_map[i];
|
|
printf(" %d type:%s value:%d mode:%d\n", i, v->type, v->value, v->mode);
|
|
}
|
|
}
|
|
|
|
int EventsProcess::getMapKey(int key) {
|
|
int i;
|
|
for (i = 0; i < num_keys; i++) {
|
|
KeyMapItem_t* v = &keys_map[i];
|
|
if (v->value == key)
|
|
return v->value;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
long checkEventTime(struct timeval *before, struct timeval *later) {
|
|
time_t before_sec = before->tv_sec;
|
|
suseconds_t before_usec = before->tv_usec;
|
|
time_t later_sec = later->tv_sec;
|
|
suseconds_t later_usec = later->tv_usec;
|
|
|
|
long sec_diff = (later_sec - before_sec) * 1000;
|
|
if (sec_diff < 0)
|
|
return true;
|
|
|
|
long ret = sec_diff + (later_usec - before_usec) / 1000;
|
|
if (ret >= KEY_EVENT_TIME_INTERVAL)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void EventsProcess::EnqueueKey(int key_code, bool flag) {
|
|
struct timeval now;
|
|
gettimeofday(&now, nullptr);
|
|
|
|
pthread_mutex_lock(&key_queue_mutex);
|
|
const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]);
|
|
if (key_queue_len < queue_max) {
|
|
if (last_key != key_code || checkEventTime(&last_queue_time, &now) || flag) {
|
|
key_queue[key_queue_len++] = key_code;
|
|
last_queue_time = now;
|
|
}
|
|
pthread_cond_signal(&key_queue_cond);
|
|
}
|
|
pthread_mutex_unlock(&key_queue_mutex);
|
|
}
|
|
|
|
#ifdef FILTER_POWERKEY
|
|
bool filter_power_key(const char* key_type)
|
|
{
|
|
#define FILTER_KEY_TIME 150 //ms
|
|
static long last_time_ms = -1;
|
|
|
|
if (!strcmp(key_type, "power")) {
|
|
struct timeval now_time;
|
|
gettimeofday(&now_time, nullptr);
|
|
long now_time_ms = now_time.tv_sec*1000 + now_time.tv_usec/1000;
|
|
if (last_time_ms == -1 || (now_time_ms - last_time_ms) >= FILTER_KEY_TIME) {
|
|
last_time_ms = now_time_ms;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
return true;
|
|
}
|
|
#undef FILTER_KEY_TIME
|
|
}
|
|
#endif
|
|
|
|
void EventsProcess::WaitKey() {
|
|
bool cur_longpress = false;
|
|
bool execute = false;
|
|
pthread_mutex_lock(&key_queue_mutex);
|
|
|
|
do {
|
|
struct timeval now;
|
|
struct timespec timeout;
|
|
gettimeofday(&now, nullptr);
|
|
timeout.tv_sec = now.tv_sec;
|
|
timeout.tv_nsec = now.tv_usec * 1000;
|
|
timeout.tv_sec += WAIT_KEY_TIMEOUT_SEC;
|
|
|
|
int rc = 0;
|
|
while (key_queue_len == 0 && rc != ETIMEDOUT) {
|
|
rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex, &timeout);
|
|
}
|
|
} while (key_queue_len == 0);
|
|
|
|
int key = -1;
|
|
KeyEvent *keyEvent = nullptr;
|
|
char* event_str;
|
|
char buf[100];
|
|
if (key_queue_len > 0) {
|
|
key = key_queue[0];
|
|
memmove(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len);
|
|
}
|
|
|
|
std::vector<KeyEvent *>::iterator iter = mKeyEventVec.begin();
|
|
//should remove zombie key down long long time?
|
|
for (; iter != mKeyEventVec.end();) {
|
|
if ((*iter)->keyCode == key && ((*iter)->keyState == 0 || (*iter)->curlongPress)) {
|
|
keyEvent = *iter;
|
|
if (keyEvent->curlongPress)
|
|
cur_longpress = true;
|
|
else
|
|
iter = mKeyEventVec.erase(iter);
|
|
break;
|
|
} else {
|
|
++iter;
|
|
}
|
|
}
|
|
|
|
pthread_mutex_unlock(&key_queue_mutex);
|
|
const char* keyType=getKeyType(key);
|
|
int keymode=getKeyMode(key);
|
|
int res;
|
|
memset(buf,'\0',sizeof(buf));
|
|
if (keyType != NULL) {
|
|
if (keyEvent && cur_longpress && keyEvent->longPress && keymode != 0) {
|
|
sprintf(buf,"%s %s%s","/etc/adckey/adckey_function.sh","curlongpress",keyType);
|
|
execute = true;
|
|
} else if (keyEvent && !cur_longpress && keyEvent->longPress && keymode != 1) {
|
|
uint64_t duration = 0;
|
|
if (keyEvent->up_ts_us < keyEvent->down_ts_us) {
|
|
duration = UINT64_MAX - keyEvent->down_ts_us + keyEvent->up_ts_us;
|
|
duration /= 1000LL;
|
|
} else {
|
|
duration = (keyEvent->up_ts_us - keyEvent->down_ts_us)/1000LL;
|
|
}
|
|
|
|
sprintf(buf,"%s %s%s %llu","/etc/adckey/adckey_function.sh","longpress",keyType, duration);
|
|
execute = true;
|
|
} else if (!(keyEvent && keyEvent->longPress)) {
|
|
sprintf(buf,"%s %s","/etc/adckey/adckey_function.sh",keyType);
|
|
#ifdef FILTER_POWERKEY
|
|
execute = filter_power_key(keyType);
|
|
#else
|
|
execute = true;
|
|
#endif
|
|
}
|
|
if (execute) {
|
|
//printf("input_eventd: run %s\n", buf);
|
|
res=system(buf);
|
|
if (res != 0) printf("run %s exception!!!\n",buf);
|
|
}
|
|
}
|
|
|
|
//release
|
|
if (!cur_longpress)
|
|
delete keyEvent;
|
|
}
|