27 #define MAX_JOYSTICKS 10
28 #define EPJOY_DISABLED -2
57 static int joy_to_init = 1;
58 static const char unknown_joystick_name[] =
"Unknown joystick";
61 #define BIT_TO_SET(storage,mask) (storage) |= mask
62 #define BIT_TO_RESET(storage,mask) (storage) &= (4294967295U - mask)
64 #define AXIS_LIMIT_HIGH 20000
65 #define AXIS_LIMIT_LOW 10000
69 static const char joy_cfg_file_header[] =
70 "# Joystick configuration file. Updated by Xep128 every time if new joystick is detected." NL
71 "# You should not change anything, just write extra options after the * and # lines for" NL
72 "# the given joystick or controller you would like to use." NL
73 "# A line starting with '*' marks configuration for the given joystick/controller till" NL
74 "# the next '* line or to the end of the file, if the last one. New models are added" NL
75 "# automatically to this file, if Xep128 detects one." NL
76 "# The syntax is quite strict, you should not use extra spaces or tabs." NL
77 "# Basically you may want to add '1v=8' like options (one per line) to the given place." NL
78 "# In the example, the first number ('1') means the EP joystick number (1 or 2)," NL
79 "# 'v' or 'h' to select axis number as v(ertical) or h(orizonal) control." NL
80 "# The other options are 'a', 'b', 'c' meaning (max of three) buttons (however most EP" NL
81 "# softwares use only one fire ('a'). Here, the number after the '=' sign is button" NL
82 "# number on your joystick/controller to assign (not axis number as with 'v' and 'h')." NL
83 "# Uppercase versions of 'v', 'h', 'a', 'b', 'c' are for selecting an alternative" NL
84 "# controller if you have more than one attached, then the second one is tried to be" NL
85 "# used (ie, with two X-Box controllers connected, you can have one for joy-1 and" NL
86 "# one for joy-2, since the configuration marks per model, and you have two of the" NL
87 "# same models of controller in this case). However, if only one controller of the" NL
88 "# type is detected, upper cased options treated as lower case, possible overwriting" NL
89 "# the functionality! Confusing? Indeed. But it's hard to deal with hot-pluggable system" NL
90 "# having options for possible unknown mappings, with the possibility to use two identical" NL
91 "# models but also different ones, etc ... Also please note, that meaning of axis and button" NL
92 "# numbers varies among operating systems and even with driver versions sometimes :-(" NL NL;
94 static const char joy_cfg_file_name[] =
"@joysticks.cfg";
95 static const char joy_cfg_syntax_error[] =
"Syntax error (line %d) in joystick configuration file:\n%s";
100 static void joy_clear_assignments (
void )
103 for (a = 0; a < 2; a++) {
105 epjoys[a].button_masks[0] = epjoys[a].button_masks[1] = epjoys[a].button_masks[2] = 0;
106 epjoys[a].v_axis_mask = epjoys[a].h_axis_mask = 0;
107 epjoys[a].activated = 0;
117 static void use_and_update_config_file (
void )
119 char path[PATH_MAX + 1];
123 joy_clear_assignments();
130 while (fgets(line,
sizeof line, f)) {
132 if (line[0] ==
'*') {
134 if (joysticks[a].
joy && !strncmp(joysticks[a].
guid_str, line + 1, strlen(joysticks[a].
guid_str))) {
135 joysticks[a].cfg_updated = 1;
138 }
else if (line[0] ==
'+') {
139 char *p = strchr(line,
'=');
140 if (p >= line + 3 && (line[1] ==
'1' || line[1] ==
'2')) {
141 Uint32 param = atoi(p + 1) & 31;
142 int jnum = line[1] -
'1';
145 epjoys[jnum].id =
found[0];
148 epjoys[jnum].v_axis_mask = 1U << param;
151 epjoys[jnum].h_axis_mask = 1U << param;
156 epjoys[jnum].button_masks[line[2] -
'a'] = 1U << param;
164 }
else if (line[0] !=
'#' && line[0] > 32)
175 fprintf(f,
"%s", joy_cfg_file_header);
182 "# Axes = %d, buttons = %d, hats = %d (mapped - if any - as two virtual axes from %d per hats)" NL,
190 INFO_WINDOW(
"Joystick configuration file has been created/appended with new joystick\n%s", path);
192 ERROR_WINDOW(
"Cannot create/append joystick configuration file:\n%s", path);
202 static void set_axis_state (
int joy_id,
Uint32 axis,
int value,
int virtual )
204 Uint32 axis_mask, oldp, oldn;
208 axis = joysticks[joy_id].num_of_axes_orig + axis * 2 +
virtual - 1;
209 axis_mask = 1U << axis;
210 oldp = joysticks[joy_id].axisp;
211 oldn = joysticks[joy_id].axisn;
223 OSD(
"PC joystick #%d axis %d [%c %c]\nmask=%X value=%d virtual=%c",
226 (joysticks[joy_id].
axisn & axis_mask) ?
'-' :
' ',
227 (joysticks[joy_id].
axisp & axis_mask) ?
'+' :
' ',
241 button_mask = 1U <<
button;
242 old = joysticks[joy_id].button;
248 OSD(
"PC joystick #%d button %d\n%s", joy_id,
button,
value ?
"pressed" :
"released");
253 static void set_hat_state (
int joy_id,
Uint32 hat,
int value )
258 if (
value & SDL_HAT_UP)
259 set_axis_state(joy_id, hat, -32000, 1);
260 else if (
value & SDL_HAT_DOWN)
261 set_axis_state(joy_id, hat, 32000, 1);
263 set_axis_state(joy_id, hat, 0, 1);
264 if (
value & SDL_HAT_LEFT)
265 set_axis_state(joy_id, hat, -32000, 2);
266 else if (
value & SDL_HAT_RIGHT)
267 set_axis_state(joy_id, hat, 32000, 2);
269 set_axis_state(joy_id, hat, 0, 2);
274 static void joy_sync (
int joy_id )
279 joysticks[joy_id].button = joysticks[joy_id].axisp = joysticks[joy_id].axisn = 0;
280 for (a = 0; a < 32; a++) {
282 set_axis_state(joy_id, a, SDL_JoystickGetAxis(joysticks[joy_id].
joy, a), 0);
284 set_button_state(joy_id, a, SDL_JoystickGetButton(joysticks[joy_id].
joy, a));
286 set_hat_state(joy_id, a, SDL_JoystickGetHat(joysticks[joy_id].
joy, a));
292 static void joy_detach (
int joy_id )
296 DEBUG(
"JOY: device removed #%d" NL,
299 if (joysticks[joy_id].
haptic)
300 SDL_HapticClose(joysticks[joy_id].
haptic);
301 SDL_JoystickClose(joysticks[joy_id].
joy);
302 joysticks[joy_id].joy = NULL;
303 joysticks[joy_id].haptic = NULL;
304 OSD(
"Joy detached #%d", joy_id);
305 joysticks[joy_id].button = joysticks[joy_id].axisp = joysticks[joy_id].axisn = 0;
310 static void joy_rumble (
int joy_id,
Uint32 len,
int always )
314 if (always || joysticks[joy_id].
rumble == 1) {
315 SDL_HapticRumblePlay(joysticks[joy_id].
haptic, 1.0, len);
316 joysticks[joy_id].rumble = 2;
322 static void joy_attach (
int joy_id )
327 if (joysticks[joy_id].
joy)
329 joysticks[joy_id].joy =
joy = SDL_JoystickOpen(joy_id);
332 joysticks[joy_id].haptic = SDL_HapticOpenFromJoystick(
joy);
333 if (joysticks[joy_id].
haptic)
334 joysticks[joy_id].rumble = SDL_HapticRumbleInit(joysticks[joy_id].
haptic) ? 0 : 1;
336 joysticks[joy_id].rumble = 0;
337 joysticks[joy_id].sdl_index = joy_id;
338 joysticks[joy_id].num_of_buttons = SDL_JoystickNumButtons(
joy);
339 joysticks[joy_id].num_of_axes = SDL_JoystickNumAxes(
joy);
340 joysticks[joy_id].num_of_hats = SDL_JoystickNumHats(
joy);
341 joysticks[joy_id].name = SDL_JoystickName(
joy);
342 if (!joysticks[joy_id].
name)
343 joysticks[joy_id].name = unknown_joystick_name;
344 joysticks[joy_id].guid = SDL_JoystickGetGUID(
joy);
345 SDL_JoystickGetGUIDString(
346 joysticks[joy_id].
guid,
351 joysticks[joy_id].num_of_buttons = 32;
353 joysticks[joy_id].num_of_axes = 32;
355 joysticks[joy_id].num_of_hats = 32;
358 joysticks[joy_id].num_of_hats = 4;
361 joysticks[joy_id].num_of_axes = 32 - (joysticks[joy_id].num_of_hats * 2);
364 DEBUGPRINT(
"JOY: new device added #%d \"%s\" axes=%d buttons=%d (balls=%d) hats=%d guid=%s" NL,
366 joysticks[joy_id].
name,
369 SDL_JoystickNumBalls(
joy),
373 OSD(
"Joy attached:\n#%d %s", joy_id, joysticks[joy_id].
name);
374 joysticks[joy_id].num_of_axes_orig = joysticks[joy_id].num_of_axes;
375 joysticks[joy_id].num_of_axes += joysticks[joy_id].num_of_hats * 2;
377 use_and_update_config_file();
388 joysticks[a].joy = NULL;
389 joysticks[a].haptic = NULL;
390 joysticks[a].button = joysticks[a].axisp = joysticks[a].axisn = 0;
391 joysticks[a].cfg_updated = 0;
393 joy_clear_assignments();
395 for (a = 0; a < 2; a++) {
397 epjoys[a].button_masks[0] = 1;
398 epjoys[a].button_masks[1] = 0;
399 epjoys[a].button_masks[2] = 0;
400 epjoys[a].v_axis_mask = 2;
401 epjoys[a].h_axis_mask = 1;
404 SDL_GameControllerEventState(SDL_DISABLE);
405 SDL_JoystickEventState(SDL_ENABLE);
408 if (e)
switch (e->type) {
409 case SDL_JOYAXISMOTION:
410 set_axis_state(e->jaxis.which, e->jaxis.axis, e->jaxis.value, 0);
412 case SDL_JOYBUTTONDOWN:
413 case SDL_JOYBUTTONUP:
414 set_button_state(e->jbutton.which, e->jbutton.button, e->jbutton.state);
416 case SDL_JOYDEVICEADDED:
417 joy_attach(e->jdevice.which);
419 case SDL_JOYDEVICEREMOVED:
420 joy_detach(e->jdevice.which);
422 case SDL_JOYHATMOTION:
423 set_hat_state(e->jhat.which, e->jhat.hat, e->jhat.value);
439 if (num != 0 && num != 1)
441 switch (epjoys[num].
id) {
450 joy_rumble(epjoys[num].
id, 200, 0);
453 return joysticks[epjoys[num].id].button & epjoys[num].button_masks[0];
455 return joysticks[epjoys[num].id].axisn & epjoys[num].v_axis_mask;
457 return joysticks[epjoys[num].id].axisp & epjoys[num].v_axis_mask;
459 return joysticks[epjoys[num].id].axisn & epjoys[num].h_axis_mask;
461 return joysticks[epjoys[num].id].axisp & epjoys[num].h_axis_mask;
463 return joysticks[epjoys[num].id].button & epjoys[num].button_masks[1];
465 return joysticks[epjoys[num].id].button & epjoys[num].button_masks[2];