HamsterBear F1C200s Linux Joypad 输入事件适配
HamsterBear F1C200s Linux Joypad 输入事件适配
如果内核未开启相应驱动支持,那么joypad会注册到/dev/input/eventx
,而不是/dev/input/jsx
两者事件结构体有区别,前者对应input_event
,后者对应js_event
,本文使用js_event
struct input_event {
#if (__BITS_PER_LONG != 32 || !defined(__USE_TIME_BITS64)) && !defined(__KERNEL__)
struct timeval time;
#define input_event_sec time.tv_sec
#define input_event_usec time.tv_usec
#else
__kernel_ulong_t __sec;
#if defined(__sparc__) && defined(__arch64__)
unsigned int __usec;
unsigned int __pad;
#else
__kernel_ulong_t __usec;
#endif
#define input_event_sec __sec
#define input_event_usec __usec
#endif
__u16 type;
__u16 code;
__s32 value;
};
struct js_event {
__u32 time; /* event timestamp in milliseconds */
__s16 value; /* value */
__u8 type; /* event type */
__u8 number; /* axis/button number */
};
内核输入驱动位于
(待更)
简单讲一下事件分离过程
(待更)
如何适配到其他gui?
(待更)
1. lvgl 文章连接:
2. Gtk
3. Qt
4. FLTK
等
暂时没空整理,先把代码贴上来吧
joypad_input.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "joypad_input.h"
static struct joypad_device *g_pt_joypad_head = NULL;
static struct joypad_data g_joypad_data;
static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t g_cond = PTHREAD_COND_INITIALIZER;
uint32_t joypad_process_event(struct joypad_data *data)
{
int pjs_event_type = data->jsevent.type;
static uint8_t axis_area;
pjs_event_type = pjs_event_type & ~JS_EVENT_INIT;
switch (pjs_event_type)
{
case JS_EVENT_BUTTON:
if (data->jsevent.value)
{
data->buttons_state |= (1 << data->jsevent.number);
}
else
{
data->buttons_state &= ~(1 << data->jsevent.number);
}
break;
case JS_EVENT_AXIS: /* Only two adjacent keys can be pressed at the same time */
if (data->jsevent.value != 0)
{
data->axis_state |= (1 << (data->jsevent.number + (data->jsevent.value > 0 ? 0:2)));
if(MULTI_KEY_DETECT(data->axis_state, AXIS_KEY_DOWN, AXIS_KEY_RIGHT))
axis_area = AXIS_AREA_DOWN_RIGHT;
else if(MULTI_KEY_DETECT(data->axis_state, AXIS_KEY_RIGHT, AXIS_KEY_TOP))
axis_area = AXIS_AREA_TOP_RIGHT;
else if(MULTI_KEY_DETECT(data->axis_state, AXIS_KEY_DOWN, AXIS_KEY_LEFT))
axis_area = AXIS_AREA_LEFT_DOWN;
else if(MULTI_KEY_DETECT(data->axis_state, AXIS_KEY_LEFT, AXIS_KEY_TOP))
axis_area = AXIS_AREA_LEFT_TOP;
else
axis_area = AXIS_AREA_SINGLE;
pr_debug("axis area : %d\n",axis_area);
}
else
{
switch(axis_area){
case AXIS_AREA_LEFT_DOWN:
if(data->jsevent.number == 0) /* AXIS LEFT RELEASE */
data->axis_state &= ~(AXIS_KEY_LEFT);
else if(data->jsevent.number == 1) /* AXIS DOWN RELEASE */
data->axis_state &= ~(AXIS_KEY_DOWN);
break;
case AXIS_AREA_TOP_RIGHT:
if(data->jsevent.number == 0) /* AXIS RIGHT RELEASE */
data->axis_state &= ~(AXIS_KEY_RIGHT);
else if(data->jsevent.number == 1) /* AXIS TOP RELEASE */
data->axis_state &= ~(AXIS_KEY_TOP);
break;
case AXIS_AREA_LEFT_TOP:
if(data->jsevent.number == 0) /* AXIS RIGHT RELEASE */
data->axis_state &= ~(AXIS_KEY_LEFT);
else if(data->jsevent.number == 1) /* AXIS TOP RELEASE */
data->axis_state &= ~(AXIS_KEY_TOP);
break;
case AXIS_AREA_DOWN_RIGHT:
if(data->jsevent.number == 0) /* AXIS RIGHT RELEASE */
data->axis_state &= ~(AXIS_KEY_RIGHT);
else if(data->jsevent.number == 1) /* AXIS TOP RELEASE */
data->axis_state &= ~(AXIS_KEY_DOWN);
break;
case AXIS_AREA_SINGLE:
data->axis_state = 0;
break;
default:
data->axis_state = 0;
break;
}
}
break;
default:
break;
}
pr_debug("axis : %d, button : %d\n", data->axis_state, data->buttons_state);
return (data->combined_state = (data->axis_state << 16 | data->buttons_state));
}
static unsigned int usb_joypad_get_event(struct joypad_data *pdata)
{
uint32_t state = 0;
if (!pdata)
{
pr_debug("joypad_data has no memory!\n");
return -1;
}
if (-1 == pdata->joypad_fd)
{
pr_debug("joypad has not been opened!\n");
}
if(read(pdata->joypad_fd, &pdata->jsevent, sizeof(struct js_event)) > 0)
{
joypad_process_event(pdata);
pr_debug("time : %u\n",pdata->jsevent.time)
pr_debug("value : %d\n",pdata->jsevent.value)
pr_debug("type : %d\n",pdata->jsevent.type)
pr_debug("number : %d\n",pdata->jsevent.number)
}
/* EAGAIN is returned when the queue is empty */
if (errno != EAGAIN)
{
/* error */
}
return state;
}
static int usb_joypad_init(struct joypad_data *pdata)
{
int flag; /* fcntl flag */
struct joypad_device *pdev = container_of(pdata, struct joypad_device, data);
/* device open */
pr_debug("%s, device opening\n", DEFAULT_USB_JOYPAD_PATH);
pdata->joypad_fd = open(DEFAULT_USB_JOYPAD_PATH, O_RDONLY);
if (-1 == pdata->joypad_fd)
{
pr_debug("%s device not found\n", DEFAULT_USB_JOYPAD_PATH);
return -ENODEV;
}
pr_debug("device init done\n");
return 0;
}
static int usb_joypad_exit(struct joypad_data *pdata)
{
if (pdata)
{
close(pdata->joypad_fd);
free(pdata);
}
pr_debug("device exit done\n");
return 0;
}
static struct joypad_device joypad_devs[] = {
[0] = {
.name = "usb_joypad",
.number = 0,
.type = JOYPAD_TYPE_USB,
.ops = {
.init = usb_joypad_init,
.exit = usb_joypad_exit,
.get_event = usb_joypad_get_event,
},
},
};
static void *joypad_input_thread_function(void *privdata)
{
struct joypad_device *pdev = (struct joypad_device *)privdata;
while(1){
pdev->ops.get_event(&pdev->data);
pthread_mutex_lock(&g_mutex);
g_joypad_data = pdev->data;
pthread_cond_signal(&g_cond);
pthread_mutex_unlock(&g_mutex);
}
}
static int register_joypad_device(struct joypad_device *pdev)
{
struct joypad_device *p_tmp;
pr_debug("registing %s\n", pdev->name);
if (!pdev)
{
return -EINVAL;
}
/* call current device init*/
if (pdev->ops.init && pdev->ops.init(&pdev->data))
{
pr_debug("device init failed, %s\n", pdev->name);
return -ENODEV;
}
/* create a thread to read event */
pthread_create(&pdev->tid, NULL, joypad_input_thread_function, (void *)pdev);
if (!g_pt_joypad_head)
{ /* first node */
g_pt_joypad_head = pdev;
}
else
{
p_tmp = g_pt_joypad_head;
while (p_tmp->p_next)
{
p_tmp = p_tmp->p_next;
}
p_tmp->p_next = pdev;
}
pdev->p_next = NULL;
return 0;
}
static int unregister_joypad_devices()
{
int ret;
struct joypad_device *p_tmp;
if (!g_pt_joypad_head){
return -1;
}
p_tmp = g_pt_joypad_head;
while(p_tmp){
/* because joydevs was alloc staticly, so we can't free it */
/* call each device exit */
if (p_tmp->ops.exit && p_tmp->ops.exit(&p_tmp->data))
{
pr_debug("device exit failed, %s\n", p_tmp->name);
}
p_tmp = p_tmp->p_next;
}
/* list head remove */
g_pt_joypad_head = NULL;
return 0;
}
int joypad_input_init(void)
{
int ret = 0;
for (int i = 0; i < ARRAY_SIZE(joypad_devs); i++)
{
ret = register_joypad_device(&joypad_devs[i]);
if(ret < 0)
pr_debug("failed to register %s\n", joypad_devs[i].name);
}
return ret;
}
int joypad_input_exit(void)
{
int ret = 0;
ret = unregister_joypad_devices();
return ret;
}
struct joypad_data joypad_input_get_event(void)
{
pthread_mutex_lock(&g_mutex);
pthread_cond_wait(&g_cond, &g_mutex);
pthread_mutex_unlock(&g_mutex);
return g_joypad_data;
}
#if 1
int main(int argc, char **argv)
{
int ret;
uint32_t state;
uint16_t buttons;
uint16_t axis;
uint8_t axis_high;
uint8_t axis_low;
ret = joypad_input_init();
if (ret < 0)
return -1;
while (1)
{
/*
state = USBjoypadGet();
`
buttons = state & 0xffff;
axis = state >> 16;
printf("state: %d, buttons: %d, axis: %d\n", state, buttons, axis);
if(state & JOYPAD_KEY_X){
printf("key X pressed!\n");
}
if(state & JOYPAD_KEY_A){
printf("key A pressed!\n");
}
if(state & JOYPAD_KEY_B){
printf("key B pressed!\n");
}
if(state & JOYPAD_KEY_Y){
printf("key Y pressed!\n");
}
if(state & JOYPAD_KEY_NL){
printf("key NL pressed!\n");
}
if(state & JOYPAD_KEY_NR){
printf("key NR pressed!\n");
}
if(state & JOYPAD_KEY_DOWN){
printf("key DOWN pressed!\n");
}
if(state & JOYPAD_KEY_START){
printf("key START pressed!\n");
}
*/
}
joypad_input_exit();
return 0;
}
#endif
joypad_input.h
#ifndef __JOYPAD_INPUT_H
#define __JOYPAD_INPUT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* DEFINES
*********************/
#define DEFAULT_USB_JOYPAD_PATH "/dev/input/js0"
#define BIT(x) (1 << x)
#define JOYPAD_KEY_X BIT(0)
#define JOYPAD_KEY_A BIT(1)
#define JOYPAD_KEY_B BIT(2)
#define JOYPAD_KEY_Y BIT(3)
#define JOYPAD_KEY_NL BIT(4)
#define JOYPAD_KEY_NR BIT(5)
#define JOYPAD_KEY_LEFT BIT(16)
#define JOYPAD_KEY_TOP BIT(17)
#define JOYPAD_KEY_RIGHT BIT(18)
#define JOYPAD_KEY_DOWN BIT(19)
#define JOYPAD_KEY_SELECT BIT(8)
#define JOYPAD_KEY_START BIT(9)
#define DEBUG 1
#if DEBUG
#define pr_debug(fmt, ...) printf("%s: "fmt, __func__, ##__VA_ARGS__);
#else
#define pr_debug
#endif
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#ifndef container_of
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) * __mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
#endif
#define MULTI_KEY_DETECT(state, key1, key2) (state == (key1 | key2))
/**********************
* TYPEDEFS
**********************/
struct joypad_data {
int joypad_fd;
uint16_t buttons_state;
uint16_t axis_state;
uint32_t combined_state;
struct js_event jsevent;
};
struct joypad_operations {
int (*init)(struct joypad_data *pdev);
int (*exit)(struct joypad_data *pdev);
unsigned int (*get_event)(struct joypad_data *pdev);
};
struct joypad_device {
uint8_t number;
uint8_t *name;
uint8_t type;
pthread_t tid;
struct joypad_data data;
struct joypad_operations ops;
struct joypad_device *p_next;
};
struct joypad_key{
uint8_t num:7;
uint8_t state:1;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* GLOBAL VALUES
**********************/
enum {
JOYPAD_TYPE_USB = 0x00,
};
enum {
AXIS_AREA_LEFT_DOWN = 0x00,
AXIS_AREA_TOP_RIGHT = 0x01,
AXIS_AREA_LEFT_TOP = 0x02,
AXIS_AREA_DOWN_RIGHT = 0x03,
AXIS_AREA_SINGLE = 0x04,
};
enum {
AXIS_KEY_RIGHT = BIT(0),
AXIS_KEY_DOWN = BIT(1),
AXIS_KEY_LEFT = BIT(2),
AXIS_KEY_TOP = BIT(3),
};
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif