如果内核未开启相应驱动支持,那么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 文章连接:https://www.cnblogs.com/hfwz/p/16168091.html
2. Gtk
3. Qt
4. FLTK
等
暂时没空整理,先把代码贴上来吧
joypad_input.c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdint.h> #include <stdlib.h> #include <stddef.h> #include <poll.h> #include <signal.h> #include <errno.h> #include <linux/joystick.h> #include <linux/kernel.h> #include <pthread.h> #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