Skip to content

Commit c9fd365

Browse files
Merge pull request #632 from coderocket/linux-joystick
joystick/gamepad support for Linux
2 parents b930975 + 3709f7e commit c9fd365

File tree

1 file changed

+300
-10
lines changed

1 file changed

+300
-10
lines changed

platforms/unix/plugins/JoystickTabletPlugin/sqUnixJoystickTablet.c

Lines changed: 300 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,313 @@
2626
*/
2727

2828
/* Author: Ian.Piumarta@INRIA.Fr
29+
/* Author: davidf@afeka.ac.il
2930
*/
3031

31-
#include "sq.h"
32+
#include <assert.h>
33+
#include <limits.h>
34+
#include <stdio.h>
35+
#include <string.h>
36+
#include <stdint.h>
37+
#include <unistd.h>
38+
#include <stdlib.h>
39+
#include <sys/ioctl.h>
40+
#include <sys/types.h>
41+
#include <fcntl.h>
42+
#include <dirent.h>
43+
#include <errno.h>
44+
#include <poll.h>
45+
#include <linux/input-event-codes.h>
46+
#include <linux/input.h>
3247
#include "JoystickTabletPlugin.h"
3348

34-
/* we don't have any joysticks */
49+
#define DEVICE_DIR "/dev/input/by-id/"
50+
#define JOYSTICK_TOKEN "-event-joystick"
51+
#define MAX_JOYSTICK 2
52+
#define BITS_IN_WORD 32
3553

36-
void *joySticks= 0;
54+
#define EVENT_BUF_SIZE 10
3755

38-
int joystickRead(int index) { return 0; }
39-
int joystickInit(void) { return 0; }
56+
typedef struct _input_event {
57+
struct timeval time;
58+
unsigned short type;
59+
unsigned short code;
60+
unsigned int value;
61+
} input_event;
4062

41-
/* we don't have any tablets either */
63+
typedef struct {
64+
int fd;
65+
int button_index[4]; // key code for each button
66+
int button_state[4];
67+
struct input_absinfo abs_x;
68+
struct input_absinfo abs_y;
69+
} joystick_state_t ;
70+
71+
joystick_state_t joystick_state[MAX_JOYSTICK];
72+
int joystick_count;
73+
74+
int
75+
testBit(int i, uint32_t* x) {
76+
return x[i / BITS_IN_WORD] & (1 << (i % BITS_IN_WORD));
77+
}
78+
79+
int
80+
enumerateJoysticks()
81+
{
82+
83+
char dev_path[PATH_MAX];
84+
struct dirent* current_entry;
85+
DIR* device_dir;
86+
int result;
87+
88+
result = 1;
89+
errno = 0;
90+
device_dir = opendir(DEVICE_DIR);
91+
92+
if (device_dir == NULL) {
93+
perror("opendir");
94+
return 0;
95+
}
96+
97+
current_entry = readdir(device_dir);
98+
99+
if (current_entry == NULL) {
100+
perror("readdir");
101+
result = 0;
102+
}
103+
104+
while (current_entry != NULL) {
105+
106+
if (current_entry->d_type == DT_LNK &&
107+
strstr(current_entry->d_name, JOYSTICK_TOKEN) != NULL &&
108+
strlen(current_entry->d_name) + strlen(DEVICE_DIR) < PATH_MAX + 1 &&
109+
joystick_count < MAX_JOYSTICK) {
110+
111+
dev_path[0] = '\0';
112+
strcat(dev_path, DEVICE_DIR);
113+
strcat(dev_path, current_entry->d_name);
114+
115+
joystick_state[joystick_count].fd = open(dev_path, O_RDONLY);
116+
117+
if (joystick_state[joystick_count].fd != -1) {
118+
++joystick_count;
119+
}
120+
121+
}
122+
current_entry = readdir(device_dir);
123+
}
124+
125+
closedir(device_dir);
126+
127+
return result;
128+
}
129+
130+
int
131+
detectButtons(joystick_state_t* pj)
132+
{
133+
134+
int fd;
135+
136+
uint32_t supported_keys[KEY_CNT / BITS_IN_WORD + 1] = { 0 };
137+
138+
fd = pj->fd;
139+
140+
if (ioctl(fd, EVIOCGBIT(EV_KEY, KEY_MAX), &supported_keys) == -1) {
141+
perror("ioctl");
142+
return -1;
143+
}
144+
145+
if (testBit(BTN_JOYSTICK, supported_keys) == 0 && testBit(BTN_GAMEPAD, supported_keys) == 0) {
146+
147+
// we did not find any buttons, something is wrong with this device.
148+
return -1;
149+
}
150+
151+
if (testBit(BTN_JOYSTICK, supported_keys) == 1) {
152+
153+
// it's an old style joystick device
154+
155+
pj->button_index[0] = BTN_JOYSTICK;
156+
pj->button_index[1] = BTN_THUMB;
157+
pj->button_index[2] = BTN_THUMB2;
158+
pj->button_index[3] = BTN_TOP;
159+
160+
}
161+
else {
162+
// it's a modern gamepad device
163+
164+
pj->button_index[0] = BTN_NORTH;
165+
pj->button_index[1] = BTN_EAST;
166+
pj->button_index[2] = BTN_SOUTH;
167+
pj->button_index[3] = BTN_WEST;
168+
}
169+
170+
return 1;
171+
}
172+
173+
int
174+
detectAxes(joystick_state_t* pj)
175+
{
176+
177+
int fd;
178+
179+
uint32_t supported_axes[ABS_CNT / BITS_IN_WORD + 1] = { 0 };
180+
181+
fd = pj->fd;
182+
183+
if (ioctl(fd, EVIOCGBIT(EV_ABS, ABS_MAX), &supported_axes) == -1) {
184+
perror("ioctl");
185+
return -1;
186+
}
187+
188+
if (testBit(ABS_X, supported_axes) == 0 || testBit(ABS_Y, supported_axes) == 0) {
189+
// something is wrong, we could not find both required axes
190+
return -1;
191+
}
192+
193+
return 1;
194+
}
195+
196+
int
197+
readJoystickState(joystick_state_t* pj)
198+
{
199+
200+
if (ioctl(pj->fd, EVIOCGABS(ABS_X), &pj->abs_x) == -1)
201+
return -1;
202+
203+
if (ioctl(pj->fd, EVIOCGABS(ABS_Y), &pj->abs_y) == -1)
204+
return -1;
205+
206+
return 1;
207+
}
208+
209+
int
210+
initializeJoystickState(joystick_state_t* pj)
211+
{
212+
if (detectButtons(pj) == -1) {
213+
return 0;
214+
}
215+
216+
if (detectAxes(pj) == -1) {
217+
return 0;
218+
}
219+
220+
if (readJoystickState(pj) == -1) {
221+
return 0;
222+
}
223+
224+
return 1;
225+
}
226+
227+
int
228+
joystickInit(void)
229+
{
230+
231+
if (enumerateJoysticks() == 0) {
232+
return 0;
233+
}
234+
235+
for(int i = 0; i < joystick_count; ++i) {
236+
if(initializeJoystickState(&joystick_state[i]) == 0)
237+
return 0;
238+
}
239+
240+
return 1;
241+
}
242+
243+
double
244+
map(double x, double from_begin,double from_end, double to_begin, double
245+
to_end)
246+
{
247+
return to_begin + (to_end-to_begin)*(x-from_begin)/(from_end-from_begin);
248+
}
249+
250+
int
251+
readNewEvents(joystick_state_t* pj)
252+
{
253+
input_event event[EVENT_BUF_SIZE];
254+
int count;
255+
int i, j;
256+
257+
count = read(pj->fd, &event, sizeof(input_event)*EVENT_BUF_SIZE);
258+
259+
for(i = 0 ; i < count / sizeof(input_event) ; ++i) {
260+
261+
if (event[i].type == EV_KEY) {
262+
for(j = 0 ; j < 4 ; ++j) {
263+
if (pj->button_index[j] == event[i].code) {
264+
pj->button_state[j] = event[i].value;
265+
}
266+
}
267+
}
268+
269+
if (event[i].type == EV_ABS) {
270+
if (event[i].code == ABS_X) {
271+
pj->abs_x.value = event[i].value;
272+
}
273+
274+
if (event[i].code == ABS_Y) {
275+
pj->abs_y.value = event[i].value;
276+
}
277+
}
278+
}
279+
return 1;
280+
}
281+
282+
int
283+
joystickRead(int index)
284+
{
285+
uint32_t result;
286+
joystick_state_t* pj;
287+
struct pollfd poll_fds;
288+
int ret;
289+
int x, y;
290+
291+
result = 0;
292+
--index;
293+
294+
if (index < 0 || index >= joystick_count)
295+
return 0;
296+
297+
pj = &joystick_state[index];
298+
299+
poll_fds.fd = pj->fd;
300+
poll_fds.events = POLLIN;
301+
302+
ret = poll(&poll_fds, 1, 0);
303+
304+
if (ret == -1)
305+
return 0;
306+
307+
if (ret > 0) {
308+
309+
ret = readNewEvents(pj);
310+
311+
if (ret == -1)
312+
return 0;
313+
}
314+
315+
x = (int)map(pj->abs_x.value, pj->abs_x.minimum, pj->abs_x.maximum, 0, 0x7FF);
316+
y = (int)map(pj->abs_y.value, pj->abs_y.minimum, pj->abs_y.maximum, 0, 0x7FF);
317+
318+
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;
319+
320+
}
321+
322+
int
323+
joystickShutdown(void)
324+
{
325+
int i;
326+
327+
for(i = 0; i < joystick_count; ++i) {
328+
close(joystick_state[i].fd);
329+
}
330+
331+
joystick_count = 0;
332+
}
333+
334+
335+
/* we don't have any tablets */
42336

43337
int tabletInit(void)
44338
{
@@ -60,7 +354,3 @@ int tabletResultSize(void)
60354
return 0;
61355
}
62356

63-
int joystickShutdown(void)
64-
{
65-
return 0;
66-
}

0 commit comments

Comments
 (0)