-
Notifications
You must be signed in to change notification settings - Fork 111
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #632 from coderocket/linux-joystick
joystick/gamepad support for Linux
- Loading branch information
Showing
1 changed file
with
300 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,19 +26,313 @@ | |
*/ | ||
|
||
/* Author: [email protected] | ||
/* Author: [email protected] | ||
*/ | ||
|
||
#include "sq.h" | ||
#include <assert.h> | ||
#include <limits.h> | ||
#include <stdio.h> | ||
#include <string.h> | ||
#include <stdint.h> | ||
#include <unistd.h> | ||
#include <stdlib.h> | ||
#include <sys/ioctl.h> | ||
#include <sys/types.h> | ||
#include <fcntl.h> | ||
#include <dirent.h> | ||
#include <errno.h> | ||
#include <poll.h> | ||
#include <linux/input-event-codes.h> | ||
#include <linux/input.h> | ||
#include "JoystickTabletPlugin.h" | ||
|
||
/* we don't have any joysticks */ | ||
#define DEVICE_DIR "/dev/input/by-id/" | ||
#define JOYSTICK_TOKEN "-event-joystick" | ||
#define MAX_JOYSTICK 2 | ||
#define BITS_IN_WORD 32 | ||
|
||
void *joySticks= 0; | ||
#define EVENT_BUF_SIZE 10 | ||
|
||
int joystickRead(int index) { return 0; } | ||
int joystickInit(void) { return 0; } | ||
typedef struct _input_event { | ||
struct timeval time; | ||
unsigned short type; | ||
unsigned short code; | ||
unsigned int value; | ||
} input_event; | ||
|
||
/* we don't have any tablets either */ | ||
typedef struct { | ||
int fd; | ||
int button_index[4]; // key code for each button | ||
int button_state[4]; | ||
struct input_absinfo abs_x; | ||
struct input_absinfo abs_y; | ||
} joystick_state_t ; | ||
|
||
joystick_state_t joystick_state[MAX_JOYSTICK]; | ||
int joystick_count; | ||
|
||
int | ||
testBit(int i, uint32_t* x) { | ||
return x[i / BITS_IN_WORD] & (1 << (i % BITS_IN_WORD)); | ||
} | ||
|
||
int | ||
enumerateJoysticks() | ||
{ | ||
|
||
char dev_path[PATH_MAX]; | ||
struct dirent* current_entry; | ||
DIR* device_dir; | ||
int result; | ||
|
||
result = 1; | ||
errno = 0; | ||
device_dir = opendir(DEVICE_DIR); | ||
|
||
if (device_dir == NULL) { | ||
perror("opendir"); | ||
return 0; | ||
} | ||
|
||
current_entry = readdir(device_dir); | ||
|
||
if (current_entry == NULL) { | ||
perror("readdir"); | ||
result = 0; | ||
} | ||
|
||
while (current_entry != NULL) { | ||
|
||
if (current_entry->d_type == DT_LNK && | ||
strstr(current_entry->d_name, JOYSTICK_TOKEN) != NULL && | ||
strlen(current_entry->d_name) + strlen(DEVICE_DIR) < PATH_MAX + 1 && | ||
joystick_count < MAX_JOYSTICK) { | ||
|
||
dev_path[0] = '\0'; | ||
strcat(dev_path, DEVICE_DIR); | ||
strcat(dev_path, current_entry->d_name); | ||
|
||
joystick_state[joystick_count].fd = open(dev_path, O_RDONLY); | ||
|
||
if (joystick_state[joystick_count].fd != -1) { | ||
++joystick_count; | ||
} | ||
|
||
} | ||
current_entry = readdir(device_dir); | ||
} | ||
|
||
closedir(device_dir); | ||
|
||
return result; | ||
} | ||
|
||
int | ||
detectButtons(joystick_state_t* pj) | ||
{ | ||
|
||
int fd; | ||
|
||
uint32_t supported_keys[KEY_CNT / BITS_IN_WORD + 1] = { 0 }; | ||
|
||
fd = pj->fd; | ||
|
||
if (ioctl(fd, EVIOCGBIT(EV_KEY, KEY_MAX), &supported_keys) == -1) { | ||
perror("ioctl"); | ||
return -1; | ||
} | ||
|
||
if (testBit(BTN_JOYSTICK, supported_keys) == 0 && testBit(BTN_GAMEPAD, supported_keys) == 0) { | ||
|
||
// we did not find any buttons, something is wrong with this device. | ||
return -1; | ||
} | ||
|
||
if (testBit(BTN_JOYSTICK, supported_keys) == 1) { | ||
|
||
// it's an old style joystick device | ||
|
||
pj->button_index[0] = BTN_JOYSTICK; | ||
pj->button_index[1] = BTN_THUMB; | ||
pj->button_index[2] = BTN_THUMB2; | ||
pj->button_index[3] = BTN_TOP; | ||
|
||
} | ||
else { | ||
// it's a modern gamepad device | ||
|
||
pj->button_index[0] = BTN_NORTH; | ||
pj->button_index[1] = BTN_EAST; | ||
pj->button_index[2] = BTN_SOUTH; | ||
pj->button_index[3] = BTN_WEST; | ||
} | ||
|
||
return 1; | ||
} | ||
|
||
int | ||
detectAxes(joystick_state_t* pj) | ||
{ | ||
|
||
int fd; | ||
|
||
uint32_t supported_axes[ABS_CNT / BITS_IN_WORD + 1] = { 0 }; | ||
|
||
fd = pj->fd; | ||
|
||
if (ioctl(fd, EVIOCGBIT(EV_ABS, ABS_MAX), &supported_axes) == -1) { | ||
perror("ioctl"); | ||
return -1; | ||
} | ||
|
||
if (testBit(ABS_X, supported_axes) == 0 || testBit(ABS_Y, supported_axes) == 0) { | ||
// something is wrong, we could not find both required axes | ||
return -1; | ||
} | ||
|
||
return 1; | ||
} | ||
|
||
int | ||
readJoystickState(joystick_state_t* pj) | ||
{ | ||
|
||
if (ioctl(pj->fd, EVIOCGABS(ABS_X), &pj->abs_x) == -1) | ||
return -1; | ||
|
||
if (ioctl(pj->fd, EVIOCGABS(ABS_Y), &pj->abs_y) == -1) | ||
return -1; | ||
|
||
return 1; | ||
} | ||
|
||
int | ||
initializeJoystickState(joystick_state_t* pj) | ||
{ | ||
if (detectButtons(pj) == -1) { | ||
return 0; | ||
} | ||
|
||
if (detectAxes(pj) == -1) { | ||
return 0; | ||
} | ||
|
||
if (readJoystickState(pj) == -1) { | ||
return 0; | ||
} | ||
|
||
return 1; | ||
} | ||
|
||
int | ||
joystickInit(void) | ||
{ | ||
|
||
if (enumerateJoysticks() == 0) { | ||
return 0; | ||
} | ||
|
||
for(int i = 0; i < joystick_count; ++i) { | ||
if(initializeJoystickState(&joystick_state[i]) == 0) | ||
return 0; | ||
} | ||
|
||
return 1; | ||
} | ||
|
||
double | ||
map(double x, double from_begin,double from_end, double to_begin, double | ||
to_end) | ||
{ | ||
return to_begin + (to_end-to_begin)*(x-from_begin)/(from_end-from_begin); | ||
} | ||
|
||
int | ||
readNewEvents(joystick_state_t* pj) | ||
{ | ||
input_event event[EVENT_BUF_SIZE]; | ||
int count; | ||
int i, j; | ||
|
||
count = read(pj->fd, &event, sizeof(input_event)*EVENT_BUF_SIZE); | ||
|
||
for(i = 0 ; i < count / sizeof(input_event) ; ++i) { | ||
|
||
if (event[i].type == EV_KEY) { | ||
for(j = 0 ; j < 4 ; ++j) { | ||
if (pj->button_index[j] == event[i].code) { | ||
pj->button_state[j] = event[i].value; | ||
} | ||
} | ||
} | ||
|
||
if (event[i].type == EV_ABS) { | ||
if (event[i].code == ABS_X) { | ||
pj->abs_x.value = event[i].value; | ||
} | ||
|
||
if (event[i].code == ABS_Y) { | ||
pj->abs_y.value = event[i].value; | ||
} | ||
} | ||
} | ||
return 1; | ||
} | ||
|
||
int | ||
joystickRead(int index) | ||
{ | ||
uint32_t result; | ||
joystick_state_t* pj; | ||
struct pollfd poll_fds; | ||
int ret; | ||
int x, y; | ||
|
||
result = 0; | ||
--index; | ||
|
||
if (index < 0 || index >= joystick_count) | ||
return 0; | ||
|
||
pj = &joystick_state[index]; | ||
|
||
poll_fds.fd = pj->fd; | ||
poll_fds.events = POLLIN; | ||
|
||
ret = poll(&poll_fds, 1, 0); | ||
|
||
if (ret == -1) | ||
return 0; | ||
|
||
if (ret > 0) { | ||
|
||
ret = readNewEvents(pj); | ||
|
||
if (ret == -1) | ||
return 0; | ||
} | ||
|
||
x = (int)map(pj->abs_x.value, pj->abs_x.minimum, pj->abs_x.maximum, 0, 0x7FF); | ||
y = (int)map(pj->abs_y.value, pj->abs_y.minimum, pj->abs_y.maximum, 0, 0x7FF); | ||
|
||
return (1 << 27) | (pj->button_state[0] << 22) | (pj->button_state[1] << 23) | (pj->button_state[2] << 24) | (pj->button_state[3] << 25) | (y << 11) | x; | ||
|
||
} | ||
|
||
int | ||
joystickShutdown(void) | ||
{ | ||
int i; | ||
|
||
for(i = 0; i < joystick_count; ++i) { | ||
close(joystick_state[i].fd); | ||
} | ||
|
||
joystick_count = 0; | ||
} | ||
|
||
|
||
/* we don't have any tablets */ | ||
|
||
int tabletInit(void) | ||
{ | ||
|
@@ -60,7 +354,3 @@ int tabletResultSize(void) | |
return 0; | ||
} | ||
|
||
int joystickShutdown(void) | ||
{ | ||
return 0; | ||
} |