diff --git a/addons/acodec/dumb.c b/addons/acodec/dumb.c index 5ea879908..05649d614 100644 --- a/addons/acodec/dumb.c +++ b/addons/acodec/dumb.c @@ -6,6 +6,7 @@ #define _FILE_OFFSET_BITS 64 #include +#define ALLEGRO_INTERNAL_UNSTABLE #include "allegro5/allegro.h" #include "allegro5/allegro_acodec.h" #include "allegro5/allegro_audio.h" diff --git a/addons/acodec/flac.c b/addons/acodec/flac.c index 1b764654a..a1345e0b4 100644 --- a/addons/acodec/flac.c +++ b/addons/acodec/flac.c @@ -10,8 +10,6 @@ #include "allegro5/allegro_audio.h" #include "allegro5/internal/aintern.h" #include "allegro5/internal/aintern_audio.h" -#include "allegro5/internal/aintern_exitfunc.h" -#include "allegro5/internal/aintern_system.h" #include "acodec.h" #include "helper.h" diff --git a/addons/acodec/helper.c b/addons/acodec/helper.c index bcf16c05c..ca3f8f758 100644 --- a/addons/acodec/helper.c +++ b/addons/acodec/helper.c @@ -2,7 +2,6 @@ #include "allegro5/allegro_audio.h" #include "allegro5/internal/aintern.h" #include "allegro5/internal/aintern_audio.h" -#include "allegro5/internal/aintern_system.h" #include "helper.h" void _al_acodec_start_feed_thread(ALLEGRO_AUDIO_STREAM *stream) diff --git a/addons/acodec/mp3.c b/addons/acodec/mp3.c index 160ee32ef..2631aaf8d 100644 --- a/addons/acodec/mp3.c +++ b/addons/acodec/mp3.c @@ -10,8 +10,6 @@ #include "allegro5/allegro_audio.h" #include "allegro5/internal/aintern.h" #include "allegro5/internal/aintern_audio.h" -#include "allegro5/internal/aintern_exitfunc.h" -#include "allegro5/internal/aintern_system.h" #include "acodec.h" #include "helper.h" diff --git a/addons/acodec/ogg.c b/addons/acodec/ogg.c index a44eff861..d35b49e0a 100644 --- a/addons/acodec/ogg.c +++ b/addons/acodec/ogg.c @@ -10,8 +10,6 @@ #include "allegro5/allegro_audio.h" #include "allegro5/internal/aintern.h" #include "allegro5/internal/aintern_audio.h" -#include "allegro5/internal/aintern_exitfunc.h" -#include "allegro5/internal/aintern_system.h" #include "acodec.h" #include "helper.h" diff --git a/addons/acodec/openmpt.c b/addons/acodec/openmpt.c index 80fe54582..5d51a1cf4 100644 --- a/addons/acodec/openmpt.c +++ b/addons/acodec/openmpt.c @@ -3,6 +3,7 @@ * author: Pavel Sountsov */ +#define ALLEGRO_INTERNAL_UNSTABLE #define _FILE_OFFSET_BITS 64 #include diff --git a/addons/acodec/opus.c b/addons/acodec/opus.c index b01b26b15..5a600828c 100644 --- a/addons/acodec/opus.c +++ b/addons/acodec/opus.c @@ -9,8 +9,6 @@ #include "allegro5/allegro_audio.h" #include "allegro5/internal/aintern.h" #include "allegro5/internal/aintern_audio.h" -#include "allegro5/internal/aintern_exitfunc.h" -#include "allegro5/internal/aintern_system.h" #include "acodec.h" #include "helper.h" diff --git a/addons/audio/dsound.cpp b/addons/audio/dsound.cpp index 5d39899bf..fbd747446 100644 --- a/addons/audio/dsound.cpp +++ b/addons/audio/dsound.cpp @@ -32,6 +32,8 @@ const IID GUID_NULL = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } }; static const IID _al_IID_IDirectSoundBuffer8 = { 0x6825a449, 0x7524, 0x4d82, { 0x92, 0x0f, 0x50, 0xe3, 0x6a, 0xb3, 0xab, 0x1e } }; static const IID _al_IID_IDirectSoundCaptureBuffer8 = { 0x00990df4, 0x0dbb, 0x4872, { 0x83, 0x3e, 0x6d, 0x30, 0x3e, 0x80, 0xae, 0xb6 } }; + +#define ALLEGRO_INTERNAL_UNSTABLE #include "allegro5/allegro.h" extern "C" { diff --git a/addons/font/fontbmp.c b/addons/font/fontbmp.c index 6d37de18a..446e8644d 100644 --- a/addons/font/fontbmp.c +++ b/addons/font/fontbmp.c @@ -16,6 +16,8 @@ */ +#define ALLEGRO_INTERNAL_UNSTABLE + #include #include "allegro5/allegro.h" diff --git a/addons/font/text.c b/addons/font/text.c index c4676fd04..64d19c958 100644 --- a/addons/font/text.c +++ b/addons/font/text.c @@ -20,6 +20,8 @@ */ +#define ALLEGRO_INTERNAL_UNSTABLE + #include #include #include "allegro5/allegro.h" diff --git a/addons/image/android.c b/addons/image/android.c index af4c678dc..b9d0aa0aa 100644 --- a/addons/image/android.c +++ b/addons/image/android.c @@ -1,3 +1,4 @@ +#define ALLEGRO_INTERNAL_UNSTABLE #include "allegro5/allegro.h" #include "allegro5/allegro_image.h" #include "allegro5/internal/aintern_android.h" diff --git a/addons/native_dialog/android_dialog.c b/addons/native_dialog/android_dialog.c index 10939bc3f..9ab110cfb 100644 --- a/addons/native_dialog/android_dialog.c +++ b/addons/native_dialog/android_dialog.c @@ -13,6 +13,7 @@ * By Alexandre Martins. */ +#define ALLEGRO_INTERNAL_UNSTABLE #include "allegro5/allegro.h" #include "allegro5/allegro_native_dialog.h" #include "allegro5/internal/aintern_android.h" diff --git a/addons/native_dialog/dialog.c b/addons/native_dialog/dialog.c index 85142c161..18246601b 100644 --- a/addons/native_dialog/dialog.c +++ b/addons/native_dialog/dialog.c @@ -1,3 +1,5 @@ +#define ALLEGRO_INTERNAL_UNSTABLE + #include "allegro5/allegro.h" #include "allegro5/allegro_native_dialog.h" #include "allegro5/internal/aintern_dtor.h" diff --git a/addons/native_dialog/menu.c b/addons/native_dialog/menu.c index 8a5b7e6f5..fc5904faa 100644 --- a/addons/native_dialog/menu.c +++ b/addons/native_dialog/menu.c @@ -1,3 +1,5 @@ +#define ALLEGRO_INTERNAL_UNSTABLE + #include "allegro5/allegro.h" #include "allegro5/allegro_native_dialog.h" #include "allegro5/internal/aintern_native_dialog.h" diff --git a/addons/native_dialog/osx_dialog.m b/addons/native_dialog/osx_dialog.m index 5bae62fc3..2a9ef2aab 100644 --- a/addons/native_dialog/osx_dialog.m +++ b/addons/native_dialog/osx_dialog.m @@ -4,6 +4,8 @@ #if MAC_OS_X_VERSION_MIN_REQUIRED >= 110000 #include #endif + +#define ALLEGRO_INTERNAL_UNSTABLE #include "allegro5/allegro.h" #include "allegro5/allegro_native_dialog.h" #include "allegro5/internal/aintern_native_dialog.h" diff --git a/addons/native_dialog/textlog.c b/addons/native_dialog/textlog.c index 088cefda5..315599a49 100644 --- a/addons/native_dialog/textlog.c +++ b/addons/native_dialog/textlog.c @@ -1,3 +1,5 @@ +#define ALLEGRO_INTERNAL_UNSTABLE + #include #include #include "allegro5/allegro.h" diff --git a/addons/native_dialog/win_dialog.c b/addons/native_dialog/win_dialog.c index 57c38c72e..efd4d1b9b 100644 --- a/addons/native_dialog/win_dialog.c +++ b/addons/native_dialog/win_dialog.c @@ -8,6 +8,7 @@ * */ +#define ALLEGRO_INTERNAL_UNSTABLE #include "allegro5/allegro.h" #include "allegro5/allegro_native_dialog.h" #include "allegro5/internal/aintern.h" diff --git a/addons/video/video.c b/addons/video/video.c index 9c688fe0a..0f2d1cc53 100644 --- a/addons/video/video.c +++ b/addons/video/video.c @@ -37,6 +37,7 @@ * - Additional drivers. Also redo the API a bit so not everything * has to be done by the driver. */ +#define ALLEGRO_INTERNAL_UNSTABLE #include "allegro5/allegro5.h" #include "allegro5/allegro_video.h" diff --git a/allegro5.cfg b/allegro5.cfg index a67b00e80..a028f4c78 100644 --- a/allegro5.cfg +++ b/allegro5.cfg @@ -146,7 +146,7 @@ webp_quality_level = lossless # joysticks and force feedback joysticks. Xinput is the more modern # system, but DirectInput has more force feedback capabilities for older # joysticks. -driver=XINPUT +driver= # Windows: Use this to force an XInput DLL version, example "3" forces # xinput1_3.dll. By default, the latest version is used. diff --git a/cmake/FileList.cmake b/cmake/FileList.cmake index beed6cafd..de671f6d0 100644 --- a/cmake/FileList.cmake +++ b/cmake/FileList.cmake @@ -134,7 +134,6 @@ set(ALLEGRO_SRC_X_FILES set(ALLEGRO_SRC_MACOSX_FILES src/macosx/hidjoy.m - src/macosx/hidjoy-10.4.m src/macosx/hidman.m src/macosx/keybd.m src/macosx/qzmouse.m diff --git a/docs/src/refman/joystick.txt b/docs/src/refman/joystick.txt index ef3ff0a6b..8699a9f16 100644 --- a/docs/src/refman/joystick.txt +++ b/docs/src/refman/joystick.txt @@ -6,13 +6,11 @@ These functions are declared in the main Allegro header file: #include ~~~~ -On Windows there are two joystick drivers, a DirectInput one and an Xinput -one. If support for XInput was compiled in, then it can be enabled by -calling al_set_config_value(al_get_system_config(), "joystick", "driver", -"xinput") before calling al_install_joystick, or by setting the same -option in the allegro5.cfg configuration file. The Xinput and DirectInput -drivers are mutually exclusive. The haptics subsystem will use the -same driver as the joystick system does. +As of version 5.2.11, Allegro provides two types of joysticks. If a joystick is +recognized, with the help of a mapping file specified by +`al_set_joystick_mappings`, then it is treated as a gamepad, will buttons and +axes behaving in a cross-platform consistent manner. + ## API: ALLEGRO_JOYSTICK @@ -40,10 +38,66 @@ See also: [al_get_joystick_state] * ALLEGRO_JOYFLAG_DIGITAL - the stick provides digital input * ALLEGRO_JOYFLAG_ANALOGUE - the stick provides analogue input -(this enum is a holdover from the old API and may be removed) - See also: [al_get_joystick_stick_flags] +## API: ALLEGRO_JOYSTICK_TYPE + +* ALLEGRO_JOYSTICK_TYPE_UNKNOWN - unknown joystick type +* ALLEGRO_JOYSTICK_TYPE_GAMEPAD - a gamepad. This buttons and sticks have + semantics specified by `ALLEGRO_GAMEPAD_BUTTON` and `ALLEGRO_GAMEPAD_STICK`. + +Since: 5.2.11 + +See also: [al_set_joystick_mappings], [ALLEGRO_GAMEPAD_BUTTON], [ALLEGRO_GAMEPAD_STICK] + +> *[Unstable API]:* New API. + +## API: ALLEGRO_JOYSTICK_GUID + +A GUID associated with a joystick. + +Since: 5.2.11 + +See also: [al_set_joystick_guid] + +> *[Unstable API]:* New API. + +## API: ALLEGRO_GAMEPAD_BUTTON + +* ALLEGRO_GAMEPAD_BUTTON_A - A button +* ALLEGRO_GAMEPAD_BUTTON_B - B button +* ALLEGRO_GAMEPAD_BUTTON_X - X button +* ALLEGRO_GAMEPAD_BUTTON_Y - Y button +* ALLEGRO_GAMEPAD_BUTTON_LEFT_SHOULDER - left shoulder button +* ALLEGRO_GAMEPAD_BUTTON_RIGHT_SHOULDER - right shoulder button +* ALLEGRO_GAMEPAD_BUTTON_BACK - back button (also called select sometimes) +* ALLEGRO_GAMEPAD_BUTTON_START - start button +* ALLEGRO_GAMEPAD_BUTTON_GUIDE - guide/home button (this sometimes is + intercepted on the OS level, so do not rely on being able to handle it) +* ALLEGRO_GAMEPAD_BUTTON_LEFT_THUMB - button on the left thumb stick +* ALLEGRO_GAMEPAD_BUTTON_RIGHT_THUMB - button on the right thumb stick + +Since: 5.2.11 + +See also: [al_set_joystick_mappings], [ALLEGRO_JOYSTICK_TYPE] + +> *[Unstable API]:* New API. + +## API: ALLEGRO_GAMEPAD_STICK + +* ALLEGRO_GAMEPAD_STICK_DPAD - D-pad stick, it has the + `ALLEGRO_JOYFLAG_DIGITAL` set. It has two axes. +* ALLEGRO_GAMEPAD_STICK_LEFT_THUMB - Left thumb stick. It has two axes. +* ALLEGRO_GAMEPAD_STICK_RIGHT_THUMB - Right thumb stick. It has two axes. +* ALLEGRO_GAMEPAD_STICK_LEFT_TRIGGER - Left trigger. It has one axis, ranging from 0 to 1. +* ALLEGRO_GAMEPAD_STICK_RIGHT_TRIGGER - Right trigger. It has one axis, ranging from 0 to 1. + +Since: 5.2.11 + +See also: [al_set_joystick_mappings], [ALLEGRO_JOYSTICK_TYPE] + +> *[Unstable API]:* New API. + ## API: al_install_joystick Install a joystick driver, returning true if successful. If a @@ -202,3 +256,54 @@ See also: [ALLEGRO_JOYSTICK_STATE], [al_get_joystick_num_buttons], Returns the global joystick event source. All [joystick events][ALLEGRO_EVENT_JOYSTICK_AXIS] are generated by this event source. + +## API: al_get_joystick_type + +Returns the joystick type. If the type is `ALLEGRO_JOYSTICK_TYPE_GAMEPAD`, then +you can use the [ALLEGRO_GAMEPAD_BUTTON] and [ALLEGRO_GAMEPAD_STICK] to +interpret the button/stick events and states more easily. Joystick types will +typically be unknown unless [al_set_joystick_mappings] is called first. + +Since: 5.2.11 + +See also: [al_set_joystick_mappings], [ALLEGRO_JOYSTICK_TYPE] + +> *[Unstable API]:* New API. + +## API: al_get_joystick_guid + +Returns the joystick GUID, that can be used to distinguish joysticks hardware. +Despite the name, these do not need to be actually unique to do the limitations +of the system that computes them. This can be all zeros when unknown. + +Since: 5.2.11 + +See also: [ALLEGRO_JOYSTICK_GUID] + +> *[Unstable API]:* New API. + +## API: al_set_joystick_mappings + +Sets the joystick mappings from a file. This can be called multiple times, +adding to and overriding existing mappings. This uses the SDL version 2.0.16 +format mappings. This can and typically should be called before calling +`al_install_joystick`. + +Since: 5.2.11 + +See also: [al_set_joystick_mappings_f] + +> *[Unstable API]:* New API. + +## API: al_set_joystick_mappings_f + +Sets the joystick mappings from a file object. This can be called multiple +times, adding to and overriding existing mappings. This uses the SDL version +2.0.16 format mappings. This can and typically should be called before calling +`al_install_joystick`. + +Since: 5.2.11 + +See also: [al_set_joystick_mappings_f] + +> *[Unstable API]:* New API. diff --git a/examples/ex_joystick_events.c b/examples/ex_joystick_events.c index e996ed5c1..8994ad3f4 100644 --- a/examples/ex_joystick_events.c +++ b/examples/ex_joystick_events.c @@ -4,6 +4,7 @@ * This program tests joystick events. */ +#define ALLEGRO_UNSTABLE #include #include #include @@ -20,75 +21,94 @@ ALLEGRO_EVENT_QUEUE *event_queue; ALLEGRO_FONT *font; ALLEGRO_COLOR black; ALLEGRO_COLOR grey; +ALLEGRO_COLOR blue; ALLEGRO_COLOR white; int num_sticks = 0; int num_buttons = 0; +char *guid_str = NULL; int num_axes[MAX_STICKS] = { 0 }; -float joys[MAX_STICKS][MAX_AXES] = {{ 0 }}; -bool joys_buttons[MAX_BUTTONS] = { 0 }; -static void setup_joystick_values(ALLEGRO_JOYSTICK *joy) +static char *guid_to_str(ALLEGRO_JOYSTICK_GUID guid) { - ALLEGRO_JOYSTICK_STATE jst; - int i, j; + char *ret = al_malloc(33); + char *str = ret; + const char chars[] = "0123456789abcdef"; + for (int i = 0; i < (int)sizeof(guid.val); i++) { + *str++ = chars[guid.val[i] >> 4]; + *str++ = chars[guid.val[i] & 0xf]; + } + *str = '\0'; + return ret; +} + +static void setup_joystick_values(ALLEGRO_JOYSTICK *joy, ALLEGRO_JOYSTICK_STATE *state) +{ + int i; + memset(state, 0, sizeof(ALLEGRO_JOYSTICK_STATE)); if (joy == NULL) { num_sticks = 0; num_buttons = 0; return; } - al_get_joystick_state(joy, &jst); - + al_get_joystick_state(joy, state); num_sticks = al_get_joystick_num_sticks(joy); - if (num_sticks > MAX_STICKS) - num_sticks = MAX_STICKS; + num_buttons = al_get_joystick_num_buttons(joy); for (i = 0; i < num_sticks; i++) { num_axes[i] = al_get_joystick_num_axes(joy, i); - for (j = 0; j < num_axes[i]; ++j) - joys[i][j] = jst.stick[i].axis[j]; } +} - num_buttons = al_get_joystick_num_buttons(joy); - if (num_buttons > MAX_BUTTONS) { - num_buttons = MAX_BUTTONS; - } - for (i = 0; i < num_buttons; i++) { - joys_buttons[i] = (jst.button[i] >= 16384); + +static void setup_joystick_all_values(ALLEGRO_JOYSTICK *joy, ALLEGRO_JOYSTICK_STATE *state1, ALLEGRO_JOYSTICK_STATE *state2) +{ + if (joy) { + al_free(guid_str); + guid_str = guid_to_str(al_get_joystick_guid(al_get_joystick(0))); + log_printf("Joystick GUID: %s\n", guid_str); } + setup_joystick_values(joy, state1); + setup_joystick_values(joy, state2); } -static void draw_joystick_axes(ALLEGRO_JOYSTICK *joy, int cx, int cy, int stick) +static void draw_joystick_axes(ALLEGRO_JOYSTICK *joy, int cx, int cy, int stick, bool first, ALLEGRO_JOYSTICK_STATE *state) { const int size = 30; const int csize = 5; const int osize = size + csize; + int rsize = first ? 5 : 3; + ALLEGRO_COLOR rcolor = first ? black : blue; int zx = cx + osize + csize * 2; - int x = cx + joys[stick][0] * size; - int y = cy + joys[stick][1] * size; - int z = cy + joys[stick][2] * size; + int x = cx + state->stick[stick].axis[0] * size; + int y = cy + state->stick[stick].axis[1] * size; + int z = cy + state->stick[stick].axis[2] * size; int i; - al_draw_filled_rectangle(cx-osize, cy-osize, cx+osize, cy+osize, grey); - al_draw_rectangle(cx-osize+0.5, cy-osize+0.5, cx+osize-0.5, cy+osize-0.5, black, 0); - al_draw_filled_rectangle(x-5, y-5, x+5, y+5, black); + if (first) { + al_draw_filled_rectangle(cx-osize, cy-osize, cx+osize, cy+osize, grey); + al_draw_rectangle(cx-osize+0.5, cy-osize+0.5, cx+osize-0.5, cy+osize-0.5, black, 0); + } + al_draw_filled_rectangle(x-rsize, y-rsize, x+rsize, y+rsize, rcolor); if (num_axes[stick] >= 3) { - al_draw_filled_rectangle(zx-csize, cy-osize, zx+csize, cy+osize, grey); - al_draw_rectangle(zx-csize+0.5f, cy-osize+0.5f, zx+csize-0.5f, cy+osize-0.5f, black, 0); - al_draw_filled_rectangle(zx-5, z-5, zx+5, z+5, black); + if (first) { + al_draw_filled_rectangle(zx-csize, cy-osize, zx+csize, cy+osize, grey); + al_draw_rectangle(zx-csize+0.5f, cy-osize+0.5f, zx+csize-0.5f, cy+osize-0.5f, black, 0); + } + al_draw_filled_rectangle(zx-rsize, z-rsize, zx+rsize, z+rsize, rcolor); } - if (joy) { - al_draw_text(font, black, cx, cy + osize + 1, ALLEGRO_ALIGN_CENTRE, + if (joy && first) { + al_draw_text(font, black, cx + csize + osize + 16, cy - size, ALLEGRO_ALIGN_LEFT, al_get_joystick_stick_name(joy, stick)); for (i = 0; i < num_axes[stick]; i++) { - al_draw_text(font, black, cx, cy + osize + (1 + i) * 10, - ALLEGRO_ALIGN_CENTRE, + al_draw_text(font, black, cx + csize + osize + 16, cy - size + 20 + i * 10, + ALLEGRO_ALIGN_LEFT, al_get_joystick_axis_name(joy, stick, i)); } } @@ -96,57 +116,58 @@ static void draw_joystick_axes(ALLEGRO_JOYSTICK *joy, int cx, int cy, int stick) -static void draw_joystick_button(ALLEGRO_JOYSTICK *joy, int button, bool down) +static void draw_joystick_button(ALLEGRO_JOYSTICK *joy, int button, bool first, bool down) { ALLEGRO_BITMAP *bmp = al_get_target_bitmap(); - int x = al_get_bitmap_width(bmp)/2-120 + (button % 8) * 30; - int y = al_get_bitmap_height(bmp)-120 + (button / 8) * 30; - ALLEGRO_COLOR fg; - - al_draw_filled_rectangle(x, y, x + 25, y + 25, grey); - al_draw_rectangle(x+0.5, y+0.5, x + 24.5, y + 24.5, black, 0); - if (down) { - al_draw_filled_rectangle(x + 2, y + 2, x + 23, y + 23, black); - fg = white; - } - else { - fg = black; - } - - if (joy) { - const char *name = al_get_joystick_button_name(joy, button); - if (strlen(name) < 4) { - al_draw_text(font, fg, x + 13, y + 8, ALLEGRO_ALIGN_CENTRE, name); - } - } + int x = al_get_bitmap_width(bmp) / 2; + int y = 60 + button * 30; + int rsize = first ? 12 : 10; + ALLEGRO_COLOR rcolor = first ? black : blue; + + if (first) + al_draw_rectangle(x+0.5, y+0.5, x + 24.5, y + 24.5, black, 0); + if (down) + al_draw_filled_rectangle(x + 12 - rsize, y + 12 - rsize, x + 13 + rsize, y + 13 + rsize, rcolor); + + if (joy && first) + al_draw_text(font, black, x + 33, y + 8, ALLEGRO_ALIGN_LEFT, al_get_joystick_button_name(joy, button)); } -static void draw_all(ALLEGRO_JOYSTICK *joy) +static void draw_all(ALLEGRO_JOYSTICK *joy, ALLEGRO_JOYSTICK_STATE *state1, ALLEGRO_JOYSTICK_STATE *state2) { - ALLEGRO_BITMAP *bmp = al_get_target_bitmap(); - int width = al_get_bitmap_width(bmp); - int height = al_get_bitmap_height(bmp); int i; al_clear_to_color(al_map_rgb(0xff, 0xff, 0xc0)); if (joy) { - al_draw_textf(font, black, width / 2, 10, ALLEGRO_ALIGN_CENTRE, - "Joystick: %s", al_get_joystick_name(joy)); + al_draw_textf(font, black, 10, 10, ALLEGRO_ALIGN_LEFT, + "Name: %s", al_get_joystick_name(joy)); + const char* type; + switch (al_get_joystick_type(joy)) { + case ALLEGRO_JOYSTICK_TYPE_GAMEPAD: + type = "Gamepad"; + break; + default: + type = "Unknown"; + } + al_draw_textf(font, black, 10, 20, ALLEGRO_ALIGN_LEFT, + "GUID: %s", guid_str); + al_draw_textf(font, black, 10, 30, ALLEGRO_ALIGN_LEFT, + "Type: %s", type); } for (i = 0; i < num_sticks; i++) { - int u = i%4; - int v = i/4; - int cx = (u + 0.5) * width/4; - int cy = (v + 0.5) * height/6; - draw_joystick_axes(joy, cx, cy, i); + int cx = 60; + int cy = 100 + i * 90; + draw_joystick_axes(joy, cx, cy, i, true, state1); + draw_joystick_axes(joy, cx, cy, i, false, state2); } for (i = 0; i < num_buttons; i++) { - draw_joystick_button(joy, i, joys_buttons[i]); + draw_joystick_button(joy, i, true, state1->button[i] > 16384); + draw_joystick_button(joy, i, false, state2->button[i] > 16384); } al_flip_display(); @@ -156,11 +177,16 @@ static void draw_all(ALLEGRO_JOYSTICK *joy) static void main_loop(void) { + ALLEGRO_JOYSTICK_STATE state1, state2; + ALLEGRO_JOYSTICK *joy = al_get_joystick(0); + setup_joystick_all_values(joy, &state1, &state2); ALLEGRO_EVENT event; while (true) { - if (al_is_event_queue_empty(event_queue)) - draw_all(al_get_joystick(0)); + if (al_is_event_queue_empty(event_queue)) { + setup_joystick_values(al_get_joystick(0), &state2); + draw_all(al_get_joystick(0), &state1, &state2); + } al_wait_for_event(event_queue, &event); @@ -168,21 +194,31 @@ static void main_loop(void) /* ALLEGRO_EVENT_JOYSTICK_AXIS - a joystick axis value changed. */ case ALLEGRO_EVENT_JOYSTICK_AXIS: + if (event.joystick.id != joy) + break; + log_printf("stick: %d axis: %d pos: %f\n", + event.joystick.stick, event.joystick.axis, event.joystick.pos); if (event.joystick.stick < MAX_STICKS && event.joystick.axis < MAX_AXES) { - joys[event.joystick.stick][event.joystick.axis] = event.joystick.pos; + state1.stick[event.joystick.stick].axis[event.joystick.axis] = event.joystick.pos; } break; /* ALLEGRO_EVENT_JOYSTICK_BUTTON_DOWN - a joystick button was pressed. */ case ALLEGRO_EVENT_JOYSTICK_BUTTON_DOWN: - joys_buttons[event.joystick.button] = true; + if (event.joystick.id != joy) + break; + log_printf("button down: %d\n", event.joystick.button); + state1.button[event.joystick.button] = 32768; break; /* ALLEGRO_EVENT_JOYSTICK_BUTTON_UP - a joystick button was released. */ case ALLEGRO_EVENT_JOYSTICK_BUTTON_UP: - joys_buttons[event.joystick.button] = false; + if (event.joystick.id != joy) + break; + log_printf("button up: %d\n", event.joystick.button); + state1.button[event.joystick.button] = 0; break; case ALLEGRO_EVENT_KEY_DOWN: @@ -196,8 +232,10 @@ static void main_loop(void) return; case ALLEGRO_EVENT_JOYSTICK_CONFIGURATION: + log_printf("configuration changed\n"); al_reconfigure_joysticks(); - setup_joystick_values(al_get_joystick(0)); + setup_joystick_values(al_get_joystick(0), &state1); + setup_joystick_values(al_get_joystick(0), &state2); break; /* We received an event of some type we don't know about. @@ -215,15 +253,14 @@ int main(int argc, char **argv) { ALLEGRO_DISPLAY *display; - (void)argc; - (void)argv; - if (!al_init()) { abort_example("Could not init Allegro.\n"); } al_init_primitives_addon(); al_init_font_addon(); + open_log_monospace(); + display = al_create_display(1024, 768); if (!display) { abort_example("al_create_display failed\n"); @@ -234,8 +271,15 @@ int main(int argc, char **argv) black = al_map_rgb(0, 0, 0); grey = al_map_rgb(0xe0, 0xe0, 0xe0); white = al_map_rgb(255, 255, 255); + blue = al_map_rgb(0xa0, 0xa0, 255); font = al_create_builtin_font(); + if (argc > 1) { + log_printf("Using mappings from %s\n", argv[1]); + al_set_joystick_mappings(argv[1]); + } + else + log_printf("No mappings file specified. Pass the filename as an argument to this example.\n"); al_install_joystick(); event_queue = al_create_event_queue(); @@ -249,12 +293,12 @@ int main(int argc, char **argv) al_register_event_source(event_queue, al_get_display_event_source(display)); al_register_event_source(event_queue, al_get_joystick_event_source()); - setup_joystick_values(al_get_joystick(0)); - main_loop(); al_destroy_font(font); + al_uninstall_system(); + return 0; } diff --git a/include/allegro5/internal/aintern.h b/include/allegro5/internal/aintern.h index 39017963b..196c554a9 100644 --- a/include/allegro5/internal/aintern.h +++ b/include/allegro5/internal/aintern.h @@ -39,7 +39,7 @@ int _al_get_least_multiple(int val, int mul); /* various libc stuff */ AL_FUNC(void *, _al_sane_realloc, (void *ptr, size_t size)); AL_FUNC(char *, _al_sane_strncpy, (char *dest, const char *src, size_t n)); - +AL_FUNC(char *, _al_strdup, (const char* string)); #define _AL_RAND_MAX 0xFFFF AL_FUNC(void, _al_srand, (int seed)); diff --git a/include/allegro5/internal/aintern_joystick.h b/include/allegro5/internal/aintern_joystick.h index ef313150e..68888eba0 100644 --- a/include/allegro5/internal/aintern_joystick.h +++ b/include/allegro5/internal/aintern_joystick.h @@ -40,6 +40,50 @@ extern _AL_DRIVER_INFO _al_joystick_driver_list[]; }; +/* Output mapping of a joystick button/stick/hat. + * The output button/stick/axes are the things surfaced to the user via the + * Allegro API. + */ +typedef struct _AL_JOYSTICK_OUTPUT { + int in_idx; /* The occurence index of a particular input kind. + * E.g. the second button seen by the driver has + * in_idx == 1. + */ + + int button; /* The output is a button. */ + bool button_enabled; /* Button output is enabled. */ + float button_threshold; /* When an axis is mapped to a button, when to + * trigger the button press. Typically this is + * halfway through the axis range. */ + + int pos_stick; /* The stick mapping for the positive deflection + * of the input axis. */ + int pos_axis; + float pos_min; /* What output value the minimum/maximum deflection + * maps to. */ + float pos_max; + bool pos_enabled; /* Positive deflection output is enabled. */ + + int neg_stick; + int neg_axis; + float neg_min; + float neg_max; + bool neg_enabled; +} _AL_JOYSTICK_OUTPUT; + + + +typedef struct _AL_JOYSTICK_MAPPING { + ALLEGRO_JOYSTICK_GUID guid; + char name[100]; + char platform[17]; + + _AL_VECTOR button_map; + _AL_VECTOR axis_map; + _AL_VECTOR hat_map; +} _AL_JOYSTICK_MAPPING; + + /* information about a single joystick axis */ typedef struct _AL_JOYSTICK_AXIS_INFO { @@ -60,13 +104,15 @@ typedef struct _AL_JOYSTICK_STICK_INFO /* information about a joystick button */ typedef struct _AL_JOYSTICK_BUTTON_INFO { - const char *name; + char *name; } _AL_JOYSTICK_BUTTON_INFO; /* information about an entire joystick */ typedef struct _AL_JOYSTICK_INFO { + ALLEGRO_JOYSTICK_TYPE type; + ALLEGRO_JOYSTICK_GUID guid; int num_sticks; int num_buttons; _AL_JOYSTICK_STICK_INFO stick[_AL_MAX_JOYSTICK_STICKS]; @@ -82,7 +128,22 @@ struct ALLEGRO_JOYSTICK ALLEGRO_JOYSTICK_DRIVER * driver; }; +struct ALLEGRO_JOYSTICK_STATE; + + void _al_generate_joystick_event(ALLEGRO_EVENT *event); +void _al_fill_gamepad_info(_AL_JOYSTICK_INFO *info); +void _al_destroy_joystick_info(_AL_JOYSTICK_INFO *info); +_AL_JOYSTICK_OUTPUT * _al_get_joystick_output(const _AL_VECTOR *map, int in_idx); +const _AL_JOYSTICK_MAPPING *_al_get_gamepad_mapping(const char *platform, ALLEGRO_JOYSTICK_GUID guid); +ALLEGRO_JOYSTICK_GUID _al_new_joystick_guid(uint16_t bus, uint16_t vendor, uint16_t product, uint16_t version, + const char *vendor_name, const char *product_name, uint8_t driver_signature, uint8_t driver_data); +void _al_joystick_guid_to_str(ALLEGRO_JOYSTICK_GUID guid, char* str); +ALLEGRO_JOYSTICK_GUID _al_joystick_guid_from_str(const char *str); +_AL_JOYSTICK_OUTPUT _al_new_joystick_button_output(int button); +_AL_JOYSTICK_OUTPUT _al_new_joystick_stick_output(int stick, int axis); +void _al_joystick_generate_button_event(ALLEGRO_JOYSTICK *joy, ALLEGRO_JOYSTICK_STATE *joystate, _AL_JOYSTICK_OUTPUT output, int value); +void _al_joystick_generate_axis_event(ALLEGRO_JOYSTICK *joy, ALLEGRO_JOYSTICK_STATE *joystate, _AL_JOYSTICK_OUTPUT output, float pos); #ifdef __cplusplus } diff --git a/include/allegro5/internal/aintern_ljoynu.h b/include/allegro5/internal/aintern_ljoynu.h index 355bc320c..5b713f678 100644 --- a/include/allegro5/internal/aintern_ljoynu.h +++ b/include/allegro5/internal/aintern_ljoynu.h @@ -33,8 +33,7 @@ typedef enum { #define TOTAL_JOYSTICK_AXES (0x28) typedef struct { - int stick; - int axis; + _AL_JOYSTICK_OUTPUT output; /* XXX reconsider what fields are required after the haptics driver is in */ int value; int min; @@ -47,6 +46,7 @@ typedef struct { /* Map a Linux input button code to button number on the Allegro joystick. */ typedef struct { int ev_code; + _AL_JOYSTICK_OUTPUT output; } BUTTON_MAPPING; @@ -58,7 +58,9 @@ typedef struct ALLEGRO_JOYSTICK_LINUX int fd; ALLEGRO_USTR *device_name; + /* Index is raw axis index. */ AXIS_MAPPING axis_mapping[TOTAL_JOYSTICK_AXES]; + /* Index is arbitrary, raw button index is in ev_code. */ BUTTON_MAPPING button_mapping[_AL_MAX_JOYSTICK_BUTTONS]; ALLEGRO_JOYSTICK_STATE joystate; char name[100]; diff --git a/include/allegro5/internal/aintern_wjoydxnu.h b/include/allegro5/internal/aintern_wjoydxnu.h index eeb31e4ce..face9d336 100644 --- a/include/allegro5/internal/aintern_wjoydxnu.h +++ b/include/allegro5/internal/aintern_wjoydxnu.h @@ -54,13 +54,6 @@ typedef struct } CAPS_AND_NAMES; -/* map a DirectInput axis to an Allegro (stick,axis) pair */ -typedef struct -{ - int stick, axis; -} AXIS_MAPPING; - - typedef struct ALLEGRO_JOYSTICK_DIRECTX { ALLEGRO_JOYSTICK parent; /* must be first */ @@ -72,16 +65,17 @@ typedef struct ALLEGRO_JOYSTICK_DIRECTX HANDLE waker_event; ALLEGRO_JOYSTICK_STATE joystate; - AXIS_MAPPING x_mapping; - AXIS_MAPPING y_mapping; - AXIS_MAPPING z_mapping; - AXIS_MAPPING rx_mapping; - AXIS_MAPPING ry_mapping; - AXIS_MAPPING rz_mapping; - AXIS_MAPPING slider_mapping[MAX_SLIDERS]; - int pov_mapping_stick[MAX_POVS]; - char name[80]; - char all_names[512]; /* button/stick/axis names with NUL terminators */ + _AL_JOYSTICK_OUTPUT x_mapping; + _AL_JOYSTICK_OUTPUT y_mapping; + _AL_JOYSTICK_OUTPUT z_mapping; + _AL_JOYSTICK_OUTPUT rx_mapping; + _AL_JOYSTICK_OUTPUT ry_mapping; + _AL_JOYSTICK_OUTPUT rz_mapping; + _AL_JOYSTICK_OUTPUT slider_mapping[MAX_SLIDERS]; + /* The X/Y directions are interleaved. */ + _AL_JOYSTICK_OUTPUT pov_mapping[2 * MAX_POVS]; + _AL_JOYSTICK_OUTPUT button_mapping[MAX_BUTTONS]; + char name[100]; } ALLEGRO_JOYSTICK_DIRECTX; diff --git a/include/allegro5/joystick.h b/include/allegro5/joystick.h index a502b2909..460d38aba 100644 --- a/include/allegro5/joystick.h +++ b/include/allegro5/joystick.h @@ -24,7 +24,7 @@ #endif /* internal values */ -#define _AL_MAX_JOYSTICK_AXES 3 +#define _AL_MAX_JOYSTICK_AXES 5 #define _AL_MAX_JOYSTICK_STICKS 16 #ifdef ALLEGRO_ANDROID #define _AL_MAX_JOYSTICK_BUTTONS 36 @@ -62,6 +62,54 @@ enum ALLEGRO_JOYFLAGS }; +#if defined(ALLEGRO_UNSTABLE) || defined(ALLEGRO_INTERNAL_UNSTABLE) || defined(ALLEGRO_SRC) +/* Enum: ALLEGRO_GAMEPAD_BUTTON + */ +typedef enum ALLEGRO_GAMEPAD_BUTTON +{ + ALLEGRO_GAMEPAD_BUTTON_A, + ALLEGRO_GAMEPAD_BUTTON_B, + ALLEGRO_GAMEPAD_BUTTON_X, + ALLEGRO_GAMEPAD_BUTTON_Y, + ALLEGRO_GAMEPAD_BUTTON_LEFT_SHOULDER, + ALLEGRO_GAMEPAD_BUTTON_RIGHT_SHOULDER, + ALLEGRO_GAMEPAD_BUTTON_BACK, + ALLEGRO_GAMEPAD_BUTTON_START, + ALLEGRO_GAMEPAD_BUTTON_GUIDE, + ALLEGRO_GAMEPAD_BUTTON_LEFT_THUMB, + ALLEGRO_GAMEPAD_BUTTON_RIGHT_THUMB, +} ALLEGRO_GAMEPAD_BUTTON; + + +/* Enum: ALLEGRO_GAMEPAD_STICK + */ +typedef enum ALLEGRO_GAMEPAD_STICK +{ + ALLEGRO_GAMEPAD_STICK_DPAD, + ALLEGRO_GAMEPAD_STICK_LEFT_THUMB, + ALLEGRO_GAMEPAD_STICK_RIGHT_THUMB, + ALLEGRO_GAMEPAD_STICK_LEFT_TRIGGER, + ALLEGRO_GAMEPAD_STICK_RIGHT_TRIGGER, +} ALLEGRO_GAMEPAD_STICK; + + +/* Enum: ALLEGRO_JOYSTICK_TYPE + */ +typedef enum ALLEGRO_JOYSTICK_TYPE +{ + ALLEGRO_JOYSTICK_TYPE_UNKNOWN, + ALLEGRO_JOYSTICK_TYPE_GAMEPAD, +} ALLEGRO_JOYSTICK_TYPE; + + +/* Type: ALLEGRO_JOYSTICK_GUID + */ +typedef struct ALLEGRO_JOYSTICK_GUID +{ + uint8_t val[16]; +} ALLEGRO_JOYSTICK_GUID; +#endif + AL_FUNC(bool, al_install_joystick, (void)); AL_FUNC(void, al_uninstall_joystick, (void)); @@ -86,6 +134,13 @@ AL_FUNC(const char*, al_get_joystick_button_name, (ALLEGRO_JOYSTICK *, int b AL_FUNC(void, al_get_joystick_state, (ALLEGRO_JOYSTICK *, ALLEGRO_JOYSTICK_STATE *ret_state)); +#if defined(ALLEGRO_UNSTABLE) || defined(ALLEGRO_INTERNAL_UNSTABLE) || defined(ALLEGRO_SRC) +AL_FUNC(ALLEGRO_JOYSTICK_GUID, al_get_joystick_guid, (ALLEGRO_JOYSTICK *)); +AL_FUNC(ALLEGRO_JOYSTICK_TYPE, al_get_joystick_type, (ALLEGRO_JOYSTICK *)); +AL_FUNC(bool, al_set_joystick_mappings, (const char *filename)); +AL_FUNC(bool, al_set_joystick_mappings_f, (ALLEGRO_FILE *file)); +#endif + AL_FUNC(ALLEGRO_EVENT_SOURCE *, al_get_joystick_event_source, (void)); #ifdef __cplusplus diff --git a/python/generate_python_ctypes.py b/python/generate_python_ctypes.py index 6af193bfc..707ecb003 100755 --- a/python/generate_python_ctypes.py +++ b/python/generate_python_ctypes.py @@ -148,7 +148,7 @@ def parse_funcs(self, funcs): plist.append(c_void_p) continue - mob = re.match("^.*?(\w+)$", param) + mob = re.match(r"^.*?(\w+)$", param) if mob: pnamepos = mob.start(1) if pnamepos == 0: @@ -277,26 +277,26 @@ def parse_protos(self, filename): continue # add function pointer as void pointer - mob = re.match(".*?\(\*(\w+)\)", field) + mob = re.match(r".*?\(\*(\w+)\)", field) if mob: flist.append((mob.group(1), "c_void_p")) continue # add any pointer as void pointer - mob = re.match(".*?\*(\w+)$", field) + mob = re.match(r".*?\*(\w+)$", field) if mob: flist.append((mob.group(1), "c_void_p")) continue # add an array - mob = re.match("(.*)\s+(\w+)\[(.*?)\]$", field) + mob = re.match(r"(.*)\s+(\w+)\[(.*?)\]$", field) if mob: # this is all a hack n = 0 ftype = mob.group(1) if ftype.startswith("struct"): - if ftype == "struct {float axis[3];}": - t = "c_float * 3" + if ftype == "struct {float axis[5];}": + t = "c_float * 5" else: print("Error: Can't parse " + ftype + " yet.") t = None @@ -314,8 +314,8 @@ def parse_protos(self, filename): continue vars = field.split(",") - mob = re.match("\s*(.*?)\s+(\w+)\s*$", vars[0]) - + mob = re.match(r"\s*(.*?)\s+(\w+)\s*$", vars[0]) + t = self.get_type(mob.group(1)) vname = mob.group(2) if t is not None and vname is not None: @@ -496,7 +496,7 @@ def from_param(x): x = al.types[name] base = x.__bases__[0] f.write("class " + name + "(" + base.__name__ + "):\n") - + if hasattr(x, "my_fields"): f.write(" _fields_ = [\n") diff --git a/src/joynu.c b/src/joynu.c index def75c769..3e179ae2d 100644 --- a/src/joynu.c +++ b/src/joynu.c @@ -21,6 +21,8 @@ #define ALLEGRO_NO_COMPATIBILITY +#include + #include "allegro5/allegro.h" #include "allegro5/internal/aintern.h" #include "allegro5/internal/aintern_events.h" @@ -29,10 +31,24 @@ #include "allegro5/internal/aintern_system.h" +ALLEGRO_DEBUG_CHANNEL("joystick"); + /* the active joystick driver */ static ALLEGRO_JOYSTICK_DRIVER *new_joystick_driver = NULL; static ALLEGRO_EVENT_SOURCE es; +static _AL_VECTOR joystick_mappings = _AL_VECTOR_INITIALIZER(_AL_JOYSTICK_MAPPING); + + +static void destroy_joystick_mapping(_AL_JOYSTICK_MAPPING *mapping); + + + +static bool compat_5_2_10(void) { + /* Mappings. */ + return _al_get_joystick_compat_version() < AL_ID(5, 2, 11, 0); +} + /* Function: al_install_joystick @@ -78,6 +94,10 @@ void al_uninstall_joystick(void) _al_event_source_free(&es); new_joystick_driver = NULL; } + for (int i = 0; i < (int)_al_vector_size(&joystick_mappings); i++) { + destroy_joystick_mapping(_al_vector_ref(&joystick_mappings, i)); + } + _al_vector_free(&joystick_mappings); } @@ -300,6 +320,863 @@ void al_get_joystick_state(ALLEGRO_JOYSTICK *joy, ALLEGRO_JOYSTICK_STATE *ret_st new_joystick_driver->get_joystick_state(joy, ret_state); } + + +/* Fills the gamepad info with standard gamepad buttons and sticks. + */ +void _al_fill_gamepad_info(_AL_JOYSTICK_INFO *info) +{ + ASSERT(info); + info->type = ALLEGRO_JOYSTICK_TYPE_GAMEPAD; + + info->button[0].name = _al_strdup("A"); + info->button[1].name = _al_strdup("B"); + info->button[2].name = _al_strdup("X"); + info->button[3].name = _al_strdup("Y"); + info->button[4].name = _al_strdup("Left Shoulder"); + info->button[5].name = _al_strdup("Right Shoulder"); + info->button[6].name = _al_strdup("Back"); + info->button[7].name = _al_strdup("Start"); + info->button[8].name = _al_strdup("Guide"); + info->button[9].name = _al_strdup("Left Thumb"); + info->button[10].name = _al_strdup("Right Thumb"); + info->num_buttons = 11; + + info->stick[0].axis[0].name = _al_strdup("X"); + info->stick[0].axis[1].name = _al_strdup("Y"); + info->stick[0].name = _al_strdup("DPad"); + info->stick[0].num_axes = 2; + info->stick[0].flags |= ALLEGRO_JOYFLAG_DIGITAL; + + info->stick[1].axis[0].name = _al_strdup("X"); + info->stick[1].axis[1].name = _al_strdup("Y"); + info->stick[1].name = _al_strdup("Left Thumb"); + info->stick[1].num_axes = 2; + info->stick[1].flags |= ALLEGRO_JOYFLAG_ANALOGUE; + + info->stick[2].axis[0].name = _al_strdup("X"); + info->stick[2].axis[1].name = _al_strdup("Y"); + info->stick[2].name = _al_strdup("Right Thumb"); + info->stick[2].num_axes = 2; + info->stick[2].flags |= ALLEGRO_JOYFLAG_ANALOGUE; + + info->stick[3].axis[0].name = _al_strdup("Z"); + info->stick[3].name = _al_strdup("Left Trigger"); + info->stick[3].num_axes = 1; + info->stick[3].flags |= ALLEGRO_JOYFLAG_ANALOGUE; + + info->stick[4].axis[0].name = _al_strdup("Z"); + info->stick[4].name = _al_strdup("Right Trigger"); + info->stick[4].num_axes = 1; + info->stick[4].flags |= ALLEGRO_JOYFLAG_ANALOGUE; + + info->num_sticks = 5; +} + + + +/* Destroys the joystick info. + */ +void _al_destroy_joystick_info(_AL_JOYSTICK_INFO *info) +{ + for (int i = 0; i < info->num_sticks; i++) { + al_free(info->stick[i].name); + for (int j = 0; j < info->stick[i].num_axes; j++) + al_free(info->stick[i].axis[j].name); + } + for (int i = 0; i < info->num_buttons; i++) + al_free(info->button[i].name); + info->num_sticks = 0; + info->num_buttons = 0; +} + + + +#ifdef DEBUG_JOYSTICK +static void print_output(_AL_JOYSTICK_OUTPUT out) +{ + printf("Output: "); + if (out.button_enabled) { + printf("button=%d, ", out.button); + } + if (out.pos_enabled) { + printf("pos_stick=%d, pos_axis=%d, pos_min=%f, pos_max=%f, ", out.pos_stick, out.pos_axis, out.pos_min, out.pos_max); + } + if (out.neg_enabled) { + printf("neg_stick=%d, neg_axis=%d, neg_min=%f, neg_max=%f, ", out.neg_stick, out.neg_axis, out.neg_min, out.neg_max); + } + printf("\n"); +} + + + +static void print_mapping(_AL_JOYSTICK_MAPPING *mapping) +{ + char guid_str[33]; + guid_str[32] = '\0'; + _al_joystick_guid_to_str(mapping->guid, guid_str); + printf("guid: %s\n", guid_str); + + for (int i = 0; i < (int)_al_vector_size(&mapping->button_map); i++) { + _AL_JOYSTICK_OUTPUT *o = _al_vector_ref(&mapping->button_map, i); + if (o->button_enabled || o->pos_enabled || o->neg_enabled) { + printf("button %d\n", o->in_idx); + print_output(*o); + } + } + for (int i = 0; i < (int)_al_vector_size(&mapping->axis_map); i++) { + _AL_JOYSTICK_OUTPUT *o = _al_vector_ref(&mapping->axis_map, i); + if (o->button_enabled || o->pos_enabled || o->neg_enabled) { + printf("axis %d\n", o->in_idx); + print_output(*o); + } + } + for (int i = 0; i < (int)_al_vector_size(&mapping->hat_map); i++) { + _AL_JOYSTICK_OUTPUT *o = _al_vector_ref(&mapping->hat_map, i); + if (o->button_enabled || o->pos_enabled || o->neg_enabled) { + printf("hat %d\n", o->in_idx); + print_output(*o); + } + } + printf("====================\n"); +} +#endif + + + +/* Attempts to find an output in a mapping given the occurence index of the + * input type. + */ +_AL_JOYSTICK_OUTPUT *_al_get_joystick_output(const _AL_VECTOR *map, int in_idx) +{ + for (int i = 0; i < (int)_al_vector_size(map); i++) { + _AL_JOYSTICK_OUTPUT *o = _al_vector_ref(map, i); + if (o->in_idx == in_idx) { + return o; + } + } + return NULL; +} + + + +static void init_joystick_mapping(_AL_JOYSTICK_MAPPING *mapping) +{ + memset(mapping, 0, sizeof(_AL_JOYSTICK_MAPPING)); + _al_vector_init(&mapping->hat_map, sizeof(_AL_JOYSTICK_OUTPUT)); + _al_vector_init(&mapping->axis_map, sizeof(_AL_JOYSTICK_OUTPUT)); + _al_vector_init(&mapping->button_map, sizeof(_AL_JOYSTICK_OUTPUT)); +} + + + +static void destroy_joystick_mapping(_AL_JOYSTICK_MAPPING *mapping) +{ + _al_vector_free(&mapping->hat_map); + _al_vector_free(&mapping->axis_map); + _al_vector_free(&mapping->button_map); +} + + + +/* Adjusts the output min/max if the output is a trigger. */ +static void trigger_transform(_AL_JOYSTICK_OUTPUT *output, bool is_trigger) +{ + if (!is_trigger) + return; + +#define TRANSFORM(f) do { f = (f) / 2. + 0.5; } while (0) + + if (output->pos_enabled) { + TRANSFORM(output->pos_min); + TRANSFORM(output->pos_max); + } + if (output->neg_enabled) { + TRANSFORM(output->neg_min); + TRANSFORM(output->neg_max); + } + +#undef TRANSFORM +} + + + +static _AL_JOYSTICK_OUTPUT *get_or_insert_output(_AL_VECTOR *vec, int in_idx) +{ + _AL_JOYSTICK_OUTPUT *output = _al_get_joystick_output(vec, in_idx); + if (output) + return output; + output = _al_vector_alloc_back(vec); + if (!output) + return NULL; + memset(output, 0, sizeof(_AL_JOYSTICK_OUTPUT)); + output->in_idx = in_idx; + return output; +} + + + +/* Possible fields in an SDL-style mapping. + */ +typedef struct _FIELD { + const char* name; /* Name of the output. */ + int button; /* Output button. */ + int stick; /* Output stick. */ + int axis; /* Output axis. */ + int out_sign; /* Output sign. */ + bool is_trigger; /* Whether the output axis is a trigger. */ +} _FIELD; + + + +static bool parse_sdl_joystick_mapping(const char *str, _AL_JOYSTICK_MAPPING *mapping) +{ + init_joystick_mapping(mapping); + + _FIELD fields[] = { + {"a:", ALLEGRO_GAMEPAD_BUTTON_A, 0, 0, 0, false}, + {"b:", ALLEGRO_GAMEPAD_BUTTON_B, 0, 0, 0, false}, + {"x:", ALLEGRO_GAMEPAD_BUTTON_X, 0, 0, 0, false}, + {"y:", ALLEGRO_GAMEPAD_BUTTON_Y, 0, 0, 0, false}, + {"leftshoulder:", ALLEGRO_GAMEPAD_BUTTON_LEFT_SHOULDER, 0, 0, 0, false}, + {"rightshoulder:", ALLEGRO_GAMEPAD_BUTTON_RIGHT_SHOULDER, 0, 0, 0, false}, + {"back:", ALLEGRO_GAMEPAD_BUTTON_BACK, 0, 0, 0, false}, + {"start:", ALLEGRO_GAMEPAD_BUTTON_START, 0, 0, 0, false}, + {"guide:", ALLEGRO_GAMEPAD_BUTTON_GUIDE, 0, 0, 0, false}, + {"leftstick:", ALLEGRO_GAMEPAD_BUTTON_LEFT_THUMB, 0, 0, 0, false}, + {"rightstick:", ALLEGRO_GAMEPAD_BUTTON_RIGHT_THUMB, 0, 0, 0, false}, + + {"dpup:", -1, ALLEGRO_GAMEPAD_STICK_DPAD, 1, -1, false}, + {"dpdown:", -1, ALLEGRO_GAMEPAD_STICK_DPAD, 1, 1, false}, + {"dpleft:", -1, ALLEGRO_GAMEPAD_STICK_DPAD, 0, -1, false}, + {"dpright:", -1, ALLEGRO_GAMEPAD_STICK_DPAD, 0, 1, false}, + {"leftx:", -1, ALLEGRO_GAMEPAD_STICK_LEFT_THUMB, 0, 0, false}, + {"lefty:", -1, ALLEGRO_GAMEPAD_STICK_LEFT_THUMB, 1, 0, false}, + {"rightx:", -1, ALLEGRO_GAMEPAD_STICK_RIGHT_THUMB, 0, 0, false}, + {"righty:", -1, ALLEGRO_GAMEPAD_STICK_RIGHT_THUMB, 1, 0, false}, + {"lefttrigger:", -1, ALLEGRO_GAMEPAD_STICK_LEFT_TRIGGER, 0, 0, true}, + {"righttrigger:", -1, ALLEGRO_GAMEPAD_STICK_RIGHT_TRIGGER, 0, 0, true}, + }; + + /* + * In the mapping, hats are 1,2,4,8 => up,right,down,left + */ + + const char *c = str; + size_t n = strcspn(c, ","); + char guid_str[33]; + guid_str[32] = '\0'; + if (n >= sizeof(guid_str) || c[n] != ',') { + ALLEGRO_ERROR("GUID too long or missing trailing comma.\n"); + return false; + } + memcpy(guid_str, c, n); + mapping->guid = _al_joystick_guid_from_str(guid_str); + c += n + 1; + + n = strcspn(c, ","); + if (c[n] != ',') { + ALLEGRO_ERROR("Missing trailing comma while parsing mapping name.\n"); + return false; + } + memcpy(mapping->name, c, n >= sizeof(mapping->name) ? sizeof(mapping->name) - 1 : n); + c += n + 1; + + /* field out_sign: governs min/max values + * out_sign: same as field out_sign, overrides if non-zero + * in_sign: designates whether it's pos or neg + */ + while (*c) { + int out_sign = 0; + if (*c == '+') { + out_sign = 1; + c++; + } else if (*c == '-') { + out_sign = -1; + c++; + } + + n = strlen("platform:"); + if (strncmp(c, "platform:", n) == 0) { + c += n; + n = strcspn(c, ","); + if (n >= sizeof(mapping->platform) || c[n] != ',') { + ALLEGRO_ERROR("Platform too long or missing trailing comma.\n"); + return false; + } + memcpy(mapping->platform, c, n); + c += n + 1; + } + else { + for (int i = 0; i < (int)(sizeof(fields) / sizeof(fields[0])); i++) { + const _FIELD *field = &fields[i]; + n = strlen(field->name); + if (strncmp(c, field->name, n) != 0) + continue; + + c += n; + + int in_sign = 0; + if (*c == '+') { + in_sign = 1; + c++; + } else if (*c == '-') { + in_sign = -1; + c++; + } + + /* Axes and hats are both treated as axes. */ + if (*c == 'a' || *c == 'h') { + bool is_hat = *c == 'h'; + + c += 1; + int index = (int)strtoul(c, (char**) &c, 10); + + if (is_hat) { + /* Syntax is: h0.1 */ + c += 1; + index *= 2; /* This assumes hats always have two axes. */ + int hat_bit = (int)strtoul(c, (char**) &c, 10); + if (hat_bit == 1 || hat_bit == 4) + index += 1; + switch (hat_bit) { + case 1: + in_sign = -1; + break; + case 2: + in_sign = 1; + break; + case 4: + in_sign = 1; + break; + case 8: + in_sign = -1; + break; + } + } + + float one = 1; + if (*c == '~') + one = -1; + + _AL_JOYSTICK_OUTPUT *output; + if (is_hat) { + output = get_or_insert_output(&mapping->hat_map, index); + if (!output) + return false; + } + else { + output = get_or_insert_output(&mapping->axis_map, index); + if (!output) + return false; + } + if (field->button >= 0) { + output->button = field->button; + output->button_enabled = true; + output->button_threshold = 0.5 * in_sign * one; + } + else { + if (out_sign == 0) { + /* Field sign is the default. */ + out_sign = field->out_sign; + } + + switch (in_sign) { + case -1: + output->neg_stick = field->stick; + output->neg_axis = field->axis; + output->neg_enabled = true; + switch (out_sign) { + case -1: + output->neg_min = 0; + output->neg_max = -one; + break; + case 0: + output->neg_min = -one; + output->neg_max = one; + break; + case 1: + output->neg_min = 0; + output->neg_max = one; + break; + } + break; + case 0: + output->pos_stick = field->stick; + output->pos_axis = field->axis; + output->neg_stick = field->stick; + output->neg_axis = field->axis; + output->pos_enabled = true; + output->neg_enabled = true; + switch (out_sign) { + case -1: + output->pos_min = -0.5 * one; + output->pos_max = -one; + output->neg_min = -0.5 * one; + output->neg_max = 0.; + break; + case 0: + output->pos_min = 0.; + output->pos_max = one; + output->neg_min = 0.; + output->neg_max = -one; + break; + case 1: + output->pos_min = 0.5 * one; + output->pos_max = one; + output->neg_min = 0.5 * one; + output->neg_max = 0.; + break; + } + break; + case 1: + output->pos_stick = field->stick; + output->pos_axis = field->axis; + output->pos_enabled = true; + switch (out_sign) { + case -1: + output->pos_min = 0; + output->pos_max = -one; + break; + case 0: + output->pos_min = -one; + output->pos_max = one; + break; + case 1: + output->pos_min = 0; + output->pos_max = one; + break; + } + break; + } + trigger_transform(output, field->is_trigger); + } + +#ifdef JOYSTICK_DEBUG + if (is_hat) { + printf("hat: %d field: %s\n", index, field->name); + } + else { + printf("axis: %d field: %s\n", index, field->name); + } + print_output(*output); + printf("\n"); +#endif + } + /* Buttons. */ + else if (*c == 'b') { + c += 1; + int index = (int)strtoul(c, (char**) &c, 10); + _AL_JOYSTICK_OUTPUT *output = get_or_insert_output(&mapping->button_map, index); + if (!output) + return false; + if (field->button >= 0) { + if (in_sign != 0) { + printf("Button with in sign? 2\n"); + return false; + } + output->button = field->button; + output->button_enabled = true; + } + else { + /* Buttons only use the pos mapping. */ + output->pos_stick = field->stick; + output->pos_axis = field->axis; + output->pos_enabled = true; + switch (out_sign) { + case -1: + output->pos_min = 0; + output->pos_max = -1.; + break; + case 0: + output->pos_min = -1.; + output->pos_max = 1.; + break; + case 1: + output->pos_min = 0; + output->pos_max = 1.; + break; + } + } +#ifdef JOYSTICK_DEBUG + printf("button %d %s\n", index, field->name); + print_output(*output); + printf("\n"); +#endif + trigger_transform(output, field->is_trigger); + } + else { + ALLEGRO_ERROR("Unknown input type, expected 'b', 'h', or 'a': %c.\n", *c); + return false; + } + break; + } + } + n = strcspn(c, ","); + if (c[n] != ',') + break; + c += n + 1; + } +#ifdef JOYSTICK_DEBUG + printf("=========================\n"); + fflush(stdout); +#endif + return true; +} + + + +/* Function: al_set_joystick_mappings + */ +bool al_set_joystick_mappings(const char *filename) +{ + if (!filename) + return false; + ALLEGRO_FILE *f = al_fopen(filename, "rb"); + if (!f) { + ALLEGRO_ERROR("Couldn't open joystick mapping file: %s\n", filename); + return false; + } + bool result = al_set_joystick_mappings_f(f); + al_fclose(f); + return result; +} + + +/* Function: al_set_joystick_mappings_f + */ +bool al_set_joystick_mappings_f(ALLEGRO_FILE *f) +{ + char line[1024]; + + while (al_fgets(f, line, sizeof(line))) { + if (line[0] == '#' || line[0] == '\n') + continue; + _AL_JOYSTICK_MAPPING *mapping = _al_vector_alloc_back(&joystick_mappings); + if (!mapping) + return false; + if (!parse_sdl_joystick_mapping(line, mapping)) { + ALLEGRO_ERROR("Could not parse mapping line: %s\n", line); +#ifdef JOYSTICK_DEBUG + print_mapping(mapping); + printf("========================"); +#endif + return false; + } + } + ALLEGRO_INFO("Parsed %d joystick mappings\n", (int)_al_vector_size(&joystick_mappings)); + return true; +} + + + +const _AL_JOYSTICK_MAPPING *_al_get_gamepad_mapping(const char *platform, ALLEGRO_JOYSTICK_GUID guid) +{ + if (compat_5_2_10()) + return NULL; + ALLEGRO_JOYSTICK_GUID loose_guid = guid; + /* Zero out the version. */ + loose_guid.val[12] = 0; + loose_guid.val[13] = 0; + char guid_str[33]; + guid_str[32] = '\0'; + _al_joystick_guid_to_str(guid, guid_str); + + ALLEGRO_INFO("Looking for joystick mapping for platform: %s guid: %s\n", platform, guid_str); + /* Search in reverse order, so that subsequent al_set_joystick_mappings override prior ones. */ + for (int i = (int)_al_vector_size(&joystick_mappings) - 1; i >= 0; i--) { + _AL_JOYSTICK_MAPPING *m = _al_vector_ref(&joystick_mappings, i); + if (strcmp(m->platform, platform) != 0) + continue; + if (memcmp(&m->guid, &guid, sizeof(guid)) == 0) + return m; + } + _al_joystick_guid_to_str(loose_guid, guid_str); + ALLEGRO_INFO("Looking for joystick mapping for platform: %s loose guid: %s\n", platform, guid_str); + for (int i = (int)_al_vector_size(&joystick_mappings) - 1; i >= 0; i--) { + _AL_JOYSTICK_MAPPING *m = _al_vector_ref(&joystick_mappings, i); + if (strcmp(m->platform, platform) != 0) + continue; + if (memcmp(&m->guid, &loose_guid, sizeof(loose_guid)) == 0) + return m; + } + ALLEGRO_WARN("Could not find joystick mapping for platform: %s guid: %s\n", platform, guid_str); + return NULL; +} + + + +ALLEGRO_JOYSTICK_GUID _al_new_joystick_guid(uint16_t bus, uint16_t vendor, uint16_t product, uint16_t version, + const char *vendor_name, const char *product_name, uint8_t driver_signature, uint8_t driver_data) +{ + ALLEGRO_JOYSTICK_GUID guid; + uint16_t *guid_16 = (uint16_t*)guid.val; + (void)vendor_name; + + memset(&guid, 0, sizeof(guid)); + + guid_16[0] = bus; + if (vendor) { + guid_16[2] = vendor; + guid_16[4] = product; + guid_16[6] = version; + guid.val[14] = driver_signature; + guid.val[15] = driver_data; + } + else { + size_t max_name_len = sizeof(guid) - 4; + + if (driver_signature) { + max_name_len -= 2; + guid.val[14] = driver_signature; + guid.val[15] = driver_data; + } + if (product_name) { + strncpy((char*)&guid.val[4], product_name, max_name_len); + } + } + return guid; +} + + + +void _al_joystick_guid_to_str(ALLEGRO_JOYSTICK_GUID guid, char* str) +{ + const char chars[] = "0123456789abcdef"; + for (int i = 0; i < (int)sizeof(guid.val); i++) { + *str++ = chars[guid.val[i] >> 4]; + *str++ = chars[guid.val[i] & 0xf]; + } +} + + + +static int nibble_from_char(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'A' && c <= 'F') + return 10 + c - 'A'; + if (c >= 'a' && c <= 'f') + return 10 + c - 'a'; + return 0; +} + + + +ALLEGRO_JOYSTICK_GUID _al_joystick_guid_from_str(const char *str) +{ + ALLEGRO_JOYSTICK_GUID guid; + memset(&guid, 0, sizeof(guid)); + int len = (int)strlen(str); + len &= ~0x1; + if (len / 2 > (int)sizeof(guid.val)) + len = 2 * sizeof(guid.val); + for (int i = 0; i < len; i += 2) { + guid.val[i / 2] = (nibble_from_char(str[i]) << 4) | nibble_from_char(str[i + 1]); + } + return guid; +} + + +/* Function: al_get_joystick_guid + */ +ALLEGRO_JOYSTICK_GUID al_get_joystick_guid(ALLEGRO_JOYSTICK *joy) +{ + ASSERT(joy); + return joy->info.guid; +} + + +/* Function: al_get_joystick_type + */ +ALLEGRO_JOYSTICK_TYPE al_get_joystick_type(ALLEGRO_JOYSTICK *joy) +{ + ASSERT(joy); + return joy->info.type; +} + + + +/* Make an output that maps to a button, for unknown joysticks. + */ +_AL_JOYSTICK_OUTPUT _al_new_joystick_button_output(int button) +{ + _AL_JOYSTICK_OUTPUT output; + memset(&output, 0, sizeof(output)); + output.button = button; + output.button_enabled = true; + return output; +} + + + +/* Make an output that maps to a stick + axis, for unknown joysticks. + */ +_AL_JOYSTICK_OUTPUT _al_new_joystick_stick_output(int stick, int axis) +{ + _AL_JOYSTICK_OUTPUT output; + memset(&output, 0, sizeof(output)); + output.button = -1; + output.pos_stick = stick; + output.pos_axis = axis; + output.pos_min = 0.; + output.pos_max = 1.; + output.neg_stick = stick; + output.neg_axis = axis; + output.neg_min = 0.; + output.neg_max = -1.; + output.pos_enabled = true; + output.neg_enabled = true; + return output; +} + + +/* + * Helper to generate an event after an axis is moved. + * The joystick must be locked BEFORE entering this function. + */ +void _al_joystick_generate_axis_event(ALLEGRO_JOYSTICK *joy, ALLEGRO_JOYSTICK_STATE *joystate, + _AL_JOYSTICK_OUTPUT output, float pos) +{ + ALLEGRO_EVENT event; + ALLEGRO_EVENT_SOURCE *es = al_get_joystick_event_source(); + + if (!_al_event_source_needs_to_generate_event(es)) + return; + + if (output.button_enabled) { + ALLEGRO_EVENT_TYPE event_type; + bool generate; + float threshold = output.button_threshold; + /* Threshold sign also determines which direction the axis needs to move to trigger the button. */ + if (threshold < 0.) { + threshold = -threshold; + pos = -pos; + } + if (pos > threshold) { + if (joystate->button[output.button] > 0) { + generate = false; + } + else { + event_type = ALLEGRO_EVENT_JOYSTICK_BUTTON_DOWN; + generate = true; + } + joystate->button[output.button] = 32767; + } + else { + if (joystate->button[output.button] > 0) { + event_type = ALLEGRO_EVENT_JOYSTICK_BUTTON_UP; + generate = true; + } + else { + generate = false; + } + joystate->button[output.button] = 0; + } + if (generate) { + event.joystick.type = event_type; + event.joystick.timestamp = al_get_time(); + event.joystick.id = joy; + event.joystick.stick = 0; + event.joystick.axis = 0; + event.joystick.pos = 0.0; + event.joystick.button = output.button; + _al_event_source_emit_event(es, &event); + } + } + else { + int out_stick; + int out_axis; + float out_pos; + bool enabled; + if (pos >= 0) { + out_stick = output.pos_stick; + out_axis = output.pos_axis; + out_pos = output.pos_min + (output.pos_max - output.pos_min) * pos; + enabled = output.pos_enabled; + } else { + out_stick = output.neg_stick; + out_axis = output.neg_axis; + out_pos = output.neg_min + (output.neg_max - output.neg_min) * -pos; + enabled = output.neg_enabled; + } + if (enabled) { + joystate->stick[out_stick].axis[out_axis] = out_pos; + event.joystick.type = ALLEGRO_EVENT_JOYSTICK_AXIS; + event.joystick.timestamp = al_get_time(); + event.joystick.id = joy; + event.joystick.stick = out_stick; + event.joystick.axis = out_axis; + event.joystick.pos = out_pos; + event.joystick.button = 0; + _al_event_source_emit_event(es, &event); + } + } +} + + + +/* + * Helper to generate an event after a button is pressed or released. + * The joystick must be locked BEFORE entering this function. + */ +void _al_joystick_generate_button_event(ALLEGRO_JOYSTICK *joy, ALLEGRO_JOYSTICK_STATE *joystate, _AL_JOYSTICK_OUTPUT output, int value) +{ + ALLEGRO_EVENT event; + ALLEGRO_EVENT_SOURCE *es = al_get_joystick_event_source(); + + if (!_al_event_source_needs_to_generate_event(es)) + return; + + if (output.button_enabled) { + ALLEGRO_EVENT_TYPE event_type; + bool generate; + if (value > 0) { + if (joystate->button[output.button] > 0) { + generate = false; + } + else { + event_type = ALLEGRO_EVENT_JOYSTICK_BUTTON_DOWN; + generate = true; + } + joystate->button[output.button] = 32767; + } + else { + if (joystate->button[output.button] > 0) { + event_type = ALLEGRO_EVENT_JOYSTICK_BUTTON_UP; + generate = true; + } + else { + generate = false; + } + joystate->button[output.button] = 0; + } + if (generate) { + event.joystick.type = event_type; + event.joystick.timestamp = al_get_time(); + event.joystick.id = joy; + event.joystick.stick = 0; + event.joystick.axis = 0; + event.joystick.pos = 0.0; + event.joystick.button = output.button; + _al_event_source_emit_event(es, &event); + } + } + else if (output.pos_enabled) { + float pos = value > 0 ? output.pos_max : output.pos_min; + joystate->stick[output.pos_stick].axis[output.pos_axis] = pos; + event.joystick.type = ALLEGRO_EVENT_JOYSTICK_AXIS; + event.joystick.timestamp = al_get_time(); + event.joystick.id = joy; + event.joystick.stick = output.pos_stick; + event.joystick.axis = output.pos_axis; + event.joystick.pos = pos; + event.joystick.button = 0; + _al_event_source_emit_event(es, &event); + } +} + /* * Local Variables: * c-basic-offset: 3 diff --git a/src/linux/ljoynu.c b/src/linux/ljoynu.c index 834b3d0e3..8b7709a09 100644 --- a/src/linux/ljoynu.c +++ b/src/linux/ljoynu.c @@ -103,8 +103,6 @@ static const char *ljoy_get_name(ALLEGRO_JOYSTICK *joy_); static bool ljoy_get_active(ALLEGRO_JOYSTICK *joy_); static void ljoy_process_new_data(void *data); -static void ljoy_generate_axis_event(ALLEGRO_JOYSTICK_LINUX *joy, int stick, int axis, float pos); -static void ljoy_generate_button_event(ALLEGRO_JOYSTICK_LINUX *joy, int button, ALLEGRO_EVENT_TYPE event_type); @@ -140,6 +138,7 @@ static bool hotplug_ended = false; #endif + /* Return true if a joystick-related button or key: * * BTN_MISC to BTN_9 for miscellaneous input, @@ -303,8 +302,6 @@ static ALLEGRO_JOYSTICK_LINUX *ljoy_allocate_structure(void) static void inactivate_joy(ALLEGRO_JOYSTICK_LINUX *joy) { - int i; - if (joy->config_state == LJOY_STATE_UNUSED) return; joy->config_state = LJOY_STATE_UNUSED; @@ -313,10 +310,7 @@ static void inactivate_joy(ALLEGRO_JOYSTICK_LINUX *joy) close(joy->fd); joy->fd = -1; - for (i = 0; i < joy->parent.info.num_sticks; i++) - al_free((void *)joy->parent.info.stick[i].name); - for (i = 0; i < joy->parent.info.num_buttons; i++) - al_free((void *)joy->parent.info.button[i].name); + _al_destroy_joystick_info(&joy->parent.info); memset(&joy->parent.info, 0, sizeof(joy->parent.info)); memset(&joy->joystate, 0, sizeof(joy->joystate)); @@ -326,11 +320,10 @@ static void inactivate_joy(ALLEGRO_JOYSTICK_LINUX *joy) -static void set_axis_mapping(AXIS_MAPPING *map, int stick, int axis, +static void set_axis_mapping(AXIS_MAPPING *map, _AL_JOYSTICK_OUTPUT output, const struct input_absinfo *absinfo) { - map->stick = stick; - map->axis = axis; + map->output = output; map->min = absinfo->minimum; map->max = absinfo->maximum; map->value = absinfo->value; @@ -373,14 +366,15 @@ static bool fill_joystick_axes(ALLEGRO_JOYSTICK_LINUX *joy, int fd) name_throttles++; joy->parent.info.stick[stick].flags = ALLEGRO_JOYFLAG_ANALOGUE; joy->parent.info.stick[stick].num_axes = 1; - joy->parent.info.stick[stick].axis[0].name = "X"; + joy->parent.info.stick[stick].axis[0].name = _al_strdup("X"); joy->parent.info.stick[stick].name = al_malloc(32); snprintf((char *)joy->parent.info.stick[stick].name, 32, "Throttle %d", name_throttles); - set_axis_mapping(&joy->axis_mapping[i], stick, 0, &absinfo); + set_axis_mapping(&joy->axis_mapping[i], _al_new_joystick_stick_output(stick, 0), &absinfo); stick++; } else { + _AL_JOYSTICK_OUTPUT output = _al_new_joystick_stick_output(stick, axis); /* Regular axis, two axis stick. */ if (axis == 0) { /* First axis of new joystick. */ @@ -391,18 +385,18 @@ static bool fill_joystick_axes(ALLEGRO_JOYSTICK_LINUX *joy, int fd) joy->parent.info.stick[stick].flags = ALLEGRO_JOYFLAG_ANALOGUE; } joy->parent.info.stick[stick].num_axes = 2; - joy->parent.info.stick[stick].axis[0].name = "X"; - joy->parent.info.stick[stick].axis[1].name = "Y"; + joy->parent.info.stick[stick].axis[0].name = _al_strdup("X"); + joy->parent.info.stick[stick].axis[1].name = _al_strdup("Y"); joy->parent.info.stick[stick].name = al_malloc(32); snprintf((char *)joy->parent.info.stick[stick].name, 32, "Stick %d", name_sticks); - set_axis_mapping(&joy->axis_mapping[i], stick, axis, &absinfo); + set_axis_mapping(&joy->axis_mapping[i], output, &absinfo); axis++; } else { /* Second axis. */ ASSERT(axis == 1); - set_axis_mapping(&joy->axis_mapping[i], stick, axis, &absinfo); + set_axis_mapping(&joy->axis_mapping[i], output, &absinfo); stick++; axis = 0; } @@ -416,6 +410,58 @@ static bool fill_joystick_axes(ALLEGRO_JOYSTICK_LINUX *joy, int fd) +static bool fill_joystick_axes_with_map(ALLEGRO_JOYSTICK_LINUX *joy, int fd, const _AL_JOYSTICK_MAPPING *mapping) +{ + unsigned long abs_bits[NLONGS(ABS_CNT)] = {0}; + int i; + int hat = 0; + int axis = 0; + _AL_JOYSTICK_OUTPUT *output; + int num_hats_mapped = 0; + int num_axes_mapped = 0; + + /* Scan the axes to get their properties. */ + if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)), abs_bits) < 0) + return false; + + for (i = LJOY_AXIS_RANGE_START; i < LJOY_AXIS_RANGE_END; i++) { + struct input_absinfo absinfo; + + if (!TEST_BIT(i, abs_bits)) + continue; + + if (ioctl(fd, EVIOCGABS(i), &absinfo) < 0) + continue; + + if (is_hat_axis(i)) { + output = _al_get_joystick_output(&mapping->hat_map, hat); + if (output) { + set_axis_mapping(&joy->axis_mapping[i], *output, &absinfo); + num_hats_mapped++; + } + hat++; + } + else { + output = _al_get_joystick_output(&mapping->axis_map, axis); + if (output) { + set_axis_mapping(&joy->axis_mapping[i], *output, &absinfo); + num_axes_mapped++; + } + axis++; + } + } + if (num_hats_mapped != (int)_al_vector_size(&mapping->hat_map)) { + ALLEGRO_ERROR("Could not use mapping, some hats are not mapped.\n"); + return false; + } + if (num_axes_mapped != (int)_al_vector_size(&mapping->axis_map)) { + ALLEGRO_ERROR("Could not use mapping, some axes are not mapped.\n"); + return false; + } + return true; +} + + static bool fill_joystick_buttons(ALLEGRO_JOYSTICK_LINUX *joy, int fd) { unsigned long key_bits[NLONGS(KEY_CNT)] = {0}; @@ -430,6 +476,7 @@ static bool fill_joystick_buttons(ALLEGRO_JOYSTICK_LINUX *joy, int fd) for (i = LJOY_BTN_RANGE_START; i < LJOY_BTN_RANGE_END; i++) { if (TEST_BIT(i, key_bits) && is_joystick_button(i)) { joy->button_mapping[b].ev_code = i; + joy->button_mapping[b].output = _al_new_joystick_button_output(b); ALLEGRO_DEBUG("Input event code %d maps to button %d\n", i, b); joy->parent.info.button[b].name = al_malloc(32); @@ -453,6 +500,70 @@ static bool fill_joystick_buttons(ALLEGRO_JOYSTICK_LINUX *joy, int fd) +static bool fill_joystick_buttons_with_map(ALLEGRO_JOYSTICK_LINUX *joy, int fd, const _AL_JOYSTICK_MAPPING *mapping) +{ + unsigned long key_bits[NLONGS(KEY_CNT)] = {0}; + int button; + int i; + int num_buttons_mapped = 0; + + if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), key_bits) < 0) + return false; + + button = 0; + + for (i = LJOY_BTN_RANGE_START; i < LJOY_BTN_RANGE_END; i++) { + if (TEST_BIT(i, key_bits)) { + joy->button_mapping[button].ev_code = i; + _AL_JOYSTICK_OUTPUT *output = _al_get_joystick_output(&mapping->button_map, button); + if (output) { + joy->button_mapping[button].output = *output; + num_buttons_mapped++; + } + button++; + } + } + + /* Clear the rest. */ + for (; button < _AL_MAX_JOYSTICK_BUTTONS; button++) { + joy->button_mapping[button].ev_code = -1; + } + + if (num_buttons_mapped != (int)_al_vector_size(&mapping->button_map)) { + ALLEGRO_ERROR("Could not use mapping, some buttons are not mapped.\n"); + return false; + } + + return true; +} + + + +static bool fill_gamepad(ALLEGRO_JOYSTICK_LINUX *joy, int fd) { + struct input_id id; + if (ioctl(fd, EVIOCGID, &id) < 0) + return false; + + ALLEGRO_JOYSTICK_GUID guid = _al_new_joystick_guid(id.bustype, id.vendor, id.product, id.version, NULL, joy->name, 0, 0); + joy->parent.info.guid = guid; + const _AL_JOYSTICK_MAPPING *mapping = _al_get_gamepad_mapping("Linux", guid); + if (!mapping) + return false; + + if (!fill_joystick_buttons_with_map(joy, fd, mapping)) + return false; + + if (!fill_joystick_axes_with_map(joy, fd, mapping)) + return false; + + _al_fill_gamepad_info(&joy->parent.info); + + memcpy(joy->name, mapping->name, sizeof(mapping->name)); + return true; +} + + + static void ljoy_device(ALLEGRO_USTR *device_name) { ALLEGRO_JOYSTICK_LINUX *joy = ljoy_by_device_name(device_name); if (joy) { @@ -497,11 +608,13 @@ static void ljoy_device(ALLEGRO_USTR *device_name) { /* Map Linux input API axis and button numbers to ours, and fill in * information. */ - if (!fill_joystick_axes(joy, fd) || !fill_joystick_buttons(joy, fd)) { - ALLEGRO_ERROR("fill_joystick_info failed %s\n", al_cstr(device_name)); - inactivate_joy(joy); - close(fd); - return; + if (!fill_gamepad(joy, fd)) { + if (!fill_joystick_axes(joy, fd) || !fill_joystick_buttons(joy, fd)) { + ALLEGRO_ERROR("fill_joystick_info failed %s\n", al_cstr(device_name)); + inactivate_joy(joy); + close(fd); + return; + } } /* Register the joystick with the fdwatch subsystem. */ @@ -884,16 +997,19 @@ static bool ljoy_get_active(ALLEGRO_JOYSTICK *joy_) -static int map_button_number(const ALLEGRO_JOYSTICK_LINUX *joy, int ev_code) +static bool map_button_number(const ALLEGRO_JOYSTICK_LINUX *joy, int ev_code, _AL_JOYSTICK_OUTPUT *output) { int b; for (b = 0; b < _AL_MAX_JOYSTICK_BUTTONS; b++) { - if (joy->button_mapping[b].ev_code == ev_code) - return b; + const BUTTON_MAPPING *m = &joy->button_mapping[b]; + if (m->ev_code == ev_code) { + *output = m->output; + return true; + } } - return -1; + return false; } @@ -934,29 +1050,20 @@ static void ljoy_process_new_data(void *data) int value = input_events[i].value; if (type == EV_KEY) { - int number = map_button_number(joy, code); - if (number >= 0) { - if (value) - joy->joystate.button[number] = 32767; - else - joy->joystate.button[number] = 0; - - ljoy_generate_button_event(joy, number, - (value - ? ALLEGRO_EVENT_JOYSTICK_BUTTON_DOWN - : ALLEGRO_EVENT_JOYSTICK_BUTTON_UP)); + _AL_JOYSTICK_OUTPUT output; + if (map_button_number(joy, code, &output)) { + if (output.button_enabled || output.pos_enabled || output.neg_enabled) + _al_joystick_generate_button_event((ALLEGRO_JOYSTICK *)joy, &joy->joystate, output, value); } } else if (type == EV_ABS) { int number = code; if (number < TOTAL_JOYSTICK_AXES) { const AXIS_MAPPING *map = &joy->axis_mapping[number]; - int stick = map->stick; - int axis = map->axis; + _AL_JOYSTICK_OUTPUT output = map->output; float pos = norm_pos(map, value); - - joy->joystate.stick[stick].axis[axis] = pos; - ljoy_generate_axis_event(joy, stick, axis, pos); + if (output.button_enabled || output.pos_enabled || output.neg_enabled) + _al_joystick_generate_axis_event((ALLEGRO_JOYSTICK *)joy, &joy->joystate, output, pos); } } } @@ -967,56 +1074,6 @@ static void ljoy_process_new_data(void *data) -/* ljoy_generate_axis_event: [fdwatch thread] - * - * Helper to generate an event after an axis is moved. - * The joystick must be locked BEFORE entering this function. - */ -static void ljoy_generate_axis_event(ALLEGRO_JOYSTICK_LINUX *joy, int stick, int axis, float pos) -{ - ALLEGRO_EVENT event; - ALLEGRO_EVENT_SOURCE *es = al_get_joystick_event_source(); - - if (!_al_event_source_needs_to_generate_event(es)) - return; - - event.joystick.type = ALLEGRO_EVENT_JOYSTICK_AXIS; - event.joystick.timestamp = al_get_time(); - event.joystick.id = (ALLEGRO_JOYSTICK *)joy; - event.joystick.stick = stick; - event.joystick.axis = axis; - event.joystick.pos = pos; - event.joystick.button = 0; - - _al_event_source_emit_event(es, &event); -} - - - -/* ljoy_generate_button_event: [fdwatch thread] - * - * Helper to generate an event after a button is pressed or released. - * The joystick must be locked BEFORE entering this function. - */ -static void ljoy_generate_button_event(ALLEGRO_JOYSTICK_LINUX *joy, int button, ALLEGRO_EVENT_TYPE event_type) -{ - ALLEGRO_EVENT event; - ALLEGRO_EVENT_SOURCE *es = al_get_joystick_event_source(); - - if (!_al_event_source_needs_to_generate_event(es)) - return; - - event.joystick.type = event_type; - event.joystick.timestamp = al_get_time(); - event.joystick.id = (ALLEGRO_JOYSTICK *)joy; - event.joystick.stick = 0; - event.joystick.axis = 0; - event.joystick.pos = 0.0; - event.joystick.button = button; - - _al_event_source_emit_event(es, &event); -} - #endif /* ALLEGRO_HAVE_LINUX_INPUT_H */ diff --git a/src/macosx/hidjoy-10.4.m b/src/macosx/hidjoy-10.4.m deleted file mode 100644 index 85b5d87a5..000000000 --- a/src/macosx/hidjoy-10.4.m +++ /dev/null @@ -1,445 +0,0 @@ -/* ______ ___ ___ - * /\ _ \ /\_ \ /\_ \ - * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ - * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ - * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ - * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ - * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ - * /\____/ - * \_/__/ - * - * HID Joystick driver routines for MacOS X. - * - * By Angelo Mottola. - * - * See readme.txt for copyright information. - */ - -#include "allegro5/allegro5.h" -#include "allegro5/platform/aintosx.h" -#import -#import -#import - - -#ifndef ALLEGRO_MACOSX -#error something is wrong with the makefile -#endif - -ALLEGRO_DEBUG_CHANNEL("MacOSX") - -#define _AL_MAX_JOYSTICKS 8 - -static bool init_joystick(void); -static void exit_joystick(void); -static int num_joysticks(void); -static ALLEGRO_JOYSTICK* get_joystick(int); -static void release_joystick(ALLEGRO_JOYSTICK*); -static void get_joystick_state(ALLEGRO_JOYSTICK*, ALLEGRO_JOYSTICK_STATE*); - -/* OSX HID Joystick - * Maintains an array of links which connect a HID cookie to - * an element in the ALLEGRO_JOYSTICK_STATE structure. - */ -typedef struct { - ALLEGRO_JOYSTICK parent; - struct { - IOHIDElementCookie cookie; - int* ppressed; - } button_link[_AL_MAX_JOYSTICK_BUTTONS]; - struct { - IOHIDElementCookie cookie; - SInt32 intvalue; - float* pvalue; - float offset; - float multiplier; - int stick, axis; - } axis_link[_AL_MAX_JOYSTICK_AXES * _AL_MAX_JOYSTICK_STICKS]; - int num_axis_links; - ALLEGRO_JOYSTICK_STATE state; - IOHIDDeviceInterface122** interface; - IOHIDQueueInterface** queue; - CFRunLoopSourceRef source; -} ALLEGRO_JOYSTICK_OSX; - -static ALLEGRO_JOYSTICK_OSX joysticks[_AL_MAX_JOYSTICKS]; -static unsigned int joystick_count; - -/* create_device_iterator: - * Create an iterator which will match all joysticks/ - * gamepads on the system. - */ -static io_iterator_t create_device_iterator(UInt16 usage) -{ - NSMutableDictionary* matching; - io_iterator_t iter; - matching = (NSMutableDictionary*) IOServiceMatching(kIOHIDDeviceKey); - // Add in our criteria: - [matching setObject:[NSNumber numberWithShort: usage] forKey: (NSString*) CFSTR(kIOHIDPrimaryUsageKey)]; - [matching setObject:[NSNumber numberWithShort: kHIDPage_GenericDesktop] forKey: (NSString*) CFSTR(kIOHIDPrimaryUsagePageKey)]; - // Get the iterator - IOReturn err = IOServiceGetMatchingServices(kIOMasterPortDefault, (CFDictionaryRef) matching, &iter); - return (err == kIOReturnSuccess) ? iter : 0; -} - -/* create_interface: - * Create the interface to access this device, via - * the intermediate plug-in interface -*/ -static BOOL create_interface(io_object_t device, IOHIDDeviceInterface122*** interface) -{ - io_name_t class_name; - IOReturn err = IOObjectGetClass(device,class_name); - SInt32 score; - IOCFPlugInInterface** plugin; - err = IOCreatePlugInInterfaceForService(device, - kIOHIDDeviceUserClientTypeID, - kIOCFPlugInInterfaceID, - &plugin, - &score); - (*plugin)->QueryInterface(plugin, - CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID122), - (LPVOID) interface); - - (*plugin)->Release(plugin); - return YES; -} - -/* joystick_callback: - * Called when an event occurs on the joystick event queue - * target: the joystick - * refcon: always null - * sender: the queue - */ -static void joystick_callback(void *target, IOReturn result, void *refcon __attribute__((unused)), void *sender) -{ - ALLEGRO_JOYSTICK_OSX* joy = (ALLEGRO_JOYSTICK_OSX*) target; - IOHIDQueueInterface** queue = (IOHIDQueueInterface**) sender; - AbsoluteTime past = {0,0}; - ALLEGRO_EVENT_SOURCE *src = al_get_joystick_event_source(); - if (src == NULL) { - return; - } - _al_event_source_lock(src); - while (result == kIOReturnSuccess) { - IOHIDEventStruct event; - result = (*queue)->getNextEvent(queue, &event, past, 0); - if (result == kIOReturnSuccess) { - int i; - for (i=0; iparent.info.num_buttons; ++i) { - if (joy->button_link[i].cookie == event.elementCookie) { - int newvalue = event.value; - if (*joy->button_link[i].ppressed != newvalue) { - *joy->button_link[i].ppressed = newvalue; - // emit event - ALLEGRO_EVENT evt; - if (newvalue) - evt.type = ALLEGRO_EVENT_JOYSTICK_BUTTON_DOWN; - else - evt.type = ALLEGRO_EVENT_JOYSTICK_BUTTON_UP; - evt.joystick.button = i; - _al_event_source_emit_event(src, &evt); - } - } - } - for (i=0; inum_axis_links; ++i) { - if (joy->axis_link[i].cookie == event.elementCookie) { - SInt32 newvalue = event.value; - if (joy->axis_link[i].intvalue != newvalue) { - joy->axis_link[i].intvalue = newvalue; - *joy->axis_link[i].pvalue = (joy->axis_link[i].offset + newvalue) * joy->axis_link[i].multiplier; - // emit event - ALLEGRO_EVENT evt; - evt.type = ALLEGRO_EVENT_JOYSTICK_AXIS; - evt.joystick.axis = joy->axis_link[i].axis; - evt.joystick.pos = *joy->axis_link[i].pvalue; - evt.joystick.stick = joy->axis_link[i].stick; - _al_event_source_emit_event(src, &evt); - } - } - } - } - } - _al_event_source_unlock(src); -} - -/* add_device: - * Create the joystick structure for this device - * and add it to the 'joysticks' vector - * TODO this only works for simple joysticks and - * only allows access to the primary X & Y axes. - * In reality there can be more axes than this and - * more that one distinct controller handled by the same - * interface. - * We should iterate through the application collections to - * find the joysticks then through the physical collections - * therein to identify the individual sticks. - */ -static void add_device(io_object_t device) -{ - ALLEGRO_JOYSTICK_OSX* joy; - NSArray* elements = nil; - int num_buttons = 0; - BOOL have_x = NO, have_y = NO; - IOReturn err; - joy = &joysticks[joystick_count++]; - memset(joy, 0, sizeof(*joy)); - joy->parent.info.num_sticks = 0; - joy->parent.info.num_buttons = 0; - IOHIDDeviceInterface122** interface; - create_interface(device, &interface); - // Open the device - err = (*interface)->open(interface, 0); - // Create an event queue - IOHIDQueueInterface** queue = (*interface)->allocQueue(interface); - err = (*queue)->create(queue, 0, 8); - // Create a source - err = (*queue)->createAsyncEventSource(queue, &joy->source); - err = (*queue)->setEventCallout(queue, joystick_callback, joy, NULL); - joy->queue = queue; - (*interface)->copyMatchingElements(interface, NULL, (CFArrayRef*) &elements); - NSEnumerator* enumerator = [elements objectEnumerator]; - NSDictionary* element; - while ((element = (NSDictionary*) [enumerator nextObject])) { - short usage = [((NSNumber*) [element objectForKey: (NSString*) CFSTR(kIOHIDElementUsageKey)]) shortValue]; - short usage_page = [((NSNumber*) [element objectForKey: (NSString*) CFSTR(kIOHIDElementUsagePageKey)]) shortValue]; - - if (usage_page == kHIDPage_Button && num_buttons < _AL_MAX_JOYSTICK_BUTTONS) { - joy->button_link[num_buttons].cookie = (IOHIDElementCookie) [((NSNumber*) [element objectForKey: (NSString*) CFSTR(kIOHIDElementCookieKey)]) pointerValue]; - joy->button_link[num_buttons].ppressed = &joy->state.button[num_buttons]; - // Use the provided name or make one up. - NSString* name = (NSString*) [element objectForKey: (NSString*) CFSTR(kIOHIDElementNameKey)]; - if (name == nil) { - name = [NSString stringWithFormat:@"Button %d", (num_buttons+1)]; - } - ALLEGRO_INFO("Found button named \"%s\"\n", [name UTF8String]); - // Say that we want events from this button - err = (*queue)->addElement(queue, joy->button_link[num_buttons].cookie, 0); - if (err != 0) { - ALLEGRO_WARN("Button named \"%s\" NOT added to event queue\n", [name UTF8String]); - } else { - joy->parent.info.button[num_buttons].name = strdup([name UTF8String]); - ++num_buttons; - } - } - - if (usage_page == kHIDPage_GenericDesktop) { - if ((usage == kHIDUsage_GD_X) && (!have_x)) { - NSNumber* minValue = (NSNumber*) [element objectForKey: (NSString*) CFSTR(kIOHIDElementMinKey)]; - NSNumber* maxValue = (NSNumber*) [element objectForKey: (NSString*) CFSTR(kIOHIDElementMaxKey)]; - joy->axis_link[0].axis = 0; - joy->axis_link[0].stick = 0; - joy->axis_link[0].offset = - ([minValue floatValue] + [maxValue floatValue]) / 2.0f; - joy->axis_link[0].multiplier = 2.0f / ([maxValue floatValue] - [minValue floatValue]); - joy->axis_link[0].cookie = (IOHIDElementCookie) [((NSNumber*) [element objectForKey: (NSString*) CFSTR(kIOHIDElementCookieKey)]) pointerValue]; - joy->axis_link[0].pvalue = &joy->state.stick[0].axis[0]; - NSString* name = (NSString*) [element objectForKey: (NSString*) CFSTR(kIOHIDElementNameKey)]; - if (name == nil) { - name = @"X-axis"; - } - ALLEGRO_INFO("Found X-axis named \"%s\"\n", [name UTF8String]); - // Say that we want events from this axis - err = (*queue)->addElement(queue, joy->axis_link[0].cookie, 0); - if (err != 0) { - ALLEGRO_WARN("X-axis named \"%s\" NOT added to event queue\n", [name UTF8String]); - } else { - have_x = YES; - joy->parent.info.stick[0].axis[0].name = strdup([name UTF8String]); - } - } - if ((usage == kHIDUsage_GD_Y) && (!have_y)) { - NSNumber* minValue = (NSNumber*) [element objectForKey: (NSString*) CFSTR(kIOHIDElementMinKey)]; - NSNumber* maxValue = (NSNumber*) [element objectForKey: (NSString*) CFSTR(kIOHIDElementMaxKey)]; - joy->axis_link[1].axis = 1; - joy->axis_link[1].stick = 0; - joy->axis_link[1].offset = - ([minValue floatValue] + [maxValue floatValue]) / 2.0f; - joy->axis_link[1].multiplier = 2.0f / ([maxValue floatValue] - [minValue floatValue]); - joy->axis_link[1].cookie = (IOHIDElementCookie) [((NSNumber*) [element objectForKey: (NSString*) CFSTR(kIOHIDElementCookieKey)]) pointerValue]; - joy->axis_link[1].pvalue = &joy->state.stick[0].axis[1]; - NSString* name = (NSString*) [element objectForKey: (NSString*) CFSTR(kIOHIDElementNameKey)]; - if (name == nil) { - name = @"Y-axis"; - } - ALLEGRO_INFO("Found Y-axis named \"%s\"\n", [name UTF8String]); - // Say that we want events from this axis - err = (*queue)->addElement(queue, joy->axis_link[1].cookie, 0); - if (err != 0) { - ALLEGRO_WARN("Y-axis named \"%s\" NOT added to event queue\n", [name UTF8String]); - } else { - have_y = YES; - joy->parent.info.stick[0].axis[1].name = strdup([name UTF8String]); - } - } - } - } - joy->parent.info.num_buttons = num_buttons; - if (have_x && have_y) { - joy->parent.info.stick[0].name = strdup("Primary axis"); - joy->parent.info.num_sticks = 1; - joy->parent.info.stick[0].num_axes = 2; - joy->num_axis_links = 2; - } - joy->interface = interface; - joystick_callback(joy,kIOReturnSuccess,NULL,queue); - [elements release]; -} - -// FIXME! -static const char *get_joystick_name(ALLEGRO_JOYSTICK *joy_) -{ - (void)joy_; - return "Joystick"; -} - -static bool get_joystick_active(ALLEGRO_JOYSTICK *joy_) -{ - (void)joy_; - return true; -} - -static bool reconfigure_joysticks(void) -{ - return false; -} - -ALLEGRO_JOYSTICK_DRIVER* _al_osx_get_joystick_driver_10_4(void) -{ - static ALLEGRO_JOYSTICK_DRIVER* vt = NULL; - if (vt == NULL) { - vt = al_malloc(sizeof(*vt)); - memset(vt, 0, sizeof(*vt)); - vt->joydrv_ascii_name = "OSX HID Driver"; - vt->init_joystick = init_joystick; - vt->exit_joystick = exit_joystick; - vt->num_joysticks = num_joysticks; - vt->get_joystick = get_joystick; - vt->release_joystick = release_joystick; - vt->get_joystick_state = get_joystick_state; - vt->reconfigure_joysticks = reconfigure_joysticks; - vt->get_name = get_joystick_name; - vt->get_active = get_joystick_active; - } - return vt; -} - -/* init_joystick: - * Initializes the HID joystick driver. - */ -static bool init_joystick(void) -{ - joystick_count = 0; - io_iterator_t iterator; - io_object_t device; - iterator = create_device_iterator(kHIDUsage_GD_GamePad); - if (iterator != 0) { - while ((device = IOIteratorNext(iterator))) { - add_device(device); - } - IOObjectRelease(iterator); - } - iterator = create_device_iterator(kHIDUsage_GD_Joystick); - if (iterator != 0) { - while ((device = IOIteratorNext(iterator))) { - add_device(device); - } - IOObjectRelease(iterator); - } - /* Ensure the source is added and started on the main thread */ - dispatch_sync(dispatch_get_main_queue(), ^{ - unsigned int i; - CFRunLoopRef current = CFRunLoopGetCurrent(); - for (i=0; isource,kCFRunLoopDefaultMode); - (*joy->queue)->start(joy->queue); - } - }); - return true; -} - - - -/* exit_joystick: - * Shuts down the HID joystick driver. - */ -static void exit_joystick(void) -{ - /* Ensure the source is stopped and removed on the main thread */ - dispatch_sync(dispatch_get_main_queue(), ^{ - unsigned int i; - CFRunLoopRef current = CFRunLoopGetCurrent(); - for (i=0; iqueue)->stop(joy->queue); - CFRunLoopRemoveSource(current,joy->source,kCFRunLoopDefaultMode); - } - }); - unsigned int i; - for (i=0; i< joystick_count; ++i) { - ALLEGRO_JOYSTICK_OSX* joy = &joysticks[i]; - CFRelease(joy->source); - if (joy->queue) { - (*joy->queue)->dispose(joy->queue); - (*joy->queue)->Release(joy->queue); - } - if (joy->interface) { - (*joy->interface)->close(joy->interface); - (*joy->interface)->Release(joy->interface); - } - int a, b, s; - /* Free everything we might have created - * (all fields set to NULL initially so this is OK.) - */ - for (b = 0; b < _AL_MAX_JOYSTICK_BUTTONS; ++ b) { - al_free((void*) joy->parent.info.button[b].name); - } - for (s = 0; s < _AL_MAX_JOYSTICK_STICKS; ++s) { - al_free((void*) joy->parent.info.stick[s].name); - for (a = 0; a < _AL_MAX_JOYSTICK_AXES; ++a) { - al_free((void*) joy->parent.info.stick[s].axis[a].name); - } - } - } -} - -/* num_joysticks: - * Return number of active joysticks - */ -static int num_joysticks(void) -{ - return joystick_count; -} - -/* get_joystick: - * Get a pointer to a joystick structure - */ -static ALLEGRO_JOYSTICK* get_joystick(int index) -{ - ALLEGRO_JOYSTICK* joy = NULL; - if (index >= 0 && index < (int) joystick_count) { - joy = (ALLEGRO_JOYSTICK *)&joysticks[index]; - } - return joy; -} - -/* release_joystick: - * Release a pointer that has been obtained - */ -static void release_joystick(ALLEGRO_JOYSTICK* joy __attribute__((unused)) ) -{ - // No-op -} - -/* get_joystick_state: - * Get the current status of a joystick - */ -static void get_joystick_state(ALLEGRO_JOYSTICK* ajoy, ALLEGRO_JOYSTICK_STATE* state) -{ - ALLEGRO_JOYSTICK_OSX* joy = (ALLEGRO_JOYSTICK_OSX*) ajoy; - memcpy(state, &joy->state,sizeof(*state)); -} - -/* Local variables: */ -/* c-basic-offset: 3 */ -/* indent-tabs-mode: nil */ -/* End: */ diff --git a/src/macosx/hidjoy.m b/src/macosx/hidjoy.m index 1b4fcfbbf..7815bfb6a 100644 --- a/src/macosx/hidjoy.m +++ b/src/macosx/hidjoy.m @@ -26,8 +26,6 @@ #error something is wrong with the makefile #endif -#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 - #import /* State transitions: @@ -50,16 +48,32 @@ #define JOYSTICK_USAGE_NUMBER 0x04 #define GAMEPAD_USAGE_NUMBER 0x05 +typedef struct { + IOHIDElementRef elem; + _AL_JOYSTICK_OUTPUT output; +} BUTTON_MAPPING; + +typedef struct { + IOHIDElementRef elem; + long min; + long max; + _AL_JOYSTICK_OUTPUT x_output; + _AL_JOYSTICK_OUTPUT y_output; +} HAT_MAPPING; + +typedef struct { + IOHIDElementRef elem; + long min; + long max; + _AL_JOYSTICK_OUTPUT output; +} AXIS_MAPPING; + typedef struct { ALLEGRO_JOYSTICK parent; - IOHIDElementRef buttons[_AL_MAX_JOYSTICK_BUTTONS]; - IOHIDElementRef axes[_AL_MAX_JOYSTICK_STICKS][_AL_MAX_JOYSTICK_AXES]; - IOHIDElementRef dpad; - int dpad_stick; - int dpad_axis_vert; - int dpad_axis_horiz; - long min[_AL_MAX_JOYSTICK_STICKS][_AL_MAX_JOYSTICK_AXES]; - long max[_AL_MAX_JOYSTICK_STICKS][_AL_MAX_JOYSTICK_AXES]; + BUTTON_MAPPING buttons[_AL_MAX_JOYSTICK_BUTTONS]; + AXIS_MAPPING axes[_AL_MAX_JOYSTICK_STICKS * _AL_MAX_JOYSTICK_AXES]; + HAT_MAPPING hats[_AL_MAX_JOYSTICK_STICKS]; + int hat_state[_AL_MAX_JOYSTICK_AXES]; CONFIG_STATE cfg_state; ALLEGRO_JOYSTICK_STATE state; IOHIDDeviceRef ident; @@ -154,33 +168,14 @@ static CFMutableDictionaryRef CreateDeviceMatchingDictionary( return default_name; } -static void joy_null(ALLEGRO_JOYSTICK_OSX *joy) -{ - int i, j; - - // NULL the parent - for (i = 0; i < _AL_MAX_JOYSTICK_BUTTONS; i++) { - joy->parent.info.button[i].name = NULL; - } - for (i = 0; i < _AL_MAX_JOYSTICK_STICKS; i++) { - joy->parent.info.stick[i].name = NULL; - for (j = 0; j < _AL_MAX_JOYSTICK_AXES; j++) { - joy->parent.info.stick[i].axis[j].name = NULL; - } - } +static bool compat_5_2_9(void) { + // Broken usages. + return _al_get_joystick_compat_version() < AL_ID(5, 2, 10, 0); } -static void add_axis(ALLEGRO_JOYSTICK_OSX *joy, int stick_index, int axis_index, int min, int max, char *name, IOHIDElementRef elem) -{ - if (axis_index >= _AL_MAX_JOYSTICK_AXES) - return; - - joy->min[stick_index][axis_index] = min; - joy->max[stick_index][axis_index] = max; - - joy->parent.info.stick[stick_index].axis[axis_index].name = name; - joy->parent.info.stick[stick_index].num_axes++; - joy->axes[stick_index][axis_index] = elem; +static bool compat_5_2_10(void) { + // Different axis normalization, hat events + mappings. + return _al_get_joystick_compat_version() < AL_ID(5, 2, 11, 0); } static void add_elements(CFArrayRef elements, ALLEGRO_JOYSTICK_OSX *joy) @@ -188,10 +183,12 @@ static void add_elements(CFArrayRef elements, ALLEGRO_JOYSTICK_OSX *joy) int i; char default_name[100]; int stick_class = -1; - int axis_index = 0; - bool old_style = _al_get_joystick_compat_version() < AL_ID(5, 2, 10, 0); - - joy_null(joy); + int axis = 0; + int num_sticks = -1; + int num_buttons = 0; + int num_hats = 0; + int num_axes = 0; + bool old_style = compat_5_2_9(); for (i = 0; i < CFArrayGetCount(elements); i++) { IOHIDElementRef elem = (IOHIDElementRef)CFArrayGetValueAtIndex( @@ -206,28 +203,23 @@ static void add_elements(CFArrayRef elements, ALLEGRO_JOYSTICK_OSX *joy) if (old_style) idx = usage - 1; else { - idx = joy->parent.info.num_buttons; + idx = num_buttons; // 0x09 is the Button Page. if (usage_page != 0x09) continue; } - if (idx >= 0 && idx < _AL_MAX_JOYSTICK_BUTTONS && - !joy->buttons[idx]) { - joy->buttons[idx] = elem; + if (idx >= 0 && idx < _AL_MAX_JOYSTICK_BUTTONS && !joy->buttons[idx].elem) { + joy->buttons[idx].elem = elem; + joy->buttons[idx].output = _al_new_joystick_button_output(idx); sprintf(default_name, "Button %d", idx); - const char *name = get_element_name(elem, default_name); - char *str = al_malloc(strlen(name)+1); - strcpy(str, name); - joy->parent.info.button[idx].name = str; - joy->parent.info.num_buttons++; + joy->parent.info.button[idx].name = _al_strdup(get_element_name(elem, default_name)); + num_buttons++; } } - else if ( - IOHIDElementGetType(elem) == kIOHIDElementTypeInput_Misc) { + else if (IOHIDElementGetType(elem) == kIOHIDElementTypeInput_Misc) { long min = IOHIDElementGetLogicalMin(elem); long max = IOHIDElementGetLogicalMax(elem); int new_stick_class = -1; - int stick_index = joy->parent.info.num_sticks - 1; switch (usage) { case kHIDUsage_GD_X: @@ -251,52 +243,192 @@ static void add_elements(CFArrayRef elements, ALLEGRO_JOYSTICK_OSX *joy) continue; if (stick_class != new_stick_class) { - if (joy->parent.info.num_sticks >= _AL_MAX_JOYSTICK_STICKS-1) + if (num_sticks >= _AL_MAX_JOYSTICK_STICKS - 1) break; - joy->parent.info.num_sticks++; - - stick_index++; - axis_index = 0; + num_sticks++; stick_class = new_stick_class; + axis = 0; char *buf = al_malloc(20); - sprintf(buf, "Stick %d", stick_index); - joy->parent.info.stick[stick_index].name = buf; + sprintf(buf, "Stick %d", num_sticks); + joy->parent.info.stick[num_sticks].name = buf; } else - axis_index++; + axis++; if (stick_class == 3) { - joy->dpad_stick = stick_index; - joy->dpad = elem; - - joy->dpad_axis_horiz = axis_index; - sprintf(default_name, "Axis %i", axis_index); - char *str = al_malloc(strlen(default_name)+1); - strcpy(str, default_name); - joy->parent.info.stick[stick_index].axis[axis_index].name = str; - - ++axis_index; - joy->dpad_axis_vert = axis_index; - sprintf(default_name, "Axis %i", axis_index); - str = al_malloc(strlen(default_name)+1); - strcpy(str, default_name); - add_axis(joy, stick_index, axis_index, min, max, str, elem); - joy->parent.info.stick[stick_index].axis[axis_index].name = str; - - joy->parent.info.stick[stick_index].num_axes = 2; + if (num_hats >= _AL_MAX_JOYSTICK_AXES) + continue; + + HAT_MAPPING *map = &joy->hats[num_hats]; + map->min = min; + map->max = max; + map->elem = elem; + map->x_output = _al_new_joystick_stick_output(num_sticks, 0); + map->y_output = _al_new_joystick_stick_output(num_sticks, 1); + + for (axis = 0; axis < 2; axis++) { + sprintf(default_name, "Axis %i", axis); + joy->parent.info.stick[num_sticks].axis[axis].name = _al_strdup(get_element_name(elem, default_name)); + } + joy->parent.info.stick[num_sticks].num_axes = 2; + + num_hats++; + } + else { + if (num_axes >= _AL_MAX_JOYSTICK_AXES) + continue; + + AXIS_MAPPING *map = &joy->axes[num_axes]; + map->min = min; + map->max = max; + map->elem = elem; + map->output = _al_new_joystick_stick_output(num_sticks, axis); + + sprintf(default_name, "Axis %i", axis); + joy->parent.info.stick[num_sticks].axis[axis].name = _al_strdup(get_element_name(elem, default_name)); + joy->parent.info.stick[num_sticks].num_axes++; + + num_axes++; + } + } + } + joy->parent.info.num_sticks = num_sticks + 1; + joy->parent.info.num_buttons = num_buttons; +} + +static bool add_elements_with_mapping(CFArrayRef elements, ALLEGRO_JOYSTICK_OSX *joy, const _AL_JOYSTICK_MAPPING *mapping) +{ + int num_buttons = 0; + int num_hats = 0; + int num_axes = 0; + int num_buttons_mapped = 0; + int num_hats_mapped = 0; + int num_axes_mapped = 0; + _AL_JOYSTICK_OUTPUT *output; + + for (int i = 0; i < CFArrayGetCount(elements); i++) { + IOHIDElementRef elem = (IOHIDElementRef)CFArrayGetValueAtIndex( + elements, + i + ); + + int usage = IOHIDElementGetUsage(elem); + int usage_page = IOHIDElementGetUsagePage(elem); + if (IOHIDElementGetType(elem) == kIOHIDElementTypeInput_Button) { + int idx = num_buttons; + // 0x09 is the Button Page. + if (usage_page != 0x09) + continue; + if (idx >= 0 && idx < _AL_MAX_JOYSTICK_BUTTONS && !joy->buttons[idx].elem) { + joy->buttons[idx].elem = elem; + output = _al_get_joystick_output(&mapping->button_map, idx); + if (output) { + joy->buttons[idx].output = *output; + num_buttons_mapped++; + } + num_buttons++; + } + } + else if (IOHIDElementGetType(elem) == kIOHIDElementTypeInput_Misc) { + long min = IOHIDElementGetLogicalMin(elem); + long max = IOHIDElementGetLogicalMax(elem); + + int elem_kind = -1; + switch (usage) { + case kHIDUsage_GD_X: + case kHIDUsage_GD_Y: + case kHIDUsage_GD_Z: + case kHIDUsage_GD_Rx: + case kHIDUsage_GD_Ry: + case kHIDUsage_GD_Rz: { + elem_kind = 0; + break; + } + case kHIDUsage_GD_Hatswitch: { + elem_kind = 1; + break; + } + default: + continue; + } + + if (elem_kind < 0) + continue; + + if (elem_kind == 1) { + if (num_hats >= _AL_MAX_JOYSTICK_AXES) + continue; + + HAT_MAPPING *map = &joy->hats[num_hats]; + map->min = min; + map->max = max; + map->elem = elem; + output = _al_get_joystick_output(&mapping->hat_map, 2 * num_hats); + if (output) { + map->x_output = *output; + num_hats_mapped++; + } + output = _al_get_joystick_output(&mapping->hat_map, 2 * num_hats + 1); + if (output) { + map->y_output = *output; + num_hats_mapped++; + } + num_hats++; } else { - sprintf(default_name, "Axis %i", axis_index); - const char *name = get_element_name(elem, default_name); - char *str = al_malloc(strlen(name)+1); - strcpy(str, name); - add_axis(joy, stick_index, axis_index, min, max, str, elem); + if (num_axes >= _AL_MAX_JOYSTICK_AXES) + continue; + + AXIS_MAPPING *map = &joy->axes[num_axes]; + map->min = min; + map->max = max; + map->elem = elem; + output = _al_get_joystick_output(&mapping->axis_map, num_axes); + if (output) { + map->output = *output; + num_axes_mapped++; + } + num_axes++; } } } + if (num_axes_mapped != (int)_al_vector_size(&mapping->axis_map)) { + ALLEGRO_ERROR("Could not use mapping, some axes are not mapped.\n"); + return false; + } + if (num_hats_mapped != (int)_al_vector_size(&mapping->hat_map)) { + ALLEGRO_ERROR("Could not use mapping, some hats are not mapped.\n"); + return false; + } + if (num_buttons_mapped != (int)_al_vector_size(&mapping->button_map)) { + ALLEGRO_ERROR("Could not use mapping, some buttons are not mapped.\n"); + return false; + } + return true; +} + +static void set_joystick_name(ALLEGRO_JOYSTICK_OSX *joy) +{ + CFStringRef str; + + str = IOHIDDeviceGetProperty(joy->ident, CFSTR(kIOHIDProductKey)); + if (str) { + CFIndex length = CFStringGetLength(str); + CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; + if (joy->name) { + al_free(joy->name); + } + joy->name = (char *)al_malloc(maxSize); + if (joy->name) { + if (CFStringGetCString(str, joy->name, maxSize, kCFStringEncodingUTF8)) { + return; + } + } + } + joy->name = _al_strdup("Joystick"); } static void osx_joy_generate_configure_event(void) @@ -316,8 +448,7 @@ static void add_joystick_device(IOHIDDeviceRef ref, bool emit_reconfigure_event) ALLEGRO_JOYSTICK_OSX *joy = find_joystick(ref); - if (joy && (joy->cfg_state == JOY_STATE_BORN || joy->cfg_state == JOY_STATE_ALIVE)) - { + if (joy && (joy->cfg_state == JOY_STATE_BORN || joy->cfg_state == JOY_STATE_ALIVE)) { al_unlock_mutex(add_mutex); return; } @@ -337,7 +468,48 @@ static void add_joystick_device(IOHIDDeviceRef ref, bool emit_reconfigure_event) kIOHIDOptionsTypeNone ); - add_elements(elements, joy); + CFTypeRef type; + + type = IOHIDDeviceGetProperty(joy->ident, CFSTR(kIOHIDVendorIDKey)); + int32_t vendor = 0; + if (type) + CFNumberGetValue(type, kCFNumberSInt32Type, &vendor); + + type = IOHIDDeviceGetProperty(joy->ident, CFSTR(kIOHIDProductIDKey)); + int32_t product = 0; + if (type) + CFNumberGetValue(type, kCFNumberSInt32Type, &product); + + type = IOHIDDeviceGetProperty(joy->ident, CFSTR(kIOHIDVersionNumberKey)); + int32_t version = 0; + if (type) + CFNumberGetValue(type, kCFNumberSInt32Type, &version); + + CFStringRef str; + + str = IOHIDDeviceGetProperty(joy->ident, CFSTR(kIOHIDManufacturerKey)); + char manufacturer_str[256]; + if (!str || !CFStringGetCString(str, manufacturer_str, sizeof(manufacturer_str), kCFStringEncodingUTF8)) + manufacturer_str[0] = '\0'; + + str = IOHIDDeviceGetProperty(joy->ident, CFSTR(kIOHIDProductKey)); + char product_str[256]; + if (!str || !CFStringGetCString(str, product_str, sizeof(product_str), kCFStringEncodingUTF8)) + product_str[0] = '\0'; + + ALLEGRO_JOYSTICK_GUID guid = _al_new_joystick_guid(0x03, (uint16_t)vendor, (uint16_t)product, (uint16_t)version, manufacturer_str, product_str, 0, 0); + + const _AL_JOYSTICK_MAPPING *mapping = _al_get_gamepad_mapping("Mac OS X", guid); + + joy->parent.info.guid = guid; + if (mapping && add_elements_with_mapping(elements, joy, mapping)) { + _al_fill_gamepad_info(&joy->parent.info); + joy->name = _al_strdup(mapping->name); + } + else { + set_joystick_name(joy); + add_elements(elements, joy); + } CFRelease(elements); @@ -411,54 +583,11 @@ static void device_remove_callback( } } -static void osx_joy_generate_axis_event(ALLEGRO_JOYSTICK_OSX *joy, int stick, int axis, float pos) -{ - joy->state.stick[stick].axis[axis] = pos; - - ALLEGRO_EVENT event; - ALLEGRO_EVENT_SOURCE *es = al_get_joystick_event_source(); - - if (!_al_event_source_needs_to_generate_event(es)) - return; - - event.joystick.type = ALLEGRO_EVENT_JOYSTICK_AXIS; - event.joystick.timestamp = al_current_time(); - event.joystick.id = (ALLEGRO_JOYSTICK *)joy; - event.joystick.stick = stick; - event.joystick.axis = axis; - event.joystick.pos = pos; - event.joystick.button = 0; - - _al_event_source_emit_event(es, &event); -} - -static void osx_joy_generate_button_event(ALLEGRO_JOYSTICK_OSX *joy, int button, ALLEGRO_EVENT_TYPE event_type) -{ - joy->state.button[button] = event_type == ALLEGRO_EVENT_JOYSTICK_BUTTON_UP ? - 0 : 1;; - - ALLEGRO_EVENT event; - ALLEGRO_EVENT_SOURCE *es = al_get_joystick_event_source(); - - if (!_al_event_source_needs_to_generate_event(es)) - return; - - event.joystick.type = event_type; - event.joystick.timestamp = al_current_time(); - event.joystick.id = (ALLEGRO_JOYSTICK *)joy; - event.joystick.stick = 0; - event.joystick.axis = 0; - event.joystick.pos = 0.0; - event.joystick.button = button; - - _al_event_source_emit_event(es, &event); -} - #define MAX_HAT_DIRECTIONS 8 struct HAT_MAPPING { int axisV; int axisH; -} hat_mapping[MAX_HAT_DIRECTIONS] = { +} hat_mapping[MAX_HAT_DIRECTIONS + 1] = { { -1, 0 }, // 0 { -1, 1 }, // 1 { 0, 1 }, // 2 @@ -467,6 +596,7 @@ static void osx_joy_generate_button_event(ALLEGRO_JOYSTICK_OSX *joy, int button, { 1, -1 }, // 5 { 0, -1 }, // 6 { -1, -1 }, // 7 + { 0, 0 }, // 8 }; static void value_callback( @@ -482,6 +612,7 @@ static void value_callback( (void)sender; IOHIDElementRef elem = IOHIDValueGetElement(value); + int int_value = (int)IOHIDValueGetIntegerValue(value); IOHIDDeviceRef ref = IOHIDElementGetDevice(elem); ALLEGRO_JOYSTICK_OSX *joy = find_joystick(ref); @@ -490,72 +621,94 @@ static void value_callback( ALLEGRO_EVENT_SOURCE *es = al_get_joystick_event_source(); _al_event_source_lock(es); - int i; - for (i = 0; i < joy->parent.info.num_buttons; i++) { - if (joy->buttons[i] == elem) { - ALLEGRO_EVENT_TYPE type; - if (IOHIDValueGetIntegerValue(value) == 0) - type = ALLEGRO_EVENT_JOYSTICK_BUTTON_UP; - else - type = ALLEGRO_EVENT_JOYSTICK_BUTTON_DOWN; - osx_joy_generate_button_event(joy, i, type); - goto done; - } + for (int i = 0; i < _AL_MAX_JOYSTICK_BUTTONS; i++) { + BUTTON_MAPPING *map = &joy->buttons[i]; + if (!map->elem) + break; + if (map->elem != elem) + continue; + _al_joystick_generate_button_event(&joy->parent, &joy->state, map->output, int_value); } - int int_value = IOHIDValueGetIntegerValue(value); - int min = joy->min[joy->dpad_stick][1]; - int max = joy->max[joy->dpad_stick][1]; + for (int i = 0; i < _AL_MAX_JOYSTICK_AXES; i++) { + AXIS_MAPPING *map = &joy->axes[i]; + if (!map->elem) + break; + if (map->elem != elem) + continue; - if (joy->dpad == elem){ - if (int_value >= min && int_value <= max) { - int index = int_value - min; - if (index < MAX_HAT_DIRECTIONS) { - osx_joy_generate_axis_event(joy, joy->dpad_stick, joy->dpad_axis_vert, (float)hat_mapping[index].axisV); - osx_joy_generate_axis_event(joy, joy->dpad_stick, joy->dpad_axis_horiz, (float)hat_mapping[index].axisH); + float pos; + long min = map->min; + long max = map->max; + if (compat_5_2_10()) { + if (min < 0) { + if (int_value < 0) + pos = -(float)int_value / min; + else + pos = (float)int_value / max; } - } else { - osx_joy_generate_axis_event(joy, joy->dpad_stick, joy->dpad_axis_vert, 0); - osx_joy_generate_axis_event(joy, joy->dpad_stick, joy->dpad_axis_horiz, 0); - } - goto done; - } - - int stick = -1; - int axis = -1; - for (stick = 0; stick < joy->parent.info.num_sticks; stick++) { - for(axis = 0; axis < joy->parent.info.stick[stick].num_axes; ++axis) { - if (joy->axes[stick][axis] == elem) { - goto gen_axis_event; + else { + pos = ((float)int_value / max * 2) - 1; } } + else { + long range = max - min; + if (range > 0) + pos = 2. * ((float)int_value - min) / range - 1.; + else + /* TODO: Add auto-calibration? */ + pos = (float)int_value; + } + _al_joystick_generate_axis_event(&joy->parent, &joy->state, map->output, pos); } - // Unknown event - goto done; + for (int i = 0; i < _AL_MAX_JOYSTICK_AXES; i++) { + HAT_MAPPING *map = &joy->hats[i]; + if (!map->elem) + break; + if (map->elem != elem) + continue; -gen_axis_event: - { - float pos; - long min = joy->min[stick][axis]; - long max = joy->max[stick][axis]; - if (min < 0) { - if (int_value < 0) - pos = -(float)int_value/min; - else - pos = (float)int_value/max; + long min = map->min; + long max = map->max; + if (compat_5_2_10()) { + if (int_value >= min && int_value <= max) { + long index = int_value - min; + if (index < MAX_HAT_DIRECTIONS) { + _al_joystick_generate_axis_event(&joy->parent, &joy->state, map->x_output, (float)hat_mapping[index].axisH); + _al_joystick_generate_axis_event(&joy->parent, &joy->state, map->y_output, (float)hat_mapping[index].axisV); + } + } else { + _al_joystick_generate_axis_event(&joy->parent, &joy->state, map->x_output, 0); + _al_joystick_generate_axis_event(&joy->parent, &joy->state, map->y_output, 0); + } } else { - pos = ((float)int_value/max*2) - 1; + long state = (int)(int_value - min); + if (state < 0 || state >= MAX_HAT_DIRECTIONS) + state = MAX_HAT_DIRECTIONS; + float old_dx = (float)hat_mapping[joy->hat_state[i]].axisH; + float old_dy = (float)hat_mapping[joy->hat_state[i]].axisV; + float dx = (float)hat_mapping[state].axisH; + float dy = (float)hat_mapping[state].axisV; + joy->hat_state[i] = (int)state; + + if (old_dx != dx) + _al_joystick_generate_axis_event(&joy->parent, &joy->state, map->x_output, dx); + if (old_dy != dy) + _al_joystick_generate_axis_event(&joy->parent, &joy->state, map->y_output, dy); } - - osx_joy_generate_axis_event(joy, stick, axis, pos); } -done: _al_event_source_unlock(es); } +static void destroy_joystick(ALLEGRO_JOYSTICK_OSX *joy) { + _al_destroy_joystick_info(&joy->parent.info); + al_free(joy->name); + memset(joy, 0, sizeof(ALLEGRO_JOYSTICK_OSX)); +} + /* init_joystick: * Initializes the HID joystick driver. */ @@ -675,6 +828,10 @@ static void exit_joystick(void) ); CFRelease(hidManagerRef); + for (int i = 0; i < (int)_al_vector_size(&joysticks); i++) { + ALLEGRO_JOYSTICK_OSX *joy = *(ALLEGRO_JOYSTICK_OSX **)_al_vector_ref(&joysticks, i); + destroy_joystick(joy); + } _al_vector_free(&joysticks); initialized = false; @@ -750,21 +907,8 @@ static bool reconfigure_joysticks(void) for (i = 0; i < (int)_al_vector_size(&joysticks); i++) { ALLEGRO_JOYSTICK_OSX *joy = *(ALLEGRO_JOYSTICK_OSX **)_al_vector_ref(&joysticks, i); if (joy->cfg_state == JOY_STATE_DYING) { + destroy_joystick(joy); joy->cfg_state = JOY_STATE_UNUSED; - for (i = 0; i < _AL_MAX_JOYSTICK_BUTTONS; i++) { - al_free((char *)joy->parent.info.button[i].name); - } - for (i = 0; i < _AL_MAX_JOYSTICK_STICKS; i++) { - int j; - al_free(joy->parent.info.stick[i].name); - for (j = 0; j < _AL_MAX_JOYSTICK_AXES; j++) { - al_free(joy->parent.info.stick[i].axis[j].name); - } - } - joy_null(joy); - memset(joy->buttons, 0, _AL_MAX_JOYSTICK_BUTTONS*sizeof(IOHIDElementRef)); - memset(&joy->state, 0, sizeof(ALLEGRO_JOYSTICK_STATE)); - joy->dpad=0; } else if (joy->cfg_state == JOY_STATE_BORN) joy->cfg_state = JOY_STATE_ALIVE; @@ -779,23 +923,7 @@ static bool reconfigure_joysticks(void) static const char *get_joystick_name(ALLEGRO_JOYSTICK *joy_) { ALLEGRO_JOYSTICK_OSX *joy = (ALLEGRO_JOYSTICK_OSX *)joy_; - CFStringRef str; - - str = IOHIDDeviceGetProperty(joy->ident, CFSTR(kIOHIDProductKey)); - if (str) { - CFIndex length = CFStringGetLength(str); - CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; - if (joy->name) { - al_free(joy->name); - } - joy->name = (char *)al_malloc(maxSize); - if (joy->name) { - if (CFStringGetCString(str, joy->name, maxSize, kCFStringEncodingUTF8)) { - return joy->name; - } - } - } - return "Joystick"; + return joy->name; } static bool get_joystick_active(ALLEGRO_JOYSTICK *joy_) @@ -804,7 +932,7 @@ static bool get_joystick_active(ALLEGRO_JOYSTICK *joy_) return joy->cfg_state == JOY_STATE_ALIVE || joy->cfg_state == JOY_STATE_DYING; } -ALLEGRO_JOYSTICK_DRIVER* _al_osx_get_joystick_driver_10_5(void) +ALLEGRO_JOYSTICK_DRIVER* _al_osx_get_joystick_driver(void) { static ALLEGRO_JOYSTICK_DRIVER* vt = NULL; if (vt == NULL) { @@ -824,27 +952,6 @@ static bool get_joystick_active(ALLEGRO_JOYSTICK *joy_) return vt; } -#endif // Leopard+ - -#ifndef NSAppKitVersionNumber10_5 -#define NSAppKitVersionNumber10_5 949 -#endif - - - -ALLEGRO_JOYSTICK_DRIVER* _al_osx_get_joystick_driver_10_4(void); -ALLEGRO_JOYSTICK_DRIVER* _al_osx_get_joystick_driver_10_5(void); - -ALLEGRO_JOYSTICK_DRIVER* _al_osx_get_joystick_driver(void) -{ - if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_5) { - return _al_osx_get_joystick_driver_10_5(); - } - else { - return _al_osx_get_joystick_driver_10_4(); - } -} - /* Local variables: */ /* c-basic-offset: 3 */ /* indent-tabs-mode: nil */ diff --git a/src/memory.c b/src/memory.c index 658573b0a..62ccd4cb0 100644 --- a/src/memory.c +++ b/src/memory.c @@ -17,6 +17,7 @@ #include "allegro5/allegro.h" +#include "allegro5/internal/aintern.h" /* globals */ @@ -84,4 +85,12 @@ void *al_calloc_with_context(size_t count, size_t n, } +char *_al_strdup(const char* string) +{ + size_t length = strlen(string); + char *str = al_malloc(length + 1); + return memcpy(str, string, length + 1); +} + + /* vim: set ts=8 sts=3 sw=3 et: */ diff --git a/src/sdl/sdl_joystick.c b/src/sdl/sdl_joystick.c index 492292d4c..bd26943af 100644 --- a/src/sdl/sdl_joystick.c +++ b/src/sdl/sdl_joystick.c @@ -13,6 +13,7 @@ * See LICENSE.txt for copyright information. */ #include "allegro5/allegro.h" +#include "allegro5/internal/aintern.h" #include "allegro5/internal/aintern_system.h" #include "allegro5/platform/allegro_internal_sdl.h" @@ -23,12 +24,19 @@ typedef struct ALLEGRO_JOYSTICK_SDL int id; ALLEGRO_JOYSTICK allegro; SDL_Joystick *sdl; + SDL_GameController *sdl_gc; + bool dpad_state[4]; } ALLEGRO_JOYSTICK_SDL; static ALLEGRO_JOYSTICK_DRIVER *vt; static int count; static ALLEGRO_JOYSTICK_SDL *joysticks; +static bool compat_5_2_10(void) { + /* Mappings. */ + return _al_get_joystick_compat_version() < AL_ID(5, 2, 11, 0); +} + static int get_id(ALLEGRO_JOYSTICK *allegro) { int i; @@ -36,88 +44,227 @@ static int get_id(ALLEGRO_JOYSTICK *allegro) if (&joysticks[i].allegro == allegro) return i; } - return -1; -} - -static SDL_Joystick *get_sdl(ALLEGRO_JOYSTICK *allegro) -{ - int id = get_id(allegro); - if (id < 0) - return NULL; - return joysticks[id].sdl; + ASSERT(false); + return 0; } void _al_sdl_joystick_event(SDL_Event *e) { + bool emit = false; ALLEGRO_EVENT event; memset(&event, 0, sizeof event); event.joystick.timestamp = al_get_time(); - if (e->type == SDL_JOYAXISMOTION) { - event.joystick.type = ALLEGRO_EVENT_JOYSTICK_AXIS; - event.joystick.id = &joysticks[e->jaxis.which].allegro; - event.joystick.stick = e->jaxis.axis / 2; - event.joystick.axis = e->jaxis.axis % 2; - event.joystick.pos = e->jaxis.value / 32768.0; - event.joystick.button = 0; - } - else if (e->type == SDL_JOYBUTTONDOWN) { - event.joystick.type = ALLEGRO_EVENT_JOYSTICK_BUTTON_DOWN; - event.joystick.id = &joysticks[e->jbutton.which].allegro; - event.joystick.stick = 0; - event.joystick.axis = 0; - event.joystick.pos = 0; - event.joystick.button = e->jbutton.button; - } - else if (e->type == SDL_JOYBUTTONUP) { - event.joystick.type = ALLEGRO_EVENT_JOYSTICK_BUTTON_UP; - event.joystick.id = &joysticks[e->jbutton.which].allegro; - event.joystick.stick = 0; - event.joystick.axis = 0; - event.joystick.pos = 0; - event.joystick.button = e->jbutton.button; - } - else if (e->type == SDL_JOYDEVICEADDED || e->type == SDL_JOYDEVICEREMOVED) { - event.joystick.type = ALLEGRO_EVENT_JOYSTICK_CONFIGURATION; + if (joysticks[e->jaxis.which].allegro.info.type == ALLEGRO_JOYSTICK_TYPE_GAMEPAD) { + if (e->type == SDL_CONTROLLERAXISMOTION) { + int stick = -1; + int axis = -1; + switch (e->caxis.axis) { + case SDL_CONTROLLER_AXIS_LEFTX: + stick = ALLEGRO_GAMEPAD_STICK_LEFT_THUMB; + axis = 0; + break; + case SDL_CONTROLLER_AXIS_LEFTY: + stick = ALLEGRO_GAMEPAD_STICK_LEFT_THUMB; + axis = 1; + break; + case SDL_CONTROLLER_AXIS_RIGHTX: + stick = ALLEGRO_GAMEPAD_STICK_RIGHT_THUMB; + axis = 0; + break; + case SDL_CONTROLLER_AXIS_RIGHTY: + stick = ALLEGRO_GAMEPAD_STICK_RIGHT_THUMB; + axis = 1; + break; + case SDL_CONTROLLER_AXIS_TRIGGERLEFT: + stick = ALLEGRO_GAMEPAD_STICK_LEFT_TRIGGER; + axis = 0; + break; + case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: + stick = ALLEGRO_GAMEPAD_STICK_RIGHT_TRIGGER; + axis = 0; + break; + } + if (stick >= 0 && axis >= 0) { + event.joystick.type = ALLEGRO_EVENT_JOYSTICK_AXIS; + event.joystick.id = &joysticks[e->jaxis.which].allegro; + event.joystick.stick = stick; + event.joystick.axis = axis; + event.joystick.pos = e->jaxis.value / 32768.0; + event.joystick.button = 0; + emit = true; + } + } + else if (e->type == SDL_CONTROLLERBUTTONDOWN || e->type == SDL_CONTROLLERBUTTONUP) { + int stick = 0; + int axis = 0; + int button = 0; + float pos = 0.; + bool down = e->type == SDL_CONTROLLERBUTTONDOWN; + int type = down ? ALLEGRO_EVENT_JOYSTICK_BUTTON_DOWN : ALLEGRO_EVENT_JOYSTICK_BUTTON_UP; + ALLEGRO_JOYSTICK_SDL *joy = &joysticks[e->cbutton.which]; + + switch (e->cbutton.button) { + case SDL_CONTROLLER_BUTTON_A: + button = ALLEGRO_GAMEPAD_BUTTON_A; + break; + case SDL_CONTROLLER_BUTTON_B: + button = ALLEGRO_GAMEPAD_BUTTON_B; + break; + case SDL_CONTROLLER_BUTTON_X: + button = ALLEGRO_GAMEPAD_BUTTON_X; + break; + case SDL_CONTROLLER_BUTTON_Y: + button = ALLEGRO_GAMEPAD_BUTTON_Y; + break; + case SDL_CONTROLLER_BUTTON_BACK: + button = ALLEGRO_GAMEPAD_BUTTON_BACK; + break; + case SDL_CONTROLLER_BUTTON_GUIDE: + button = ALLEGRO_GAMEPAD_BUTTON_GUIDE; + break; + case SDL_CONTROLLER_BUTTON_START: + button = ALLEGRO_GAMEPAD_BUTTON_START; + break; + case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: + button = ALLEGRO_GAMEPAD_BUTTON_LEFT_SHOULDER; + break; + case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: + button = ALLEGRO_GAMEPAD_BUTTON_RIGHT_SHOULDER; + break; + case SDL_CONTROLLER_BUTTON_LEFTSTICK: + button = ALLEGRO_GAMEPAD_BUTTON_LEFT_THUMB; + break; + case SDL_CONTROLLER_BUTTON_RIGHTSTICK: + button = ALLEGRO_GAMEPAD_BUTTON_RIGHT_THUMB; + break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: + type = ALLEGRO_EVENT_JOYSTICK_AXIS; + axis = 0; + joy->dpad_state[2] = down; + break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: + type = ALLEGRO_EVENT_JOYSTICK_AXIS; + axis = 0; + joy->dpad_state[0] = down; + break; + case SDL_CONTROLLER_BUTTON_DPAD_UP: + type = ALLEGRO_EVENT_JOYSTICK_AXIS; + axis = 1; + joy->dpad_state[3] = down; + break; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: + type = ALLEGRO_EVENT_JOYSTICK_AXIS; + axis = 1; + joy->dpad_state[1] = down; + break; + default: + type = 0; + } + if (type == ALLEGRO_EVENT_JOYSTICK_AXIS) { + stick = ALLEGRO_GAMEPAD_STICK_DPAD; + if (axis == 0) + pos = (int)joy->dpad_state[0] - (int)joy->dpad_state[2]; + else + pos = (int)joy->dpad_state[1] - (int)joy->dpad_state[3]; + } + if (type != 0) { + event.joystick.type = type; + event.joystick.id = &joy->allegro; + event.joystick.stick = stick; + event.joystick.axis = axis; + event.joystick.pos = pos; + event.joystick.button = button; + emit = true; + } + } + else if (e->type == SDL_CONTROLLERDEVICEADDED || e->type == SDL_CONTROLLERDEVICEREMOVED) { + event.joystick.type = ALLEGRO_EVENT_JOYSTICK_CONFIGURATION; + } + else { + return; + } } else { - return; + if (e->type == SDL_JOYAXISMOTION) { + event.joystick.type = ALLEGRO_EVENT_JOYSTICK_AXIS; + event.joystick.id = &joysticks[e->jaxis.which].allegro; + event.joystick.stick = e->jaxis.axis / 2; + event.joystick.axis = e->jaxis.axis % 2; + event.joystick.pos = e->jaxis.value / 32768.0; + event.joystick.button = 0; + } + else if (e->type == SDL_JOYBUTTONDOWN) { + event.joystick.type = ALLEGRO_EVENT_JOYSTICK_BUTTON_DOWN; + event.joystick.id = &joysticks[e->jbutton.which].allegro; + event.joystick.stick = 0; + event.joystick.axis = 0; + event.joystick.pos = 0; + event.joystick.button = e->jbutton.button; + } + else if (e->type == SDL_JOYBUTTONUP) { + event.joystick.type = ALLEGRO_EVENT_JOYSTICK_BUTTON_UP; + event.joystick.id = &joysticks[e->jbutton.which].allegro; + event.joystick.stick = 0; + event.joystick.axis = 0; + event.joystick.pos = 0; + event.joystick.button = e->jbutton.button; + } + else if (e->type == SDL_JOYDEVICEADDED || e->type == SDL_JOYDEVICEREMOVED) { + event.joystick.type = ALLEGRO_EVENT_JOYSTICK_CONFIGURATION; + } + else { + return; + } + emit = true; } - ALLEGRO_EVENT_SOURCE *es = al_get_joystick_event_source(); - _al_event_source_lock(es); - _al_event_source_emit_event(es, &event); - _al_event_source_unlock(es); + if (emit) { + ALLEGRO_EVENT_SOURCE *es = al_get_joystick_event_source(); + _al_event_source_lock(es); + _al_event_source_emit_event(es, &event); + _al_event_source_unlock(es); + } } static bool sdl_init_joystick(void) { count = SDL_NumJoysticks(); - joysticks = count > 0 ? calloc(count, sizeof * joysticks) : NULL; + joysticks = count > 0 ? al_calloc(count, sizeof * joysticks) : NULL; int i; for (i = 0; i < count; i++) { - joysticks[i].sdl = SDL_JoystickOpen(i); - _AL_JOYSTICK_INFO *info = &joysticks[i].allegro.info; - int an = SDL_JoystickNumAxes(joysticks[i].sdl); + ALLEGRO_JOYSTICK_SDL *joy = &joysticks[i]; + SDL_JoystickGUID sdl_guid = SDL_JoystickGetDeviceGUID(i); + memcpy(&joy->allegro.info.guid, &sdl_guid, sizeof(ALLEGRO_JOYSTICK_GUID)); + _al_fill_gamepad_info(&joy->allegro.info); + + if (SDL_IsGameController(i) && !compat_5_2_10()) { + joy->sdl_gc = SDL_GameControllerOpen(i); + SDL_GameControllerEventState(SDL_ENABLE); + continue; + } + joy->sdl = SDL_JoystickOpen(i); + _AL_JOYSTICK_INFO *info = &joy->allegro.info; + int an = SDL_JoystickNumAxes(joy->sdl); int a; info->num_sticks = an / 2; for (a = 0; a < an; a++) { info->stick[a / 2].num_axes = 2; - info->stick[a / 2].name = "stick"; - info->stick[a / 2].axis[0].name = "X"; - info->stick[a / 2].axis[1].name = "Y"; + info->stick[a / 2].name = _al_strdup("stick"); + info->stick[a / 2].axis[0].name = _al_strdup("X"); + info->stick[a / 2].axis[1].name = _al_strdup("Y"); } - int bn = SDL_JoystickNumButtons(joysticks[i].sdl); + int bn = SDL_JoystickNumButtons(joy->sdl); info->num_buttons = bn; int b; for (b = 0; b < bn; b++) { - info->button[b].name = SDL_IsGameController(i) ? - SDL_GameControllerGetStringForButton(b) : "button"; + info->button[b].name = _al_strdup(SDL_IsGameController(i) ? + SDL_GameControllerGetStringForButton(b) : "button"); } + SDL_JoystickEventState(SDL_ENABLE); } - SDL_JoystickEventState(SDL_ENABLE); return true; } @@ -125,10 +272,14 @@ static void sdl_exit_joystick(void) { int i; for (i = 0; i < count; i++) { - SDL_JoystickClose(joysticks[i].sdl); + if (joysticks[i].sdl) + SDL_JoystickClose(joysticks[i].sdl); + if (joysticks[i].sdl_gc) + SDL_GameControllerClose(joysticks[i].sdl_gc); + _al_destroy_joystick_info(&joysticks[i].allegro.info); } count = 0; - free(joysticks); + al_free(joysticks); joysticks = NULL; } @@ -159,28 +310,69 @@ static void sdl_get_joystick_state(ALLEGRO_JOYSTICK *joy, ALLEGRO_SYSTEM_INTERFACE *s = _al_sdl_system_driver(); s->heartbeat(); - SDL_Joystick *sdl = get_sdl(joy); - int an = SDL_JoystickNumAxes(sdl); - int i; - for (i = 0; i < an; i++) { - ret_state->stick[i / 2].axis[i % 2] = SDL_JoystickGetAxis(sdl, i) / 32768.0; +#define BUTTON(x) ((x) * 32767) +#define AXIS(x) ((x) / 32768.0) + + int id = get_id(joy); + if (joysticks[id].allegro.info.type == ALLEGRO_JOYSTICK_TYPE_GAMEPAD) { + SDL_GameController *sdl_gc = joysticks[id].sdl_gc; + ret_state->stick[ALLEGRO_GAMEPAD_STICK_DPAD].axis[0] = SDL_GameControllerGetButton(sdl_gc, SDL_CONTROLLER_BUTTON_DPAD_RIGHT) + - SDL_GameControllerGetButton(sdl_gc, SDL_CONTROLLER_BUTTON_DPAD_LEFT); + ret_state->stick[ALLEGRO_GAMEPAD_STICK_DPAD].axis[1] = SDL_GameControllerGetButton(sdl_gc, SDL_CONTROLLER_BUTTON_DPAD_DOWN) + - SDL_GameControllerGetButton(sdl_gc, SDL_CONTROLLER_BUTTON_DPAD_UP); + ret_state->stick[ALLEGRO_GAMEPAD_STICK_LEFT_THUMB].axis[0] = AXIS(SDL_GameControllerGetAxis(sdl_gc, SDL_CONTROLLER_AXIS_LEFTX)); + ret_state->stick[ALLEGRO_GAMEPAD_STICK_LEFT_THUMB].axis[1] = AXIS(SDL_GameControllerGetAxis(sdl_gc, SDL_CONTROLLER_AXIS_LEFTY)); + ret_state->stick[ALLEGRO_GAMEPAD_STICK_RIGHT_THUMB].axis[0] = AXIS(SDL_GameControllerGetAxis(sdl_gc, SDL_CONTROLLER_AXIS_RIGHTX)); + ret_state->stick[ALLEGRO_GAMEPAD_STICK_RIGHT_THUMB].axis[1] = AXIS(SDL_GameControllerGetAxis(sdl_gc, SDL_CONTROLLER_AXIS_RIGHTY)); + ret_state->stick[ALLEGRO_GAMEPAD_STICK_LEFT_TRIGGER].axis[0] = AXIS(SDL_GameControllerGetAxis(sdl_gc, SDL_CONTROLLER_AXIS_TRIGGERLEFT)); + ret_state->stick[ALLEGRO_GAMEPAD_STICK_RIGHT_TRIGGER].axis[0] = AXIS(SDL_GameControllerGetAxis(sdl_gc, SDL_CONTROLLER_AXIS_TRIGGERRIGHT)); + + ret_state->button[ALLEGRO_GAMEPAD_BUTTON_A] = BUTTON(SDL_GameControllerGetButton(sdl_gc, SDL_CONTROLLER_BUTTON_A)); + ret_state->button[ALLEGRO_GAMEPAD_BUTTON_B] = BUTTON(SDL_GameControllerGetButton(sdl_gc, SDL_CONTROLLER_BUTTON_B)); + ret_state->button[ALLEGRO_GAMEPAD_BUTTON_X] = BUTTON(SDL_GameControllerGetButton(sdl_gc, SDL_CONTROLLER_BUTTON_X)); + ret_state->button[ALLEGRO_GAMEPAD_BUTTON_Y] = BUTTON(SDL_GameControllerGetButton(sdl_gc, SDL_CONTROLLER_BUTTON_Y)); + ret_state->button[ALLEGRO_GAMEPAD_BUTTON_BACK] = BUTTON(SDL_GameControllerGetButton(sdl_gc, SDL_CONTROLLER_BUTTON_BACK)); + ret_state->button[ALLEGRO_GAMEPAD_BUTTON_GUIDE] = BUTTON(SDL_GameControllerGetButton(sdl_gc, SDL_CONTROLLER_BUTTON_GUIDE)); + ret_state->button[ALLEGRO_GAMEPAD_BUTTON_START] = BUTTON(SDL_GameControllerGetButton(sdl_gc, SDL_CONTROLLER_BUTTON_START)); + ret_state->button[ALLEGRO_GAMEPAD_BUTTON_LEFT_SHOULDER] = BUTTON(SDL_GameControllerGetButton(sdl_gc, SDL_CONTROLLER_BUTTON_LEFTSHOULDER)); + ret_state->button[ALLEGRO_GAMEPAD_BUTTON_RIGHT_SHOULDER] = BUTTON(SDL_GameControllerGetButton(sdl_gc, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)); + ret_state->button[ALLEGRO_GAMEPAD_BUTTON_LEFT_THUMB] = BUTTON(SDL_GameControllerGetButton(sdl_gc, SDL_CONTROLLER_BUTTON_LEFTSTICK)); + ret_state->button[ALLEGRO_GAMEPAD_BUTTON_RIGHT_THUMB] = BUTTON(SDL_GameControllerGetButton(sdl_gc, SDL_CONTROLLER_BUTTON_RIGHTSTICK)); } - int bn = SDL_JoystickNumButtons(sdl); - for (i = 0; i < bn; i++) { - ret_state->button[i] = SDL_JoystickGetButton(sdl, i) * 32767; + else { + SDL_Joystick *sdl = joysticks[id].sdl; + int an = SDL_JoystickNumAxes(sdl); + int i; + for (i = 0; i < an; i++) { + ret_state->stick[i / 2].axis[i % 2] = AXIS(SDL_JoystickGetAxis(sdl, i)); + } + int bn = SDL_JoystickNumButtons(sdl); + for (i = 0; i < bn; i++) { + ret_state->button[i] = BUTTON(SDL_JoystickGetButton(sdl, i)); + } } + +#undef AXIS +#undef BUTTON + } static const char *sdl_get_name(ALLEGRO_JOYSTICK *joy) { - SDL_Joystick *sdl = get_sdl(joy); - return SDL_JoystickName(sdl); + int id = get_id(joy); + if (joysticks[id].sdl) + return SDL_JoystickName(joysticks[id].sdl); + else + return SDL_GameControllerName(joysticks[id].sdl_gc); } static bool sdl_get_active(ALLEGRO_JOYSTICK *joy) { - SDL_Joystick *sdl = get_sdl(joy); - return SDL_JoystickGetAttached(sdl); + int id = get_id(joy); + if (joysticks[id].sdl) + return SDL_JoystickGetAttached(joysticks[id].sdl); + else + return SDL_GameControllerGetAttached(joysticks[id].sdl_gc); } ALLEGRO_JOYSTICK_DRIVER *_al_sdl_joystick_driver(void) diff --git a/src/sdl/sdl_system.c b/src/sdl/sdl_system.c index e14a67e0c..205b6d138 100644 --- a/src/sdl/sdl_system.c +++ b/src/sdl/sdl_system.c @@ -105,6 +105,11 @@ static void sdl_heartbeat(void) case SDL_JOYBUTTONUP: case SDL_JOYDEVICEADDED: case SDL_JOYDEVICEREMOVED: + case SDL_CONTROLLERAXISMOTION: + case SDL_CONTROLLERBUTTONDOWN: + case SDL_CONTROLLERBUTTONUP: + case SDL_CONTROLLERDEVICEADDED: + case SDL_CONTROLLERDEVICEREMOVED: _al_sdl_joystick_event(&events[i]); break; case SDL_QUIT: diff --git a/src/win/wjoydxnu.cpp b/src/win/wjoydxnu.cpp index 8932674b6..d0c236c29 100644 --- a/src/win/wjoydxnu.cpp +++ b/src/win/wjoydxnu.cpp @@ -115,11 +115,9 @@ static void joydx_inactivate_joy(ALLEGRO_JOYSTICK_DIRECTX *joy); static unsigned __stdcall joydx_thread_proc(LPVOID unused); static void update_joystick(ALLEGRO_JOYSTICK_DIRECTX *joy); -static void handle_axis_event(ALLEGRO_JOYSTICK_DIRECTX *joy, const AXIS_MAPPING *axis_mapping, DWORD value); -static void handle_pov_event(ALLEGRO_JOYSTICK_DIRECTX *joy, int stick, DWORD value); -static void handle_button_event(ALLEGRO_JOYSTICK_DIRECTX *joy, int button, bool down); -static void generate_axis_event(ALLEGRO_JOYSTICK_DIRECTX *joy, int stick, int axis, float pos); -static void generate_button_event(ALLEGRO_JOYSTICK_DIRECTX *joy, int button, ALLEGRO_EVENT_TYPE event_type); +static void handle_axis_event(ALLEGRO_JOYSTICK_DIRECTX *joy, _AL_JOYSTICK_OUTPUT output, DWORD value); +static void handle_pov_event(ALLEGRO_JOYSTICK_DIRECTX *joy, _AL_JOYSTICK_OUTPUT *outputs, DWORD _value); +static void handle_button_event(ALLEGRO_JOYSTICK_DIRECTX *joy, _AL_JOYSTICK_OUTPUT output, DWORD value); @@ -556,33 +554,156 @@ static BOOL CALLBACK object_enum_callback(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVO } -static char *add_string(char *buf, const TCHAR *src, int *pos, int bufsize, const char* dfl) +/* fill_standard_gamepad: [primary thread] + */ +static bool fill_standard_gamepad(ALLEGRO_JOYSTICK_DIRECTX *joy, + const CAPS_AND_NAMES *can) { - char *dest; + DIPROPDWORD property_vidpid = + { + /* the header */ + { + sizeof(DIPROPDWORD), // diph.dwSize + sizeof(DIPROPHEADER), // diph.dwHeaderSize + 0, // diph.dwObj + DIPH_DEVICE, // diph.dwHow + }, + + /* the data */ + 0, // dwData + }; + + DIPROPSTRING property_productname = + { + /* the header */ + { + sizeof(DIPROPSTRING), // diph.dwSize + sizeof(DIPROPHEADER), // diph.dwHeaderSize + 0, // diph.dwObj + DIPH_DEVICE, // diph.dwHow + }, + + /* the data */ + 0, // wsz + }; + + if (FAILED(IDirectInputDevice8_GetProperty(joy->device, DIPROP_VIDPID, &property_vidpid.diph))) + return false; + + int vendor = LOWORD(property_vidpid.dwData); + int product = HIWORD(property_vidpid.dwData); + + if (FAILED(IDirectInputDevice8_GetProperty(joy->device, DIPROP_PRODUCTNAME, &property_productname.diph))) + return false; + + const char *product_string = _twin_tchar_to_utf8(property_productname.wsz); + + + ALLEGRO_JOYSTICK_GUID guid; + if (vendor && product) + guid = _al_new_joystick_guid(0x03, vendor, product, 0, NULL, product_string, 0, 0); + else + guid = _al_new_joystick_guid(0x05, vendor, product, 0, NULL, product_string, 0, 0); + joy->parent.info.guid = guid; + + const _AL_JOYSTICK_MAPPING *mapping = _al_get_gamepad_mapping("Windows", guid); + if (!mapping) + return false; + + int axis = 0; + int num_axes_mapped = 0; + int num_hats_mapped = 0; + int num_buttons_mapped = 0; + _AL_JOYSTICK_OUTPUT *output; + int i = 0; + + if (can->have_x) { + output = _al_get_joystick_output(&mapping->axis_map, axis++); + if (output) { + joy->x_mapping = *output; + num_axes_mapped++; + } + } + + if (can->have_y) { + output = _al_get_joystick_output(&mapping->axis_map, axis++); + if (output) { + joy->y_mapping = *output; + num_axes_mapped++; + } + } + + if (can->have_z) { + output = _al_get_joystick_output(&mapping->axis_map, axis++); + if (output) { + joy->z_mapping = *output; + num_axes_mapped++; + } + } + + if (can->have_rx) { + output = _al_get_joystick_output(&mapping->axis_map, axis++); + if (output) { + joy->rx_mapping = *output; + num_axes_mapped++; + } + } + + if (can->have_ry) { + output = _al_get_joystick_output(&mapping->axis_map, axis++); + if (output) { + joy->ry_mapping = *output; + num_axes_mapped++; + } + } + + if (can->have_rz) { + output = _al_get_joystick_output(&mapping->axis_map, axis++); + if (output) { + joy->rz_mapping = *output; + num_axes_mapped++; + } + } + + for (i = 0; i < can->num_sliders; i++) { + output = _al_get_joystick_output(&mapping->axis_map, axis++); + if (output) { + joy->slider_mapping[i] = *output; + num_axes_mapped++; + } + } + + for (i = 0; i < 2 * can->num_povs; i++) { + output = _al_get_joystick_output(&mapping->hat_map, i); + if (output) { + joy->pov_mapping[i] = *output; + num_hats_mapped++; + } + } - dest = buf + *pos; - if (*pos >= bufsize - 1) { - /* Out of space. */ - ALLEGRO_ASSERT(dest[0] == '\0'); - return dest; + for (i = 0; i < can->num_buttons; i++) { + output = _al_get_joystick_output(&mapping->button_map, i); + if (output) { + joy->button_mapping[i] = *output; + num_buttons_mapped++; + } } - if (*pos > 0) { - /* Skip over NUL separator. */ - dest++; - (*pos)++; + if (num_axes_mapped != (int)_al_vector_size(&mapping->axis_map)) { + return false; } - if (src) { - dest = _twin_copy_tchar_to_utf8(dest, src, bufsize - *pos); - } else { - dest = _al_sane_strncpy(dest, dfl, bufsize - *pos); + if (num_hats_mapped != (int)_al_vector_size(&mapping->hat_map)) { + return false; } - ALLEGRO_ASSERT(dest != 0); - if (dest) { - (*pos) += strlen(dest); - ALLEGRO_ASSERT(*pos < bufsize); + if (num_buttons_mapped != (int)_al_vector_size(&mapping->button_map)) { + return false; } - return dest; + + _al_fill_gamepad_info(&joy->parent.info); + + memcpy(joy->name, mapping->name, sizeof(mapping->name)); + + return true; } @@ -594,38 +715,32 @@ static void fill_joystick_info_using_caps_and_names(ALLEGRO_JOYSTICK_DIRECTX *jo const CAPS_AND_NAMES *can) { _AL_JOYSTICK_INFO *info = &joy->parent.info; - int pos = 0; int i; #define N_STICK (info->num_sticks) #define N_AXIS (info->stick[N_STICK].num_axes) -#define ADD_STRING(A, dfl) \ - (add_string(joy->all_names, (A), &pos, \ - sizeof(joy->all_names), (dfl))) +#define ADD_STRING(A, dfl) ((A) ? _twin_tchar_to_utf8(A) : _al_strdup(dfl)) /* the X, Y, Z axes make up the first stick */ if (can->have_x || can->have_y || can->have_z) { if (can->have_x) { info->stick[N_STICK].flags = ALLEGRO_JOYFLAG_DIGITAL | ALLEGRO_JOYFLAG_ANALOGUE; info->stick[N_STICK].axis[N_AXIS].name = ADD_STRING(can->name_x, default_name_x); - joy->x_mapping.stick = N_STICK; - joy->x_mapping.axis = N_AXIS; + joy->x_mapping = _al_new_joystick_stick_output(N_STICK, N_AXIS); N_AXIS++; } if (can->have_y) { info->stick[N_STICK].flags = ALLEGRO_JOYFLAG_DIGITAL | ALLEGRO_JOYFLAG_ANALOGUE; info->stick[N_STICK].axis[N_AXIS].name = ADD_STRING(can->name_y, default_name_y); - joy->y_mapping.stick = N_STICK; - joy->y_mapping.axis = N_AXIS; + joy->y_mapping = _al_new_joystick_stick_output(N_STICK, N_AXIS); N_AXIS++; } if (can->have_z) { info->stick[N_STICK].flags = ALLEGRO_JOYFLAG_DIGITAL | ALLEGRO_JOYFLAG_ANALOGUE; info->stick[N_STICK].axis[N_AXIS].name = ADD_STRING(can->name_z, default_name_z); - joy->z_mapping.stick = N_STICK; - joy->z_mapping.axis = N_AXIS; + joy->z_mapping = _al_new_joystick_stick_output(N_STICK, N_AXIS); N_AXIS++; } @@ -638,28 +753,25 @@ static void fill_joystick_info_using_caps_and_names(ALLEGRO_JOYSTICK_DIRECTX *jo if (can->have_rx) { info->stick[N_STICK].flags = ALLEGRO_JOYFLAG_DIGITAL | ALLEGRO_JOYFLAG_ANALOGUE; info->stick[N_STICK].axis[N_AXIS].name = ADD_STRING(can->name_rx, default_name_rx); - joy->rx_mapping.stick = N_STICK; - joy->rx_mapping.axis = N_AXIS; + joy->rx_mapping = _al_new_joystick_stick_output(N_STICK, N_AXIS); N_AXIS++; } if (can->have_ry) { info->stick[N_STICK].flags = ALLEGRO_JOYFLAG_DIGITAL | ALLEGRO_JOYFLAG_ANALOGUE; info->stick[N_STICK].axis[N_AXIS].name = ADD_STRING(can->name_ry, default_name_ry); - joy->ry_mapping.stick = N_STICK; - joy->ry_mapping.axis = N_AXIS; + joy->ry_mapping = _al_new_joystick_stick_output(N_STICK, N_AXIS); N_AXIS++; } if (can->have_rz) { info->stick[N_STICK].flags = ALLEGRO_JOYFLAG_DIGITAL | ALLEGRO_JOYFLAG_ANALOGUE; info->stick[N_STICK].axis[N_AXIS].name = ADD_STRING(can->name_rz, default_name_rz); - joy->rz_mapping.stick = N_STICK; - joy->rz_mapping.axis = N_AXIS; + joy->rz_mapping = _al_new_joystick_stick_output(N_STICK, N_AXIS); N_AXIS++; } - info->stick[N_STICK].name = ADD_STRING(NULL, default_name_stick); + info->stick[N_STICK].name = _al_strdup(default_name_stick); N_STICK++; } @@ -667,27 +779,28 @@ static void fill_joystick_info_using_caps_and_names(ALLEGRO_JOYSTICK_DIRECTX *jo for (i = 0; i < can->num_sliders; i++) { info->stick[N_STICK].flags = ALLEGRO_JOYFLAG_DIGITAL | ALLEGRO_JOYFLAG_ANALOGUE; info->stick[N_STICK].num_axes = 1; - info->stick[N_STICK].axis[0].name = ADD_STRING(NULL, "axis"); + info->stick[N_STICK].axis[0].name = _al_strdup("axis"); info->stick[N_STICK].name = ADD_STRING(can->name_slider[i], default_name_slider); - joy->slider_mapping[i].stick = N_STICK; - joy->slider_mapping[i].axis = 0; + joy->slider_mapping[i] = _al_new_joystick_stick_output(N_STICK, 0); N_STICK++; } /* POV devices are assigned to one stick each */ - for (i = 0; i < can->num_povs; i++) { + for (i = 0; i < 2 * can->num_povs; i += 2) { info->stick[N_STICK].flags = ALLEGRO_JOYFLAG_DIGITAL; info->stick[N_STICK].num_axes = 2; - info->stick[N_STICK].axis[0].name = ADD_STRING(NULL, "left/right"); - info->stick[N_STICK].axis[1].name = ADD_STRING(NULL, "up/down"); + info->stick[N_STICK].axis[0].name = _al_strdup("left/right"); + info->stick[N_STICK].axis[1].name = _al_strdup("up/down"); info->stick[N_STICK].name = ADD_STRING(can->name_pov[i], default_name_hat); - joy->pov_mapping_stick[i] = N_STICK; + joy->pov_mapping[i] = _al_new_joystick_stick_output(N_STICK, 0); + joy->pov_mapping[i + 1] = _al_new_joystick_stick_output(N_STICK, 1); N_STICK++; } /* buttons */ for (i = 0; i < can->num_buttons; i++) { info->button[i].name = ADD_STRING(can->name_button[i], default_name_button[i]); + joy->button_mapping[i] = _al_new_joystick_button_output(i); } info->num_buttons = can->num_buttons; @@ -696,30 +809,34 @@ static void fill_joystick_info_using_caps_and_names(ALLEGRO_JOYSTICK_DIRECTX *jo if (strstr(joy->name, "MP-8866")) { /* axes were mapped weird; remap as expected */ /* really R-stick X axis */ - joy->z_mapping.stick = 1; - joy->z_mapping.axis = 0; + joy->z_mapping = _al_new_joystick_stick_output(1, 0); /* really R-stick Y axis */ - joy->rz_mapping.stick = 1; - joy->rz_mapping.axis = 1; + joy->rz_mapping = _al_new_joystick_stick_output(1, 1); info->stick[0].num_axes = 2; info->stick[1].num_axes = 2; /* reuse the axis names from the first stick */ - info->stick[2].axis[0].name = info->stick[1].axis[0].name = info->stick[0].axis[0].name; - info->stick[2].axis[1].name = info->stick[1].axis[1].name = info->stick[0].axis[1].name; + al_free(info->stick[1].axis[0].name); + al_free(info->stick[1].axis[1].name); + al_free(info->stick[2].axis[0].name); + al_free(info->stick[2].axis[1].name); + info->stick[1].axis[0].name = _al_strdup(info->stick[0].axis[0].name); + info->stick[1].axis[1].name = _al_strdup(info->stick[0].axis[1].name); + info->stick[2].axis[0].name = _al_strdup(info->stick[0].axis[0].name); + info->stick[2].axis[1].name = _al_strdup(info->stick[0].axis[1].name); /* first four button names contained junk; replace with valid strings */ - info->button[ 0].name = ADD_STRING(NULL, "Triangle"); - info->button[ 1].name = ADD_STRING(NULL, "Circle"); - info->button[ 2].name = ADD_STRING(NULL, "X"); - info->button[ 3].name = ADD_STRING(NULL, "Square"); + info->button[0].name = _al_strdup("Triangle"); + info->button[1].name = _al_strdup("Circle"); + info->button[2].name = _al_strdup("X"); + info->button[3].name = _al_strdup("Square"); /* while we're at it, give these controls more sensible names, too */ - info->stick[0].name = ADD_STRING(NULL, "[L-stick] or D-pad"); - info->stick[1].name = ADD_STRING(NULL, "[R-stick]"); - info->stick[2].name = ADD_STRING(NULL, "[D-pad]"); + info->stick[0].name = _al_strdup("[L-stick] or D-pad"); + info->stick[1].name = _al_strdup("[R-stick]"); + info->stick[2].name = _al_strdup("[D-pad]"); } #undef N_AXIS @@ -957,7 +1074,8 @@ static BOOL CALLBACK joystick_enum_callback(LPCDIDEVICEINSTANCE lpddi, LPVOID pv _twin_copy_tchar_to_utf8(joy->name, lpddi->tszInstanceName, sizeof(joy->name)); /* fill in the joystick structure */ - fill_joystick_info_using_caps_and_names(joy, &caps_and_names); + if (!fill_standard_gamepad(joy, &caps_and_names)) + fill_joystick_info_using_caps_and_names(joy, &caps_and_names); /* create a thread event for this joystick, unless it was already created */ joy->waker_event = CreateEvent(NULL, false, false, NULL); @@ -1040,6 +1158,7 @@ static void joydx_inactivate_joy(ALLEGRO_JOYSTICK_DIRECTX *joy) joy->waker_event = NULL; } + _al_destroy_joystick_info(&joy->parent.info); memset(&joy->parent.info, 0, sizeof(joy->parent.info)); /* XXX the joystick name really belongs in joy->parent.info too */ joy->name[0] = '\0'; @@ -1492,37 +1611,34 @@ static void update_joystick(ALLEGRO_JOYSTICK_DIRECTX *joy) const DWORD dwData = item->dwData; if (dwOfs == DIJOFS_X) - handle_axis_event(joy, &joy->x_mapping, dwData); + handle_axis_event(joy, joy->x_mapping, dwData); else if (dwOfs == DIJOFS_Y) - handle_axis_event(joy, &joy->y_mapping, dwData); + handle_axis_event(joy, joy->y_mapping, dwData); else if (dwOfs == DIJOFS_Z) - handle_axis_event(joy, &joy->z_mapping, dwData); + handle_axis_event(joy, joy->z_mapping, dwData); else if (dwOfs == DIJOFS_RX) - handle_axis_event(joy, &joy->rx_mapping, dwData); + handle_axis_event(joy, joy->rx_mapping, dwData); else if (dwOfs == DIJOFS_RY) - handle_axis_event(joy, &joy->ry_mapping, dwData); + handle_axis_event(joy, joy->ry_mapping, dwData); else if (dwOfs == DIJOFS_RZ) - handle_axis_event(joy, &joy->rz_mapping, dwData); + handle_axis_event(joy, joy->rz_mapping, dwData); else if ((unsigned int)dwOfs == DIJOFS_SLIDER(0)) - handle_axis_event(joy, &joy->slider_mapping[0], dwData); + handle_axis_event(joy, joy->slider_mapping[0], dwData); else if ((unsigned int)dwOfs == DIJOFS_SLIDER(1)) - handle_axis_event(joy, &joy->slider_mapping[1], dwData); + handle_axis_event(joy, joy->slider_mapping[1], dwData); else if ((unsigned int)dwOfs == DIJOFS_POV(0)) - handle_pov_event(joy, joy->pov_mapping_stick[0], dwData); + handle_pov_event(joy, &joy->pov_mapping[0], dwData); else if ((unsigned int)dwOfs == DIJOFS_POV(1)) - handle_pov_event(joy, joy->pov_mapping_stick[1], dwData); + handle_pov_event(joy, &joy->pov_mapping[2], dwData); else if ((unsigned int)dwOfs == DIJOFS_POV(2)) - handle_pov_event(joy, joy->pov_mapping_stick[2], dwData); + handle_pov_event(joy, &joy->pov_mapping[4], dwData); else if ((unsigned int)dwOfs == DIJOFS_POV(3)) - handle_pov_event(joy, joy->pov_mapping_stick[3], dwData); + handle_pov_event(joy, &joy->pov_mapping[6], dwData); else { /* buttons */ - if ((dwOfs >= DIJOFS_BUTTON0) && - (dwOfs < DIJOFS_BUTTON(joy->parent.info.num_buttons))) - { - int num = (dwOfs - DIJOFS_BUTTON0) / (DIJOFS_BUTTON1 - DIJOFS_BUTTON0); - handle_button_event(joy, num, (dwData & 0x80)); - } + int num = (dwOfs - DIJOFS_BUTTON0) / (DIJOFS_BUTTON1 - DIJOFS_BUTTON0); + if (num < MAX_BUTTONS) + handle_button_event(joy, joy->button_mapping[num], (dwData & 0x80)); } } } @@ -1535,21 +1651,11 @@ static void update_joystick(ALLEGRO_JOYSTICK_DIRECTX *joy) * Helper function to handle a state change in a non-POV axis. * The joystick must be locked BEFORE entering this function. */ -static void handle_axis_event(ALLEGRO_JOYSTICK_DIRECTX *joy, const AXIS_MAPPING *axis_mapping, DWORD value) +static void handle_axis_event(ALLEGRO_JOYSTICK_DIRECTX *joy, _AL_JOYSTICK_OUTPUT output, DWORD value) { - const int stick = axis_mapping->stick; - const int axis = axis_mapping->axis; - float pos; - - if (stick < 0 || stick >= joy->parent.info.num_sticks) - return; - - if (axis < 0 || axis >= joy->parent.info.stick[stick].num_axes) - return; - - pos = (int)value / 32767.0; - joy->joystate.stick[stick].axis[axis] = pos; - generate_axis_event(joy, stick, axis, pos); + float pos = (int)value / 32767.0; + if (output.button_enabled || output.pos_enabled || output.neg_enabled) + _al_joystick_generate_axis_event((ALLEGRO_JOYSTICK*)joy, &joy->joystate, output, pos); } @@ -1558,44 +1664,36 @@ static void handle_axis_event(ALLEGRO_JOYSTICK_DIRECTX *joy, const AXIS_MAPPING * Helper function to handle a state change in a POV device. * The joystick must be locked BEFORE entering this function. */ -static void handle_pov_event(ALLEGRO_JOYSTICK_DIRECTX *joy, int stick, DWORD _value) +static void handle_pov_event(ALLEGRO_JOYSTICK_DIRECTX *joy, _AL_JOYSTICK_OUTPUT *outputs, DWORD _value) { int value = _value; - float old_p0, old_p1; - float p0, p1; - - if (stick < 0 || stick >= joy->parent.info.num_sticks) - return; - - old_p0 = joy->joystate.stick[stick].axis[0]; - old_p1 = joy->joystate.stick[stick].axis[1]; - + float px, py; /* left */ if ((value > JOY_POVBACKWARD) && (value < JOY_POVFORWARD_WRAP)) - joy->joystate.stick[stick].axis[0] = p0 = -1.0; + px = -1.0; /* right */ else if ((value > JOY_POVFORWARD) && (value < JOY_POVBACKWARD)) - joy->joystate.stick[stick].axis[0] = p0 = +1.0; + px = +1.0; else - joy->joystate.stick[stick].axis[0] = p0 = 0.0; + px = 0.0; /* forward */ if (((value > JOY_POVLEFT) && (value <= JOY_POVFORWARD_WRAP)) || ((value >= JOY_POVFORWARD) && (value < JOY_POVRIGHT))) - joy->joystate.stick[stick].axis[1] = p1 = -1.0; + py = -1.0; /* backward */ else if ((value > JOY_POVRIGHT) && (value < JOY_POVLEFT)) - joy->joystate.stick[stick].axis[1] = p1 = +1.0; + py = +1.0; else - joy->joystate.stick[stick].axis[1] = p1 = 0.0; - - if (old_p0 != p0) { - generate_axis_event(joy, stick, 0, p0); - } - - if (old_p1 != p1) { - generate_axis_event(joy, stick, 1, p1); - } + py = 0.0; + + _AL_JOYSTICK_OUTPUT output; + output = outputs[0]; + if (output.button_enabled || output.pos_enabled || output.neg_enabled) + _al_joystick_generate_axis_event((ALLEGRO_JOYSTICK*)joy, &joy->joystate, output, px); + output = outputs[1]; + if (output.button_enabled || output.pos_enabled || output.neg_enabled) + _al_joystick_generate_axis_event((ALLEGRO_JOYSTICK*)joy, &joy->joystate, output, py); } @@ -1604,69 +1702,10 @@ static void handle_pov_event(ALLEGRO_JOYSTICK_DIRECTX *joy, int stick, DWORD _va * Helper function to handle a state change in a button. * The joystick must be locked BEFORE entering this function. */ -static void handle_button_event(ALLEGRO_JOYSTICK_DIRECTX *joy, int button, bool down) +static void handle_button_event(ALLEGRO_JOYSTICK_DIRECTX *joy, _AL_JOYSTICK_OUTPUT output, DWORD value) { - if (button < 0 && button >= joy->parent.info.num_buttons) - return; - - if (down) { - joy->joystate.button[button] = 32767; - generate_button_event(joy, button, ALLEGRO_EVENT_JOYSTICK_BUTTON_DOWN); - } - else { - joy->joystate.button[button] = 0; - generate_button_event(joy, button, ALLEGRO_EVENT_JOYSTICK_BUTTON_UP); - } -} - - - -/* generate_axis_event: [joystick thread] - * Helper to generate an event after an axis is moved. - * The joystick must be locked BEFORE entering this function. - */ -static void generate_axis_event(ALLEGRO_JOYSTICK_DIRECTX *joy, int stick, int axis, float pos) -{ - ALLEGRO_EVENT event; - ALLEGRO_EVENT_SOURCE *es = al_get_joystick_event_source(); - - if (!_al_event_source_needs_to_generate_event(es)) - return; - - event.joystick.type = ALLEGRO_EVENT_JOYSTICK_AXIS; - event.joystick.timestamp = al_get_time(); - event.joystick.id = (ALLEGRO_JOYSTICK *)joy; - event.joystick.stick = stick; - event.joystick.axis = axis; - event.joystick.pos = pos; - event.joystick.button = 0; - - _al_event_source_emit_event(es, &event); -} - - - -/* generate_button_event: [joystick thread] - * Helper to generate an event after a button is pressed or released. - * The joystick must be locked BEFORE entering this function. - */ -static void generate_button_event(ALLEGRO_JOYSTICK_DIRECTX *joy, int button, ALLEGRO_EVENT_TYPE event_type) -{ - ALLEGRO_EVENT event; - ALLEGRO_EVENT_SOURCE *es = al_get_joystick_event_source(); - - if (!_al_event_source_needs_to_generate_event(es)) - return; - - event.joystick.type = event_type; - event.joystick.timestamp = al_get_time(); - event.joystick.id = (ALLEGRO_JOYSTICK *)joy; - event.joystick.stick = 0; - event.joystick.axis = 0; - event.joystick.pos = 0.0; - event.joystick.button = button; - - _al_event_source_emit_event(es, &event); + if (output.button_enabled || output.pos_enabled || output.neg_enabled) + _al_joystick_generate_button_event((ALLEGRO_JOYSTICK *)joy, &joy->joystate, output, value); } diff --git a/src/win/wjoyxi.c b/src/win/wjoyxi.c index f27369e5c..a860ee567 100644 --- a/src/win/wjoyxi.c +++ b/src/win/wjoyxi.c @@ -199,6 +199,11 @@ static const struct _AL_XINPUT_BUTTON_MAPPING { XINPUT_GAMEPAD_DPAD_UP, 13, "UP DPAD" }, }; +static bool compat_5_2_10(void) { + /* New layout. */ + return _al_get_joystick_compat_version() < AL_ID(5, 2, 11, 0); +} + static void unload_xinput_module(void) { FreeLibrary(_imp_xinput_module); @@ -365,6 +370,11 @@ static float joyxi_convert_trigger(BYTE value) return(((float)value) / 255.0); } +static int joyxi_convert_button(int value) +{ + return value ? 32767 : 0; +} + /* Converts an XInput state to an Allegro joystick state. */ static void joyxi_convert_state(ALLEGRO_JOYSTICK_STATE *alstate, XINPUT_STATE *xistate) { @@ -372,36 +382,67 @@ static void joyxi_convert_state(ALLEGRO_JOYSTICK_STATE *alstate, XINPUT_STATE *x /* Wipe the allegro state clean. */ memset(alstate, 0, sizeof(*alstate)); - /* Map the buttons. Make good use of the mapping data. */ - for (index = 0; index < MAX_BUTTONS; index++) { - const struct _AL_XINPUT_BUTTON_MAPPING *mapping = joyxi_button_mapping + index; - if (xistate->Gamepad.wButtons & mapping->flags) { - alstate->button[mapping->button] = 32767; + if (!compat_5_2_10()) { + alstate->button[ALLEGRO_GAMEPAD_BUTTON_A] = joyxi_convert_button(xistate->Gamepad.wButtons & XINPUT_GAMEPAD_A); + alstate->button[ALLEGRO_GAMEPAD_BUTTON_B] = joyxi_convert_button(xistate->Gamepad.wButtons & XINPUT_GAMEPAD_B); + alstate->button[ALLEGRO_GAMEPAD_BUTTON_X] = joyxi_convert_button(xistate->Gamepad.wButtons & XINPUT_GAMEPAD_X); + alstate->button[ALLEGRO_GAMEPAD_BUTTON_Y] = joyxi_convert_button(xistate->Gamepad.wButtons & XINPUT_GAMEPAD_Y); + alstate->button[ALLEGRO_GAMEPAD_BUTTON_LEFT_SHOULDER] = joyxi_convert_button(xistate->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER); + alstate->button[ALLEGRO_GAMEPAD_BUTTON_RIGHT_SHOULDER] = joyxi_convert_button(xistate->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER); + alstate->button[ALLEGRO_GAMEPAD_BUTTON_BACK] = joyxi_convert_button(xistate->Gamepad.wButtons & XINPUT_GAMEPAD_BACK); + alstate->button[ALLEGRO_GAMEPAD_BUTTON_START] = joyxi_convert_button(xistate->Gamepad.wButtons & XINPUT_GAMEPAD_START); + alstate->button[ALLEGRO_GAMEPAD_BUTTON_LEFT_THUMB] = joyxi_convert_button(xistate->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB); + alstate->button[ALLEGRO_GAMEPAD_BUTTON_RIGHT_THUMB] = joyxi_convert_button(xistate->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB); + + float dpadx = 0; + float dpady = 0; + if (xistate->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) { + dpadx += 1.; + } + if (xistate->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) { + dpadx -= 1.; + } + if (xistate->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) { + dpady += 1.; } - else { - alstate->button[mapping->button] = 0; + if (xistate->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) { + dpady -= 1.; } + alstate->stick[ALLEGRO_GAMEPAD_STICK_DPAD].axis[0] = dpadx; + alstate->stick[ALLEGRO_GAMEPAD_STICK_DPAD].axis[1] = dpady; + alstate->stick[ALLEGRO_GAMEPAD_STICK_LEFT_THUMB].axis[0] = joyxi_convert_axis(xistate->Gamepad.sThumbLX); + alstate->stick[ALLEGRO_GAMEPAD_STICK_LEFT_THUMB].axis[1] = -joyxi_convert_axis(xistate->Gamepad.sThumbLY); + alstate->stick[ALLEGRO_GAMEPAD_STICK_RIGHT_THUMB].axis[0] = joyxi_convert_axis(xistate->Gamepad.sThumbRX); + alstate->stick[ALLEGRO_GAMEPAD_STICK_RIGHT_THUMB].axis[1] = -joyxi_convert_axis(xistate->Gamepad.sThumbRY); + alstate->stick[ALLEGRO_GAMEPAD_STICK_LEFT_TRIGGER].axis[0] = joyxi_convert_trigger(xistate->Gamepad.bLeftTrigger); + alstate->stick[ALLEGRO_GAMEPAD_STICK_RIGHT_TRIGGER].axis[0] = joyxi_convert_trigger(xistate->Gamepad.bRightTrigger); + } + else { + /* Map the buttons. Make good use of the mapping data. */ + for (index = 0; index < MAX_BUTTONS; index++) { + const struct _AL_XINPUT_BUTTON_MAPPING *mapping = joyxi_button_mapping + index; + alstate->button[mapping->button] = joyxi_convert_button(xistate->Gamepad.wButtons & mapping->flags); + } + + /* Map the x and y axes of both sticks. */ + alstate->stick[0].axis[0] = joyxi_convert_axis(xistate->Gamepad.sThumbLX); + alstate->stick[0].axis[1] = -joyxi_convert_axis(xistate->Gamepad.sThumbLY); + alstate->stick[1].axis[0] = joyxi_convert_axis(xistate->Gamepad.sThumbRX); + alstate->stick[1].axis[1] = -joyxi_convert_axis(xistate->Gamepad.sThumbRY); + /* Map the triggers as two individual sticks and axes each . */ + alstate->stick[2].axis[0] = joyxi_convert_trigger(xistate->Gamepad.bLeftTrigger); + alstate->stick[3].axis[0] = joyxi_convert_trigger(xistate->Gamepad.bRightTrigger); } - /* Map the x and y axes of both sticks. */ - alstate->stick[0].axis[0] = joyxi_convert_axis(xistate->Gamepad.sThumbLX); - alstate->stick[0].axis[1] = -joyxi_convert_axis(xistate->Gamepad.sThumbLY); - alstate->stick[1].axis[0] = joyxi_convert_axis(xistate->Gamepad.sThumbRX); - alstate->stick[1].axis[1] = -joyxi_convert_axis(xistate->Gamepad.sThumbRY); - /* Map the triggers as two individual sticks and axes each . */ - alstate->stick[2].axis[0] = joyxi_convert_trigger(xistate->Gamepad.bLeftTrigger); - alstate->stick[3].axis[0] = joyxi_convert_trigger(xistate->Gamepad.bRightTrigger); - return; } /* Emits joystick events for the difference between the new and old joystick state. */ static void joyxi_emit_events( - ALLEGRO_JOYSTICK_XINPUT *xjoy, - ALLEGRO_JOYSTICK_STATE *newstate, ALLEGRO_JOYSTICK_STATE *oldstate) + ALLEGRO_JOYSTICK_XINPUT *xjoy, ALLEGRO_JOYSTICK_STATE *newstate, ALLEGRO_JOYSTICK_STATE *oldstate, _AL_JOYSTICK_INFO *info) { int index, subdex; /* Send events for buttons. */ - for (index = 0; index < MAX_BUTTONS; index++) { + for (index = 0; index < info->num_buttons; index++) { int newbutton = newstate->button[index]; int oldbutton = oldstate->button[index]; if (newbutton != oldbutton) { @@ -412,8 +453,8 @@ static void joyxi_emit_events( } } /* Send events for the thumb pad axes and triggers . */ - for (index = 0; index < MAX_STICKS; index++) { - for (subdex = 0; subdex < joyxi_axis_per_stick[index]; subdex++) { + for (index = 0; index < info->num_sticks; index++) { + for (subdex = 0; subdex < info->stick[index].num_axes; subdex++) { float oldaxis = oldstate->stick[index].axis[subdex]; float newaxis = newstate->stick[index].axis[subdex]; if (oldaxis != newaxis) { @@ -445,7 +486,7 @@ static void joyxi_poll_connected_joystick(ALLEGRO_JOYSTICK_XINPUT *xjoy) /* If we get here translate the state and send the needed events. */ joyxi_convert_state(&alstate, &xistate); - joyxi_emit_events(xjoy, &alstate, &xjoy->joystate); + joyxi_emit_events(xjoy, &alstate, &xjoy->joystate, &xjoy->parent.info); /* Finally copy over the states. */ xjoy->state = xistate; @@ -538,23 +579,29 @@ static void *joyxi_poll_disconnected_thread(ALLEGRO_THREAD *thread, void *arg) /* Initializes the info part of the joystick. */ static void joyxi_init_joystick_info(ALLEGRO_JOYSTICK_XINPUT *xjoy) { - int index, subdex; - _AL_JOYSTICK_INFO *info = &xjoy->parent.info; - /* Map xinput to 4 sticks: 2 thumb pads and 2 triggers. */ - info->num_sticks = 4; - /* Map xinput to 14 buttons */ - info->num_buttons = MAX_BUTTONS; - /* Map button names. */ - for (index = 0; index < MAX_BUTTONS; index++) { - info->button[index].name = joyxi_button_mapping[index].name; + /* TODO: Fill the GUID? */ + if (!compat_5_2_10()) { + _al_fill_gamepad_info(&xjoy->parent.info); } - /* Map stick and axis names. */ - for (index = 0; index < MAX_STICKS; index++) { - info->stick[index].name = joyxi_stick_names[index]; - info->stick[index].num_axes = joyxi_axis_per_stick[index]; - info->stick[index].flags = ALLEGRO_JOYFLAG_ANALOGUE; - for (subdex = 0; subdex < joyxi_axis_per_stick[index]; subdex++) { - info->stick[index].axis[subdex].name = joyxi_axis_names[index][subdex]; + else { + int index, subdex; + _AL_JOYSTICK_INFO *info = &xjoy->parent.info; + /* Map xinput to 4 sticks: 2 thumb pads and 2 triggers. */ + info->num_sticks = 4; + /* Map xinput to 14 buttons */ + info->num_buttons = MAX_BUTTONS; + /* Map button names. */ + for (index = 0; index < MAX_BUTTONS; index++) { + info->button[index].name = _al_strdup(joyxi_button_mapping[index].name); + } + /* Map stick and axis names. */ + for (index = 0; index < MAX_STICKS; index++) { + info->stick[index].name = _al_strdup(joyxi_stick_names[index]); + info->stick[index].num_axes = joyxi_axis_per_stick[index]; + info->stick[index].flags = ALLEGRO_JOYFLAG_ANALOGUE; + for (subdex = 0; subdex < joyxi_axis_per_stick[index]; subdex++) { + info->stick[index].axis[subdex].name = _al_strdup(joyxi_axis_names[index][subdex]); + } } } } @@ -643,6 +690,7 @@ static void joyxi_exit_joystick(void) /* Wipe the joystick structs */ for (index = 0; index < MAX_JOYSTICKS; index++) { joyxi_joysticks[index].active = false; + _al_destroy_joystick_info(&joyxi_joysticks[index].parent.info); } al_unlock_mutex(joyxi_mutex); al_destroy_mutex(joyxi_mutex);