176 lines
3.7 KiB
C
176 lines
3.7 KiB
C
#include <stddef.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
#include <linux/input.h>
|
|
#include "scode.h"
|
|
#include "config.h"
|
|
#include "log.h"
|
|
#include "msg_queue.h"
|
|
#include "gpio.h"
|
|
#include "time_func.h"
|
|
|
|
#define INPUT_EVENT_BATCH 16
|
|
|
|
PMSG_QUEUE Sys_Queue = NULL;
|
|
|
|
static pthread_t ithread;
|
|
static volatile sig_atomic_t running = 1;
|
|
static int ts_fd = 0;
|
|
|
|
static UINT32 last_bstate = 0;
|
|
static TIMESTAMP button_event_ok[GPIO_BUTTON_COUNT];
|
|
|
|
static UINT_PTR touch_x = 0;
|
|
static UINT_PTR touch_y = 0;
|
|
static UINT32 touch_nextmsg = WM_TOUCHMOVE;
|
|
|
|
static void poll_buttons(void)
|
|
{
|
|
UINT32 st, down, up, mask;
|
|
UINT_PTR attr;
|
|
TIMESTAMP now;
|
|
|
|
/* poll hardware buttons */
|
|
st = Gpio_read_buttons();
|
|
if (st != last_bstate)
|
|
{
|
|
now = Time_since_start();
|
|
up = last_bstate & ~st;
|
|
down = st & ~last_bstate;
|
|
for (attr = 1, mask = GRB_STATE_BUTTON1; attr <= GPIO_BUTTON_COUNT; attr++, mask <<= 1)
|
|
{
|
|
if (now < button_event_ok[attr - 1])
|
|
continue;
|
|
if (up & mask)
|
|
{
|
|
button_event_ok[attr - 1] = now + Gconfig.button_debounce;
|
|
Mq_post1(Sys_Queue, 0, WM_HWBUTTONUP, attr);
|
|
}
|
|
else if (down & mask)
|
|
Mq_post1(Sys_Queue, 0, WM_HWBUTTONDOWN, attr);
|
|
}
|
|
last_bstate = st;
|
|
}
|
|
}
|
|
|
|
static void poll_touchscreen(void)
|
|
{
|
|
int nb, nev, xerrno, i;
|
|
struct input_event buffer[INPUT_EVENT_BATCH];
|
|
|
|
nb = read(ts_fd, buffer, INPUT_EVENT_BATCH * sizeof(struct input_event));
|
|
if (nb == -1)
|
|
{
|
|
xerrno = errno;
|
|
if ((xerrno != EAGAIN) && (xerrno != EWOULDBLOCK))
|
|
Log(LERROR, "Error reading from touchscreen device (%d)", xerrno);
|
|
return;
|
|
}
|
|
else if (nb == 0)
|
|
{
|
|
Log(LERROR, "Unexpected end of file reading from touchscreen device");
|
|
return;
|
|
}
|
|
nev = nb / sizeof(struct input_event);
|
|
xerrno = nev * sizeof(struct input_event);
|
|
if (nb > xerrno)
|
|
Log(LERROR, "read %d bytes from touchscreen but we can only use %d", nb, xerrno);
|
|
for (i=0; i<nev; i++)
|
|
{
|
|
switch (buffer[i].type)
|
|
{
|
|
case EV_SYN:
|
|
if (buffer[i].code == SYN_REPORT)
|
|
{
|
|
Mq_post2(Sys_Queue, 0, touch_nextmsg, touch_x, touch_y);
|
|
touch_nextmsg = WM_TOUCHMOVE;
|
|
}
|
|
break;
|
|
|
|
case EV_ABS:
|
|
if (buffer[i].code == ABS_X)
|
|
touch_x = buffer[i].value;
|
|
else if (buffer[i].code == ABS_Y)
|
|
touch_y = buffer[i].value;
|
|
break;
|
|
|
|
case EV_KEY:
|
|
if (buffer[i].code == BTN_TOUCH)
|
|
touch_nextmsg = (buffer[i].value ? WM_TOUCHDOWN : WM_TOUCHUP);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void *input_thread(void *arg)
|
|
{
|
|
last_bstate = 0;
|
|
memset(button_event_ok, 0, GPIO_BUTTON_COUNT * sizeof(TIMESTAMP));
|
|
touch_x = touch_y = 0;
|
|
touch_nextmsg = WM_TOUCHMOVE;
|
|
|
|
while (running)
|
|
{
|
|
poll_buttons();
|
|
poll_touchscreen();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void do_disable_input(void)
|
|
{
|
|
running = 0;
|
|
pthread_join(ithread, NULL);
|
|
close(ts_fd);
|
|
ts_fd = -1;
|
|
Mq_destroy(Sys_Queue);
|
|
Sys_Queue = NULL;
|
|
}
|
|
|
|
HRESULT Sys_enable_input(void)
|
|
{
|
|
HRESULT rc = S_OK;
|
|
int threadrc;
|
|
|
|
Sys_Queue = Mq_alloc(Gconfig.sys_mq_length);
|
|
if (!Sys_Queue)
|
|
{
|
|
Log(LFATAL, "Unable to allocate system message queue.");
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
ts_fd = open(Gconfig.touchscreen_device, O_RDONLY|O_NONBLOCK);
|
|
if (ts_fd < 0)
|
|
{
|
|
rc = ERRNO_AS_SCODE;
|
|
Mq_destroy(Sys_Queue);
|
|
Log(LFATAL, "Unable to open touchscreen device (%08X).", rc);
|
|
return rc;
|
|
}
|
|
|
|
running = 1;
|
|
threadrc = pthread_create(&ithread, NULL, input_thread, NULL);
|
|
if (threadrc != 0)
|
|
{
|
|
rc = SCODE_FROM_ERRNO(threadrc);
|
|
close(ts_fd);
|
|
ts_fd = -1;
|
|
Mq_destroy(Sys_Queue);
|
|
Log(LFATAL, "Unable to start system input thread (%08X).", rc);
|
|
}
|
|
if (SUCCEEDED(rc))
|
|
{
|
|
rc = Config_exitfunc(do_disable_input);
|
|
if (FAILED(rc))
|
|
do_disable_input();
|
|
}
|
|
return rc;
|
|
}
|