2017-02-23 10:33:19 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
*
|
|
|
|
* Copyright (C) 2016 Amaury Pouly
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
* KIND, either express or implied.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
#include "button.h"
|
2017-09-10 19:55:35 +00:00
|
|
|
//#define LOGF_ENABLE
|
2017-02-23 10:33:19 +00:00
|
|
|
#include "logf.h"
|
|
|
|
#include "panic.h"
|
|
|
|
#include "backlight.h"
|
|
|
|
|
|
|
|
#include "nwz_keys.h"
|
|
|
|
#include "nwz_ts.h"
|
|
|
|
|
|
|
|
#include <poll.h>
|
|
|
|
#include <dir.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <linux/input.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
2017-09-06 21:30:15 +00:00
|
|
|
#include <system.h>
|
2017-02-23 10:33:19 +00:00
|
|
|
|
|
|
|
/* device types */
|
|
|
|
#define DEV_KEY 0 /* icx_keys driver */
|
|
|
|
#define DEV_TOUCH 1 /* icx_touch_screen driver */
|
|
|
|
|
|
|
|
/* HOLD status */
|
|
|
|
static bool hold_status;
|
|
|
|
/* button bitmap */
|
|
|
|
static int button_bitmap;
|
|
|
|
/* poll() descriptors (up to 2 for now: keys and touchscreen) */
|
|
|
|
#define NR_POLL_DESC 2
|
|
|
|
static struct pollfd poll_fds[NR_POLL_DESC];
|
|
|
|
static nfds_t poll_nfds;
|
|
|
|
int dev_type[NR_POLL_DESC]; /* DEV_* */
|
|
|
|
|
|
|
|
#ifdef HAVE_TOUCHSCREEN
|
|
|
|
/* structure to track touch state */
|
|
|
|
static struct
|
|
|
|
{
|
|
|
|
int x, y; /* current position (valid is touch is true) */
|
|
|
|
int max_x, max_y; /* maximum possible values */
|
|
|
|
int pressure, tool_width; /* current pressure and tool width */
|
|
|
|
int max_pressure, max_tool_width; /* maximum possible values */
|
|
|
|
bool touch; /* is the user touching the screen? */
|
|
|
|
/* the hardware supports "flick" gesture */
|
|
|
|
bool flick; /* was the action a flick? */
|
|
|
|
int flick_x, flick_y; /* if so, this is the flick direction */
|
|
|
|
}ts_state;
|
|
|
|
/* rockbox state, updated from ts state on SYN event */
|
|
|
|
static int touch_x, touch_y;
|
|
|
|
static bool touch_detect;
|
|
|
|
|
|
|
|
/* get touchscreen information and init state */
|
|
|
|
int ts_init_state(int fd)
|
|
|
|
{
|
2017-09-06 21:30:15 +00:00
|
|
|
memset(&ts_state, 0, sizeof(ts_state));
|
2017-02-23 10:33:19 +00:00
|
|
|
struct input_absinfo info;
|
|
|
|
if(ioctl(fd, EVIOCGABS(ABS_X), &info) < 0)
|
|
|
|
return -1;
|
2017-09-06 21:30:15 +00:00
|
|
|
ts_state.max_x = info.maximum;
|
2017-02-23 10:33:19 +00:00
|
|
|
if(ioctl(fd, EVIOCGABS(ABS_Y), &info) < 0)
|
|
|
|
return -1;
|
2017-09-06 21:30:15 +00:00
|
|
|
ts_state.max_y = info.maximum;
|
2017-02-23 10:33:19 +00:00
|
|
|
if(ioctl(fd, EVIOCGABS(ABS_PRESSURE), &info) < 0)
|
|
|
|
return -1;
|
2017-09-06 21:30:15 +00:00
|
|
|
ts_state.max_pressure = info.maximum;
|
2017-02-23 10:33:19 +00:00
|
|
|
if(ioctl(fd, EVIOCGABS(ABS_TOOL_WIDTH), &info) < 0)
|
|
|
|
return -1;
|
2017-09-06 21:30:15 +00:00
|
|
|
ts_state.max_tool_width = info.maximum;
|
2017-02-23 10:33:19 +00:00
|
|
|
touch_detect = false;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-09-06 21:30:15 +00:00
|
|
|
void handle_touch(struct input_event evt)
|
2017-02-23 10:33:19 +00:00
|
|
|
{
|
2017-09-06 21:30:15 +00:00
|
|
|
switch(evt.type)
|
2017-02-23 10:33:19 +00:00
|
|
|
{
|
|
|
|
case EV_SYN:
|
|
|
|
/* on SYN, we copy the state to the rockbox state */
|
2017-09-06 21:30:15 +00:00
|
|
|
touch_x = ts_state.x;
|
|
|
|
touch_y = ts_state.y;
|
2017-02-23 10:33:19 +00:00
|
|
|
/* map coordinate to screen */
|
2017-09-06 21:30:15 +00:00
|
|
|
touch_x = touch_x * LCD_WIDTH / ts_state.max_x;
|
|
|
|
touch_y = touch_y * LCD_HEIGHT / ts_state.max_y;
|
2017-02-23 10:33:19 +00:00
|
|
|
/* don't trust driver reported ranges */
|
2017-09-06 21:30:15 +00:00
|
|
|
touch_x = MAX(0, MIN(touch_x, LCD_WIDTH - 1));
|
|
|
|
touch_y = MAX(0, MIN(touch_y, LCD_HEIGHT - 1));
|
|
|
|
touch_detect = ts_state.touch;
|
2017-02-23 10:33:19 +00:00
|
|
|
/* reset flick */
|
2017-09-06 21:30:15 +00:00
|
|
|
ts_state.flick = false;
|
2017-02-23 10:33:19 +00:00
|
|
|
break;
|
|
|
|
case EV_REL:
|
2017-09-06 21:30:15 +00:00
|
|
|
if(evt.code == REL_RX)
|
|
|
|
ts_state.flick_x = evt.value;
|
|
|
|
else if(evt.code == REL_RY)
|
|
|
|
ts_state.flick_y = evt.value;
|
2017-02-23 10:33:19 +00:00
|
|
|
else
|
|
|
|
break;
|
2017-09-06 21:30:15 +00:00
|
|
|
ts_state.flick = true;
|
2017-02-23 10:33:19 +00:00
|
|
|
break;
|
|
|
|
case EV_ABS:
|
2017-09-06 21:30:15 +00:00
|
|
|
if(evt.code == ABS_X)
|
|
|
|
ts_state.x = evt.value;
|
|
|
|
else if(evt.code == ABS_Y)
|
|
|
|
ts_state.y = evt.value;
|
|
|
|
else if(evt.code == ABS_PRESSURE)
|
|
|
|
ts_state.pressure = evt.value;
|
|
|
|
else if(evt.code == ABS_TOOL_WIDTH)
|
|
|
|
ts_state.tool_width = evt.value;
|
2017-02-23 10:33:19 +00:00
|
|
|
break;
|
|
|
|
case EV_KEY:
|
2017-09-06 21:30:15 +00:00
|
|
|
if(evt.code == BTN_TOUCH)
|
|
|
|
ts_state.touch = evt.value;
|
2017-02-23 10:33:19 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void load_hold_status(int fd)
|
|
|
|
{
|
|
|
|
/* HOLD is reported as the first LED */
|
|
|
|
unsigned long led_hold = 0;
|
|
|
|
if(ioctl(fd, EVIOCGLED(sizeof(led_hold)), &led_hold) < 0)
|
|
|
|
logf("cannot read HOLD status: %s", strerror(errno));
|
|
|
|
hold_status = !!led_hold;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void key_init_state(int fd)
|
|
|
|
{
|
|
|
|
/* the driver knows the HOLD statu at all times */
|
|
|
|
load_hold_status(fd);
|
|
|
|
/* the driver can be queried for button status but the output is garbage
|
|
|
|
* so just assume no keys are pressed */
|
|
|
|
button_bitmap = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void open_input_device(const char *path)
|
|
|
|
{
|
|
|
|
int fd = open(path, O_RDWR);
|
|
|
|
if(fd < 0)
|
|
|
|
return;
|
|
|
|
/* query name */
|
|
|
|
char name[256];
|
|
|
|
if(ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0)
|
|
|
|
{
|
|
|
|
close(fd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(strcmp(name, NWZ_KEY_NAME) == 0)
|
|
|
|
dev_type[poll_nfds] = DEV_KEY;
|
|
|
|
else if(strcmp(name, NWZ_TS_NAME) == 0)
|
|
|
|
dev_type[poll_nfds] = DEV_TOUCH;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* only keep devices we know about */
|
|
|
|
close(fd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* if we found a key driver, we can read the hold status from it (and keep
|
|
|
|
* it updated with events) */
|
|
|
|
if(dev_type[poll_nfds] == DEV_KEY)
|
|
|
|
key_init_state(fd);
|
|
|
|
#ifdef HAVE_TOUCHSCREEN
|
|
|
|
else if(dev_type[poll_nfds] == DEV_TOUCH)
|
|
|
|
ts_init_state(fd);
|
|
|
|
#endif
|
|
|
|
/* fill poll descriptor */
|
|
|
|
poll_fds[poll_nfds].fd = fd;
|
|
|
|
poll_fds[poll_nfds].events = POLLIN;
|
|
|
|
poll_fds[poll_nfds].revents = 0;
|
|
|
|
poll_nfds++;
|
|
|
|
}
|
|
|
|
|
2017-09-10 19:55:35 +00:00
|
|
|
#if defined(SONY_NWZA860)
|
|
|
|
/* keycode -> rockbox button mapping */
|
|
|
|
static int button_map[NWZ_KEY_MASK + 1] =
|
|
|
|
{
|
|
|
|
[0 ... NWZ_KEY_MASK] = 0,
|
|
|
|
[NWZ_KEY_PLAY] = BUTTON_PLAY,
|
|
|
|
[NWZ_KEY_RIGHT] = BUTTON_FF,
|
|
|
|
[NWZ_KEY_LEFT] = BUTTON_REW,
|
|
|
|
[NWZ_KEY_VOL_DOWN] = BUTTON_VOL_DOWN,
|
|
|
|
[NWZ_KEY_VOL_UP] = BUTTON_VOL_UP,
|
|
|
|
[NWZ_KEY_BACK] = BUTTON_BACK,
|
|
|
|
};
|
|
|
|
#else /* SONY_NWZA860 */
|
2017-02-23 10:33:19 +00:00
|
|
|
/* keycode -> rockbox button mapping */
|
|
|
|
static int button_map[NWZ_KEY_MASK + 1] =
|
|
|
|
{
|
|
|
|
[0 ... NWZ_KEY_MASK] = 0,
|
|
|
|
[NWZ_KEY_PLAY] = BUTTON_PLAY,
|
|
|
|
[NWZ_KEY_RIGHT] = BUTTON_RIGHT,
|
|
|
|
[NWZ_KEY_LEFT] = BUTTON_LEFT,
|
|
|
|
[NWZ_KEY_UP] = BUTTON_UP,
|
|
|
|
[NWZ_KEY_DOWN] = BUTTON_DOWN,
|
|
|
|
[NWZ_KEY_ZAPPIN] = 0,
|
|
|
|
[NWZ_KEY_AD0_6] = 0,
|
|
|
|
[NWZ_KEY_AD0_7] = 0,
|
|
|
|
[NWZ_KEY_NONE] = 0,
|
|
|
|
[NWZ_KEY_VOL_DOWN] = BUTTON_VOL_DOWN,
|
|
|
|
[NWZ_KEY_VOL_UP] = BUTTON_VOL_UP,
|
|
|
|
[NWZ_KEY_BACK] = BUTTON_BACK,
|
|
|
|
[NWZ_KEY_OPTION] = BUTTON_POWER,
|
|
|
|
[NWZ_KEY_BT] = 0,
|
|
|
|
[NWZ_KEY_AD1_5] = 0,
|
|
|
|
[NWZ_KEY_AD1_6] = 0,
|
|
|
|
[NWZ_KEY_AD1_7] = 0,
|
|
|
|
};
|
2017-09-10 19:55:35 +00:00
|
|
|
#endif /* SONY_NWZA860 */
|
2017-02-23 10:33:19 +00:00
|
|
|
|
|
|
|
static void handle_key(struct input_event evt)
|
|
|
|
{
|
|
|
|
/* See headers/nwz_keys.h for explanation of Sony's nonstandard interface */
|
|
|
|
int keycode = evt.code & NWZ_KEY_MASK;
|
|
|
|
bool press = (evt.value == 0);
|
|
|
|
if(press)
|
|
|
|
button_bitmap |= button_map[keycode];
|
|
|
|
else
|
|
|
|
button_bitmap &= ~button_map[keycode];
|
|
|
|
bool new_hold_status = !!(evt.code & NWZ_KEY_HOLD_MASK);
|
|
|
|
if(new_hold_status != hold_status)
|
|
|
|
{
|
|
|
|
hold_status = new_hold_status;
|
|
|
|
#ifndef BOOTLOADER
|
|
|
|
backlight_hold_changed(hold_status);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool button_hold(void)
|
|
|
|
{
|
|
|
|
return hold_status;
|
|
|
|
}
|
|
|
|
|
|
|
|
void button_init_device(void)
|
|
|
|
{
|
|
|
|
const char *input_path = "/dev/input";
|
|
|
|
char device_name[PATH_MAX];
|
|
|
|
/* find what input devices are available */
|
|
|
|
DIR* input_dir = opendir(input_path);
|
|
|
|
if(input_dir == NULL)
|
|
|
|
panicf("Cannot read /dev/input directory: %s", strerror(errno));
|
|
|
|
strcpy(device_name, input_path);
|
|
|
|
strcat(device_name, "/");
|
|
|
|
char *device_name_p = device_name + strlen(device_name);
|
|
|
|
struct dirent *dir_entry;
|
|
|
|
while((dir_entry = readdir(input_dir)))
|
|
|
|
{
|
|
|
|
/* skip '.' and '..' entries */
|
|
|
|
if(strcmp(dir_entry->d_name, ".") == 0 || strcmp(dir_entry->d_name, "..") == 0)
|
|
|
|
continue;
|
|
|
|
/* create device full path and open it */
|
|
|
|
strcpy(device_name_p, dir_entry->d_name);
|
|
|
|
open_input_device(device_name);
|
|
|
|
}
|
|
|
|
closedir(input_dir);
|
|
|
|
/* check if we have at least one device */
|
|
|
|
if(poll_nfds == 0)
|
|
|
|
panicf("No input device found");
|
|
|
|
}
|
|
|
|
|
|
|
|
int button_read_device(
|
|
|
|
#ifdef HAVE_BUTTON_DATA
|
|
|
|
int *data
|
|
|
|
#else
|
|
|
|
void
|
|
|
|
#endif
|
|
|
|
)
|
|
|
|
{
|
|
|
|
struct input_event event;
|
|
|
|
/* check if there are any events pending and process them */
|
|
|
|
while(true)
|
|
|
|
{
|
|
|
|
/* stop when there are no more events */
|
|
|
|
if(poll(poll_fds, poll_nfds, 0) == 0)
|
|
|
|
break;
|
|
|
|
for(unsigned int i = 0; i < poll_nfds; i++)
|
|
|
|
{
|
|
|
|
/* only read if we won't block */
|
|
|
|
if(!(poll_fds[i].revents & POLLIN))
|
|
|
|
continue;
|
|
|
|
if(read(poll_fds[i].fd, &event, sizeof(event)) != (int)sizeof(event))
|
|
|
|
continue;
|
|
|
|
if(dev_type[i] == DEV_KEY)
|
|
|
|
handle_key(event);
|
|
|
|
#ifdef HAVE_TOUCHSCREEN
|
|
|
|
else if(dev_type[i] == DEV_TOUCH)
|
|
|
|
handle_touch(event);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
2017-09-10 19:55:35 +00:00
|
|
|
int btns = button_bitmap;
|
2017-02-23 10:33:19 +00:00
|
|
|
#ifdef HAVE_TOUCHSCREEN
|
2017-09-10 19:55:35 +00:00
|
|
|
/* WARNING we must call touchscreen_to_pixels even if there is no touch,
|
|
|
|
* otherwsise *data is not filled with the last position and it breaks
|
|
|
|
* everything */
|
|
|
|
int touch_bitmap = touchscreen_to_pixels(touch_x, touch_y, data);
|
2017-09-06 21:30:15 +00:00
|
|
|
if(touch_detect)
|
2017-09-10 19:55:35 +00:00
|
|
|
btns |= touch_bitmap;
|
2017-02-23 10:33:19 +00:00
|
|
|
#endif
|
2017-09-10 19:55:35 +00:00
|
|
|
return hold_status ? 0 : btns;
|
2017-02-23 10:33:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void nwz_button_reload_after_suspend(void)
|
|
|
|
{
|
|
|
|
/* reinit everything, particularly important for keys and HOLD */
|
|
|
|
for(unsigned int i = 0; i < poll_nfds; i++)
|
|
|
|
{
|
|
|
|
if(dev_type[i] == DEV_KEY)
|
|
|
|
key_init_state(poll_fds[i].fd);
|
|
|
|
#ifdef HAVE_TOUCHSCREEN
|
|
|
|
else if(dev_type[i] == DEV_TOUCH)
|
|
|
|
ts_init_state(poll_fds[i].fd);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void button_close_device(void)
|
|
|
|
{
|
|
|
|
/* close descriptors */
|
|
|
|
for(unsigned int i = 0; i < poll_nfds; i++)
|
|
|
|
close(poll_fds[i].fd);
|
|
|
|
}
|