From 3928325615284e335e33f328d0e47b1ab932995f Mon Sep 17 00:00:00 2001 From: Galleon Date: Sun, 18 Jan 2015 04:20:57 -0800 Subject: [PATCH] Primarily implements gamepad support (via _DEVICES, STICK/STRIG) Corrects dependency detection bug in prev update which rendered _SCREENIMAGE unusable Implements new command _FPS which limits the maximum hardware fps (primarily for throttling SUB _GL) [default is 60] Usage example: _FPS 100 --- internal/c/common.cpp | 23 +- internal/c/libqb.cpp | 284 ++++- internal/c/parts/core/src/freeglut_main.c | 134 +- .../gamepad_1.4.0_pre1_source/Changes.txt | 47 + .../License.txt | 4 +- .../gamepad_1.4.0_pre1_source/Makefile | 787 ++++++++++++ .../gamepad_1.4.0_pre1_source/ReadMe.txt | 5 + .../msvc/Win32Project1/Win32Project1.vcxproj | 162 +++ .../Win32Project1.vcxproj.filters | 22 + .../msvc/libstem_gamepad.sln | 49 + .../libstem_gamepad/libstem_gamepad.vcxproj | 142 +++ .../libstem_gamepad.vcxproj.filters | 30 + .../resources}/Info_testharness_macosx.plist | 0 .../source/gamepad/Gamepad.h | 128 ++ .../source/gamepad/Gamepad_linux.c | 344 +++--- .../source/gamepad/Gamepad_macosx.c | 133 +- .../source/gamepad/Gamepad_private.c | 61 + .../source/gamepad/Gamepad_private.h | 76 ++ .../source/gamepad/Gamepad_windows_dinput.c | 1052 ++++++++++++++++ .../source/gamepad/Gamepad_windows_mm.c} | 92 +- .../source/testharness/TestHarness_main.c | 214 ++++ .../version | 2 +- .../gamepad_standalone_source_1.1.0/Makefile | 420 ------- .../include/shell/Shell.h | 10 - .../include/shell/ShellKeyCodes.h | 176 --- .../include/shell/Target.h | 18 - .../source/gamepad/Gamepad.h | 128 -- .../source/utilities/EventDispatcher.c | 122 -- .../source/utilities/EventDispatcher.h | 80 -- .../testharness/TestHarness_main.c | 242 ---- .../test_source/unittest/framework/TestList.c | 41 - .../unittest/framework/TestSuite.h | 57 - .../unittest/framework/unittest_main.c | 84 -- .../test_source/unittest/suites/GamepadTest.c | 7 - .../c/parts/input/game_controller/notes.txt | 1 + .../input/game_controller/os/lnx/build.sh | 14 + .../game_controller/os/lnx/temp/ignore.bin | 0 .../game_controller/os/osx/build.command | 14 + .../game_controller/os/osx/temp/ignore.bin | 0 .../input/game_controller/os/win/build.bat | 4 + .../game_controller/os/win/temp/ignore.bin | 0 internal/c/parts/input/game_controller/src.c | 582 ++++----- .../input/game_controller/src/Gamepad_linux.c | 894 +++++++------- .../game_controller/src/Gamepad_macosx.c | 1087 +++++++++-------- .../game_controller/src/Gamepad_private.c | 61 + .../game_controller/src/Gamepad_windows.c | 416 ------- .../src/Gamepad_windows_dinput.c | 1055 ++++++++++++++++ .../game_controller/src/Gamepad_windows_mm.c | 400 ++++++ .../game_controller/src/gamepad/Gamepad.h | 256 ++-- .../src/gamepad/Gamepad_private.h | 76 ++ .../input/game_controller/src/shell/Shell.h | 10 - .../game_controller/src/shell/ShellKeyCodes.h | 176 --- .../input/game_controller/src/shell/Target.h | 18 - .../src/utilities/EventDispatcher.c | 122 -- .../src/utilities/EventDispatcher.h | 80 -- source/qb64.bas | 38 +- source/subs_functions/subs_functions.bas | 43 +- 57 files changed, 6561 insertions(+), 3962 deletions(-) create mode 100644 internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/Changes.txt rename internal/c/parts/input/game_controller/download/{gamepad_standalone_source_1.1.0 => gamepad_1.4.0_pre1_source}/License.txt (92%) create mode 100644 internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/Makefile create mode 100644 internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/ReadMe.txt create mode 100644 internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/msvc/Win32Project1/Win32Project1.vcxproj create mode 100644 internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/msvc/Win32Project1/Win32Project1.vcxproj.filters create mode 100644 internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/msvc/libstem_gamepad.sln create mode 100644 internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/msvc/libstem_gamepad/libstem_gamepad.vcxproj create mode 100644 internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/msvc/libstem_gamepad/libstem_gamepad.vcxproj.filters rename internal/c/parts/input/game_controller/download/{gamepad_standalone_source_1.1.0/test_resources => gamepad_1.4.0_pre1_source/resources}/Info_testharness_macosx.plist (100%) create mode 100644 internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad.h rename internal/c/parts/input/game_controller/download/{gamepad_standalone_source_1.1.0 => gamepad_1.4.0_pre1_source}/source/gamepad/Gamepad_linux.c (60%) rename internal/c/parts/input/game_controller/download/{gamepad_standalone_source_1.1.0 => gamepad_1.4.0_pre1_source}/source/gamepad/Gamepad_macosx.c (82%) create mode 100644 internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_private.c create mode 100644 internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_private.h create mode 100644 internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_windows_dinput.c rename internal/c/parts/input/game_controller/download/{gamepad_standalone_source_1.1.0/source/gamepad/Gamepad_windows.c => gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_windows_mm.c} (81%) create mode 100644 internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/testharness/TestHarness_main.c rename internal/c/parts/input/game_controller/download/{gamepad_standalone_source_1.1.0 => gamepad_1.4.0_pre1_source}/version (66%) delete mode 100644 internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/Makefile delete mode 100644 internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/include/shell/Shell.h delete mode 100644 internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/include/shell/ShellKeyCodes.h delete mode 100644 internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/include/shell/Target.h delete mode 100644 internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/source/gamepad/Gamepad.h delete mode 100644 internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/source/utilities/EventDispatcher.c delete mode 100644 internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/source/utilities/EventDispatcher.h delete mode 100644 internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_source/testharness/TestHarness_main.c delete mode 100644 internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_source/unittest/framework/TestList.c delete mode 100644 internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_source/unittest/framework/TestSuite.h delete mode 100644 internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_source/unittest/framework/unittest_main.c delete mode 100644 internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_source/unittest/suites/GamepadTest.c create mode 100644 internal/c/parts/input/game_controller/notes.txt create mode 100644 internal/c/parts/input/game_controller/os/lnx/build.sh create mode 100644 internal/c/parts/input/game_controller/os/lnx/temp/ignore.bin create mode 100644 internal/c/parts/input/game_controller/os/osx/build.command create mode 100644 internal/c/parts/input/game_controller/os/osx/temp/ignore.bin create mode 100644 internal/c/parts/input/game_controller/os/win/build.bat create mode 100644 internal/c/parts/input/game_controller/os/win/temp/ignore.bin create mode 100644 internal/c/parts/input/game_controller/src/Gamepad_private.c delete mode 100644 internal/c/parts/input/game_controller/src/Gamepad_windows.c create mode 100644 internal/c/parts/input/game_controller/src/Gamepad_windows_dinput.c create mode 100644 internal/c/parts/input/game_controller/src/Gamepad_windows_mm.c create mode 100644 internal/c/parts/input/game_controller/src/gamepad/Gamepad_private.h delete mode 100644 internal/c/parts/input/game_controller/src/shell/Shell.h delete mode 100644 internal/c/parts/input/game_controller/src/shell/ShellKeyCodes.h delete mode 100644 internal/c/parts/input/game_controller/src/shell/Target.h delete mode 100644 internal/c/parts/input/game_controller/src/utilities/EventDispatcher.c delete mode 100644 internal/c/parts/input/game_controller/src/utilities/EventDispatcher.h diff --git a/internal/c/common.cpp b/internal/c/common.cpp index 2a58252ab..ad34f364f 100644 --- a/internal/c/common.cpp +++ b/internal/c/common.cpp @@ -385,9 +385,10 @@ struct device_struct{ int32 used; int32 type; //0=Unallocated - //1=SDL joystick interface - //2=? - qbs *name; + //1=Joystick/Gamepad + //2=Keybaord + //3=Mouse + char *name; int32 connected; int32 lastbutton; int32 lastaxis; @@ -400,13 +401,15 @@ struct device_struct{ //-------------- uint8 STRIG_button_pressed[256];//checked and cleared by the STRIG function //-------------- - uint8 id[1024]; - /////SDL_Joystick *SDL_js; - int32 SDL_js_index; - int32 SDL_buttons; - int32 SDL_axes; - int32 SDL_balls; - int32 SDL_hats; + void *handle_pointer;//handle as pointer + int64 handle_int;//handle as integer + char *description;//description provided by manufacturer + int64 product_id; + int64 vendor_id; + int32 buttons; + int32 axes; + int32 balls; + int32 hats; }; #define QUEUED_EVENTS_LIMIT 1024 diff --git a/internal/c/libqb.cpp b/internal/c/libqb.cpp index bede42a41..17cb05c03 100644 --- a/internal/c/libqb.cpp +++ b/internal/c/libqb.cpp @@ -23,6 +23,7 @@ #include #endif + #include "libqb/printer.h" void alert(int32 x); @@ -81,6 +82,8 @@ int32 window_exists=0; int32 create_window=0; uint8 *window_title=NULL; +double max_fps=60;//60 is the default +int32 auto_fps=0;//set to 1 to make QB64 auto-adjust fps based on load int32 os_resize_event=0; @@ -111,17 +114,24 @@ int32 sub_gl_called=0; extern void evnt(uint32 linenumber); +extern "C" int qb64_custom_event(int event,int v1,int v2,int v3,int v4,int v5,int v6,int v7,int v8,void *p1,void *p2); +#ifdef QB64_WINDOWS + extern "C" LRESULT qb64_os_event_windows(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, int *qb64_os_event_info); +#endif +#ifdef QB64_LINUX +#ifdef QB64_GUI //Cannot have X11 events without a GUI +#ifndef QB64_MACOSX + extern "C" void qb64_os_event_linux(XEvent *event, int *qb64_os_event_info); +#endif +#endif +#endif - -extern "C" int QB64_Custom_Event(int event,int v1,int v2,int v3,int v4,int v5,int v6,int v7,int v8,void *p1,void *p2); #define QB64_EVENT_CLOSE 1 #define QB64_EVENT_KEY 2 #define QB64_EVENT_RELATIVE_MOUSE_MOVEMENT 3 #define QB64_EVENT_KEY_PAUSE 1000 - - static int32 image_qbicon16_handle; static int32 image_qbicon32_handle; @@ -198,7 +208,7 @@ int32 dont_call_sub_gl=0; void GLUT_DISPLAY_REQUEST(); -void timerCB(int millisec) +void timerCB(int millisec)//not currently being used { #ifdef QB64_GLUT glutPostRedisplay(); @@ -1060,9 +1070,23 @@ void key_update(); int32 key_display_state=0; int32 key_display=0; int32 key_display_redraw=0; + extern int32 device_last; extern int32 device_max; extern device_struct *devices; + +extern ontimer_struct *ontimer; +extern onkey_struct *onkey; +extern int32 onkey_inprogress; +extern onstrig_struct *onstrig; +extern int32 onstrig_inprogress; + +extern uint32 qbevent; + +#ifdef DEPENDENCY_DEVICEINPUT +#include "parts/input/game_controller/src.c" +#endif + extern int32 console; extern int32 screen_hide_startup; //... @@ -6792,7 +6816,7 @@ uint32 qb64_firsttimervalue;//based on time of day uint32 clock_firsttimervalue;//based on program launch time -extern uint32 qbevent; + uint8 wait_needed=1; @@ -14087,6 +14111,22 @@ void sub__delay(double seconds){ } } +void sub__fps(double fps, int32 passed){ +//passed=1 means _AUTO +//passed=2 means use fps +if (new_error) return; +if (passed!=1 && passed!=2){error(5); return;} +if (passed==1){ + auto_fps=1;//_AUTO +} +if (passed==2){ + if (fps<1){error(5); return;} + if (fps>200) fps=200; + max_fps=fps; + auto_fps=0; +} +} + void sub__limit(double fps){ if (new_error) return; static double prev=0; @@ -21786,12 +21826,6 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ return start; } - extern ontimer_struct *ontimer; - extern onkey_struct *onkey; - extern int32 onkey_inprogress; - extern onstrig_struct *onstrig; - extern int32 onstrig_inprogress; - void sub_run_init(){ //Reset ON KEY trapping //note: KEY bar F-key bindings are not affected @@ -28442,14 +28476,18 @@ void sub__maptriangle(int32 cull_options,float sx1,float sy1,float sx2,float sy2 void GLUT_TIMER_EVENT(int ignore){ #ifdef QB64_GLUT glutPostRedisplay(); - glutTimerFunc(8,GLUT_TIMER_EVENT,0); + int32 msdelay=1000.0/max_fps; + if (msdelay<1) msdelay=1; + glutTimerFunc(msdelay,GLUT_TIMER_EVENT,0); #endif } #else void GLUT_IDLEFUNC(){ #ifdef QB64_GLUT glutPostRedisplay(); - Sleep(8);//<=125hz + int32 msdelay=1000.0/max_fps; + if (msdelay<1) msdelay=1; + Sleep(msdelay); #endif } #endif @@ -28536,7 +28574,6 @@ void sub__maptriangle(int32 cull_options,float sx1,float sy1,float sx2,float sy2 - /********** Render State **********/ render_state.dest=NULL; render_state.source=NULL; @@ -29182,6 +29219,46 @@ render_state.cull_mode=CULL_MODE__UNKNOWN; + +//Init _DEVICEs +i=1; +//keyboard +devices[i].type=2; +devices[i].name="[KEYBOARD][BUTTON]"; +devices[i].lastbutton=512; +//calculate queue message size +x=512+8; +devices[i].event_size=x; +//create initial 'current' and 'previous' events +devices[i].events=(uint8*)calloc(2,x); +devices[i].max_events=2; +devices[i].queued_events=2; +devices[i].connected=1; +devices[i].used=1; +devices[i].description="Keyboard"; +i++; +//mouse +devices[i].type=3; +devices[i].name="[MOUSE][BUTTON][AXIS][WHEEL]"; +devices[i].lastbutton=3; +devices[i].lastaxis=2; +devices[i].lastwheel=3; +//calculate queue message size +x=devices[i].lastbutton+devices[i].lastaxis*4+devices[i].lastwheel*4+8; +devices[i].event_size=x; +//create initial 'current' and 'previous' events +devices[i].events=(uint8*)calloc(2,x); +devices[i].max_events=2; +devices[i].queued_events=2; +devices[i].connected=1; +devices[i].used=1; +devices[i].description="Mouse"; +device_last=i; + +#ifdef DEPENDENCY_DEVICEINPUT +QB64_GAMEPAD_INIT(); +#endif + #ifdef QB64_WINDOWS _beginthread(QBMAIN_WINDOWS,0,NULL); #else @@ -29333,8 +29410,10 @@ render_state.cull_mode=CULL_MODE__UNKNOWN; int32 update=0;//0=update input,1=update display main_loop: - - + +#ifdef DEPENDENCY_DEVICEINPUT +QB64_GAMEPAD_POLL(); +#endif if (lock_mainloop==1){ lock_mainloop=2; @@ -30618,28 +30697,29 @@ render_state.cull_mode=CULL_MODE__UNKNOWN; //NO_S_D_L//SDL_WaitThread(thread, NULL); #endif while (exit_ok!=3) Sleep(16); + if (lprint_buffered){ sub__printimage(lprint_image);//print any pending content } + + //close all open files sub_close(NULL,0); - - //NO_S_D_L//SDL_ShowCursor(0); - //NO_S_D_L//SDL_FreeCursor(mycursor); + //shutdown device interface + #ifdef DEPENDENCY_DEVICEINPUT + QB64_GAMEPAD_SHUTDOWN(); + #endif if (!cloud_app){ snd_un_init(); - - //program ends here } if (cloud_app){ FILE *f = fopen("..\\final.txt", "w"); - if (f != NULL) - { - fprintf(f, "Program exited normally"); - fclose(f); - } + if (f != NULL){ + fprintf(f, "Program exited normally"); + fclose(f); + } exit(0);//should log error } @@ -32413,9 +32493,153 @@ render_state.cull_mode=CULL_MODE__UNKNOWN; port60h_events++; } + #define OS_EVENT_PRE_PROCESSING 1 + #define OS_EVENT_POST_PROCESSING 2 + #define OS_EVENT_RETURN_IMMEDIATELY 3 + + #ifdef QB64_WINDOWS + extern "C" LRESULT qb64_os_event_windows(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, int *qb64_os_event_info){ + if (*qb64_os_event_info==OS_EVENT_PRE_PROCESSING){ + //example usage + /* + if (uMsg==WM_CLOSE){ + alert("goodbye"); + *qb64_os_event_info=OS_EVENT_RETURN_IMMEDIATELY; + } + */ - extern "C" int QB64_Custom_Event(int event,int v1,int v2,int v3,int v4,int v5,int v6,int v7,int v8,void *p1,void *p2){ + if (uMsg==WM_KEYDOWN){ + + if (device_last){//core devices required? + + /* + 16-23 The scan code. The value depends on the OEM. + 24 Indicates whether the key is an extended key, such as the right-hand ALT and CTRL keys that appear on an enhanced 101- or 102-key keyboard. The value is 1 if it is an extended key; otherwise, it is 0. + */ + + static int32 code,special; + special=0;//set to 2 for keys which we cannot detect a release for + code=(lParam>>16)&511; + + keydown_special: + static device_struct *d; + d=&devices[1];//keyboard + if (*(d->events+((d->queued_events-1)*d->event_size)+code)!=1){//don't add message if already on + uint8 *cp,*cp2; + if (d->queued_events==d->max_events){//expand/shift event buffer + if (d->max_events>=(QUEUED_EVENTS_LIMIT/4)){//note: default event limit divied by 4 + //discard base message + memmove(d->events,d->events+d->event_size,(d->queued_events-1)*d->event_size); + d->queued_events--; + }else{ + cp=(uint8*)calloc(d->max_events*2,d->event_size); + memcpy(cp,d->events,d->queued_events*d->event_size);//copy existing events + cp2=d->events; + d->events=cp; + free(cp2); + d->max_events*=2; + } + } + memmove(d->events+d->queued_events*d->event_size,d->events+(d->queued_events-1)*d->event_size,d->event_size);//duplicate last event + *(int64*)(d->events+(d->queued_events*d->event_size)+(d->event_size-8))=device_event_index++;//store global event index + //make required changes + *(d->events+(d->queued_events*d->event_size)+code)=1; + if (special==2){special=1; d->queued_events++; goto keydown_special;} + if (special==1) *(d->events+(d->queued_events*d->event_size)+code)=0; + d->queued_events++; + }//not 1 + }//core devices required + +}//WM_KEYDOWN + + + if (uMsg==WM_KEYUP){ + + if (device_last){//core devices required? + + /* + 16-23 The scan code. The value depends on the OEM. + 24 Indicates whether the key is an extended key, such as the right-hand ALT and CTRL keys that appear on an enhanced 101- or 102-key keyboard. The value is 1 if it is an extended key; otherwise, it is 0. + */ + + static int32 code; + + code=(lParam>>16)&511; + + static device_struct *d; + d=&devices[1];//keyboard + if (*(d->events+((d->queued_events-1)*d->event_size)+code)!=0){//don't add message if already on + uint8 *cp,*cp2; + if (d->queued_events==d->max_events){//expand/shift event buffer + if (d->max_events>=(QUEUED_EVENTS_LIMIT/4)){//note: default event limit divied by 4 + //discard base message + memmove(d->events,d->events+d->event_size,(d->queued_events-1)*d->event_size); + d->queued_events--; + }else{ + cp=(uint8*)calloc(d->max_events*2,d->event_size); + memcpy(cp,d->events,d->queued_events*d->event_size);//copy existing events + cp2=d->events; + d->events=cp; + free(cp2); + d->max_events*=2; + } + } + memmove(d->events+d->queued_events*d->event_size,d->events+(d->queued_events-1)*d->event_size,d->event_size);//duplicate last event + *(int64*)(d->events+(d->queued_events*d->event_size)+(d->event_size-8))=device_event_index++;//store global event index + //make required changes + *(d->events+(d->queued_events*d->event_size)+code)=0; + d->queued_events++; + }//not 1 + }//core devices required + +}//WM_KEYUP + + + + + + + + + + + + + + + + } + if (*qb64_os_event_info==OS_EVENT_POST_PROCESSING){ + + + + } + return 0; + } + #endif + + #ifdef QB64_LINUX + #ifdef QB64_GUI //Cannot have X11 events without a GUI + #ifndef QB64_MACOSX + extern "C" void qb64_os_event_linux(XEvent *event, int *qb64_os_event_info){ + if (*qb64_os_event_info==OS_EVENT_PRE_PROCESSING){ + + + + } + if (*qb64_os_event_info==OS_EVENT_POST_PROCESSING){ + + + + } + return; + } + #endif + #endif + #endif + + extern "C" int qb64_custom_event(int event,int v1,int v2,int v3,int v4,int v5,int v6,int v7,int v8,void *p1,void *p2){ if (event==QB64_EVENT_CLOSE){ exit_value|=1; return NULL; @@ -32432,7 +32656,7 @@ render_state.cull_mode=CULL_MODE__UNKNOWN; return -1; }//key - if (event==QB64_EVENT_RELATIVE_MOUSE_MOVEMENT){ //QB64_Custom_Event(QB64_EVENT_RELATIVE_MOUSE_MOVEMENT,xPosRelative,yPosRelative,0,0,0,0,0,0,NULL,NULL); + if (event==QB64_EVENT_RELATIVE_MOUSE_MOVEMENT){ //qb64_custom_event(QB64_EVENT_RELATIVE_MOUSE_MOVEMENT,xPosRelative,yPosRelative,0,0,0,0,0,0,NULL,NULL); static int32 i; //message #1 i=(last_mouse_message+1)&65535; @@ -32456,7 +32680,7 @@ render_state.cull_mode=CULL_MODE__UNKNOWN; }//QB64_EVENT_RELATIVE_MOUSE_MOVEMENT return -1;//Unknown command (use for debugging purposes only) - }//QB64_Custom_Event + }//qb64_custom_event void reinit_glut_callbacks(){ diff --git a/internal/c/parts/core/src/freeglut_main.c b/internal/c/parts/core/src/freeglut_main.c index 40729c9af..6a0968921 100644 --- a/internal/c/parts/core/src/freeglut_main.c +++ b/internal/c/parts/core/src/freeglut_main.c @@ -1,8 +1,56 @@ #ifndef FREEGLUT_STATIC -#define FREEGLUT_STATIC + #define FREEGLUT_STATIC +#endif + +/* + * freeglut_main.c + * + * The windows message processing methods. + * + * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved. + * Written by Pawel W. Olszta, + * Creation date: Fri Dec 3 1999 + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "freeglut.h" +#include "freeglut_internal.h" +#ifdef HAVE_ERRNO_H +# include +#endif +#include +#ifdef HAVE_VFPRINTF +# define VFPRINTF(s,f,a) vfprintf((s),(f),(a)) +#elif defined(HAVE__DOPRNT) +# define VFPRINTF(s,f,a) _doprnt((f),(a),(s)) +#else +# define VFPRINTF(s,f,a) +#endif + +int qb64_custom_event(int event,int v1,int v2,int v3,int v4,int v5,int v6,int v7,int v8,void *p1,void *p2); +#ifdef TARGET_HOST_MS_WINDOWS + LRESULT qb64_os_event_windows(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, int *qb64_os_event_info); +#endif +#if TARGET_HOST_POSIX_X11 + void qb64_os_event_linux(XEvent *event, int *qb64_os_event_info); #endif -int QB64_Custom_Event(int event,int v1,int v2,int v3,int v4,int v5,int v6,int v7,int v8,void *p1,void *p2); #define QB64_EVENT_CLOSE 1 #define QB64_EVENT_KEY 2 #define QB64_EVENT_RELATIVE_MOUSE_MOVEMENT 3 @@ -294,46 +342,6 @@ typedef enum { #define KMOD_ALT (KMOD_LALT|KMOD_RALT) #define KMOD_META (KMOD_LMETA|KMOD_RMETA) -/* - * freeglut_main.c - * - * The windows message processing methods. - * - * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved. - * Written by Pawel W. Olszta, - * Creation date: Fri Dec 3 1999 - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "freeglut.h" -#include "freeglut_internal.h" -#ifdef HAVE_ERRNO_H -# include -#endif -#include -#ifdef HAVE_VFPRINTF -# define VFPRINTF(s,f,a) vfprintf((s),(f),(a)) -#elif defined(HAVE__DOPRNT) -# define VFPRINTF(s,f,a) _doprnt((f),(a),(s)) -#else -# define VFPRINTF(s,f,a) -#endif #ifdef _WIN32_WCE @@ -1287,6 +1295,9 @@ void FGAPIENTRY glutMainLoopEvent( void ) window->State.MouseX = event.a.x; \ window->State.MouseY = event.a.y; + + + FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" ); while( XPending( fgDisplay.Display ) ) @@ -1296,6 +1307,12 @@ void FGAPIENTRY glutMainLoopEvent( void ) fghPrintEvent( &event ); #endif + int qb64_os_event_info=0; + + qb64_os_event_info=1; + qb64_os_event_linux(&event, &qb64_os_event_info); + if (qb64_os_event_info==3) return; + switch( event.type ) { case ClientMessage: @@ -1747,6 +1764,11 @@ void FGAPIENTRY glutMainLoopEvent( void ) #endif break; } + + qb64_os_event_info=2; + qb64_os_event_linux(&event, &qb64_os_event_info); + if (qb64_os_event_info==3) return;//(although we would return anyway) + } #elif TARGET_HOST_MS_WINDOWS @@ -1897,6 +1919,16 @@ static int fghGetWin32Modifiers (void) LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { + + int qb64_os_event_info=0; + LRESULT qb64_os_event_return=1; + + qb64_os_event_info=1; + qb64_os_event_return=qb64_os_event_windows( + hWnd, uMsg, wParam,lParam, &qb64_os_event_info + ); + if (qb64_os_event_info==3) return qb64_os_event_return; + static unsigned char lControl = 0, rControl = 0, lShift = 0, rShift = 0, lAlt = 0, rAlt = 0; @@ -2198,7 +2230,7 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, if ( fgState.ActionOnWindowClose != GLUT_ACTION_CONTINUE_EXECUTION ) PostQuitMessage(0); */ - QB64_Custom_Event(QB64_EVENT_CLOSE,0,0,0,0,0,0,0,0,NULL,NULL); + qb64_custom_event(QB64_EVENT_CLOSE,0,0,0,0,0,0,0,0,NULL,NULL); break; @@ -2227,7 +2259,7 @@ if (raw_setup){ { int xPosRelative = raw->data.mouse.lLastX; int yPosRelative = raw->data.mouse.lLastY; - if (xPosRelative||yPosRelative) QB64_Custom_Event(QB64_EVENT_RELATIVE_MOUSE_MOVEMENT,xPosRelative,yPosRelative,0,0,0,0,0,0,NULL,NULL); + if (xPosRelative||yPosRelative) qb64_custom_event(QB64_EVENT_RELATIVE_MOUSE_MOVEMENT,xPosRelative,yPosRelative,0,0,0,0,0,0,NULL,NULL); } } @@ -2475,11 +2507,11 @@ if (raw_setup){ //QB64 if (wParam==VK_PAUSE){ - QB64_Custom_Event(QB64_EVENT_KEY,VK+QBVK_PAUSE,1,0,0,0,0,0,0,NULL,NULL); + qb64_custom_event(QB64_EVENT_KEY,VK+QBVK_PAUSE,1,0,0,0,0,0,0,NULL,NULL); break; } if (wParam==VK_CANCEL){ - QB64_Custom_Event(QB64_EVENT_KEY,VK+QBVK_BREAK,1,0,0,0,0,0,0,NULL,NULL); + qb64_custom_event(QB64_EVENT_KEY,VK+QBVK_BREAK,1,0,0,0,0,0,0,NULL,NULL); break; } @@ -2580,11 +2612,11 @@ if (wParam==VK_CANCEL){ //QB64 if (wParam==VK_PAUSE){ - QB64_Custom_Event(QB64_EVENT_KEY,VK+QBVK_PAUSE,-1,0,0,0,0,0,0,NULL,NULL); + qb64_custom_event(QB64_EVENT_KEY,VK+QBVK_PAUSE,-1,0,0,0,0,0,0,NULL,NULL); break; } if (wParam==VK_CANCEL){ - QB64_Custom_Event(QB64_EVENT_KEY,VK+QBVK_BREAK,-1,0,0,0,0,0,0,NULL,NULL); + qb64_custom_event(QB64_EVENT_KEY,VK+QBVK_BREAK,-1,0,0,0,0,0,0,NULL,NULL); break; } @@ -2880,6 +2912,12 @@ if (wParam==VK_CANCEL){ break; } + qb64_os_event_info=2; + qb64_os_event_return=qb64_os_event_windows( + hWnd, uMsg, wParam,lParam, &qb64_os_event_info + ); + if (qb64_os_event_info==3) return qb64_os_event_return; + return lRet; } #endif diff --git a/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/Changes.txt b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/Changes.txt new file mode 100644 index 000000000..4d0fddbbf --- /dev/null +++ b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/Changes.txt @@ -0,0 +1,47 @@ +1.4.0 (2014-04-06) + * Windows implementation completely redone to use DirectInput and XInput instead of joyGetPosEx + * Added MSVC support + +1.3.1 (2014-02-01) + * Gamepad_init() on Mac OS X now detects devices immediately rather than waiting until the next run loop cycle + * Gamepad_detectDevices() and Gamepad_processEvents() now work on Mac OS X in applicactions that call them outside the main CF/NSRunLoop + * Fixed a bug on Mac OS X that could cause a crash on Gamepad_shutdown() + * Removed all remaining stem library dependencies (shell, glutshell, and glgraphics for testharness) + +1.3.0 (2013-09-01) + * Gamepad callbacks now include context pointers + * Axis move callback now reports previous axis value in addition to current one + * Fixed a major bug (introduced in 1.2.0) that caused button up events never to be reported on Windows + +1.2.0 (2013-07-18) + * Removed dependencies on utilities and stemobject. Library is now completely standalone, though test harness still requires shell and glutshell. + * Gamepad API no longer uses EventDispatcher, instead providing GLUT-style callback registration for all previous event types + +1.1.6 (2013-07-17) + * Added 64-bit Windows and Linux support + +1.1.5 (2011-11-16) + * Gamepad_detectDevices() significantly sped up on Linux + +1.1.4 (2011-11-08) + * Gamepad_processEvents() will now do nothing if called from within itself + +1.1.3 (2011-09-29) + * Updated event dispatching to work with utilities 1.5.0 + +1.1.2 (2011-09-20) + * Updated dependencies: utilities 1.4.0 -> 1.4.2 + +1.1.1 (2011-08-24) + * Fixed crashes from retrieving gamepad description strings + * Fixed a memory leak + * Fixed a potential thread deadlock on Linux + * Removed leftover joystick debug code + +1.1.0 (2010-01-28) + * Added vendor and product ID fields to Gamepad_device + * Worked around a crash on the Mac with Sixaxis controllers + * Fixed a problem that caused Saitek X52 hat switches to report incorrect values on Mac OS X + +1.0.0 (2010-01-19) + * Initial version diff --git a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/License.txt b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/License.txt similarity index 92% rename from internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/License.txt rename to internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/License.txt index f5d0f5076..9845d7c6c 100644 --- a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/License.txt +++ b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/License.txt @@ -1,4 +1,4 @@ -Copyright (c) 2010 Alex Diener +Copyright (c) 2014 Alex Diener This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -16,4 +16,4 @@ freely, subject to the following restrictions: misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. -Alex Diener adiener@sacredsoftware.net \ No newline at end of file +Alex Diener alex@ludobloom.com \ No newline at end of file diff --git a/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/Makefile b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/Makefile new file mode 100644 index 000000000..09f07c837 --- /dev/null +++ b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/Makefile @@ -0,0 +1,787 @@ +.PHONY: all +all: library testharness include + +UNAME = ${shell uname} +ifeq (${UNAME},Linux) +-include ~/.stem.defines +STEM_SHARED_DIR ?= /usr/local/stem +HOST_PLATFORM = linux +else ifeq (${UNAME},Darwin) +-include ~/.stem.defines +STEM_SHARED_DIR ?= /usr/local/stem +HOST_PLATFORM = macosx +else +STEM_SHARED_DIR ?= C:/stem +-include ${STEM_SHARED_DIR}/stem.defines +HOST_PLATFORM = windows +endif + +include version + +define newline_and_tab + + +endef + +iphone_sdk_version_integer = ${subst .,0,$1}${word ${words ${wordlist 2, ${words ${subst ., ,$1}}, ${subst ., ,$1}}}, 00} + +TARGET_PLATFORMS_macosx = macosx iphonesimulator iphoneos +TARGET_PLATFORMS_linux = linux32 linux64 +TARGET_PLATFORMS_windows = win32 win64 + +PROJECT_NAME = gamepad +IPHONE_BUILD_SDK_VERSION ?= 4.2 +IPHONE_DEPLOYMENT_TARGET_VERSION ?= 3.1 +CODESIGN_IDENTITY ?= "iPhone Developer" + +LIBRARY_TARGETS = library +EXECUTABLE_TARGETS = +APPLICATION_TARGETS = testharness +TARGETS = ${LIBRARY_TARGETS} ${EXECUTABLE_TARGETS} ${APPLICATION_TARGETS} +PLATFORMS = ${filter ${TARGET_PLATFORMS_${HOST_PLATFORM}},macosx linux32 linux64 win32 win64} +ANALYZERS = splint clang + +TARGET_NAME_library = libstem_${PROJECT_NAME} +TARGET_NAME_unittest = ${PROJECT_NAME}_unittest +TARGET_NAME_testharness = ${PROJECT_NAME}_testharness +HUMAN_READABLE_TARGET_NAME_testharness = GamepadTestHarness + +#Per-target configurations +CONFIGURATIONS_library = debug profile release +CONFIGURATIONS_unittest = debug +CONFIGURATIONS_testharness = debug profile release + +#Per-target platforms +PLATFORMS_library = ${filter ${PLATFORMS},macosx linux32 linux64 win32 win64} +PLATFORMS_unittest = ${filter ${PLATFORMS},macosx linux32 linux64 win32 win64} +PLATFORMS_testharness = ${filter ${PLATFORMS},macosx linux32 linux64 win32 win64} + +#Per-target compile/link settings +CCFLAGS_testharness = -DGLEW_STATIC + +#Per-target analyzer settings +CLANGFLAGS_unittest = ${CCFLAGS_unittest} +SPLINTFLAGS_unittest = ${CCFLAGS_unittest} + +#Per-configuration compile/link settings +CCFLAGS_debug = -g -DDEBUG +CCFLAGS_profile = -g -O3 +CCFLAGS_release = -O3 + +#Per-platform compile/link settings +CC_macosx_i386 = /usr/bin/clang -arch i386 +CC_macosx_x86_64 = /usr/bin/clang -arch x86_64 +AR_macosx = /usr/bin/ar +RANLIB_macosx = /usr/bin/ranlib +SPLINT_macosx = /usr/local/bin/splint +CLANG_macosx = /usr/bin/clang +SDKROOT_macosx = /Developer/SDKs/MacOSX10.6.sdk +ARCHS_macosx = i386 x86_64 +CCFLAGS_macosx = -isysroot ${SDKROOT_macosx} -mmacosx-version-min=10.6 +LINKFLAGS_macosx = -isysroot ${SDKROOT_macosx} -mmacosx-version-min=10.6 -framework IOKit -framework CoreFoundation -framework OpenGL -framework GLUT -framework ApplicationServices + +CC_linux32_i386 = /usr/bin/gcc +AR_linux32 = /usr/bin/ar +RANLIB_linux32 = /usr/bin/ranlib +SPLINT_linux32 = /usr/local/bin/splint +CLANG_linux32 = /usr/local/bin/clang +ARCHS_linux32 = i386 +CCFLAGS_linux32 = -m32 +LINKFLAGS_linux32 = -m32 -ldl -lglut -lGLU -lGL -lm -Wl,-E + +CC_linux64_x86_64 = /usr/bin/gcc +AR_linux64 = /usr/bin/ar +RANLIB_linux64 = /usr/bin/ranlib +SPLINT_linux64 = /usr/local/bin/splint +CLANG_linux64 = /usr/local/bin/clang +ARCHS_linux64 = x86_64 +CCFLAGS_linux64 = -m64 +LINKFLAGS_linux64 = -m64 -ldl -lglut -lGLU -lGL -lm -Wl,-E + +MINGW_W32_PATH ?= C:/MinGW +MINGW_W32_VERSION ?= 4.6.2 +SPLINT_WIN_PATH ?= C:/splint-3.1.1/bin/splint.exe +CLANG_WIN_PATH ?= C:/llvm/bin/clang.exe +DX9_INCLUDE_PATH ?= C:/MinGW/dx9/include +DX9_LIB_PATH ?= C:/MinGW/dx9/lib +DX9_LIB_PATH_i386 ?= ${DX9_LIB_PATH}/x86 +WMI_LIB_PATH_i386 ?= C:/MinGW/WinSDK/Lib +CC_win32_i386 = ${MINGW_W32_PATH}/bin/gcc.exe +AR_win32 = ${MINGW_W32_PATH}/bin/ar.exe +RANLIB_win32 = ${MINGW_W32_PATH}/bin/ranlib.exe +SPLINT_win32 = ${SPLINT_WIN_PATH} +CLANG_win32 = ${CLANG_WIN_PATH} +ARCHS_win32 = i386 +CCFLAGS_win32 = -DFREEGLUT_STATIC -I ${DX9_INCLUDE_PATH} +LINKFLAGS_win32 = -lfreeglut32_static -lopengl32 -lglu32 -lpthread -lwinmm -lgdi32 ${DX9_LIB_PATH_i386}/Xinput.lib ${DX9_LIB_PATH_i386}/dinput8.lib ${DX9_LIB_PATH_i386}/dxguid.lib ${WMI_LIB_PATH_i386}/WbemUuid.Lib ${WMI_LIB_PATH_i386}/Ole32.Lib ${WMI_LIB_PATH_i386}/OleAut32.Lib +EXECUTABLE_SUFFIX_win32 = .exe + +MINGW_W64_PATH ?= C:/MinGW-w64 +MINGW_W64_VERSION ?= 4.7.0 +DX9_LIB_PATH_x86_64 ?= ${DX9_LIB_PATH}/x64 +WMI_LIB_PATH_x86_64 ?= C:/MinGW/WinSDK/Lib/x64 +CC_win64_x86_64 = ${MINGW_W64_PATH}/bin/x86_64-w64-mingw32-gcc.exe +AR_win64 = ${MINGW_W64_PATH}/bin/x86_64-w64-mingw32-ar.exe +RANLIB_win64 = ${MINGW_W64_PATH}/bin/x86_64-w64-mingw32-ranlib.exe +SPLINT_win64 = ${SPLINT_WIN_PATH} +CLANG_win64 = ${CLANG_WIN_PATH} +ARCHS_win64 = x86_64 +CCFLAGS_win64 = -DFREEGLUT_STATIC -I ${DX9_INCLUDE_PATH} +LINKFLAGS_win64 = -lfreeglut64_static -lopengl32 -lglu32 -lpthread -lwinmm -lgdi32 ${DX9_LIB_PATH_x86_64}/Xinput.lib ${DX9_LIB_PATH_x86_64}/dinput8.lib ${DX9_LIB_PATH_x86_64}/dxguid.lib ${WMI_LIB_PATH_x86_64}/WbemUuid.Lib ${WMI_LIB_PATH_x86_64}/Ole32.Lib ${WMI_LIB_PATH_x86_64}/OleAut32.Lib +EXECUTABLE_SUFFIX_win64 = .exe + +#General compile/link settings +DEFINE_CCFLAGS = -DVERSION_MAJOR=${VERSION_MAJOR}u -DVERSION_MINOR=${VERSION_MINOR}u -DVERSION_TWEAK=${VERSION_TWEAK}u +WARNING_CCFLAGS = -Wall -Wextra -Wno-unused-parameter -Werror +OTHER_CCFLAGS = -std=gnu99 +CCFLAGS = ${DEFINE_CCFLAGS} ${WARNING_CCFLAGS} ${OTHER_CCFLAGS} + +FRAMEWORK_LINKFLAGS = +LIBRARY_LINKFLAGS = +OTHER_LINKFLAGS = +LINKFLAGS = ${FRAMEWORK_LINKFLAGS} ${LIBRARY_LINKFLAGS} ${OTHER_LINKFLAGS} + +LINK_ORDER = \ + library + +#Dependencies (can optionally be per-target or per-target-per-platform) + +PROJECT_LIBRARY_DEPENDENCIES_unittest = library +PROJECT_LIBRARY_DEPENDENCIES_testharness = library +STEM_LIBRARY_DEPENDENCIES = +STEM_LIBRARY_DEPENDENCIES_testharness = +STEM_SOURCE_DEPENDENCIES = +THIRDPARTY_LIBRARY_DEPENDENCIES = + +#Per-target source file lists + +SOURCES_library = \ + source/${PROJECT_NAME}/Gamepad_private.c + +SOURCES_library_macosx = \ + source/${PROJECT_NAME}/Gamepad_macosx.c + +SOURCES_library_win32 = \ + source/${PROJECT_NAME}/Gamepad_windows_dinput.c + +SOURCES_library_win64 = \ + source/${PROJECT_NAME}/Gamepad_windows_dinput.c + +SOURCES_library_linux32 = \ + source/${PROJECT_NAME}/Gamepad_linux.c + +SOURCES_library_linux64 = \ + source/${PROJECT_NAME}/Gamepad_linux.c + +SOURCES_unittest = \ + build/intermediate/TestList.c \ + ${SOURCES_unittest_suites} + +SOURCES_unittest_suites = + +SOURCES_testharness = \ + source/testharness/TestHarness_main.c + +#Include files to be distributed with library + +INCLUDES = \ + source/${PROJECT_NAME}/Gamepad.h + +#Target resources + +RESOURCES_testharness = +RESOURCES_unittest = +RESOURCES_testharness_macosx = +#... + +#General analyzer settings +CLANGFLAGS = +CLANGFLAGS_win32 = -I ${MINGW_W32_PATH}/include -I ${MINGW_W32_PATH}/lib/gcc/mingw32/${MINGW_W32_VERSION}/include +CLANGFLAGS_win64 = -I ${MINGW_W64_PATH}/include -I ${MINGW_W64_PATH}/lib/gcc/mingw32/${MINGW_W64_VERSION}/include +SPLINTFLAGS = -exportlocal + +#Source files excluded from static analysis + +ANALYZER_EXCLUDE_SOURCES_clang = +ANALYZER_EXCLUDE_SOURCES_splint = ${SOURCES_unittest} + +#Additional target build prerequisites +PREREQS_unittest = + +#TestList.c is automatically generated from ${SOURCES_unittest_suites}. It is used by the unit test framework to determine which tests to run. +build/intermediate/TestList.c: build/intermediate/TestSuites.txt | build/intermediate + echo 'const char * UnitTest_suiteNameList[] = {${foreach file,${SOURCES_unittest_suites},"${basename ${notdir ${file}}}",} (void *) 0};' > $@ + +#TestSuites.txt tracks the state of ${SOURCES_unittest_suites} so that TestList.c can be updated if and only if ${SOURCES_unittest_suites} has changed. .PHONY is abused slightly to cause the target to be conditionally remade. +ifneq (${shell echo "${SOURCES_unittest_suites}" | cmp - build/intermediate/TestSuites.txt 2>&1},) +.PHONY: build/intermediate/TestSuites.txt +endif +build/intermediate/TestSuites.txt: | build/intermediate + echo "${SOURCES_unittest_suites}" > $@ + + + +define configuration_object_list_template #(target, configuration) + ${foreach platform,${PLATFORMS_$1}, \ + ${call platform_object_list_template,$1,$2,${platform}} \ + } +endef + +define platform_object_list_template #(target, configuration, platform) + ${foreach arch,${ARCHS_$3}, \ + ${call arch_object_list_template,$1,$2,$3,${arch}} \ + } +endef + +define arch_object_list_template #(target, configuration, platform, arch) + ${foreach source,${SOURCES_$1} ${SOURCES_$1_$3}, \ + build/intermediate/$1-$2-$3-$4/${notdir ${basename ${source}}}.o \ + } +endef + +#Produces OBJECTS_${target}_${configuration} variables for each permutation of target and configuration in that target +${foreach target,${TARGETS}, \ + ${foreach configuration,${CONFIGURATIONS_${target}}, \ + ${eval OBJECTS_${target}_${configuration} = ${call configuration_object_list_template,${target},${configuration}}} \ + } \ +} + + + +define create_directory_target_template #(dir) +.LOW_RESOLUTION_TIME: $1 +$1: + mkdir -p $1 +endef + +${foreach target,${TARGETS}, \ + ${foreach configuration,${CONFIGURATIONS_${target}}, \ + ${foreach platform,${PLATFORMS_${target}}, \ + ${eval ${call create_directory_target_template,build/${target}/${configuration}-${platform}}} \ + ${foreach arch,${ARCHS_${platform}}, \ + ${eval ${call create_directory_target_template,build/intermediate/${target}-${configuration}-${platform}-${arch}}} \ + } \ + } \ + } \ +} +${eval ${call create_directory_target_template,build/intermediate}} + + + +define include_ccflags_template #(target, platform) +-I source \ +${foreach stem_dependency,${STEM_LIBRARY_DEPENDENCIES} ${STEM_LIBRARY_DEPENDENCIES_$1} ${STEM_LIBRARY_DEPENDENCIES_$2} ${STEM_LIBRARY_DEPENDENCIES_$1_$2},-I ${STEM_SHARED_DIR}/${stem_dependency}/include} \ +${foreach thirdparty_dependency,${THIRDPARTY_LIBRARY_DEPENDENCIES} ${THIRDPARTY_LIBRARY_DEPENDENCIES_$1} ${THIRDPARTY_LIBRARY_DEPENDENCIES_$2} ${THIRDPARTY_LIBRARY_DEPENDENCIES_$1_$2},-I ${STEM_SHARED_DIR}/${dir ${thirdparty_dependency}}include} \ +${foreach source_dependency,${STEM_SOURCE_DEPENDENCIES} ${STEM_SOURCE_DEPENDENCIES_$1} ${STEM_SOURCE_DEPENDENCIES_$2} ${STEM_SOURCE_DEPENDENCIES_$1_$2},-I dep/${word 1,${subst /, ,${source_dependency}}}/source} +endef + +define define_ccflags_template #(target, configuration, platform, arch) +-DSTEM_TARGET_$1 -DSTEM_CONFIGURATION_$2 -DSTEM_PLATFORM_$3 -DSTEM_ARCH_$4 +endef + +define dependency_template #(target, configuration, platform, arch, source_file) +build/intermediate/$1-$2-$3-$4/${notdir ${basename $5}}.d: $5 ${PREREQS_$1} | build/intermediate/$1-$2-$3-$4 + @${CC_$3_$4} ${CCFLAGS} ${CCFLAGS_$1} ${CCFLAGS_$2} ${CCFLAGS_$3} ${call include_ccflags_template,$1,$3} ${call define_ccflags_template,$1,$2,$3,$4} -MM -o $$@.temp $5 + @sed 's,\(${notdir ${basename $5}}\)\.o[ :]*,$${basename $$@}.o $${basename $$@}.d: ,g' < $$@.temp > $$@ + @rm $$@.temp +endef + +#Produces dependency build targets for all source files in each configuration/platform/arch +ifeq ($(filter clean full_dist commit_dist analyze analyze_clang analyze_splint,${MAKECMDGOALS}),) +${foreach target,${TARGETS}, \ + ${foreach configuration,${CONFIGURATIONS_${target}}, \ + ${foreach platform,${PLATFORMS_${target}}, \ + ${foreach arch,${ARCHS_${platform}}, \ + ${foreach source,${SOURCES_${target}} ${SOURCES_${target}_${platform}}, \ + ${eval ${call dependency_template,${target},${configuration},${platform},${arch},${source}}} \ + ${eval -include build/intermediate/${target}-${configuration}-${platform}-${arch}/${notdir ${basename ${source}}}.d} \ + } \ + } \ + } \ + } \ +} +endif + + + +define compile_template #(target, configuration, platform, arch, source_file) +build/intermediate/$1-$2-$3-$4/${notdir ${basename $5}}.o: $5 ${PREREQS_$1} | build/intermediate/$1-$2-$3-$4 + ${CC_$3_$4} ${CCFLAGS} ${CCFLAGS_$1} ${CCFLAGS_$2} ${CCFLAGS_$3} ${call include_ccflags_template,$1,$3} ${call define_ccflags_template,$1,$2,$3,$4} -c -o $$@ $5 +endef + +#Produces object build targets for all source files in each configuration/platform/arch +${foreach target,${TARGETS}, \ + ${foreach configuration,${CONFIGURATIONS_${target}}, \ + ${foreach platform,${PLATFORMS_${target}}, \ + ${foreach arch,${ARCHS_${platform}}, \ + ${foreach source,${SOURCES_${target}} ${SOURCES_${target}_${platform}}, \ + ${eval ${call compile_template,${target},${configuration},${platform},${arch},${source}}} \ + } \ + } \ + } \ + } \ +} + + + +define library_template #(target, configuration, platform, arch, output_file) +build/intermediate/$1-$2-$3-$4/$5: ${call arch_object_list_template,$1,$2,$3,$4} + ${AR_$3} rc $$@ $$^ + ${RANLIB_$3} $$@ +endef + +#Produces static library build targets for each arch/platform/target for library targets +${foreach target,${LIBRARY_TARGETS}, \ + ${foreach configuration,${CONFIGURATIONS_${target}}, \ + ${foreach platform,${PLATFORMS_${target}}, \ + ${foreach arch,${ARCHS_${platform}}, \ + ${eval ${call library_template,${target},${configuration},${platform},${arch},${TARGET_NAME_${target}}.a}} \ + } \ + } \ + } \ +} + + + +define executable_template #(target, configuration, platform, arch, output_file, dependent_libraries) +build/intermediate/$1-$2-$3-$4/$5: ${call arch_object_list_template,$1,$2,$3,$4} $6 + ${CC_$3_$4} -o $$@ $$^ ${LINKFLAGS} ${LINKFLAGS_$3} +endef + +define library_dependency_template #(target, configuration, platform) + ${foreach link_library,${LINK_ORDER}, \ + ${foreach library,${filter ${link_library}%,${PROJECT_LIBRARY_DEPENDENCIES} ${PROJECT_LIBRARY_DEPENDENCIES_$1} ${PROJECT_LIBRARY_DEPENDENCIES_$3} ${PROJECT_LIBRARY_DEPENDENCIES_$1_$3}}, \ + build/${library}/$2-$3/${TARGET_NAME_${library}}.a \ + } \ + ${foreach library,${filter ${link_library}%,${STEM_LIBRARY_DEPENDENCIES} ${STEM_LIBRARY_DEPENDENCIES_$1} ${STEM_LIBRARY_DEPENDENCIES_$3} ${STEM_LIBRARY_DEPENDENCIES_$1_$3}}, \ + ${STEM_SHARED_DIR}/${library}/library/$2-$3/libstem_${word 1,${subst /, ,${library}}}.a \ + } \ + ${foreach library,${filter ${link_library}%,${STEM_SOURCE_DEPENDENCIES} ${STEM_SOURCE_DEPENDENCIES_$1} ${STEM_SOURCE_DEPENDENCIES_$3} ${STEM_SOURCE_DEPENDENCIES_$1_$3}}, \ + dep/${word 1,${subst /, ,${library}}}/build/${word 2,${subst /, ,${library}}}/$2-$3/${word 3,${subst /, ,${library}}} \ + } \ + ${foreach library,${filter ${link_library}%,${THIRDPARTY_LIBRARY_DEPENDENCIES} ${THIRDPARTY_LIBRARY_DEPENDENCIES_$1} ${THIRDPARTY_LIBRARY_DEPENDENCIES_$3} ${THIRDPARTY_LIBRARY_DEPENDENCIES_$1_$3}}, \ + ${STEM_SHARED_DIR}/${dir ${library}}library/$3/${notdir ${library}} \ + } \ + } +endef + +#Produces executable build targets for each arch/platform/target for executable and application targets +${foreach target,${EXECUTABLE_TARGETS} ${APPLICATION_TARGETS}, \ + ${foreach configuration,${CONFIGURATIONS_${target}}, \ + ${foreach platform,${PLATFORMS_${target}}, \ + ${foreach arch,${ARCHS_${platform}}, \ + ${eval ${call executable_template,${target},${configuration},${platform},${arch},${TARGET_NAME_${target}}${EXECUTABLE_SUFFIX_${platform}},${call library_dependency_template,${target},${configuration},${platform}}}} \ + } \ + } \ + } \ +} + + + +define dependency_submake_template #(dependency) +.PHONY: $1 +$1: + ${MAKE} -C dep/${word 1,${subst /, ,$1}} +endef + +#Invokes make for each source dependency +${foreach dependency,${sort ${foreach target,${TARGETS},${foreach platform,${PLATFORMS_${target}},${STEM_SOURCE_DEPENDENCIES} ${STEM_SOURCE_DEPENDENCIES_${target}} ${STEM_SOURCE_DEPENDENCIES_${platform}} ${STEM_SOURCE_DEPENDENCIES_${target}_${platform}}}}}, \ + ${eval ${call dependency_submake_template,${dependency}}} \ +} + + + +define thin_binary_list_template #(target, configuration, platform, target_name) + ${foreach arch,${ARCHS_$3}, \ + build/intermediate/$1-$2-$3-${arch}/$4 \ + } +endef + +#Produces THIN_BINARIES_${target}_${configuration}_${platform} variables for each target/configuration/platform for library targets +${foreach target,${LIBRARY_TARGETS}, \ + ${foreach configuration,${CONFIGURATIONS_${target}}, \ + ${foreach platform,${PLATFORMS_${target}}, \ + ${eval THIN_BINARIES_${target}_${configuration}_${platform} = ${call thin_binary_list_template,${target},${configuration},${platform},${TARGET_NAME_${target}}.a}} \ + } \ + } \ +} + +#Produces THIN_BINARIES_${target}_${configuration}_${platform} variables for each target/configuration/platform for executable targets +${foreach target,${EXECUTABLE_TARGETS}, \ + ${foreach configuration,${CONFIGURATIONS_${target}}, \ + ${foreach platform,${PLATFORMS_${target}}, \ + ${eval THIN_BINARIES_${target}_${configuration}_${platform} = ${call thin_binary_list_template,${target},${configuration},${platform},${TARGET_NAME_${target}}${EXECUTABLE_SUFFIX_${platform}}}} \ + } \ + } \ +} + +#Produces THIN_BINARIES_${target}_${configuration}_${platform} variables for each target/configuration/platform for application targets +${foreach target,${APPLICATION_TARGETS}, \ + ${foreach configuration,${CONFIGURATIONS_${target}}, \ + ${foreach platform,${PLATFORMS_${target}}, \ + ${eval THIN_BINARIES_${target}_${configuration}_${platform} = ${call thin_binary_list_template,${target},${configuration},${platform},${TARGET_NAME_${target}}${EXECUTABLE_SUFFIX_${platform}}}} \ + } \ + } \ +} + + + +define assemble_library_macosx #(target, configuration, platform) +build/$1/$2-$3/${TARGET_NAME_$1}.a: ${THIN_BINARIES_$1_$2_$3} | build/$1/$2-$3 + lipo -create -output $$@ ${THIN_BINARIES_$1_$2_$3} +endef + +define assemble_library_linux #(target, configuration, platform) +build/$1/$2-$3/${TARGET_NAME_$1}.a: ${THIN_BINARIES_$1_$2_$3} | build/$1/$2-$3 + cp ${THIN_BINARIES_$1_$2_$3} $$@ +endef + +define assemble_library_windows #(target, configuration, platform) +build/$1/$2-$3/${TARGET_NAME_$1}.a: ${THIN_BINARIES_$1_$2_$3} | build/$1/$2-$3 + cp ${THIN_BINARIES_$1_$2_$3} $$@ +endef + +#Produces final library build targets +${foreach target,${LIBRARY_TARGETS}, \ + ${foreach configuration,${CONFIGURATIONS_${target}}, \ + ${foreach platform,${PLATFORMS_${target}}, \ + ${eval ${call assemble_library_${HOST_PLATFORM},${target},${configuration},${platform}}} \ + } \ + } \ +} + +define copy_target_resources #(target, platform, resources_dir) + ${if ${strip ${RESOURCES_$1} ${RESOURCES_$1_$2}},mkdir -p $3,} + ${foreach resource,${RESOURCES_$1} ${RESOURCES_$1_$2}, \ + cp -r ${resource} $3${newline_and_tab} \ + } + ${if ${strip ${RESOURCES_$1} ${RESOURCES_$1_$2}},find $3 -name .svn -print0 -or -name .DS_Store -print0 | xargs -0 rm -rf} +endef + +define assemble_executable_macosx #(target, configuration, platform) +build/$1/$2-$3/${TARGET_NAME_$1}: ${THIN_BINARIES_$1_$2_$3} ${RESOURCES_$1} ${RESOURCES_$1_$3} | build/$1/$2-$3 + lipo -create -output $$@ ${THIN_BINARIES_$1_$2_$3} + ${call copy_target_resources,$1,$3,$${dir $$@}} +endef + +define assemble_executable_linux #(target, configuration, platform) +build/$1/$2-$3/${TARGET_NAME_$1}: ${THIN_BINARIES_$1_$2_$3} ${RESOURCES_$1} ${RESOURCES_$1_$3} | build/$1/$2-$3 + cp ${THIN_BINARIES_$1_$2_$3} $$@ + ${call copy_target_resources,$1,$3,$${dir $$@}} +endef + +define assemble_executable_windows #(target, configuration, platform) +build/$1/$2-$3/${TARGET_NAME_$1}.exe: ${THIN_BINARIES_$1_$2_$3} ${RESOURCES_$1} ${RESOURCES_$1_$3} | build/$1/$2-$3 + cp ${THIN_BINARIES_$1_$2_$3} $$@ + ${call copy_target_resources,$1,$3,$${dir $$@}} +endef + +#Produces final executable build targets +${foreach target,${EXECUTABLE_TARGETS}, \ + ${foreach configuration,${CONFIGURATIONS_${target}}, \ + ${foreach platform,${PLATFORMS_${target}}, \ + ${eval ${call assemble_executable_${HOST_PLATFORM},${target},${configuration},${platform}}} \ + } \ + } \ +} + +PLIST_FILE_testharness_macosx = resources/Info_testharness_macosx.plist + +PLIST_FILE_testharness_iphonesimulator = resources/Info_testharness_iphone.plist +PLIST_PLATFORM_CASED_iphonesimulator = iPhoneSimulator +PLIST_PLATFORM_LOWER_iphonesimulator = iphonesimulator +PLIST_SDK_NAME_iphonesimulator = iphonesimulator${IPHONE_BUILD_SDK_VERSION} + +PLIST_FILE_testharness_iphoneos = resources/Info_testharness_iphone.plist +PLIST_PLATFORM_CASED_iphoneos = iPhoneOS +PLIST_PLATFORM_LOWER_iphoneos = iphoneos +PLIST_SDK_NAME_iphoneos = iphoneos${IPHONE_BUILD_SDK_VERSION} + +define create_app_bundle #(target, platform, executable_dir, plist_dir, resources_dir) + mkdir -p $3 $4 $5 + sed -e "s/\$$$${PRODUCT_NAME}/${TARGET_NAME_$1}/g" \ + -e "s/\$$$${HUMAN_READABLE_PRODUCT_NAME}/${HUMAN_READABLE_TARGET_NAME_$1}/g" \ + -e "s/\$$$${VERSION}/${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_TWEAK}/g" \ + -e "s/\$$$${COPYRIGHT_YEAR}/"`date +%Y`"/g" \ + -e "s/\$$$${BUILD_NUMBER}/0/g" \ + -e "s/\$$$${PLATFORM_CASED}/${PLIST_PLATFORM_CASED_$2}/g" \ + -e "s/\$$$${PLATFORM_LOWER}/${PLIST_PLATFORM_LOWER_$2}/g" \ + -e "s/\$$$${SDK}/${PLIST_SDK_NAME_$2}/g" \ + ${PLIST_FILE_$1_$2} > $4/Info.plist + echo "APPL????" > $4/PkgInfo + ${call copy_target_resources,$1,$2,$5} +endef + +define assemble_application_macosx #(target, configuration) +build/$1/$2-macosx/$${HUMAN_READABLE_TARGET_NAME_$1}.app/Contents/MacOS/${TARGET_NAME_$1}: ${THIN_BINARIES_$1_$2_macosx} ${RESOURCES_$1} ${RESOURCES_$1_macosx} | build/$1/$2-macosx + ${call create_app_bundle,$1,macosx,build/$1/$2-macosx/$${HUMAN_READABLE_TARGET_NAME_$1}.app/Contents/MacOS,build/$1/$2-macosx/$${HUMAN_READABLE_TARGET_NAME_$1}.app/Contents,build/$1/$2-macosx/$${HUMAN_READABLE_TARGET_NAME_$1}.app/Contents/Resources} + lipo -create -output "$$@" ${THIN_BINARIES_$1_$2_macosx} +endef + +define assemble_application_iphonesimulator #(target, configuration) +build/$1/$2-iphonesimulator/${TARGET_NAME_$1}.app/${TARGET_NAME_$1}: ${THIN_BINARIES_$1_$2_iphonesimulator} ${RESOURCES_$1} ${RESOURCES_$1_iphonesimulator} | build/$1/$2-iphonesimulator + ${call create_app_bundle,$1,iphonesimulator,build/$1/$2-iphonesimulator/${TARGET_NAME_$1}.app,build/$1/$2-iphonesimulator/${TARGET_NAME_$1}.app,build/$1/$2-iphonesimulator/${TARGET_NAME_$1}.app} + lipo -create -output "$$@" ${THIN_BINARIES_$1_$2_iphonesimulator} +endef + +define assemble_application_iphoneos #(target, configuration) +build/$1/$2-iphoneos/${TARGET_NAME_$1}.app/${TARGET_NAME_$1}: ${THIN_BINARIES_$1_$2_iphoneos} ${RESOURCES_$1} ${RESOURCES_$1_iphoneos} | build/$1/$2-iphoneos + ${call create_app_bundle,$1,iphoneos,build/$1/$2-iphoneos/${TARGET_NAME_$1}.app,build/$1/$2-iphoneos/${TARGET_NAME_$1}.app,build/$1/$2-iphoneos/${TARGET_NAME_$1}.app} + lipo -create -output "$$@" ${THIN_BINARIES_$1_$2_iphoneos} +endef + +define assemble_application_linux32 #(target, configuration) +build/$1/$2-linux32/${TARGET_NAME_$1}: ${THIN_BINARIES_$1_$2_linux32} ${RESOURCES_$1} ${RESOURCES_$1_linux32} | build/$1/$2-linux32 + ${call copy_target_resources,$1,linux32,build/$1/$2-linux32/Resources} + cp ${THIN_BINARIES_$1_$2_linux32} "$$@" +endef + +define assemble_application_linux64 #(target, configuration) +build/$1/$2-linux64/${TARGET_NAME_$1}: ${THIN_BINARIES_$1_$2_linux64} ${RESOURCES_$1} ${RESOURCES_$1_linux64} | build/$1/$2-linux64 + ${call copy_target_resources,$1,linux64,build/$1/$2-linux64/Resources} + cp ${THIN_BINARIES_$1_$2_linux64} "$$@" +endef + +define assemble_application_win32 #(target, configuration) +build/$1/$2-win32/${TARGET_NAME_$1}.exe: ${THIN_BINARIES_$1_$2_win32} ${RESOURCES_$1} ${RESOURCES_$1_win32} | build/$1/$2-win32 + ${call copy_target_resources,$1,win32,build/$1/$2-win32/Resources} + cp ${THIN_BINARIES_$1_$2_win32} "$$@" +endef + +define assemble_application_win64 #(target, configuration) +build/$1/$2-win64/${TARGET_NAME_$1}.exe: ${THIN_BINARIES_$1_$2_win64} ${RESOURCES_$1} ${RESOURCES_$1_win64} | build/$1/$2-win64 + ${call copy_target_resources,$1,win64,build/$1/$2-win64/Resources} + cp ${THIN_BINARIES_$1_$2_win64} "$$@" +endef + +#Produces final application build targets +${foreach target,${APPLICATION_TARGETS}, \ + ${foreach configuration,${CONFIGURATIONS_${target}}, \ + ${foreach platform,${PLATFORMS_${target}}, \ + ${eval ${call assemble_application_${platform},${target},${configuration}}} \ + } \ + } \ +} + +define library_dependency_template #(target, configuration, platform) +${STEM_SOURCE_DEPENDENCIES} ${STEM_SOURCE_DEPENDENCIES_$1} ${STEM_SOURCE_DEPENDENCIES_$1_$3} build/$1/$2-$3/${TARGET_NAME_$1}.a +endef + +define executable_dependency_template #(target, configuration, platform) +${STEM_SOURCE_DEPENDENCIES} ${STEM_SOURCE_DEPENDENCIES_$1} ${STEM_SOURCE_DEPENDENCIES_$1_$3} build/$1/$2-$3/${TARGET_NAME_$1}${EXECUTABLE_SUFFIX_$3} +endef + +define application_dependency_template #(target, configuration, platform) +${STEM_SOURCE_DEPENDENCIES} ${STEM_SOURCE_DEPENDENCIES_$1} ${STEM_SOURCE_DEPENDENCIES_$1_$3} build/$1/$2-$3/${call application_file_template_$3,$1} +endef + +define application_file_template_macosx #(target) +$${HUMAN_READABLE_TARGET_NAME_$1}.app/Contents/MacOS/${TARGET_NAME_$1} +endef + +define application_file_template_iphonesimulator #(target) +${TARGET_NAME_$1}.app/${TARGET_NAME_$1} +endef + +define application_file_template_iphoneos #(target) +${TARGET_NAME_$1}.app/${TARGET_NAME_$1} +endef + +define application_file_template_linux32 #(target) +${TARGET_NAME_$1} +endef + +define application_file_template_linux64 #(target) +${TARGET_NAME_$1} +endef + +define application_file_template_win32 #(target) +${TARGET_NAME_$1}.exe +endef + +define application_file_template_win64 #(target) +${TARGET_NAME_$1}.exe +endef + +define target_template #(target, target_type) +.PHONY: $1 +$1: ${foreach configuration,${CONFIGURATIONS_$1},${foreach platform,${PLATFORMS_$1},${call $2_dependency_template,$1,${configuration},${platform}}}} +endef + +${foreach target,${LIBRARY_TARGETS}, \ + ${eval ${call target_template,${target},library}} \ +} + +${foreach target,${EXECUTABLE_TARGETS}, \ + ${eval ${call target_template,${target},executable}} \ +} + +${foreach target,${APPLICATION_TARGETS}, \ + ${eval ${call target_template,${target},application}} \ +} + +.PHONY: test +test: ${foreach platform,${PLATFORMS_unittest},run_unittests_${platform}} + +.PHONY: run_unittests_macosx +run_unittests_macosx: unittest + ./build/unittest/debug-macosx/${TARGET_NAME_unittest} "${CURDIR}/build/unittest/debug-macosx" + +.PHONY: run_unittests_iphonesimulator +run_unittests_iphonesimulator: unittest + DYLD_ROOT_PATH=${SDKROOT_iphonesimulator} \ + ./build/unittest/debug-iphonesimulator/${TARGET_NAME_unittest} "${CURDIR}/build/unittest/debug-iphonesimulator" + +.PHONY: run_unittests_linux32 +run_unittests_linux32: unittest + ./build/unittest/debug-linux32/${TARGET_NAME_unittest} "${CURDIR}/build/unittest/debug-linux32" + +.PHONY: run_unittests_linux64 +run_unittests_linux64: unittest + ./build/unittest/debug-linux64/${TARGET_NAME_unittest} "${CURDIR}/build/unittest/debug-linux64" + +.PHONY: run_unittests_win32 +run_unittests_win32: unittest + ./build/unittest/debug-win32/${TARGET_NAME_unittest}.exe "${CURDIR}/build/unittest/debug-win32" + +.PHONY: run_unittests_win64 +run_unittests_win64: unittest + ./build/unittest/debug-win64/${TARGET_NAME_unittest}.exe "${CURDIR}/build/unittest/debug-win64" + +define analyze_file_template_clang #(target, platform, file) +build/analyzer-results/clang-$1-$2/${basename ${notdir $3}}.txt: $3 ${PREREQS_$1} | build/analyzer-results/clang-$1-$2 + ${CLANG_$2} --analyze ${call include_ccflags_template,$1,$2} ${call define_ccflags_template,$1,analyze,$2,none} ${CLANGFLAGS} ${CLANGFLAGS_$1} ${CLANGFLAGS_$2} -o $${basename $$@}.plist $3 > $$@ 2>&1; true + @cat $$@ +endef + +define analyze_file_template_splint #(target, platform, file) +build/analyzer-results/splint-$1-$2/${basename ${notdir $3}}.txt: $3 ${PREREQS_$1} | build/analyzer-results/splint-$1-$2 + ${SPLINT_$2} ${call include_ccflags_template,$1,$2} ${call define_ccflags_template,$1,analyze,$2,none} ${SPLINTFLAGS} ${SPLINTFLAGS_$1} ${SPLINTFLAGS_$2} $3 > $$@ 2>&1; true + @cat $$@ +endef + +define analyzed_sources_template #(analyzer, target, platform) + ${sort ${filter-out ${ANALYZER_EXCLUDE_SOURCES_$1},${SOURCES_$2} ${SOURCES_$2_$3}}} +endef + +define analyzer_output_template #(analyzer, target, platform) + ${foreach file,${call analyzed_sources_template,$1,$2,$3}, \ + build/analyzer-results/$1-$2-$3/${basename ${notdir ${file}}}.txt \ + } +endef + +define analyze_target_template #(analyzer, target, platform) +.PHONY: analyze_$1_$2_$3 +analyze_$1_$2_$3: ${call analyzer_output_template,$1,$2,$3} +endef + +define analyze_template #(analyzer) +.PHONY: analyze_$1 +analyze_$1: ${foreach target,${TARGETS},${foreach platform,${PLATFORMS_${target}},analyze_$1_${target}_${platform}}} +endef + +${foreach analyzer,${ANALYZERS}, \ + ${eval ${call analyze_template,${analyzer}}} \ + ${foreach target,${TARGETS}, \ + ${foreach platform,${PLATFORMS_${target}}, \ + ${eval ${call analyze_target_template,${analyzer},${target},${platform}}} \ + ${foreach file,${call analyzed_sources_template,${analyzer},${target},${platform}}, \ + ${eval ${call analyze_file_template_${analyzer},${target},${platform},${file}}} \ + } \ + } \ + } \ +} + +${foreach analyzer,${ANALYZERS}, \ + ${foreach target,${TARGETS}, \ + ${foreach platform,${PLATFORMS_${target}}, \ + ${eval ${call create_directory_target_template,build/analyzer-results/${analyzer}-${target}-${platform}}} \ + } \ + } \ +} + +.PHONY: analyze +analyze: ${foreach analyzer,${ANALYZERS},analyze_${analyzer}} + +${foreach dir,${sort ${foreach include_file,${INCLUDES},build/include/${notdir ${patsubst %/,%,${dir ${include_file}}}}}}, \ + ${eval ${call create_directory_target_template,${dir}}} \ +} + +.PHONY: include +include: ${INCLUDES} | ${foreach include_file,${INCLUDES},build/include/${notdir ${patsubst %/,%,${dir ${include_file}}}}} + ${foreach include_file,${INCLUDES}, \ + cp ${include_file} build/include/${notdir ${patsubst %/,%,${dir ${include_file}}}}${newline_and_tab} \ + } + +.PHONY: clean +clean: + rm -rf build + ${foreach dependency,${sort ${foreach target,${TARGETS},${foreach platform,${PLATFORMS_${target}},${STEM_SOURCE_DEPENDENCIES} ${STEM_SOURCE_DEPENDENCIES_${target}} ${STEM_SOURCE_DEPENDENCIES_${platform}} ${STEM_SOURCE_DEPENDENCIES_${target}_${platform}}}}}, \ + ${MAKE} -C dep/${word 1,${subst /, ,${dependency}}} clean${newline_and_tab} \ + } + +TARGET_SUFFIX_ipad = _ipad +TARGET_SUFFIX_iphone4 = _iphone4 +IPHONE_SDK_VERSION_iphone ?= 4.2 +IPHONE_SDK_VERSION_ipad ?= 3.2 +IPHONE_SDK_VERSION_iphone4 ?= 4.2 +IPHONESIMULATOR_APPLICATIONS_DIR_iphone ?= ${HOME}/Library/Application Support/iPhone Simulator/${IPHONE_SDK_VERSION_iphone}/Applications +IPHONESIMULATOR_APPLICATIONS_DIR_ipad ?= ${HOME}/Library/Application Support/iPhone Simulator/${IPHONE_SDK_VERSION_ipad}/Applications +IPHONESIMULATOR_APPLICATIONS_DIR_iphone4 ?= ${HOME}/Library/Application Support/iPhone Simulator/${IPHONE_SDK_VERSION_iphone4}/Applications +SIMULATE_DEVICE_iphone = iPhone +SIMULATE_DEVICE_ipad = iPad +SIMULATE_DEVICE_iphone4 = iPhone 4 +SIMULATE_SDKROOT_iphone = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator${IPHONE_SDK_VERSION_iphone}.sdk +SIMULATE_SDKROOT_ipad = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator${IPHONE_SDK_VERSION_ipad}.sdk +SIMULATE_SDKROOT_iphone4 = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator${IPHONE_SDK_VERSION_iphone4}.sdk +IPHONE_SIMULATOR_PATH ?= /Developer/Platforms/iPhoneSimulator.platform/Developer/Applications/iPhone Simulator.app + +define install_target_iphonesimulator_template #(target, simulate_device) +.PHONY: install_$1_iphonesimulator${TARGET_SUFFIX_$2} +install_$1_iphonesimulator${TARGET_SUFFIX_$2}: $1 + killall "iPhone Simulator"; true + rm -rf "${IPHONESIMULATOR_APPLICATIONS_DIR_$2}/${TARGET_NAME_$1}" + mkdir -p "${IPHONESIMULATOR_APPLICATIONS_DIR_$2}/${TARGET_NAME_$1}/Documents" + mkdir -p "${IPHONESIMULATOR_APPLICATIONS_DIR_$2}/${TARGET_NAME_$1}/Library/Preferences" + mkdir -p "${IPHONESIMULATOR_APPLICATIONS_DIR_$2}/${TARGET_NAME_$1}/tmp" + cp -r "build/$1/debug-iphonesimulator/${TARGET_NAME_$1}.app" "${IPHONESIMULATOR_APPLICATIONS_DIR_$2}/${TARGET_NAME_$1}" + defaults write com.apple.iphonesimulator SimulateDevice -string "${SIMULATE_DEVICE_$2}" + defaults write com.apple.iphonesimulator SimulateSDKRoot -string "${SIMULATE_SDKROOT_$2}" + defaults write com.apple.iphonesimulator currentSDKRoot -string "${SIMULATE_SDKROOT_$2}" + open "${IPHONE_SIMULATOR_PATH}" +endef + +define add_blob_header #(source_file, target_file) + ruby -e "contents = \"\"; File.open(\"$1\", \"r\") {|file| contents = file.read}; File.open(\"$2\", \"w\") {|file| file.write(\"\xFA\xDE\x71\x71\"); file.write([contents.length + 8].pack(\"N\")); file.write(contents)}" +endef + +RESOURCE_RULES_PLIST = /Developer/Platforms/MacOSX.platform/ResourceRules.plist + +define codesign_target_iphoneos_template #(target) +.PHONY: codesign_$1_iphoneos +codesign_$1_iphoneos: $1 + sed -e "s/\$$$${PRODUCT_NAME}/${TARGET_NAME_$1}/g" resources/Entitlements.plist > build/intermediate/Entitlements.plist + ${call add_blob_header,build/intermediate/Entitlements.plist,build/intermediate/Entitlements.xcent} + export CODESIGN_ALLOCATE=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate + ${foreach configuration,${CONFIGURATIONS_$1},\ + cp "${RESOURCE_RULES_PLIST}" "build/$1/${configuration}-iphoneos/${TARGET_NAME_$1}.app"${newline_and_tab} \ + /usr/bin/codesign -f -s ${CODESIGN_IDENTITY} --resource-rules=${RESOURCE_RULES_PLIST} --entitlements=build/intermediate/Entitlements.xcent "build/$1/${configuration}-iphoneos/${TARGET_NAME_$1}.app"${newline_and_tab} \ + } +endef + +${foreach target,${APPLICATION_TARGETS}, \ + ${eval ${call install_target_iphonesimulator_template,${target},iphone}} \ + ${eval ${call install_target_iphonesimulator_template,${target},ipad}} \ + ${eval ${call install_target_iphonesimulator_template,${target},iphone4}} \ + ${eval ${call codesign_target_iphoneos_template,${target}}} \ +} + +INSTALL_DIR = ${STEM_SHARED_DIR}/${PROJECT_NAME}/${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_TWEAK} + +.PHONY: install +install: + mkdir -p "${INSTALL_DIR}/include" "${INSTALL_DIR}/library" "${INSTALL_DIR}/testharness" + cp Changes.txt License.txt ReadMe.txt version ${INSTALL_DIR} + cp -r build/include/* ${INSTALL_DIR}/include + cp -r build/library/* ${INSTALL_DIR}/library + cp -r build/testharness/* ${INSTALL_DIR}/testharness diff --git a/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/ReadMe.txt b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/ReadMe.txt new file mode 100644 index 000000000..30ab16208 --- /dev/null +++ b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/ReadMe.txt @@ -0,0 +1,5 @@ +Gamepad provides a low-level interface for USB game controller input. Each element on an attached game controller is mapped to zero or more buttons and zero or more axes. Buttons are binary controls; axes are continuous values ranging from -1.0f to 1.0f. The presence and ordering of elements depends on the platform and driver. + +Typical usage: Register a callback to notify you when a new device is attached with Gamepad_deviceAttachFunc(), then call Gamepad_init() and Gamepad_detectDevices(). Your callback will be called once per connected game controller. Also register callbacks for button and axis events with Gamepad_buttonDownFunc(), Gamepad_buttonUpFunc(), and Gamepad_axisMoveFunc(). Call Gamepad_processEvents() every frame, and Gamepad_detectDevices() occasionally to be notified of new devices that were plugged in after your Gamepad_init() call. If you're interested in knowing when a device was disconnected, you can call Gamepad_deviceRemoveFunc() get be notified of it. + +See Gamepad.h for more details. \ No newline at end of file diff --git a/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/msvc/Win32Project1/Win32Project1.vcxproj b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/msvc/Win32Project1/Win32Project1.vcxproj new file mode 100644 index 000000000..6a15760ee --- /dev/null +++ b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/msvc/Win32Project1/Win32Project1.vcxproj @@ -0,0 +1,162 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724} + Win32Proj + testharness + testharness + + + + Application + true + v120 + Unicode + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + ..\..\source;C:\Program Files\Common Files\MSVC\freeglut\include;%(AdditionalIncludeDirectories) + + + Windows + true + C:\Projects\StemLibProjects\gamepad_trunk\msvc\Debug;C:\Program Files\Common Files\MSVC\freeglut\lib;%(AdditionalLibraryDirectories) + mainCRTStartup + libstem_gamepad.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + ..\..\source;C:\Program Files\Common Files\MSVC\freeglut\include;%(AdditionalIncludeDirectories) + + + Windows + true + C:\Projects\StemLibProjects\gamepad_trunk\msvc\x64\Debug;C:\Program Files\Common Files\MSVC\freeglut\lib\x64;%(AdditionalLibraryDirectories) + mainCRTStartup + libstem_gamepad.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + C:\Program Files\Common Files\MSVC\freeglut\include;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + C:\Program Files\Common Files\MSVC\freeglut\lib;%(AdditionalLibraryDirectories) + mainCRTStartup + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + C:\Program Files\Common Files\MSVC\freeglut\include;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + C:\Program Files\Common Files\MSVC\freeglut\lib\x64;%(AdditionalLibraryDirectories) + mainCRTStartup + + + + + + + + + \ No newline at end of file diff --git a/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/msvc/Win32Project1/Win32Project1.vcxproj.filters b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/msvc/Win32Project1/Win32Project1.vcxproj.filters new file mode 100644 index 000000000..4a68046f1 --- /dev/null +++ b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/msvc/Win32Project1/Win32Project1.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/msvc/libstem_gamepad.sln b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/msvc/libstem_gamepad.sln new file mode 100644 index 000000000..6c35fd871 --- /dev/null +++ b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/msvc/libstem_gamepad.sln @@ -0,0 +1,49 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Express 2013 for Windows Desktop +VisualStudioVersion = 12.0.21005.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libstem_gamepad", "libstem_gamepad\libstem_gamepad.vcxproj", "{4579D2EA-39EB-4766-9EB6-497500F379D6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testharness", "Win32Project1\Win32Project1.vcxproj", "{EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}" + ProjectSection(ProjectDependencies) = postProject + {4579D2EA-39EB-4766-9EB6-497500F379D6} = {4579D2EA-39EB-4766-9EB6-497500F379D6} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Mixed Platforms = Release|Mixed Platforms + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4579D2EA-39EB-4766-9EB6-497500F379D6}.Debug|Mixed Platforms.ActiveCfg = Debug|x64 + {4579D2EA-39EB-4766-9EB6-497500F379D6}.Debug|Mixed Platforms.Build.0 = Debug|x64 + {4579D2EA-39EB-4766-9EB6-497500F379D6}.Debug|Win32.ActiveCfg = Debug|Win32 + {4579D2EA-39EB-4766-9EB6-497500F379D6}.Debug|Win32.Build.0 = Debug|Win32 + {4579D2EA-39EB-4766-9EB6-497500F379D6}.Debug|x64.ActiveCfg = Debug|x64 + {4579D2EA-39EB-4766-9EB6-497500F379D6}.Debug|x64.Build.0 = Debug|x64 + {4579D2EA-39EB-4766-9EB6-497500F379D6}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {4579D2EA-39EB-4766-9EB6-497500F379D6}.Release|Mixed Platforms.Build.0 = Release|Win32 + {4579D2EA-39EB-4766-9EB6-497500F379D6}.Release|Win32.ActiveCfg = Release|Win32 + {4579D2EA-39EB-4766-9EB6-497500F379D6}.Release|Win32.Build.0 = Release|Win32 + {4579D2EA-39EB-4766-9EB6-497500F379D6}.Release|x64.ActiveCfg = Release|x64 + {4579D2EA-39EB-4766-9EB6-497500F379D6}.Release|x64.Build.0 = Release|x64 + {EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}.Debug|Mixed Platforms.ActiveCfg = Debug|x64 + {EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}.Debug|Mixed Platforms.Build.0 = Debug|x64 + {EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}.Debug|Win32.ActiveCfg = Debug|Win32 + {EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}.Debug|Win32.Build.0 = Debug|Win32 + {EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}.Debug|x64.ActiveCfg = Debug|Win32 + {EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}.Release|Mixed Platforms.Build.0 = Release|Win32 + {EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}.Release|Win32.ActiveCfg = Release|Win32 + {EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}.Release|Win32.Build.0 = Release|Win32 + {EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}.Release|x64.ActiveCfg = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/msvc/libstem_gamepad/libstem_gamepad.vcxproj b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/msvc/libstem_gamepad/libstem_gamepad.vcxproj new file mode 100644 index 000000000..fe986f082 --- /dev/null +++ b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/msvc/libstem_gamepad/libstem_gamepad.vcxproj @@ -0,0 +1,142 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {4579D2EA-39EB-4766-9EB6-497500F379D6} + Win32Proj + libstem_gamepad + + + + StaticLibrary + true + v120 + Unicode + + + StaticLibrary + true + v120 + Unicode + + + StaticLibrary + false + v120 + true + Unicode + + + StaticLibrary + false + v120 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + ../../source + + + Windows + true + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + ../../source + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + + + Windows + true + true + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + + + Windows + true + true + true + + + + + + + + + + + + + \ No newline at end of file diff --git a/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/msvc/libstem_gamepad/libstem_gamepad.vcxproj.filters b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/msvc/libstem_gamepad/libstem_gamepad.vcxproj.filters new file mode 100644 index 000000000..38d456bbf --- /dev/null +++ b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/msvc/libstem_gamepad/libstem_gamepad.vcxproj.filters @@ -0,0 +1,30 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_resources/Info_testharness_macosx.plist b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/resources/Info_testharness_macosx.plist similarity index 100% rename from internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_resources/Info_testharness_macosx.plist rename to internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/resources/Info_testharness_macosx.plist diff --git a/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad.h b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad.h new file mode 100644 index 000000000..7c8994ee1 --- /dev/null +++ b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad.h @@ -0,0 +1,128 @@ +/* + Copyright (c) 2014 Alex Diener + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Alex Diener alex@ludobloom.com +*/ + +#ifndef __GAMEPAD_H__ +#define __GAMEPAD_H__ +#ifdef __cplusplus +extern "C" { +#endif + +#include + +struct Gamepad_device { + // Unique device identifier for application session, starting at 0 for the first device attached and + // incrementing by 1 for each additional device. If a device is removed and subsequently reattached + // during the same application session, it will have a new deviceID. + unsigned int deviceID; + + // Human-readable device name + const char * description; + + // USB vendor/product IDs as returned by the driver. Can be used to determine the particular model of device represented. + int vendorID; + int productID; + + // Number of axis elements belonging to the device + unsigned int numAxes; + + // Number of button elements belonging to the device + unsigned int numButtons; + + // Array[numAxes] of values representing the current state of each axis, in the range [-1..1] + float * axisStates; + + // Array[numButtons] of values representing the current state of each button + bool * buttonStates; + + // Platform-specific device data storage. Don't touch unless you know what you're doing and don't + // mind your code breaking in future versions of this library. + void * privateData; +}; + +/* Initializes gamepad library and detects initial devices. Call this before any other Gamepad_*() + function, other than callback registration functions. If you want to receive deviceAttachFunc + callbacks from devices detected in Gamepad_init(), you must call Gamepad_deviceAttachFunc() + before calling Gamepad_init(). + + This function must be called from the same thread that will be calling Gamepad_processEvents() + and Gamepad_detectDevices(). */ +void Gamepad_init(); + +/* Tears down all data structures created by the gamepad library and releases any memory that was + allocated. It is not necessary to call this function at application termination, but it's + provided in case you want to free memory associated with gamepads at some earlier time. */ +void Gamepad_shutdown(); + +/* Returns the number of currently attached gamepad devices. */ +unsigned int Gamepad_numDevices(); + +/* Returns the specified Gamepad_device struct, or NULL if deviceIndex is out of bounds. */ +struct Gamepad_device * Gamepad_deviceAtIndex(unsigned int deviceIndex); + +/* Polls for any devices that have been attached since the last call to Gamepad_detectDevices() or + Gamepad_init(). If any new devices are found, the callback registered with + Gamepad_deviceAttachFunc() (if any) will be called once per newly detected device. + + Note that depending on implementation, you may receive button and axis event callbacks for + devices that have not yet been detected with Gamepad_detectDevices(). You can safely ignore + these events, but be aware that your callbacks might receive a device ID that hasn't been seen + by your deviceAttachFunc. */ +void Gamepad_detectDevices(); + +/* Reads pending input from all attached devices and calls the appropriate input callbacks, if any + have been registered. */ +void Gamepad_processEvents(); + +/* Registers a function to be called whenever a device is attached. The specified function will be + called only during calls to Gamepad_init() and Gamepad_detectDevices(), in the thread from + which those functions were called. Calling this function with a NULL argument will stop any + previously registered callback from being called subsequently. */ +void Gamepad_deviceAttachFunc(void (* callback)(struct Gamepad_device * device, void * context), void * context); + +/* Registers a function to be called whenever a device is detached. The specified function can be + called at any time, and will not necessarily be called from the main thread. Calling this + function with a NULL argument will stop any previously registered callback from being called + subsequently. */ +void Gamepad_deviceRemoveFunc(void (* callback)(struct Gamepad_device * device, void * context), void * context); + +/* Registers a function to be called whenever a button on any attached device is pressed. The + specified function will be called only during calls to Gamepad_processEvents(), in the + thread from which Gamepad_processEvents() was called. Calling this function with a NULL + argument will stop any previously registered callback from being called subsequently. */ +void Gamepad_buttonDownFunc(void (* callback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context), void * context); + +/* Registers a function to be called whenever a button on any attached device is released. The + specified function will be called only during calls to Gamepad_processEvents(), in the + thread from which Gamepad_processEvents() was called. Calling this function with a NULL + argument will stop any previously registered callback from being called subsequently. */ +void Gamepad_buttonUpFunc(void (* callback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context), void * context); + +/* Registers a function to be called whenever an axis on any attached device is moved. The + specified function will be called only during calls to Gamepad_processEvents(), in the + thread from which Gamepad_processEvents() was called. Calling this function with a NULL + argument will stop any previously registered callback from being called subsequently. */ +void Gamepad_axisMoveFunc(void (* callback)(struct Gamepad_device * device, unsigned int axisID, float value, float lastValue, double timestamp, void * context), void * context); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/source/gamepad/Gamepad_linux.c b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_linux.c similarity index 60% rename from internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/source/gamepad/Gamepad_linux.c rename to internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_linux.c index babb074da..ca5d53dfa 100644 --- a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/source/gamepad/Gamepad_linux.c +++ b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_linux.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2010 Alex Diener + Copyright (c) 2014 Alex Diener This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -17,15 +17,17 @@ misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. - Alex Diener adiener@sacredsoftware.net + Alex Diener alex@ludobloom.com */ #include "gamepad/Gamepad.h" +#include "gamepad/Gamepad_private.h" #include #include #include #include #include +#define __USE_UNIX98 #include #include #include @@ -44,8 +46,8 @@ struct Gamepad_devicePrivate { }; struct Gamepad_queuedEvent { - EventDispatcher * dispatcher; - const char * eventType; + unsigned int deviceID; + enum Gamepad_eventType eventType; void * eventData; }; @@ -59,74 +61,24 @@ static size_t eventQueueSize = 0; static size_t eventCount = 0; static pthread_mutex_t eventQueueMutex; -static EventDispatcher * eventDispatcher = NULL; static bool inited = false; #define test_bit(bitIndex, array) \ ((array[(bitIndex) / (sizeof(int) * 8)] >> ((bitIndex) % (sizeof(int) * 8))) & 0x1) -static char ** findGamepadPaths(unsigned int * outNumGamepads) { - DIR * dev_input; - struct dirent * entity; - unsigned int numGamepads = 0; - char ** gamepadDevs = NULL; - unsigned int charsConsumed; - int num; - int fd; - int evCapBits[(EV_CNT - 1) / sizeof(int) * 8 + 1]; - int evKeyBits[(KEY_CNT - 1) / sizeof(int) * 8 + 1]; - int evAbsBits[(ABS_CNT - 1) / sizeof(int) * 8 + 1]; - char fileName[PATH_MAX]; - - dev_input = opendir("/dev/input"); - if (dev_input != NULL) { - for (entity = readdir(dev_input); entity != NULL; entity = readdir(dev_input)) { - charsConsumed = 0; - if (sscanf(entity->d_name, "event%d%n", &num, &charsConsumed) && charsConsumed == strlen(entity->d_name)) { - snprintf(fileName, PATH_MAX, "/dev/input/%s", entity->d_name); - fd = open(fileName, O_RDONLY, 0); - memset(evCapBits, 0, sizeof(evCapBits)); - memset(evKeyBits, 0, sizeof(evKeyBits)); - memset(evAbsBits, 0, sizeof(evAbsBits)); - if (ioctl(fd, EVIOCGBIT(0, sizeof(evCapBits)), evCapBits) < 0 || - ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(evKeyBits)), evKeyBits) < 0 || - ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(evAbsBits)), evAbsBits) < 0) { - close(fd); - continue; - } - if (!test_bit(EV_KEY, evCapBits) || !test_bit(EV_ABS, evCapBits) || - !test_bit(ABS_X, evAbsBits) || !test_bit(ABS_Y, evAbsBits) || - (!test_bit(BTN_TRIGGER, evKeyBits) && !test_bit(BTN_A, evKeyBits) && !test_bit(BTN_1, evKeyBits))) { - close(fd); - continue; - } - close(fd); - - numGamepads++; - gamepadDevs = realloc(gamepadDevs, sizeof(char *) * numGamepads); - gamepadDevs[numGamepads - 1] = malloc(strlen(fileName) + 1); - strcpy(gamepadDevs[numGamepads - 1], fileName); - } - } - closedir(dev_input); - } - - *outNumGamepads = numGamepads; - return gamepadDevs; -} - void Gamepad_init() { if (!inited) { - pthread_mutex_init(&devicesMutex, NULL); - pthread_mutex_init(&eventQueueMutex, NULL); + pthread_mutexattr_t recursiveLock; + pthread_mutexattr_init(&recursiveLock); + pthread_mutexattr_settype(&recursiveLock, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&devicesMutex, &recursiveLock); + pthread_mutex_init(&eventQueueMutex, &recursiveLock); inited = true; Gamepad_detectDevices(); } } static void disposeDevice(struct Gamepad_device * device) { - device->eventDispatcher->dispose(device->eventDispatcher); - close(((struct Gamepad_devicePrivate *) device->privateData)->fd); free(((struct Gamepad_devicePrivate *) device->privateData)->path); free(device->privateData); @@ -134,7 +86,6 @@ static void disposeDevice(struct Gamepad_device * device) { free((void *) device->description); free(device->axisStates); free(device->buttonStates); - free(device->eventDispatcher); free(device); } @@ -169,7 +120,7 @@ void Gamepad_shutdown() { devices = NULL; for (eventIndex = 0; eventIndex < eventCount; eventIndex++) { - if (!strcmp(eventQueue[eventIndex].eventType, GAMEPAD_EVENT_DEVICE_REMOVED)) { + if (eventQueue[eventIndex].eventType == GAMEPAD_EVENT_DEVICE_REMOVED) { disposeDevice(eventQueue[eventIndex].eventData); } } @@ -179,23 +130,10 @@ void Gamepad_shutdown() { free(eventQueue); eventQueue = NULL; - if (eventDispatcher != NULL) { - eventDispatcher->dispose(eventDispatcher); - free(eventDispatcher); - eventDispatcher = NULL; - } - inited = false; } } -EventDispatcher * Gamepad_eventDispatcher() { - if (eventDispatcher == NULL) { - eventDispatcher = EventDispatcher_create(NULL); - } - return eventDispatcher; -} - unsigned int Gamepad_numDevices() { unsigned int result; @@ -219,10 +157,10 @@ struct Gamepad_device * Gamepad_deviceAtIndex(unsigned int deviceIndex) { return result; } -static void queueEvent(EventDispatcher * dispatcher, const char * eventType, void * eventData) { +static void queueEvent(unsigned int deviceID, enum Gamepad_eventType eventType, void * eventData) { struct Gamepad_queuedEvent queuedEvent; - queuedEvent.dispatcher = dispatcher; + queuedEvent.deviceID = deviceID; queuedEvent.eventType = eventType; queuedEvent.eventData = eventData; @@ -235,7 +173,7 @@ static void queueEvent(EventDispatcher * dispatcher, const char * eventType, voi pthread_mutex_unlock(&eventQueueMutex); } -static void queueAxisEvent(struct Gamepad_device * device, double timestamp, unsigned int axisID, float value) { +static void queueAxisEvent(struct Gamepad_device * device, double timestamp, unsigned int axisID, float value, float lastValue) { struct Gamepad_axisEvent * axisEvent; axisEvent = malloc(sizeof(struct Gamepad_axisEvent)); @@ -243,8 +181,9 @@ static void queueAxisEvent(struct Gamepad_device * device, double timestamp, uns axisEvent->timestamp = timestamp; axisEvent->axisID = axisID; axisEvent->value = value; + axisEvent->lastValue = lastValue; - queueEvent(device->eventDispatcher, GAMEPAD_EVENT_AXIS_MOVED, axisEvent); + queueEvent(device->deviceID, GAMEPAD_EVENT_AXIS_MOVED, axisEvent); } static void queueButtonEvent(struct Gamepad_device * device, double timestamp, unsigned int buttonID, bool down) { @@ -256,7 +195,7 @@ static void queueButtonEvent(struct Gamepad_device * device, double timestamp, u buttonEvent->buttonID = buttonID; buttonEvent->down = down; - queueEvent(device->eventDispatcher, down ? GAMEPAD_EVENT_BUTTON_DOWN : GAMEPAD_EVENT_BUTTON_UP, buttonEvent); + queueEvent(device->deviceID, down ? GAMEPAD_EVENT_BUTTON_DOWN : GAMEPAD_EVENT_BUTTON_UP, buttonEvent); } static void * deviceThread(void * context) { @@ -280,7 +219,8 @@ static void * deviceThread(void * context) { queueAxisEvent(device, event.time.tv_sec + event.time.tv_usec * 0.000001, devicePrivate->axisMap[event.code], - value); + value, + device->axisStates[devicePrivate->axisMap[event.code]]); device->axisStates[devicePrivate->axisMap[event.code]] = value; @@ -298,7 +238,7 @@ static void * deviceThread(void * context) { } } - queueEvent(eventDispatcher, GAMEPAD_EVENT_DEVICE_REMOVED, device); + queueEvent(device->deviceID, GAMEPAD_EVENT_DEVICE_REMOVED, device); pthread_mutex_lock(&devicesMutex); for (gamepadIndex = 0; gamepadIndex < numDevices; gamepadIndex++) { @@ -318,123 +258,197 @@ static void * deviceThread(void * context) { } void Gamepad_detectDevices() { - unsigned int numPaths; - char ** gamepadPaths; + struct input_id id; + DIR * dev_input; + struct dirent * entity; + unsigned int charsConsumed; + int num; + int fd; + int evCapBits[(EV_CNT - 1) / sizeof(int) * 8 + 1]; + int evKeyBits[(KEY_CNT - 1) / sizeof(int) * 8 + 1]; + int evAbsBits[(ABS_CNT - 1) / sizeof(int) * 8 + 1]; + char fileName[PATH_MAX]; bool duplicate; - unsigned int pathIndex, gamepadIndex; + unsigned int gamepadIndex; struct stat statBuf; struct Gamepad_device * deviceRecord; struct Gamepad_devicePrivate * deviceRecordPrivate; - int fd; char name[128]; char * description; - int evKeyBits[(KEY_CNT - 1) / sizeof(int) * 8 + 1]; - int evAbsBits[(ABS_CNT - 1) / sizeof(int) * 8 + 1]; int bit; - struct input_id id; + time_t currentTime; + static time_t lastInputStatTime; if (!inited) { return; } - gamepadPaths = findGamepadPaths(&numPaths); - pthread_mutex_lock(&devicesMutex); - for (pathIndex = 0; pathIndex < numPaths; pathIndex++) { - duplicate = false; - for (gamepadIndex = 0; gamepadIndex < numDevices; gamepadIndex++) { - if (!strcmp(((struct Gamepad_devicePrivate *) devices[gamepadIndex]->privateData)->path, gamepadPaths[pathIndex])) { - duplicate = true; - break; - } - } - if (duplicate) { - free(gamepadPaths[pathIndex]); - continue; - } - - if (!stat(gamepadPaths[pathIndex], &statBuf)) { - deviceRecord = malloc(sizeof(struct Gamepad_device)); - deviceRecord->deviceID = nextDeviceID++; - deviceRecord->eventDispatcher = EventDispatcher_create(deviceRecord); - devices = realloc(devices, sizeof(struct Gamepad_device *) * (numDevices + 1)); - devices[numDevices++] = deviceRecord; - - fd = open(gamepadPaths[pathIndex], O_RDONLY, 0); - - deviceRecordPrivate = malloc(sizeof(struct Gamepad_devicePrivate)); - deviceRecordPrivate->fd = fd; - deviceRecordPrivate->path = gamepadPaths[pathIndex]; - memset(deviceRecordPrivate->buttonMap, 0xFF, sizeof(deviceRecordPrivate->buttonMap)); - memset(deviceRecordPrivate->axisMap, 0xFF, sizeof(deviceRecordPrivate->axisMap)); - deviceRecord->privateData = deviceRecordPrivate; - - if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) > 0) { - description = malloc(strlen(name + 1)); - strcpy(description, name); - } else { - description = malloc(strlen(gamepadPaths[pathIndex] + 1)); - strcpy(description, gamepadPaths[pathIndex]); - } - deviceRecord->description = description; - - if (!ioctl(fd, EVIOCGID, &id)) { - deviceRecord->vendorID = id.vendor; - deviceRecord->productID = id.product; - } else { - deviceRecord->vendorID = deviceRecord->productID = 0; - } - - memset(evKeyBits, 0, sizeof(evKeyBits)); - memset(evAbsBits, 0, sizeof(evAbsBits)); - ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(evKeyBits)), evKeyBits); - ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(evAbsBits)), evAbsBits); - - deviceRecord->numAxes = 0; - for (bit = 0; bit < ABS_CNT; bit++) { - if (test_bit(bit, evAbsBits)) { - if (ioctl(fd, EVIOCGABS(bit), &deviceRecordPrivate->axisInfo[bit]) < 0 || - deviceRecordPrivate->axisInfo[bit].minimum == deviceRecordPrivate->axisInfo[bit].maximum) { - continue; + + dev_input = opendir("/dev/input"); + currentTime = time(NULL); + if (dev_input != NULL) { + while ((entity = readdir(dev_input)) != NULL) { + charsConsumed = 0; + if (sscanf(entity->d_name, "event%d%n", &num, &charsConsumed) && charsConsumed == strlen(entity->d_name)) { + snprintf(fileName, PATH_MAX, "/dev/input/%s", entity->d_name); + if (stat(fileName, &statBuf) || statBuf.st_mtime < lastInputStatTime) { + continue; + } + + duplicate = false; + for (gamepadIndex = 0; gamepadIndex < numDevices; gamepadIndex++) { + if (!strcmp(((struct Gamepad_devicePrivate *) devices[gamepadIndex]->privateData)->path, fileName)) { + duplicate = true; + break; } - deviceRecordPrivate->axisMap[bit] = deviceRecord->numAxes; - deviceRecord->numAxes++; } - } - deviceRecord->numButtons = 0; - for (bit = BTN_MISC; bit < KEY_CNT; bit++) { - if (test_bit(bit, evKeyBits)) { - deviceRecordPrivate->buttonMap[bit - BTN_MISC] = deviceRecord->numButtons; - deviceRecord->numButtons++; + if (duplicate) { + continue; } + + fd = open(fileName, O_RDONLY, 0); + memset(evCapBits, 0, sizeof(evCapBits)); + memset(evKeyBits, 0, sizeof(evKeyBits)); + memset(evAbsBits, 0, sizeof(evAbsBits)); + if (ioctl(fd, EVIOCGBIT(0, sizeof(evCapBits)), evCapBits) < 0 || + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(evKeyBits)), evKeyBits) < 0 || + ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(evAbsBits)), evAbsBits) < 0) { + close(fd); + continue; + } + if (!test_bit(EV_KEY, evCapBits) || !test_bit(EV_ABS, evCapBits) || + !test_bit(ABS_X, evAbsBits) || !test_bit(ABS_Y, evAbsBits) || + (!test_bit(BTN_TRIGGER, evKeyBits) && !test_bit(BTN_A, evKeyBits) && !test_bit(BTN_1, evKeyBits))) { + close(fd); + continue; + } + + deviceRecord = malloc(sizeof(struct Gamepad_device)); + deviceRecord->deviceID = nextDeviceID++; + devices = realloc(devices, sizeof(struct Gamepad_device *) * (numDevices + 1)); + devices[numDevices++] = deviceRecord; + + deviceRecordPrivate = malloc(sizeof(struct Gamepad_devicePrivate)); + deviceRecordPrivate->fd = fd; + deviceRecordPrivate->path = malloc(strlen(fileName) + 1); + strcpy(deviceRecordPrivate->path, fileName); + memset(deviceRecordPrivate->buttonMap, 0xFF, sizeof(deviceRecordPrivate->buttonMap)); + memset(deviceRecordPrivate->axisMap, 0xFF, sizeof(deviceRecordPrivate->axisMap)); + deviceRecord->privateData = deviceRecordPrivate; + + if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) > 0) { + description = malloc(strlen(name) + 1); + strcpy(description, name); + } else { + description = malloc(strlen(fileName) + 1); + strcpy(description, fileName); + } + deviceRecord->description = description; + + if (!ioctl(fd, EVIOCGID, &id)) { + deviceRecord->vendorID = id.vendor; + deviceRecord->productID = id.product; + } else { + deviceRecord->vendorID = deviceRecord->productID = 0; + } + + memset(evKeyBits, 0, sizeof(evKeyBits)); + memset(evAbsBits, 0, sizeof(evAbsBits)); + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(evKeyBits)), evKeyBits); + ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(evAbsBits)), evAbsBits); + + deviceRecord->numAxes = 0; + for (bit = 0; bit < ABS_CNT; bit++) { + if (test_bit(bit, evAbsBits)) { + if (ioctl(fd, EVIOCGABS(bit), &deviceRecordPrivate->axisInfo[bit]) < 0 || + deviceRecordPrivate->axisInfo[bit].minimum == deviceRecordPrivate->axisInfo[bit].maximum) { + continue; + } + deviceRecordPrivate->axisMap[bit] = deviceRecord->numAxes; + deviceRecord->numAxes++; + } + } + deviceRecord->numButtons = 0; + for (bit = BTN_MISC; bit < KEY_CNT; bit++) { + if (test_bit(bit, evKeyBits)) { + deviceRecordPrivate->buttonMap[bit - BTN_MISC] = deviceRecord->numButtons; + deviceRecord->numButtons++; + } + } + + deviceRecord->axisStates = calloc(sizeof(float), deviceRecord->numAxes); + deviceRecord->buttonStates = calloc(sizeof(bool), deviceRecord->numButtons); + + if (Gamepad_deviceAttachCallback != NULL) { + Gamepad_deviceAttachCallback(deviceRecord, Gamepad_deviceAttachContext); + } + + pthread_create(&deviceRecordPrivate->thread, NULL, deviceThread, deviceRecord); } - - deviceRecord->axisStates = calloc(sizeof(float), deviceRecord->numAxes); - deviceRecord->buttonStates = calloc(sizeof(bool), deviceRecord->numButtons); - - Gamepad_eventDispatcher()->dispatchEvent(Gamepad_eventDispatcher(), GAMEPAD_EVENT_DEVICE_ATTACHED, deviceRecord); - - pthread_create(&deviceRecordPrivate->thread, NULL, deviceThread, deviceRecord); } + closedir(dev_input); } + + lastInputStatTime = currentTime; pthread_mutex_unlock(&devicesMutex); } +static void processQueuedEvent(struct Gamepad_queuedEvent event) { + switch (event.eventType) { + case GAMEPAD_EVENT_DEVICE_ATTACHED: + if (Gamepad_deviceAttachCallback != NULL) { + Gamepad_deviceAttachCallback(event.eventData, Gamepad_deviceAttachContext); + } + break; + + case GAMEPAD_EVENT_DEVICE_REMOVED: + if (Gamepad_deviceRemoveCallback != NULL) { + Gamepad_deviceRemoveCallback(event.eventData, Gamepad_deviceRemoveContext); + } + break; + + case GAMEPAD_EVENT_BUTTON_DOWN: + if (Gamepad_buttonDownCallback != NULL) { + struct Gamepad_buttonEvent * buttonEvent = event.eventData; + Gamepad_buttonDownCallback(buttonEvent->device, buttonEvent->buttonID, buttonEvent->timestamp, Gamepad_buttonDownContext); + } + break; + + case GAMEPAD_EVENT_BUTTON_UP: + if (Gamepad_buttonUpCallback != NULL) { + struct Gamepad_buttonEvent * buttonEvent = event.eventData; + Gamepad_buttonUpCallback(buttonEvent->device, buttonEvent->buttonID, buttonEvent->timestamp, Gamepad_buttonUpContext); + } + break; + + case GAMEPAD_EVENT_AXIS_MOVED: + if (Gamepad_axisMoveCallback != NULL) { + struct Gamepad_axisEvent * axisEvent = event.eventData; + Gamepad_axisMoveCallback(axisEvent->device, axisEvent->axisID, axisEvent->value, axisEvent->lastValue, axisEvent->timestamp, Gamepad_axisMoveContext); + } + break; + } +} + void Gamepad_processEvents() { - unsigned int eventIndex; + unsigned int eventIndex; + static bool inProcessEvents; - if (!inited) { + if (!inited || inProcessEvents) { return; } + inProcessEvents = true; pthread_mutex_lock(&eventQueueMutex); for (eventIndex = 0; eventIndex < eventCount; eventIndex++) { - eventQueue[eventIndex].dispatcher->dispatchEvent(eventQueue[eventIndex].dispatcher, eventQueue[eventIndex].eventType, eventQueue[eventIndex].eventData); - if (!strcmp(eventQueue[eventIndex].eventType, GAMEPAD_EVENT_DEVICE_REMOVED)) { + processQueuedEvent(eventQueue[eventIndex]); + if (eventQueue[eventIndex].eventType == GAMEPAD_EVENT_DEVICE_REMOVED) { disposeDevice(eventQueue[eventIndex].eventData); } } eventCount = 0; pthread_mutex_unlock(&eventQueueMutex); + inProcessEvents = false; } diff --git a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/source/gamepad/Gamepad_macosx.c b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_macosx.c similarity index 82% rename from internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/source/gamepad/Gamepad_macosx.c rename to internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_macosx.c index 5b30644fb..a5ff09126 100644 --- a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/source/gamepad/Gamepad_macosx.c +++ b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_macosx.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2010 Alex Diener + Copyright (c) 2014 Alex Diener This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -17,15 +17,18 @@ misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. - Alex Diener adiener@sacredsoftware.net + Alex Diener alex@ludobloom.com */ #include "gamepad/Gamepad.h" +#include "gamepad/Gamepad_private.h" #include #include #include #include +#define GAMEPAD_RUN_LOOP_MODE CFSTR("GamepadRunLoopMode") + struct HIDGamepadAxis { IOHIDElementCookie cookie; CFIndex logicalMin; @@ -46,8 +49,8 @@ struct Gamepad_devicePrivate { }; struct Gamepad_queuedEvent { - EventDispatcher * dispatcher; - const char * eventType; + unsigned int deviceID; + enum Gamepad_eventType eventType; void * eventData; }; @@ -64,8 +67,6 @@ static struct Gamepad_queuedEvent * deviceEventQueue = NULL; static size_t deviceEventQueueSize = 0; static size_t deviceEventCount = 0; -static EventDispatcher * eventDispatcher = NULL; - static void hatValueToXY(CFIndex value, CFIndex range, int * outX, int * outY) { if (value == range) { *outX = *outY = 0; @@ -93,10 +94,10 @@ static void hatValueToXY(CFIndex value, CFIndex range, int * outX, int * outY) { } } -static void queueInputEvent(EventDispatcher * dispatcher, const char * eventType, void * eventData) { +static void queueInputEvent(unsigned int deviceID, enum Gamepad_eventType eventType, void * eventData) { struct Gamepad_queuedEvent queuedEvent; - queuedEvent.dispatcher = dispatcher; + queuedEvent.deviceID = deviceID; queuedEvent.eventType = eventType; queuedEvent.eventData = eventData; @@ -107,7 +108,7 @@ static void queueInputEvent(EventDispatcher * dispatcher, const char * eventType inputEventQueue[inputEventCount++] = queuedEvent; } -static void queueAxisEvent(struct Gamepad_device * device, double timestamp, unsigned int axisID, float value) { +static void queueAxisEvent(struct Gamepad_device * device, double timestamp, unsigned int axisID, float value, float lastValue) { struct Gamepad_axisEvent * axisEvent; axisEvent = malloc(sizeof(struct Gamepad_axisEvent)); @@ -115,8 +116,9 @@ static void queueAxisEvent(struct Gamepad_device * device, double timestamp, uns axisEvent->timestamp = timestamp; axisEvent->axisID = axisID; axisEvent->value = value; + axisEvent->lastValue = lastValue; - queueInputEvent(device->eventDispatcher, GAMEPAD_EVENT_AXIS_MOVED, axisEvent); + queueInputEvent(device->deviceID, GAMEPAD_EVENT_AXIS_MOVED, axisEvent); } static void queueButtonEvent(struct Gamepad_device * device, double timestamp, unsigned int buttonID, bool down) { @@ -128,7 +130,7 @@ static void queueButtonEvent(struct Gamepad_device * device, double timestamp, u buttonEvent->buttonID = buttonID; buttonEvent->down = down; - queueInputEvent(device->eventDispatcher, down ? GAMEPAD_EVENT_BUTTON_DOWN : GAMEPAD_EVENT_BUTTON_UP, buttonEvent); + queueInputEvent(device->deviceID, down ? GAMEPAD_EVENT_BUTTON_DOWN : GAMEPAD_EVENT_BUTTON_UP, buttonEvent); } static void onDeviceValueChanged(void * context, IOReturn result, void * sender, IOHIDValueRef value) { @@ -163,7 +165,6 @@ static void onDeviceValueChanged(void * context, IOReturn result, void * sender, int x, y; // Fix for Saitek X52 - hidDeviceRecord->axisElements[axisIndex].hasNullState = false; if (!hidDeviceRecord->axisElements[axisIndex].hasNullState) { if (integerValue < hidDeviceRecord->axisElements[axisIndex].logicalMin) { integerValue = hidDeviceRecord->axisElements[axisIndex].logicalMax - hidDeviceRecord->axisElements[axisIndex].logicalMin + 1; @@ -178,7 +179,8 @@ static void onDeviceValueChanged(void * context, IOReturn result, void * sender, queueAxisEvent(deviceRecord, IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001, axisIndex, - x); + x, + deviceRecord->axisStates[axisIndex]); deviceRecord->axisStates[axisIndex] = x; } @@ -187,7 +189,8 @@ static void onDeviceValueChanged(void * context, IOReturn result, void * sender, queueAxisEvent(deviceRecord, IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001, axisIndex + 1, - y); + y, + deviceRecord->axisStates[axisIndex + 1]); deviceRecord->axisStates[axisIndex + 1] = y; } @@ -206,7 +209,8 @@ static void onDeviceValueChanged(void * context, IOReturn result, void * sender, queueAxisEvent(deviceRecord, IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001, axisIndex, - floatValue); + floatValue, + deviceRecord->axisStates[axisIndex]); deviceRecord->axisStates[axisIndex] = floatValue; } @@ -270,7 +274,6 @@ static void onDeviceMatched(void * context, IOReturn result, void * sender, IOHI deviceRecord->productID = IOHIDDeviceGetProductID(device); deviceRecord->numAxes = 0; deviceRecord->numButtons = 0; - deviceRecord->eventDispatcher = EventDispatcher_create(deviceRecord); devices = realloc(devices, sizeof(struct Gamepad_device *) * (numDevices + 1)); devices[numDevices++] = deviceRecord; @@ -286,11 +289,12 @@ static void onDeviceMatched(void * context, IOReturn result, void * sender, IOHI strcpy(description, "[Unknown]"); } else { - const char * cStringPtr; + CFIndex length; - cStringPtr = CFStringGetCStringPtr(cfProductName, CFStringGetSmallestEncoding(cfProductName)); - description = malloc(strlen(cStringPtr + 1)); - strcpy(description, cStringPtr); + CFStringGetBytes(cfProductName, CFRangeMake(0, CFStringGetLength(cfProductName)), kCFStringEncodingUTF8, '?', false, NULL, 100, &length); + description = malloc(length + 1); + CFStringGetBytes(cfProductName, CFRangeMake(0, CFStringGetLength(cfProductName)), kCFStringEncodingUTF8, '?', false, (UInt8 *) description, length + 1, NULL); + description[length] = '\x00'; } deviceRecord->description = description; @@ -331,7 +335,7 @@ static void onDeviceMatched(void * context, IOReturn result, void * sender, IOHI IOHIDDeviceRegisterInputValueCallback(device, onDeviceValueChanged, deviceRecord); - queuedEvent.dispatcher = Gamepad_eventDispatcher(); + queuedEvent.deviceID = deviceRecord->deviceID; queuedEvent.eventType = GAMEPAD_EVENT_DEVICE_ATTACHED; queuedEvent.eventData = deviceRecord; @@ -348,7 +352,7 @@ static void disposeDevice(struct Gamepad_device * deviceRecord) { IOHIDDeviceRegisterInputValueCallback(((struct Gamepad_devicePrivate *) deviceRecord->privateData)->deviceRef, NULL, NULL); for (inputEventIndex = 0; inputEventIndex < inputEventCount; inputEventIndex++) { - if (inputEventQueue[inputEventIndex].dispatcher == deviceRecord->eventDispatcher) { + if (inputEventQueue[inputEventIndex].deviceID == deviceRecord->deviceID) { unsigned int inputEventIndex2; free(inputEventQueue[inputEventIndex].eventData); @@ -361,7 +365,7 @@ static void disposeDevice(struct Gamepad_device * deviceRecord) { } for (deviceEventIndex = 0; deviceEventIndex < deviceEventCount; deviceEventIndex++) { - if (deviceEventQueue[deviceEventIndex].dispatcher == deviceRecord->eventDispatcher) { + if (deviceEventQueue[deviceEventIndex].deviceID == deviceRecord->deviceID) { unsigned int deviceEventIndex2; deviceEventCount--; @@ -372,8 +376,6 @@ static void disposeDevice(struct Gamepad_device * deviceRecord) { } } - deviceRecord->eventDispatcher->dispose(deviceRecord->eventDispatcher); - free(((struct Gamepad_devicePrivate *) deviceRecord->privateData)->axisElements); free(((struct Gamepad_devicePrivate *) deviceRecord->privateData)->buttonElements); free(deviceRecord->privateData); @@ -381,7 +383,6 @@ static void disposeDevice(struct Gamepad_device * deviceRecord) { free((void *) deviceRecord->description); free(deviceRecord->axisStates); free(deviceRecord->buttonStates); - free(deviceRecord->eventDispatcher); free(deviceRecord); } @@ -391,7 +392,9 @@ static void onDeviceRemoved(void * context, IOReturn result, void * sender, IOHI for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) { if (((struct Gamepad_devicePrivate *) devices[deviceIndex]->privateData)->deviceRef == device) { - Gamepad_eventDispatcher()->dispatchEvent(Gamepad_eventDispatcher(), GAMEPAD_EVENT_DEVICE_REMOVED, devices[deviceIndex]); + if (Gamepad_deviceRemoveCallback != NULL) { + Gamepad_deviceRemoveCallback(devices[deviceIndex], Gamepad_deviceRemoveContext); + } disposeDevice(devices[deviceIndex]); numDevices--; @@ -412,8 +415,6 @@ void Gamepad_init() { CFArrayRef array; hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); - IOHIDManagerOpen(hidManager, kIOHIDOptionsTypeNone); - IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); keys[0] = CFSTR(kIOHIDDeviceUsagePageKey); keys[1] = CFSTR(kIOHIDDeviceUsageKey); @@ -451,6 +452,13 @@ void Gamepad_init() { IOHIDManagerRegisterDeviceMatchingCallback(hidManager, onDeviceMatched, NULL); IOHIDManagerRegisterDeviceRemovalCallback(hidManager, onDeviceRemoved, NULL); + + IOHIDManagerOpen(hidManager, kIOHIDOptionsTypeNone); + + // Force gamepads to be recognized immediately. The normal run loop mode takes a few frames, + // but we can run one iteration with a custom mode to do it without a delay. + IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetCurrent(), GAMEPAD_RUN_LOOP_MODE); + CFRunLoopRunInMode(GAMEPAD_RUN_LOOP_MODE, 0, true); } } @@ -459,9 +467,6 @@ void Gamepad_shutdown() { unsigned int deviceIndex; IOHIDManagerUnscheduleFromRunLoop(hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); - IOHIDManagerClose(hidManager, 0); - CFRelease(hidManager); - hidManager = NULL; for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) { disposeDevice(devices[deviceIndex]); @@ -470,21 +475,12 @@ void Gamepad_shutdown() { devices = NULL; numDevices = 0; - if (eventDispatcher != NULL) { - eventDispatcher->dispose(eventDispatcher); - free(eventDispatcher); - eventDispatcher = NULL; - } + IOHIDManagerClose(hidManager, 0); + CFRelease(hidManager); + hidManager = NULL; } } -EventDispatcher * Gamepad_eventDispatcher() { - if (eventDispatcher == NULL) { - eventDispatcher = EventDispatcher_create(NULL); - } - return eventDispatcher; -} - unsigned int Gamepad_numDevices() { return numDevices; } @@ -496,6 +492,43 @@ struct Gamepad_device * Gamepad_deviceAtIndex(unsigned int deviceIndex) { return devices[deviceIndex]; } +static void processQueuedEvent(struct Gamepad_queuedEvent event) { + switch (event.eventType) { + case GAMEPAD_EVENT_DEVICE_ATTACHED: + if (Gamepad_deviceAttachCallback != NULL) { + Gamepad_deviceAttachCallback(event.eventData, Gamepad_deviceAttachContext); + } + break; + + case GAMEPAD_EVENT_DEVICE_REMOVED: + if (Gamepad_deviceRemoveCallback != NULL) { + Gamepad_deviceRemoveCallback(event.eventData, Gamepad_deviceRemoveContext); + } + break; + + case GAMEPAD_EVENT_BUTTON_DOWN: + if (Gamepad_buttonDownCallback != NULL) { + struct Gamepad_buttonEvent * buttonEvent = event.eventData; + Gamepad_buttonDownCallback(buttonEvent->device, buttonEvent->buttonID, buttonEvent->timestamp, Gamepad_buttonDownContext); + } + break; + + case GAMEPAD_EVENT_BUTTON_UP: + if (Gamepad_buttonUpCallback != NULL) { + struct Gamepad_buttonEvent * buttonEvent = event.eventData; + Gamepad_buttonUpCallback(buttonEvent->device, buttonEvent->buttonID, buttonEvent->timestamp, Gamepad_buttonUpContext); + } + break; + + case GAMEPAD_EVENT_AXIS_MOVED: + if (Gamepad_axisMoveCallback != NULL) { + struct Gamepad_axisEvent * axisEvent = event.eventData; + Gamepad_axisMoveCallback(axisEvent->device, axisEvent->axisID, axisEvent->value, axisEvent->lastValue, axisEvent->timestamp, Gamepad_axisMoveContext); + } + break; + } +} + void Gamepad_detectDevices() { unsigned int eventIndex; @@ -503,23 +536,27 @@ void Gamepad_detectDevices() { return; } + CFRunLoopRunInMode(GAMEPAD_RUN_LOOP_MODE, 0, true); for (eventIndex = 0; eventIndex < deviceEventCount; eventIndex++) { - deviceEventQueue[eventIndex].dispatcher->dispatchEvent(deviceEventQueue[eventIndex].dispatcher, deviceEventQueue[eventIndex].eventType, deviceEventQueue[eventIndex].eventData); + processQueuedEvent(deviceEventQueue[eventIndex]); } deviceEventCount = 0; } void Gamepad_processEvents() { unsigned int eventIndex; + static bool inProcessEvents; - if (hidManager == NULL) { + if (hidManager == NULL || inProcessEvents) { return; } + inProcessEvents = true; + CFRunLoopRunInMode(GAMEPAD_RUN_LOOP_MODE, 0, true); for (eventIndex = 0; eventIndex < inputEventCount; eventIndex++) { - inputEventQueue[eventIndex].dispatcher->dispatchEvent(inputEventQueue[eventIndex].dispatcher, inputEventQueue[eventIndex].eventType, inputEventQueue[eventIndex].eventData); + processQueuedEvent(inputEventQueue[eventIndex]); free(inputEventQueue[eventIndex].eventData); } inputEventCount = 0; + inProcessEvents = false; } - diff --git a/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_private.c b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_private.c new file mode 100644 index 000000000..55ee159a6 --- /dev/null +++ b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_private.c @@ -0,0 +1,61 @@ +/* + Copyright (c) 2014 Alex Diener + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Alex Diener alex@ludobloom.com +*/ + +#include "gamepad/Gamepad.h" +#include "gamepad/Gamepad_private.h" +#include + +void (* Gamepad_deviceAttachCallback)(struct Gamepad_device * device, void * context) = NULL; +void (* Gamepad_deviceRemoveCallback)(struct Gamepad_device * device, void * context) = NULL; +void (* Gamepad_buttonDownCallback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context) = NULL; +void (* Gamepad_buttonUpCallback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context) = NULL; +void (* Gamepad_axisMoveCallback)(struct Gamepad_device * device, unsigned int buttonID, float value, float lastValue, double timestamp, void * context) = NULL; +void * Gamepad_deviceAttachContext = NULL; +void * Gamepad_deviceRemoveContext = NULL; +void * Gamepad_buttonDownContext = NULL; +void * Gamepad_buttonUpContext = NULL; +void * Gamepad_axisMoveContext = NULL; + +void Gamepad_deviceAttachFunc(void (* callback)(struct Gamepad_device * device, void * context), void * context) { + Gamepad_deviceAttachCallback = callback; + Gamepad_deviceAttachContext = context; +} + +void Gamepad_deviceRemoveFunc(void (* callback)(struct Gamepad_device * device, void * context), void * context) { + Gamepad_deviceRemoveCallback = callback; + Gamepad_deviceRemoveContext = context; +} + +void Gamepad_buttonDownFunc(void (* callback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context), void * context) { + Gamepad_buttonDownCallback = callback; + Gamepad_buttonDownContext = context; +} + +void Gamepad_buttonUpFunc(void (* callback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context), void * context) { + Gamepad_buttonUpCallback = callback; + Gamepad_buttonUpContext = context; +} + +void Gamepad_axisMoveFunc(void (* callback)(struct Gamepad_device * device, unsigned int axisID, float value, float lastValue, double timestamp, void * context), void * context) { + Gamepad_axisMoveCallback = callback; + Gamepad_axisMoveContext = context; +} diff --git a/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_private.h b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_private.h new file mode 100644 index 000000000..9d10c2bac --- /dev/null +++ b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_private.h @@ -0,0 +1,76 @@ +/* + Copyright (c) 2014 Alex Diener + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Alex Diener alex@ludobloom.com +*/ + +#ifndef __GAMEPAD_PRIVATE_H__ +#define __GAMEPAD_PRIVATE_H__ + +enum Gamepad_eventType { + GAMEPAD_EVENT_DEVICE_ATTACHED, + GAMEPAD_EVENT_DEVICE_REMOVED, + GAMEPAD_EVENT_BUTTON_DOWN, + GAMEPAD_EVENT_BUTTON_UP, + GAMEPAD_EVENT_AXIS_MOVED +}; + +struct Gamepad_buttonEvent { + // Device that generated the event + struct Gamepad_device * device; + + // Relative time of the event, in seconds + double timestamp; + + // Button being pushed or released + unsigned int buttonID; + + // True if button is down + bool down; +}; + +struct Gamepad_axisEvent { + // Device that generated the event + struct Gamepad_device * device; + + // Relative time of the event, in seconds + double timestamp; + + // Axis being moved + unsigned int axisID; + + // Axis position value, in the range [-1..1] + float value; + + // Previous axis position value, in the range [-1..1] + float lastValue; +}; + +extern void (* Gamepad_deviceAttachCallback)(struct Gamepad_device * device, void * context); +extern void (* Gamepad_deviceRemoveCallback)(struct Gamepad_device * device, void * context); +extern void (* Gamepad_buttonDownCallback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context); +extern void (* Gamepad_buttonUpCallback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context); +extern void (* Gamepad_axisMoveCallback)(struct Gamepad_device * device, unsigned int axisID, float value, float lastValue, double timestamp, void * context); +extern void * Gamepad_deviceAttachContext; +extern void * Gamepad_deviceRemoveContext; +extern void * Gamepad_buttonDownContext; +extern void * Gamepad_buttonUpContext; +extern void * Gamepad_axisMoveContext; + +#endif diff --git a/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_windows_dinput.c b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_windows_dinput.c new file mode 100644 index 000000000..06e8b844f --- /dev/null +++ b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_windows_dinput.c @@ -0,0 +1,1052 @@ +/* + Copyright (c) 2014 Alex Diener + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Alex Diener alex@ludobloom.com +*/ + +// Special thanks to SDL2 for portions of DirectInput and XInput code used in this implementation + +#define _WIN32_WINNT 0x0501 +#define INITGUID +#define DIRECTINPUT_VERSION 0x0800 +#ifdef _MSC_VER +#define strdup _strdup +#undef UNICODE +#else +#define __in +#define __out +#define __reserved +#endif + +#include "gamepad/Gamepad.h" +#include "gamepad/Gamepad_private.h" +#include +#include + +#include +#include +#include +#include + +// Copy from MinGW-w64 to MinGW, along with wbemcli.h, wbemprov.h, wbemdisp.h, and wbemtran.h +#ifndef __MINGW_EXTENSION +#define __MINGW_EXTENSION +#endif +#define COBJMACROS 1 +#include +#include +// Super helpful info: http://www.wreckedgames.com/forum/index.php?topic=2584.0 + +#define INPUT_QUEUE_SIZE 32 +#define XINPUT_GAMEPAD_GUIDE 0x400 + +typedef struct { + WORD wButtons; + BYTE bLeftTrigger; + BYTE bRightTrigger; + SHORT sThumbLX; + SHORT sThumbLY; + SHORT sThumbRX; + SHORT sThumbRY; + DWORD dwPaddingReserved; +} XINPUT_GAMEPAD_EX; + +typedef struct { + DWORD dwPacketNumber; + XINPUT_GAMEPAD_EX Gamepad; +} XINPUT_STATE_EX; + +struct diAxisInfo { + DWORD offset; + bool isPOV; + bool isPOVSecondAxis; +}; + +struct Gamepad_devicePrivate { + bool isXInput; + + // DInput only + GUID guidInstance; + IDirectInputDevice8 * deviceInterface; + bool buffered; + unsigned int sliderCount; + unsigned int povCount; + struct diAxisInfo * axisInfo; + DWORD * buttonOffsets; + + // XInput only + unsigned int playerIndex; +}; + +static struct Gamepad_device ** devices = NULL; +static unsigned int numDevices = 0; +static unsigned int nextDeviceID = 0; +static struct Gamepad_device * registeredXInputDevices[4]; +static const char * xInputDeviceNames[4] = { + "XInput Controller 1", + "XInput Controller 2", + "XInput Controller 3", + "XInput Controller 4" +}; +static DWORD (WINAPI * XInputGetStateEx_proc)(DWORD dwUserIndex, XINPUT_STATE_EX * pState); +static DWORD (WINAPI * XInputGetState_proc)(DWORD dwUserIndex, XINPUT_STATE * pState); +static DWORD (WINAPI * XInputGetCapabilities_proc)(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES * pCapabilities); + +static LPDIRECTINPUT directInputInterface; +static bool inited = false; +static bool xInputAvailable; + +void Gamepad_init() { + if (!inited) { + HRESULT result; + HMODULE module; + HRESULT (WINAPI * DirectInput8Create_proc)(HINSTANCE, DWORD, REFIID, LPVOID *, LPUNKNOWN); + + module = LoadLibrary("XInput1_4.dll"); + if (module == NULL) { + module = LoadLibrary("XInput1_3.dll"); + } + if (module == NULL) { + module = LoadLibrary("bin\\XInput1_3.dll"); + } + if (module == NULL) { + fprintf(stderr, "Gamepad_init couldn't load XInput1_4.dll or XInput1_3.dll; proceeding with DInput only\n"); + xInputAvailable = false; + } else { + xInputAvailable = true; + XInputGetStateEx_proc = (DWORD (WINAPI *)(DWORD, XINPUT_STATE_EX *)) GetProcAddress(module, (LPCSTR) 100); + XInputGetState_proc = (DWORD (WINAPI *)(DWORD, XINPUT_STATE *)) GetProcAddress(module, "XInputGetState"); + XInputGetCapabilities_proc = (DWORD (WINAPI *)(DWORD, DWORD, XINPUT_CAPABILITIES *)) GetProcAddress(module, "XInputGetCapabilities"); + } + + //result = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8, (void **) &directInputInterface, NULL); + // Calling DirectInput8Create directly crashes in 64-bit builds for some reason. Loading it with GetProcAddress works though! + + module = LoadLibrary("DINPUT8.dll"); + if (module == NULL) { + fprintf(stderr, "Gamepad_init fatal error: Couldn't load DINPUT8.dll\n"); + abort(); + } + DirectInput8Create_proc = (HRESULT (WINAPI *)(HINSTANCE, DWORD, REFIID, LPVOID *, LPUNKNOWN)) GetProcAddress(module, "DirectInput8Create"); + result = DirectInput8Create_proc(GetModuleHandle(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8, (void **) &directInputInterface, NULL); + + if (result != DI_OK) { + fprintf(stderr, "Warning: DirectInput8Create returned 0x%X\n", (unsigned int) result); + } + + inited = true; + Gamepad_detectDevices(); + } +} + +static void disposeDevice(struct Gamepad_device * deviceRecord) { + struct Gamepad_devicePrivate * deviceRecordPrivate = deviceRecord->privateData; + + if (!deviceRecordPrivate->isXInput) { + IDirectInputDevice8_Release(deviceRecordPrivate->deviceInterface); + free(deviceRecordPrivate->axisInfo); + free(deviceRecordPrivate->buttonOffsets); + free((void *) deviceRecord->description); + } + free(deviceRecordPrivate); + + free(deviceRecord->axisStates); + free(deviceRecord->buttonStates); + + free(deviceRecord); +} + +void Gamepad_shutdown() { + unsigned int deviceIndex; + + if (inited) { + for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) { + disposeDevice(devices[deviceIndex]); + } + free(devices); + devices = NULL; + numDevices = 0; + inited = false; + } +} + +unsigned int Gamepad_numDevices() { + return numDevices; +} + +struct Gamepad_device * Gamepad_deviceAtIndex(unsigned int deviceIndex) { + if (deviceIndex >= numDevices) { + return NULL; + } + return devices[deviceIndex]; +} + +static double currentTime() { + static LARGE_INTEGER frequency; + LARGE_INTEGER currentTime; + + if (frequency.QuadPart == 0) { + QueryPerformanceFrequency(&frequency); + } + QueryPerformanceCounter(¤tTime); + + return (double) currentTime.QuadPart / frequency.QuadPart; +} + +#if 0 +// This code from http://msdn.microsoft.com/en-us/library/windows/desktop/ee417014(v=vs.85).aspx is really really really slow +static bool isXInputDevice(const GUID * pGuidProductFromDirectInput) { + IWbemLocator * pIWbemLocator = NULL; + IEnumWbemClassObject * pEnumDevices = NULL; + IWbemClassObject * pDevices[20] = {0}; + IWbemServices * pIWbemServices = NULL; + BSTR bstrNamespace = NULL; + BSTR bstrDeviceID = NULL; + BSTR bstrClassName = NULL; + DWORD uReturned = 0; + bool bIsXinputDevice = false; + UINT iDevice = 0; + VARIANT var; + HRESULT hr; + + hr = CoInitialize(NULL); + bool bCleanupCOM = SUCCEEDED(hr); + + hr = CoCreateInstance(&CLSID_WbemLocator, + NULL, + CLSCTX_INPROC_SERVER, + &IID_IWbemLocator, + (LPVOID *) &pIWbemLocator); + if (FAILED(hr) || pIWbemLocator == NULL) { + goto LCleanup; + } + + bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2"); if (bstrNamespace == NULL) {goto LCleanup;} + bstrClassName = SysAllocString(L"Win32_PNPEntity"); if (bstrClassName == NULL) {goto LCleanup;} + bstrDeviceID = SysAllocString(L"DeviceID"); if (bstrDeviceID == NULL) {goto LCleanup;} + + hr = IWbemLocator_ConnectServer(pIWbemLocator, bstrNamespace, NULL, NULL, 0L, + 0L, NULL, NULL, &pIWbemServices); + if (FAILED(hr) || pIWbemServices == NULL) { + goto LCleanup; + } + + CoSetProxyBlanket((IUnknown *) pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, + RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE); + + hr = IWbemServices_CreateInstanceEnum(pIWbemServices, bstrClassName, 0, NULL, &pEnumDevices); + if (FAILED(hr) || pEnumDevices == NULL) { + goto LCleanup; + } + + for (;;) { + hr = IEnumWbemClassObject_Next(pEnumDevices, 10000, 20, pDevices, &uReturned); + if (FAILED(hr)) { + goto LCleanup; + } + if (uReturned == 0) { + break; + } + for (iDevice = 0; iDevice < uReturned; iDevice++) { + hr = IWbemClassObject_Get(pDevices[iDevice], bstrDeviceID, 0L, &var, NULL, NULL); + if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != NULL) { + if (wcsstr(var.bstrVal, L"IG_")) { + DWORD dwPid = 0, dwVid = 0; + WCHAR * strVid = wcsstr(var.bstrVal, L"VID_"); + if (strVid && swscanf(strVid, L"VID_%4X", &dwVid) != 1) { + dwVid = 0; + } + WCHAR * strPid = wcsstr(var.bstrVal, L"PID_"); + if (strPid != NULL && swscanf(strPid, L"PID_%4X", &dwPid) != 1) { + dwPid = 0; + } + DWORD dwVidPid = MAKELONG(dwVid, dwPid); + if (dwVidPid == pGuidProductFromDirectInput->Data1) { + bIsXinputDevice = true; + goto LCleanup; + } + } + } + if (pDevices[iDevice] != NULL) { + IWbemClassObject_Release(pDevices[iDevice]); + pDevices[iDevice] = NULL; + } + } + } + +LCleanup: + if (bstrNamespace != NULL) { + SysFreeString(bstrNamespace); + } + if (bstrDeviceID != NULL) { + SysFreeString(bstrDeviceID); + } + if (bstrClassName != NULL) { + SysFreeString(bstrClassName); + } + for (iDevice = 0; iDevice < uReturned; iDevice++) { + if (pDevices[iDevice] != NULL) { + IWbemClassObject_Release(pDevices[iDevice]); + } + } + if (pEnumDevices != NULL) { + IEnumWbemClassObject_Release(pEnumDevices); + } + if (pIWbemLocator != NULL) { + IWbemLocator_Release(pIWbemLocator); + } + if (pIWbemServices != NULL) { + IWbemServices_Release(pIWbemServices); + } + + if (bCleanupCOM) { + CoUninitialize(); + } + + return bIsXinputDevice; +} +#else +// This code from SDL2 is much faster + +DEFINE_GUID(IID_ValveStreamingGamepad, MAKELONG(0x28DE, 0x11FF),0x0000,0x0000,0x00,0x00,0x50,0x49,0x44,0x56,0x49,0x44); +DEFINE_GUID(IID_X360WiredGamepad, MAKELONG(0x045E, 0x02A1),0x0000,0x0000,0x00,0x00,0x50,0x49,0x44,0x56,0x49,0x44); +DEFINE_GUID(IID_X360WirelessGamepad, MAKELONG(0x045E, 0x028E),0x0000,0x0000,0x00,0x00,0x50,0x49,0x44,0x56,0x49,0x44); + +static PRAWINPUTDEVICELIST rawDevList = NULL; +static UINT rawDevListCount = 0; + +static bool isXInputDevice(const GUID * pGuidProductFromDirectInput) { + static const GUID * s_XInputProductGUID[] = { + &IID_ValveStreamingGamepad, + &IID_X360WiredGamepad, // Microsoft's wired X360 controller for Windows + &IID_X360WirelessGamepad // Microsoft's wireless X360 controller for Windows + }; + + size_t iDevice; + UINT i; + + // Check for well known XInput device GUIDs + // This lets us skip RAWINPUT for popular devices. Also, we need to do this for the Valve Streaming Gamepad because it's virtualized and doesn't show up in the device list. + for (iDevice = 0; iDevice < sizeof(s_XInputProductGUID) / sizeof(s_XInputProductGUID[0]); ++iDevice) { + if (!memcmp(pGuidProductFromDirectInput, s_XInputProductGUID[iDevice], sizeof(GUID))) { + return true; + } + } + + // Go through RAWINPUT (WinXP and later) to find HID devices. + // Cache this if we end up using it. + if (rawDevList == NULL) { + if ((GetRawInputDeviceList(NULL, &rawDevListCount, sizeof(RAWINPUTDEVICELIST)) == (UINT) -1) || rawDevListCount == 0) { + return false; + } + + rawDevList = malloc(sizeof(RAWINPUTDEVICELIST) * rawDevListCount); + + if (GetRawInputDeviceList(rawDevList, &rawDevListCount, sizeof(RAWINPUTDEVICELIST)) == (UINT) -1) { + free(rawDevList); + rawDevList = NULL; + return false; + } + } + + for (i = 0; i < rawDevListCount; i++) { + RID_DEVICE_INFO rdi; + char devName[128]; + UINT rdiSize = sizeof(rdi); + UINT nameSize = sizeof(devName); + + rdi.cbSize = sizeof(rdi); + if (rawDevList[i].dwType == RIM_TYPEHID && + GetRawInputDeviceInfoA(rawDevList[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) != (UINT) -1 && + MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == (LONG) pGuidProductFromDirectInput->Data1 && + GetRawInputDeviceInfoA(rawDevList[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) != (UINT) -1 && + strstr(devName, "IG_") != NULL) { + return true; + } + } + + return false; +} +#endif + +static BOOL CALLBACK countAxesCallback(LPCDIDEVICEOBJECTINSTANCE instance, LPVOID context) { + struct Gamepad_device * deviceRecord = context; + + deviceRecord->numAxes++; + if (instance->dwType & DIDFT_POV) { + deviceRecord->numAxes++; + } + return DIENUM_CONTINUE; +} + +static BOOL CALLBACK countButtonsCallback(LPCDIDEVICEOBJECTINSTANCE instance, LPVOID context) { + struct Gamepad_device * deviceRecord = context; + + deviceRecord->numButtons++; + return DIENUM_CONTINUE; +} + +#define AXIS_MIN -32768 +#define AXIS_MAX 32767 + +static BOOL CALLBACK enumAxesCallback(LPCDIDEVICEOBJECTINSTANCE instance, LPVOID context) { + struct Gamepad_device * deviceRecord = context; + struct Gamepad_devicePrivate * deviceRecordPrivate = deviceRecord->privateData; + DWORD offset; + + deviceRecord->numAxes++; + if (instance->dwType & DIDFT_POV) { + offset = DIJOFS_POV(deviceRecordPrivate->povCount); + deviceRecordPrivate->axisInfo[deviceRecord->numAxes - 1].offset = offset; + deviceRecordPrivate->axisInfo[deviceRecord->numAxes - 1].isPOV = true; + deviceRecord->numAxes++; + deviceRecordPrivate->axisInfo[deviceRecord->numAxes - 1].offset = offset; + deviceRecordPrivate->axisInfo[deviceRecord->numAxes - 1].isPOV = true; + deviceRecordPrivate->povCount++; + + } else { + DIPROPRANGE range; + DIPROPDWORD deadZone; + HRESULT result; + + if (!memcmp(&instance->guidType, &GUID_XAxis, sizeof(instance->guidType))) { + offset = DIJOFS_X; + } else if (!memcmp(&instance->guidType, &GUID_YAxis, sizeof(instance->guidType))) { + offset = DIJOFS_Y; + } else if (!memcmp(&instance->guidType, &GUID_ZAxis, sizeof(instance->guidType))) { + offset = DIJOFS_Z; + } else if (!memcmp(&instance->guidType, &GUID_RxAxis, sizeof(instance->guidType))) { + offset = DIJOFS_RX; + } else if (!memcmp(&instance->guidType, &GUID_RyAxis, sizeof(instance->guidType))) { + offset = DIJOFS_RY; + } else if (!memcmp(&instance->guidType, &GUID_RzAxis, sizeof(instance->guidType))) { + offset = DIJOFS_RZ; + } else if (!memcmp(&instance->guidType, &GUID_Slider, sizeof(instance->guidType))) { + offset = DIJOFS_SLIDER(deviceRecordPrivate->sliderCount++); + } else { + offset = -1; + } + deviceRecordPrivate->axisInfo[deviceRecord->numAxes - 1].offset = offset; + deviceRecordPrivate->axisInfo[deviceRecord->numAxes - 1].isPOV = false; + + range.diph.dwSize = sizeof(range); + range.diph.dwHeaderSize = sizeof(range.diph); + range.diph.dwObj = instance->dwType; + range.diph.dwHow = DIPH_BYID; + range.lMin = AXIS_MIN; + range.lMax = AXIS_MAX; + + result = IDirectInputDevice8_SetProperty(deviceRecordPrivate->deviceInterface, DIPROP_RANGE, &range.diph); + if (result != DI_OK) { + fprintf(stderr, "Warning: IDIrectInputDevice8_SetProperty returned 0x%X\n", (unsigned int) result); + } + + deadZone.diph.dwSize = sizeof(deadZone); + deadZone.diph.dwHeaderSize = sizeof(deadZone.diph); + deadZone.diph.dwObj = instance->dwType; + deadZone.diph.dwHow = DIPH_BYID; + deadZone.dwData = 0; + result = IDirectInputDevice8_SetProperty(deviceRecordPrivate->deviceInterface, DIPROP_DEADZONE, &deadZone.diph); + if (result != DI_OK) { + fprintf(stderr, "Warning: IDIrectInputDevice8_SetProperty returned 0x%X\n", (unsigned int) result); + } + } + return DIENUM_CONTINUE; +} + +static BOOL CALLBACK enumButtonsCallback(LPCDIDEVICEOBJECTINSTANCE instance, LPVOID context) { + struct Gamepad_device * deviceRecord = context; + struct Gamepad_devicePrivate * deviceRecordPrivate = deviceRecord->privateData; + + deviceRecordPrivate->buttonOffsets[deviceRecord->numButtons] = DIJOFS_BUTTON(deviceRecord->numButtons); + deviceRecord->numButtons++; + return DIENUM_CONTINUE; +} + +#ifdef _MSC_VER +#ifndef DIDFT_OPTIONAL +#define DIDFT_OPTIONAL 0x80000000 +#endif + +/* Taken from Wine - Thanks! */ +DIOBJECTDATAFORMAT dfDIJoystick2[] = { + { &GUID_XAxis, DIJOFS_X, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_YAxis, DIJOFS_Y, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_ZAxis, DIJOFS_Z, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RxAxis, DIJOFS_RX, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RyAxis, DIJOFS_RY, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RzAxis, DIJOFS_RZ, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, DIJOFS_SLIDER(0), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, DIJOFS_SLIDER(1), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_POV, DIJOFS_POV(0), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 }, + { &GUID_POV, DIJOFS_POV(1), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 }, + { &GUID_POV, DIJOFS_POV(2), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 }, + { &GUID_POV, DIJOFS_POV(3), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(0), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(1), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(2), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(3), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(4), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(5), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(6), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(7), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(8), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(9), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(10), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(11), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(12), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(13), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(14), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(15), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(16), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(17), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(18), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(19), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(20), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(21), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(22), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(23), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(24), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(25), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(26), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(27), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(28), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(29), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(30), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(31), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(32), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(33), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(34), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(35), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(36), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(37), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(38), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(39), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(40), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(41), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(42), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(43), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(44), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(45), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(46), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(47), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(48), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(49), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(50), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(51), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(52), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(53), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(54), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(55), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(56), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(57), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(58), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(59), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(60), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(61), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(62), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(63), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(64), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(65), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(66), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(67), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(68), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(69), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(70), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(71), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(72), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(73), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(74), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(75), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(76), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(77), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(78), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(79), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(80), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(81), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(82), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(83), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(84), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(85), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(86), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(87), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(88), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(89), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(90), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(91), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(92), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(93), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(94), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(95), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(96), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(97), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(98), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(99), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(100), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(101), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(102), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(103), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(104), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(105), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(106), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(107), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(108), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(109), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(110), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(111), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(112), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(113), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(114), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(115), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(116), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(117), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(118), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(119), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(120), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(121), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(122), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(123), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(124), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(125), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(126), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(127), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lVX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lVY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lVZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lVRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lVRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lVRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglVSlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglVSlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lAX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lAY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lAZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lARx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lARy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lARz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglASlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglASlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lFX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lFY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lFZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lFRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lFRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lFRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglFSlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglFSlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, +}; + +const DIDATAFORMAT c_dfDIJoystick2 = { + sizeof(DIDATAFORMAT), + sizeof(DIOBJECTDATAFORMAT), + DIDF_ABSAXIS, + sizeof(DIJOYSTATE2), + sizeof(dfDIJoystick2) / sizeof(dfDIJoystick2[0]), + dfDIJoystick2 +}; +#endif + +static BOOL CALLBACK enumDevicesCallback(const DIDEVICEINSTANCE * instance, LPVOID context) { + struct Gamepad_device * deviceRecord; + struct Gamepad_devicePrivate * deviceRecordPrivate; + unsigned int deviceIndex; + IDirectInputDevice * diDevice; + IDirectInputDevice8 * di8Device; + HRESULT result; + DIPROPDWORD bufferSizeProp; + bool buffered = true; + + if (xInputAvailable && isXInputDevice(&instance->guidProduct)) { + return DIENUM_CONTINUE; + } + + for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) { + if (!memcmp(&((struct Gamepad_devicePrivate *) devices[deviceIndex]->privateData)->guidInstance, &instance->guidInstance, sizeof(GUID))) { + return DIENUM_CONTINUE; + } + } + + result = IDirectInput8_CreateDevice(directInputInterface, &instance->guidInstance, &diDevice, NULL); + if (result != DI_OK) { + fprintf(stderr, "Warning: IDirectInput8_CreateDevice returned 0x%X\n", (unsigned int) result); + } + result = IDirectInputDevice8_QueryInterface(diDevice, &IID_IDirectInputDevice8, (LPVOID *) &di8Device); + if (result != DI_OK) { + fprintf(stderr, "Warning: IDirectInputDevice8_QueryInterface returned 0x%X\n", (unsigned int) result); + } + IDirectInputDevice8_Release(diDevice); + + result = IDirectInputDevice8_SetCooperativeLevel(di8Device, GetActiveWindow(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); + if (result != DI_OK) { + fprintf(stderr, "Warning: IDirectInputDevice8_SetCooperativeLevel returned 0x%X\n", (unsigned int) result); + } + + result = IDirectInputDevice8_SetDataFormat(di8Device, &c_dfDIJoystick2); + if (result != DI_OK) { + fprintf(stderr, "Warning: IDirectInputDevice8_SetDataFormat returned 0x%X\n", (unsigned int) result); + } + + bufferSizeProp.diph.dwSize = sizeof(DIPROPDWORD); + bufferSizeProp.diph.dwHeaderSize = sizeof(DIPROPHEADER); + bufferSizeProp.diph.dwObj = 0; + bufferSizeProp.diph.dwHow = DIPH_DEVICE; + bufferSizeProp.dwData = INPUT_QUEUE_SIZE; + result = IDirectInputDevice8_SetProperty(di8Device, DIPROP_BUFFERSIZE, &bufferSizeProp.diph); + if (result == DI_POLLEDDEVICE) { + buffered = false; + } else if (result != DI_OK) { + fprintf(stderr, "Warning: IDirectInputDevice8_SetProperty returned 0x%X\n", (unsigned int) result); + } + + deviceRecord = malloc(sizeof(struct Gamepad_device)); + deviceRecordPrivate = malloc(sizeof(struct Gamepad_devicePrivate)); + deviceRecordPrivate->guidInstance = instance->guidInstance; + deviceRecordPrivate->isXInput = false; + deviceRecordPrivate->deviceInterface = di8Device; + deviceRecordPrivate->buffered = buffered; + deviceRecordPrivate->sliderCount = 0; + deviceRecordPrivate->povCount = 0; + deviceRecord->privateData = deviceRecordPrivate; + deviceRecord->deviceID = nextDeviceID++; + deviceRecord->description = strdup(instance->tszProductName); + deviceRecord->vendorID = instance->guidProduct.Data1 & 0xFFFF; + deviceRecord->productID = instance->guidProduct.Data1 >> 16 & 0xFFFF; + deviceRecord->numAxes = 0; + IDirectInputDevice_EnumObjects(di8Device, countAxesCallback, deviceRecord, DIDFT_AXIS | DIDFT_POV); + deviceRecord->numButtons = 0; + IDirectInputDevice_EnumObjects(di8Device, countButtonsCallback, deviceRecord, DIDFT_BUTTON); + deviceRecord->axisStates = calloc(sizeof(float), deviceRecord->numAxes); + deviceRecord->buttonStates = calloc(sizeof(bool), deviceRecord->numButtons); + deviceRecordPrivate->axisInfo = calloc(sizeof(struct diAxisInfo), deviceRecord->numAxes); + deviceRecordPrivate->buttonOffsets = calloc(sizeof(DWORD), deviceRecord->numButtons); + deviceRecord->numAxes = 0; + IDirectInputDevice_EnumObjects(di8Device, enumAxesCallback, deviceRecord, DIDFT_AXIS | DIDFT_POV); + deviceRecord->numButtons = 0; + IDirectInputDevice_EnumObjects(di8Device, enumButtonsCallback, deviceRecord, DIDFT_BUTTON); + devices = realloc(devices, sizeof(struct Gamepad_device *) * (numDevices + 1)); + devices[numDevices++] = deviceRecord; + + return DIENUM_CONTINUE; +} + +static void removeDevice(unsigned int deviceIndex) { + if (Gamepad_deviceRemoveCallback != NULL) { + Gamepad_deviceRemoveCallback(devices[deviceIndex], Gamepad_deviceRemoveContext); + } + + disposeDevice(devices[deviceIndex]); + numDevices--; + for (; deviceIndex < numDevices; deviceIndex++) { + devices[deviceIndex] = devices[deviceIndex + 1]; + } +} + +void Gamepad_detectDevices() { + HRESULT result; + DWORD xResult; + XINPUT_CAPABILITIES capabilities; + unsigned int playerIndex, deviceIndex; + + if (!inited) { + return; + } + + result = IDirectInput_EnumDevices(directInputInterface, DI8DEVCLASS_GAMECTRL, enumDevicesCallback, NULL, DIEDFL_ALLDEVICES); + if (result != DI_OK) { + fprintf(stderr, "Warning: IDirectInput_EnumDevices returned 0x%X\n", (unsigned int) result); + } + + if (xInputAvailable) { + for (playerIndex = 0; playerIndex < 4; playerIndex++) { + xResult = XInputGetCapabilities_proc(playerIndex, 0, &capabilities); + if (xResult == ERROR_SUCCESS && registeredXInputDevices[playerIndex] == NULL) { + struct Gamepad_device * deviceRecord; + struct Gamepad_devicePrivate * deviceRecordPrivate; + + deviceRecord = malloc(sizeof(struct Gamepad_device)); + deviceRecordPrivate = malloc(sizeof(struct Gamepad_devicePrivate)); + deviceRecordPrivate->isXInput = true; + deviceRecordPrivate->playerIndex = playerIndex; + deviceRecord->privateData = deviceRecordPrivate; + deviceRecord->deviceID = nextDeviceID++; + deviceRecord->description = xInputDeviceNames[playerIndex]; + // HACK: XInput doesn't provide any way to get vendor and product ID, nor any way to map player index to + // DirectInput device enumeration. All we can do is assume all XInput devices are XBox 360 controllers. + deviceRecord->vendorID = 0x45E; + deviceRecord->productID = 0x28E; + deviceRecord->numAxes = 6; + deviceRecord->numButtons = 15; + deviceRecord->axisStates = calloc(sizeof(float), deviceRecord->numAxes); + deviceRecord->buttonStates = calloc(sizeof(bool), deviceRecord->numButtons); + devices = realloc(devices, sizeof(struct Gamepad_device *) * (numDevices + 1)); + devices[numDevices++] = deviceRecord; + registeredXInputDevices[playerIndex] = deviceRecord; + + } else if (xResult != ERROR_SUCCESS && registeredXInputDevices[playerIndex] != NULL) { + for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) { + if (devices[deviceIndex] == registeredXInputDevices[playerIndex]) { + removeDevice(deviceIndex); + break; + } + } + registeredXInputDevices[playerIndex] = NULL; + } + } + } +} + +static void updateButtonValue(struct Gamepad_device * device, unsigned int buttonIndex, bool down, double timestamp) { + if (down != device->buttonStates[buttonIndex]) { + device->buttonStates[buttonIndex] = down; + if (down && Gamepad_buttonDownCallback != NULL) { + Gamepad_buttonDownCallback(device, buttonIndex, timestamp, Gamepad_buttonDownContext); + } else if (!down && Gamepad_buttonUpCallback != NULL) { + Gamepad_buttonUpCallback(device, buttonIndex, timestamp, Gamepad_buttonUpContext); + } + } +} + +static void updateAxisValueFloat(struct Gamepad_device * device, unsigned int axisIndex, float value, double timestamp) { + float lastValue; + + lastValue = device->axisStates[axisIndex]; + device->axisStates[axisIndex] = value; + if (value != lastValue && Gamepad_axisMoveCallback != NULL) { + Gamepad_axisMoveCallback(device, axisIndex, value, lastValue, timestamp, Gamepad_axisMoveContext); + } +} + +static void updateAxisValue(struct Gamepad_device * device, unsigned int axisIndex, LONG ivalue, double timestamp) { + updateAxisValueFloat(device, axisIndex, (ivalue - AXIS_MIN) / (float) (AXIS_MAX - AXIS_MIN) * 2.0f - 1.0f, timestamp); +} + +#define POV_UP 0 +#define POV_RIGHT 9000 +#define POV_DOWN 18000 +#define POV_LEFT 27000 + +static void povToXY(DWORD pov, float * outX, float * outY) { + if (LOWORD(pov) == 0xFFFF) { + *outX = *outY = 0.0f; + + } else { + if (pov > POV_UP && pov < POV_DOWN) { + *outX = 1.0f; + + } else if (pov > POV_DOWN) { + *outX = -1.0f; + + } else { + *outX = 0.0f; + } + + if (pov > POV_LEFT || pov < POV_RIGHT) { + *outY = -1.0f; + + } else if (pov > POV_RIGHT && pov < POV_LEFT) { + *outY = 1.0f; + + } else { + *outY = 0.0f; + } + } +} + +static void updatePOVAxisValues(struct Gamepad_device * device, unsigned int axisIndex, DWORD ivalue, double timestamp) { + float x = 0.0f, y = 0.0f; + + povToXY(ivalue, &x, &y); + updateAxisValueFloat(device, axisIndex, x, timestamp); + updateAxisValueFloat(device, axisIndex + 1, y, timestamp); +} + +void Gamepad_processEvents() { + static bool inProcessEvents; + unsigned int deviceIndex, buttonIndex, axisIndex; + struct Gamepad_device * device; + struct Gamepad_devicePrivate * devicePrivate; + HRESULT result; + + if (!inited || inProcessEvents) { + return; + } + + inProcessEvents = true; + for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) { + device = devices[deviceIndex]; + devicePrivate = device->privateData; + + if (devicePrivate->isXInput) { + XINPUT_STATE state; + DWORD xResult; + + if (XInputGetStateEx_proc != NULL) { + XINPUT_STATE_EX stateEx; + + xResult = XInputGetStateEx_proc(devicePrivate->playerIndex, &stateEx); + state.Gamepad.wButtons = stateEx.Gamepad.wButtons; + state.Gamepad.sThumbLX = stateEx.Gamepad.sThumbLX; + state.Gamepad.sThumbLY = stateEx.Gamepad.sThumbLY; + state.Gamepad.sThumbRX = stateEx.Gamepad.sThumbRX; + state.Gamepad.sThumbRY = stateEx.Gamepad.sThumbRY; + state.Gamepad.bLeftTrigger = stateEx.Gamepad.bLeftTrigger; + state.Gamepad.bRightTrigger = stateEx.Gamepad.bRightTrigger; + } else { + xResult = XInputGetState_proc(devicePrivate->playerIndex, &state); + } + if (xResult == ERROR_SUCCESS) { + updateButtonValue(device, 0, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP), currentTime()); + updateButtonValue(device, 1, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN), currentTime()); + updateButtonValue(device, 2, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT), currentTime()); + updateButtonValue(device, 3, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT), currentTime()); + updateButtonValue(device, 4, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_START), currentTime()); + updateButtonValue(device, 5, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK), currentTime()); + updateButtonValue(device, 6, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB), currentTime()); + updateButtonValue(device, 7, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB), currentTime()); + updateButtonValue(device, 8, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER), currentTime()); + updateButtonValue(device, 9, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER), currentTime()); + updateButtonValue(device, 10, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_A), currentTime()); + updateButtonValue(device, 11, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_B), currentTime()); + updateButtonValue(device, 12, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_X), currentTime()); + updateButtonValue(device, 13, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_Y), currentTime()); + updateButtonValue(device, 14, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE), currentTime()); + updateAxisValue(device, 0, state.Gamepad.sThumbLX, currentTime()); + updateAxisValue(device, 1, state.Gamepad.sThumbLY, currentTime()); + updateAxisValue(device, 2, state.Gamepad.sThumbRX, currentTime()); + updateAxisValue(device, 3, state.Gamepad.sThumbRY, currentTime()); + updateAxisValueFloat(device, 4, state.Gamepad.bLeftTrigger / 127.5f - 1.0f, currentTime()); + updateAxisValueFloat(device, 5, state.Gamepad.bRightTrigger / 127.5f - 1.0f, currentTime()); + + } else { + registeredXInputDevices[devicePrivate->playerIndex] = NULL; + removeDevice(deviceIndex); + deviceIndex--; + continue; + } + + } else { + result = IDirectInputDevice8_Poll(devicePrivate->deviceInterface); + if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) { + IDirectInputDevice8_Acquire(devicePrivate->deviceInterface); + IDirectInputDevice8_Poll(devicePrivate->deviceInterface); + } + + if (devicePrivate->buffered) { + DWORD eventCount = INPUT_QUEUE_SIZE; + DIDEVICEOBJECTDATA events[INPUT_QUEUE_SIZE]; + unsigned int eventIndex; + + result = IDirectInputDevice8_GetDeviceData(devicePrivate->deviceInterface, sizeof(DIDEVICEOBJECTDATA), events, &eventCount, 0); + if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) { + IDirectInputDevice8_Acquire(devicePrivate->deviceInterface); + result = IDirectInputDevice8_GetDeviceData(devicePrivate->deviceInterface, sizeof(DIDEVICEOBJECTDATA), events, &eventCount, 0); + } + if (result != DI_OK) { + removeDevice(deviceIndex); + deviceIndex--; + continue; + } + + for (eventIndex = 0; eventIndex < eventCount; eventIndex++) { + for (buttonIndex = 0; buttonIndex < device->numButtons; buttonIndex++) { + if (events[eventIndex].dwOfs == devicePrivate->buttonOffsets[buttonIndex]) { + updateButtonValue(device, buttonIndex, !!events[eventIndex].dwData, events[eventIndex].dwTimeStamp / 1000.0); + } + } + for (axisIndex = 0; axisIndex < device->numAxes; axisIndex++) { + if (events[eventIndex].dwOfs == devicePrivate->axisInfo[axisIndex].offset) { + if (devicePrivate->axisInfo[axisIndex].isPOV) { + updatePOVAxisValues(device, axisIndex, events[eventIndex].dwData, events[eventIndex].dwTimeStamp / 1000.0); + axisIndex++; + } else { + updateAxisValue(device, axisIndex, events[eventIndex].dwData, events[eventIndex].dwTimeStamp / 1000.0); + } + } + } + } + + } else { + DIJOYSTATE2 state; + + result = IDirectInputDevice8_GetDeviceState(devicePrivate->deviceInterface, sizeof(DIJOYSTATE2), &state); + if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) { + IDirectInputDevice8_Acquire(devicePrivate->deviceInterface); + result = IDirectInputDevice8_GetDeviceState(devicePrivate->deviceInterface, sizeof(DIJOYSTATE2), &state); + } + + if (result != DI_OK) { + removeDevice(deviceIndex); + deviceIndex--; + continue; + } + + for (buttonIndex = 0; buttonIndex < device->numButtons; buttonIndex++) { + updateButtonValue(device, buttonIndex, !!state.rgbButtons[buttonIndex], currentTime()); + } + + for (axisIndex = 0; axisIndex < device->numAxes; axisIndex++) { + switch (devicePrivate->axisInfo[axisIndex].offset) { + case DIJOFS_X: + updateAxisValue(device, axisIndex, state.lX, currentTime()); + break; + case DIJOFS_Y: + updateAxisValue(device, axisIndex, state.lY, currentTime()); + break; + case DIJOFS_Z: + updateAxisValue(device, axisIndex, state.lZ, currentTime()); + break; + case DIJOFS_RX: + updateAxisValue(device, axisIndex, state.lRx, currentTime()); + break; + case DIJOFS_RY: + updateAxisValue(device, axisIndex, state.lRy, currentTime()); + break; + case DIJOFS_RZ: + updateAxisValue(device, axisIndex, state.lRz, currentTime()); + break; + case DIJOFS_SLIDER(0): + updateAxisValue(device, axisIndex, state.rglSlider[0], currentTime()); + break; + case DIJOFS_SLIDER(1): + updateAxisValue(device, axisIndex, state.rglSlider[1], currentTime()); + break; + case DIJOFS_POV(0): + updatePOVAxisValues(device, axisIndex, state.rgdwPOV[0], currentTime()); + axisIndex++; + break; + case DIJOFS_POV(1): + updatePOVAxisValues(device, axisIndex, state.rgdwPOV[1], currentTime()); + axisIndex++; + break; + case DIJOFS_POV(2): + updatePOVAxisValues(device, axisIndex, state.rgdwPOV[2], currentTime()); + axisIndex++; + break; + case DIJOFS_POV(3): + updatePOVAxisValues(device, axisIndex, state.rgdwPOV[3], currentTime()); + axisIndex++; + break; + } + } + } + } + } + inProcessEvents = false; +} + diff --git a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/source/gamepad/Gamepad_windows.c b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_windows_mm.c similarity index 81% rename from internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/source/gamepad/Gamepad_windows.c rename to internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_windows_mm.c index 7e9658668..fcf93b46c 100644 --- a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/source/gamepad/Gamepad_windows.c +++ b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/gamepad/Gamepad_windows_mm.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2010 Alex Diener + Copyright (c) 2014 Alex Diener This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -17,10 +17,11 @@ misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. - Alex Diener adiener@sacredsoftware.net + Alex Diener alex@ludobloom.com */ #include "gamepad/Gamepad.h" +#include "gamepad/Gamepad_private.h" #include #include #include @@ -44,7 +45,6 @@ static struct Gamepad_device ** devices = NULL; static unsigned int numDevices = 0; static unsigned int nextDeviceID = 0; -static EventDispatcher * eventDispatcher = NULL; static bool inited = false; void Gamepad_init() { @@ -55,15 +55,12 @@ void Gamepad_init() { } static void disposeDevice(struct Gamepad_device * deviceRecord) { - deviceRecord->eventDispatcher->dispose(deviceRecord->eventDispatcher); - free(((struct Gamepad_devicePrivate *) deviceRecord->privateData)->axisRanges); free(deviceRecord->privateData); free((void *) deviceRecord->description); free(deviceRecord->axisStates); free(deviceRecord->buttonStates); - free(deviceRecord->eventDispatcher); free(deviceRecord); } @@ -78,22 +75,10 @@ void Gamepad_shutdown() { free(devices); devices = NULL; numDevices = 0; - if (eventDispatcher != NULL) { - eventDispatcher->dispose(eventDispatcher); - free(eventDispatcher); - eventDispatcher = NULL; - } inited = false; } } -EventDispatcher * Gamepad_eventDispatcher() { - if (eventDispatcher == NULL) { - eventDispatcher = EventDispatcher_create(NULL); - } - return eventDispatcher; -} - unsigned int Gamepad_numDevices() { return numDevices; } @@ -108,7 +93,7 @@ struct Gamepad_device * Gamepad_deviceAtIndex(unsigned int deviceIndex) { #define REG_STRING_MAX 256 static char * getDeviceDescription(UINT joystickID, JOYCAPS caps) { - char * description; + char * description = NULL; char subkey[REG_STRING_MAX]; HKEY topKey, key; LONG result; @@ -199,7 +184,6 @@ void Gamepad_detectDevices() { deviceRecord->numButtons = caps.wNumButtons; deviceRecord->axisStates = calloc(sizeof(float), deviceRecord->numAxes); deviceRecord->buttonStates = calloc(sizeof(bool), deviceRecord->numButtons); - deviceRecord->eventDispatcher = EventDispatcher_create(deviceRecord); devices = realloc(devices, sizeof(struct Gamepad_device *) * (numDevices + 1)); devices[numDevices++] = deviceRecord; @@ -242,7 +226,9 @@ void Gamepad_detectDevices() { deviceRecord->privateData = deviceRecordPrivate; - Gamepad_eventDispatcher()->dispatchEvent(Gamepad_eventDispatcher(), GAMEPAD_EVENT_DEVICE_ATTACHED, deviceRecord); + if (Gamepad_deviceAttachCallback != NULL) { + Gamepad_deviceAttachCallback(deviceRecord, Gamepad_deviceAttachContext); + } } } } @@ -260,8 +246,8 @@ static double currentTime() { return (double) currentTime.QuadPart / frequency.QuadPart; } -static void handleAxisChange(struct Gamepad_device * device, int axisIndex, DWORD value) { - struct Gamepad_axisEvent axisEvent; +static void handleAxisChange(struct Gamepad_device * device, int axisIndex, DWORD ivalue) { + float value, lastValue; struct Gamepad_devicePrivate * devicePrivate; if (axisIndex < 0 || axisIndex >= (int) device->numAxes) { @@ -269,29 +255,29 @@ static void handleAxisChange(struct Gamepad_device * device, int axisIndex, DWOR } devicePrivate = device->privateData; + value = (ivalue - devicePrivate->axisRanges[axisIndex][0]) / (float) (devicePrivate->axisRanges[axisIndex][1] - devicePrivate->axisRanges[axisIndex][0]) * 2.0f - 1.0f; - axisEvent.device = device; - axisEvent.timestamp = currentTime(); - axisEvent.axisID = axisIndex; - axisEvent.value = (value - devicePrivate->axisRanges[axisIndex][0]) / (float) (devicePrivate->axisRanges[axisIndex][1] - devicePrivate->axisRanges[axisIndex][0]) * 2.0f - 1.0f; - - device->axisStates[axisIndex] = axisEvent.value; - device->eventDispatcher->dispatchEvent(device->eventDispatcher, GAMEPAD_EVENT_AXIS_MOVED, &axisEvent); + lastValue = device->axisStates[axisIndex]; + device->axisStates[axisIndex] = value; + if (Gamepad_axisMoveCallback != NULL) { + Gamepad_axisMoveCallback(device, axisIndex, value, lastValue, currentTime(), Gamepad_axisMoveContext); + } } static void handleButtonChange(struct Gamepad_device * device, DWORD lastValue, DWORD value) { - struct Gamepad_buttonEvent buttonEvent; + bool down; unsigned int buttonIndex; for (buttonIndex = 0; buttonIndex < device->numButtons; buttonIndex++) { if ((lastValue ^ value) & (1 << buttonIndex)) { - buttonEvent.device = device; - buttonEvent.timestamp = currentTime(); - buttonEvent.buttonID = buttonIndex; - buttonEvent.down = !!(value & (1 << buttonIndex)); + down = !!(value & (1 << buttonIndex)); - device->buttonStates[buttonIndex] = buttonEvent.down; - device->eventDispatcher->dispatchEvent(device->eventDispatcher, buttonEvent.down ? GAMEPAD_EVENT_BUTTON_DOWN : GAMEPAD_EVENT_BUTTON_UP, &buttonEvent); + device->buttonStates[buttonIndex] = down; + if (down && Gamepad_buttonDownCallback != NULL) { + Gamepad_buttonDownCallback(device, buttonIndex, currentTime(), Gamepad_buttonDownContext); + } else if (!down && Gamepad_buttonUpCallback != NULL) { + Gamepad_buttonUpCallback(device, buttonIndex, currentTime(), Gamepad_buttonUpContext); + } } } } @@ -326,7 +312,6 @@ static void povToXY(DWORD pov, int * outX, int * outY) { static void handlePOVChange(struct Gamepad_device * device, DWORD lastValue, DWORD value) { struct Gamepad_devicePrivate * devicePrivate; int lastX, lastY, newX, newY; - struct Gamepad_axisEvent axisEvent; devicePrivate = device->privateData; @@ -338,36 +323,32 @@ static void handlePOVChange(struct Gamepad_device * device, DWORD lastValue, DWO povToXY(value, &newX, &newY); if (newX != lastX) { - axisEvent.device = device; - axisEvent.timestamp = currentTime(); - axisEvent.axisID = devicePrivate->povXAxisIndex; - axisEvent.value = newX; - - device->axisStates[devicePrivate->povXAxisIndex] = axisEvent.value; - device->eventDispatcher->dispatchEvent(device->eventDispatcher, GAMEPAD_EVENT_AXIS_MOVED, &axisEvent); + device->axisStates[devicePrivate->povXAxisIndex] = newX; + if (Gamepad_axisMoveCallback != NULL) { + Gamepad_axisMoveCallback(device, devicePrivate->povXAxisIndex, newX, lastX, currentTime(), Gamepad_axisMoveContext); + } } if (newY != lastY) { - axisEvent.device = device; - axisEvent.timestamp = currentTime(); - axisEvent.axisID = devicePrivate->povYAxisIndex; - axisEvent.value = newY; - - device->axisStates[devicePrivate->povYAxisIndex] = axisEvent.value; - device->eventDispatcher->dispatchEvent(device->eventDispatcher, GAMEPAD_EVENT_AXIS_MOVED, &axisEvent); + device->axisStates[devicePrivate->povYAxisIndex] = newY; + if (Gamepad_axisMoveCallback != NULL) { + Gamepad_axisMoveCallback(device, devicePrivate->povYAxisIndex, newY, lastY, currentTime(), Gamepad_axisMoveContext); + } } } void Gamepad_processEvents() { unsigned int deviceIndex; + static bool inProcessEvents; JOYINFOEX info; MMRESULT result; struct Gamepad_device * device; struct Gamepad_devicePrivate * devicePrivate; - if (!inited) { + if (!inited || inProcessEvents) { return; } + inProcessEvents = true; for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) { device = devices[deviceIndex]; devicePrivate = device->privateData; @@ -376,7 +357,9 @@ void Gamepad_processEvents() { info.dwFlags = JOY_RETURNALL; result = joyGetPosEx(devicePrivate->joystickID, &info); if (result == JOYERR_UNPLUGGED) { - Gamepad_eventDispatcher()->dispatchEvent(Gamepad_eventDispatcher(), GAMEPAD_EVENT_DEVICE_REMOVED, device); + if (Gamepad_deviceRemoveCallback != NULL) { + Gamepad_deviceRemoveCallback(device, Gamepad_deviceRemoveContext); + } disposeDevice(device); numDevices--; @@ -412,5 +395,6 @@ void Gamepad_processEvents() { devicePrivate->lastState = info; } } + inProcessEvents = false; } diff --git a/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/testharness/TestHarness_main.c b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/testharness/TestHarness_main.c new file mode 100644 index 000000000..35f44e3a1 --- /dev/null +++ b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/source/testharness/TestHarness_main.c @@ -0,0 +1,214 @@ +#include "gamepad/Gamepad.h" +#include +#include +#include + +#ifdef __APPLE__ +#include +#include +#else +#include +#include +#endif + +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + +static bool verbose = false; + +void onButtonDown(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context) { + if (verbose) { + printf("Button %u down on device %u at %f with context %p\n", buttonID, device->deviceID, timestamp, context); + } +} + +void onButtonUp(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context) { + if (verbose) { + printf("Button %u up on device %u at %f with context %p\n", buttonID, device->deviceID, timestamp, context); + } +} + +void onAxisMoved(struct Gamepad_device * device, unsigned int axisID, float value, float lastValue, double timestamp, void * context) { + if (verbose) { + printf("Axis %u moved from %f to %f on device %u at %f with context %p\n", axisID, lastValue, value, device->deviceID, timestamp, context); + } +} + +void onDeviceAttached(struct Gamepad_device * device, void * context) { + if (verbose) { + printf("Device ID %u attached (vendor = 0x%X; product = 0x%X) with context %p\n", device->deviceID, device->vendorID, device->productID, context); + } +} + +void onDeviceRemoved(struct Gamepad_device * device, void * context) { + if (verbose) { + printf("Device ID %u removed with context %p\n", device->deviceID, context); + } +} + +static unsigned int windowWidth = 800, windowHeight = 600; + +static void initGamepad() { + Gamepad_deviceAttachFunc(onDeviceAttached, (void *) 0x1); + Gamepad_deviceRemoveFunc(onDeviceRemoved, (void *) 0x2); + Gamepad_buttonDownFunc(onButtonDown, (void *) 0x3); + Gamepad_buttonUpFunc(onButtonUp, (void *) 0x4); + Gamepad_axisMoveFunc(onAxisMoved, (void *) 0x5); + Gamepad_init(); +} + +static void drawGlutString(int rasterPosX, int rasterPosY, const char * string) { + size_t length, charIndex; + + glRasterPos2i(rasterPosX, rasterPosY); + length = strlen(string); + for (charIndex = 0; charIndex < length; charIndex++) { + glutBitmapCharacter(GLUT_BITMAP_8_BY_13, string[charIndex]); + } +} + +#define POLL_ITERATION_INTERVAL 30 + +static void displayFunc(void) { + unsigned int gamepadIndex; + struct Gamepad_device * device; + unsigned int axesPerRow, buttonsPerRow; + unsigned int axisRowIndex, axisIndex; + unsigned int buttonRowIndex, buttonIndex; + float axisState; + char indexString[16]; + static unsigned int iterationsToNextPoll = POLL_ITERATION_INTERVAL; + char descriptionString[256]; + + iterationsToNextPoll--; + if (iterationsToNextPoll == 0) { + Gamepad_detectDevices(); + iterationsToNextPoll = POLL_ITERATION_INTERVAL; + } + Gamepad_processEvents(); + + axesPerRow = (windowWidth - 10) / 60; + buttonsPerRow = (windowWidth - 10) / 30; + + glClear(GL_COLOR_BUFFER_BIT); + glLoadIdentity(); + glTranslatef(5.0f, 20.0f, 0.0f); + for (gamepadIndex = 0; gamepadIndex < Gamepad_numDevices(); gamepadIndex++) { + device = Gamepad_deviceAtIndex(gamepadIndex); + + glColor3f(0.0f, 0.0f, 0.0f); + snprintf(descriptionString, 256, "%s (0x%X 0x%X %u)", device->description, device->vendorID, device->productID, device->deviceID); + drawGlutString(0, 0, descriptionString); + + for (axisRowIndex = 0; axisRowIndex <= device->numAxes / axesPerRow; axisRowIndex++) { + glPushMatrix(); + for (axisIndex = axisRowIndex * axesPerRow; axisIndex < (axisRowIndex + 1) * axesPerRow && axisIndex < device->numAxes; axisIndex++) { + axisState = device->axisStates[axisIndex]; + + sprintf(indexString, "a%d", axisIndex); + glColor3f(0.0f, 0.0f, 0.0f); + drawGlutString(2, 28, indexString); + + glBegin(GL_QUADS); + glVertex2f(2.0f, 5.0f); + glVertex2f(58.0f, 5.0f); + glVertex2f(58.0f, 15.0f); + glVertex2f(2.0f, 15.0f); + glColor3f(0.5f, 1.0f, 0.5f); + glVertex2f(29.0f + axisState * 26, 6.0f); + glVertex2f(31.0f + axisState * 26, 6.0f); + glVertex2f(31.0f + axisState * 26, 14.0f); + glVertex2f(29.0f + axisState * 26, 14.0f); + glEnd(); + glTranslatef(60.0f, 0.0f, 0.0f); + } + glPopMatrix(); + glTranslatef(0.0f, 32.0f, 0.0f); + } + + for (buttonRowIndex = 0; buttonRowIndex <= device->numButtons / buttonsPerRow; buttonRowIndex++) { + glPushMatrix(); + for (buttonIndex = buttonRowIndex * buttonsPerRow; buttonIndex < (buttonRowIndex + 1) * buttonsPerRow && buttonIndex < device->numButtons; buttonIndex++) { + sprintf(indexString, "b%d", buttonIndex); + glColor3f(0.0f, 0.0f, 0.0f); + drawGlutString(2, 32, indexString); + + glBegin(GL_QUADS); + glColor3f(0.0f, 0.0f, 0.0f); + glVertex2f(2.0f, 2.0f); + glVertex2f(28.0f, 2.0f); + glVertex2f(28.0f, 18.0f); + glVertex2f(2.0f, 18.0f); + if (device->buttonStates[buttonIndex]) { + glColor3f(0.5f, 1.0f, 0.5f); + glVertex2f(3.0f, 3.0f); + glVertex2f(27.0f, 3.0f); + glVertex2f(27.0f, 17.0f); + glVertex2f(3.0f, 17.0f); + } + glEnd(); + glTranslatef(30.0f, 0.0f, 0.0f); + } + glPopMatrix(); + glTranslatef(0.0f, 38.0f, 0.0f); + } + glTranslatef(0.0f, 40.0f, 0.0f); + } + + if (gamepadIndex == 0) { + glLoadIdentity(); + glTranslatef(5.0f, 20.0f, 0.0f); + glColor3f(0.0f, 0.0f, 0.0f); + drawGlutString(0, 0, "No devices found; plug in a USB gamepad and it will be detected automatically"); + } + + glutSwapBuffers(); + glutPostRedisplay(); +} + +static void keyDownFunc(unsigned char charCode, int x, int y) { + if (charCode == 'r') { + Gamepad_shutdown(); + initGamepad(); + } +} + +static void reshapeFunc(int newWidth, int newHeight) { + windowWidth = newWidth; + windowHeight = newHeight; + glViewport(0, 0, newWidth, newHeight); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0f, windowWidth, windowHeight, 0.0f, -1.0f, 1.0f); + glMatrixMode(GL_MODELVIEW); +} + +int main(int argc, char ** argv) { + int argIndex; + + for (argIndex = 1; argIndex < argc; argIndex++) { + if (!strcmp(argv[argIndex], "-v")) { + verbose = true; + } + } + + glutInit(&argc, argv); + glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); + glutInitWindowPosition(30, 30); + glutInitWindowSize(800, 600); + glutCreateWindow("Gamepad Test Harness"); + glutReshapeFunc(reshapeFunc); + glutDisplayFunc(displayFunc); + glutKeyboardFunc(keyDownFunc); + + initGamepad(); + + glClearColor(1.0f, 1.0f, 1.0f, 0.0f); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0f, windowWidth, windowHeight, 0.0f, -1.0f, 1.0f); + glMatrixMode(GL_MODELVIEW); + + glutMainLoop(); +} diff --git a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/version b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/version similarity index 66% rename from internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/version rename to internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/version index a6a04dfc8..f7d0beec5 100644 --- a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/version +++ b/internal/c/parts/input/game_controller/download/gamepad_1.4.0_pre1_source/version @@ -1,3 +1,3 @@ VERSION_MAJOR=1 -VERSION_MINOR=1 +VERSION_MINOR=4 VERSION_TWEAK=0 diff --git a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/Makefile b/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/Makefile deleted file mode 100644 index e23061427..000000000 --- a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/Makefile +++ /dev/null @@ -1,420 +0,0 @@ -.PHONY: all -all: library unittest testharness test include - -UNAME = ${shell uname} -ifeq (${UNAME},Linux) -HOST_PLATFORM = linux -else ifeq (${UNAME},Darwin) -HOST_PLATFORM = macosx -else -HOST_PLATFORM = windows -endif - -TARGET_PLATFORMS_macosx = macosx -TARGET_PLATFORMS_linux = linux -TARGET_PLATFORMS_windows = windows - -include version - -PROJECT_NAME = gamepad - -LIBRARY_TARGETS = library -EXECUTABLE_TARGETS = unittest -APPLICATION_TARGETS = testharness -TARGETS = ${LIBRARY_TARGETS} ${EXECUTABLE_TARGETS} ${APPLICATION_TARGETS} -CONFIGURATIONS = debug profile release -PLATFORMS = ${filter ${TARGET_PLATFORMS_${HOST_PLATFORM}},macosx linux windows} -ARCHS = ppc i386 i686 x86_64 - -TARGET_NAME_library = libstem_gamepad -TARGET_NAME_unittest = unittest -TARGET_NAME_testharness = GamepadTestHarness - -#Per-target configurations -CONFIGURATIONS_library = debug profile release -CONFIGURATIONS_unittest = debug -CONFIGURATIONS_testharness = debug profile - -#Per-target platforms -PLATFORMS_library = ${filter ${PLATFORMS},macosx linux windows} -PLATFORMS_unittest = ${filter ${PLATFORMS},macosx linux windows} -PLATFORMS_testharness = ${filter ${PLATFORMS},macosx linux windows} - -#Per-target compile/link settings -CCFLAGS_unittest = -I test_source -DSUITE_FILE_LIST='${foreach file,${SOURCES_unittest_suites},"${basename ${notdir ${file}}}",} NULL' - -#Per-configuration compile/link settings -CCFLAGS_debug = -g -CCFLAGS_profile = -g -O3 -CCFLAGS_release = -O3 - -#Per-platform compile/link settings -CC_macosx_ppc = /usr/bin/gcc-4.2 -arch ppc -CC_macosx_i386 = /Developer/usr/bin/clang -arch i386 -CC_macosx_x86_64 = /Developer/usr/bin/clang -arch x86_64 -AR_macosx = /usr/bin/ar -RANLIB_macosx = /usr/bin/ranlib -SDKROOT_macosx = /Developer/SDKs/MacOSX10.5.sdk -ARCHS_macosx = ppc i386 x86_64 -CCFLAGS_macosx = -isysroot ${SDKROOT_macosx} -mmacosx-version-min=10.5 -LINKFLAGS_macosx = -isysroot ${SDKROOT_macosx} -mmacosx-version-min=10.5 -framework IOKit -framework CoreFoundation -framework OpenGL -framework GLUT - -CC_linux_i686 = /usr/bin/gcc -AR_linux = /usr/bin/ar -RANLIB_linux = /usr/bin/ranlib -ARCHS_linux = i686 -CCFLAGS_linux = -LINKFLAGS_linux = -lm -ldl -lglut -Wl,-E - -CC_windows_i686 = \\MinGW\\bin\\gcc.exe -AR_windows = \\MinGW\\bin\\ar.exe -RANLIB_windows = \\MinGW\\bin\\ranlib.exe -ARCHS_windows = i686 -CCFLAGS_windows = -DFREEGLUT_STATIC -LINKFLAGS_windows = -lfreeglut_static -lopengl32 -lglu32 -lwinmm -lgdi32 -mwindows -mconsole - -#General compile/link settings -DEFINE_CCFLAGS = -DVERSION_MAJOR=${VERSION_MAJOR}u -DVERSION_MINOR=${VERSION_MINOR}u -DVERSION_TWEAK=${VERSION_TWEAK}u -WARNING_CCFLAGS = -Wall -Wextra -Wno-unused-parameter -Werror -INCLUDE_CCFLAGS = -I source -I include -OTHER_CCFLAGS = -std=gnu99 -CCFLAGS = ${DEFINE_CCFLAGS} ${WARNING_CCFLAGS} ${INCLUDE_CCFLAGS} ${OTHER_CCFLAGS} - -FRAMEWORK_LINKFLAGS = -LIBRARY_LINKFLAGS = -OTHER_LINKFLAGS = -LINKFLAGS = ${FRAMEWORK_LINKFLAGS} ${LIBRARY_LINKFLAGS} ${OTHER_LINKFLAGS} - -#Per-target depencies - -LIBRARY_DEPENDENCIES_unittest = -LIBRARY_DEPENDENCIES_testharness = utilities/libstem_utilities.a glutshell/libstemshell_glut.a - -#Per-target source file lists - -SOURCES_library = \ - source/gamepad/Gamepad_${HOST_PLATFORM}.c \ - source/utilities/EventDispatcher.c - -SOURCES_unittest = \ - test_source/unittest/framework/unittest_main.c \ - test_source/unittest/framework/TestList.c \ - ${SOURCES_unittest_suites} - -SOURCES_unittest_suites = \ - test_source/unittest/suites/GamepadTest.c - -SOURCES_testharness = \ - test_source/testharness/TestHarness_main.c - -SOURCES = ${sort ${foreach target,${TARGETS},${SOURCES_${target}}}} - -INCLUDES = \ - source/gamepad/Gamepad.h \ - source/utilities/EventDispatcher.h - - - -define configuration_object_list_template #(target, configuration) - ${foreach platform,${PLATFORMS_${1}}, \ - ${call platform_object_list_template,${1},${2},${platform}} \ - } -endef - -define platform_object_list_template #(target, configuration, platform) - ${foreach arch,${ARCHS_${3}}, \ - ${call arch_object_list_template,${1},${2},${3},${arch}} \ - } -endef - -define arch_object_list_template #(target, configuration, platform, arch) - ${foreach source,${SOURCES_${1}}, \ - build/intermediate/${2}-${3}-${4}/${notdir ${basename ${source}}}.o \ - } -endef - -#Produces OBJECTS_${target}_${configuration} variables for each permutation of target and configuration in that target -${foreach target,${TARGETS}, \ - ${foreach configuration,${CONFIGURATIONS_${target}}, \ - ${eval OBJECTS_${target}_${configuration} = ${call configuration_object_list_template,${target},${configuration}}} \ - } \ -} - - - -define compile_template #(target, configuration, platform, arch, source_file) -build/intermediate/${2}-${3}-${4}/${notdir ${basename ${5}}}.o: ${5} - mkdir -p build/intermediate/${2}-${3}-${4} - ${CC_${3}_${4}} ${CCFLAGS} ${CCFLAGS_${1}} ${CCFLAGS_${2}} ${CCFLAGS_${3}} -c -o $$@ $$^ -endef - -#Produces object build targets for all source files in each configuration/platform/arch -${foreach target,${TARGETS}, \ - ${foreach configuration,${CONFIGURATIONS}, \ - ${foreach platform,${PLATFORMS_${target}}, \ - ${foreach arch,${ARCHS_${platform}}, \ - ${foreach source,${SOURCES_${target}}, \ - ${eval ${call compile_template,${target},${configuration},${platform},${arch},${source}}} \ - } \ - } \ - } \ - } \ -} - - - -define library_template #(target, configuration, platform, arch, output_file) -build/intermediate/${2}-${3}-${4}/${5}: ${call arch_object_list_template,${1},${2},${3},${4}} - ${AR_${3}} rc $$@ $$^ - ${RANLIB_${3}} $$@ -endef - -#Produces static library build targets for each arch/platform/target for library targets -${foreach target,${LIBRARY_TARGETS}, \ - ${foreach configuration,${CONFIGURATIONS_${target}}, \ - ${foreach platform,${PLATFORMS_${target}}, \ - ${foreach arch,${ARCHS_${platform}}, \ - ${eval ${call library_template,${target},${configuration},${platform},${arch},${TARGET_NAME_${target}}.a}} \ - } \ - } \ - } \ -} - - - -define executable_template #(target, configuration, platform, arch, output_file, dependent_libraries) -build/intermediate/${2}-${3}-${4}/${5}: ${call arch_object_list_template,${1},${2},${3},${4}} ${6} - ${CC_${3}_${4}} -o $$@ $$^ ${LINKFLAGS} ${LINKFLAGS_${3}} -endef - -define library_dependency_template #(target, configuration, platform) - build/library/debug-${3}/${TARGET_NAME_library}.a \ - ${foreach library,${LIBRARY_DEPENDENCIES_${1}}, \ - lib/${dir ${library}}${configuration}-${platform}/${notdir ${library}} \ - } -endef - -#Produces executable build targets for each arch/platform/target for executable and application targets -${foreach target,${EXECUTABLE_TARGETS} ${APPLICATION_TARGETS}, \ - ${foreach configuration,${CONFIGURATIONS_${target}}, \ - ${foreach platform,${PLATFORMS_${target}}, \ - ${foreach arch,${ARCHS_${platform}}, \ - ${eval ${call executable_template,${target},${configuration},${platform},${arch},${TARGET_NAME_${target}},${call library_dependency_template,${target},${configuration},${platform}}}} \ - } \ - } \ - } \ -} - - - -define thin_binary_list_template #(target_name, configuration, platform) - ${foreach arch,${ARCHS_${3}}, \ - build/intermediate/${2}-${3}-${arch}/${1} \ - } -endef - -#Produces THIN_BINARIES_${target}_${configuration}_${platform} variables for each target/configuration/platform for library targets -${foreach target,${LIBRARY_TARGETS}, \ - ${foreach configuration,${CONFIGURATIONS_${target}}, \ - ${foreach platform,${PLATFORMS_${target}}, \ - ${eval THIN_BINARIES_${target}_${configuration}_${platform} = ${call thin_binary_list_template,${TARGET_NAME_${target}}.a,${configuration},${platform}}} \ - } \ - } \ -} - -#Produces THIN_BINARIES_${target}_${configuration}_${platform} variables for each target/configuration/platform for executable targets -${foreach target,${EXECUTABLE_TARGETS}, \ - ${foreach configuration,${CONFIGURATIONS_${target}}, \ - ${foreach platform,${PLATFORMS_${target}}, \ - ${eval THIN_BINARIES_${target}_${configuration}_${platform} = ${call thin_binary_list_template,${TARGET_NAME_${target}},${configuration},${platform}}} \ - } \ - } \ -} - -#Produces THIN_BINARIES_${target}_${configuration}_${platform} variables for each target/configuration/platform for application targets -${foreach target,${APPLICATION_TARGETS}, \ - ${foreach configuration,${CONFIGURATIONS_${target}}, \ - ${foreach platform,${PLATFORMS_${target}}, \ - ${eval THIN_BINARIES_${target}_${configuration}_${platform} = ${call thin_binary_list_template,${TARGET_NAME_${target}},${configuration},${platform}}} \ - } \ - } \ -} - - - -define assemble_library_macosx #(target, configuration, platform) -build/${1}/${2}-${3}/${TARGET_NAME_${1}}.a: ${THIN_BINARIES_${1}_${2}_${3}} - mkdir -p $${dir $$@} - lipo -create -output $$@ $$^ -endef - -define assemble_library_linux #(target, configuration, platform) -build/${1}/${2}-${3}/${TARGET_NAME_${1}}.a: ${THIN_BINARIES_${1}_${2}_${3}} - mkdir -p $${dir $$@} - cp $$^ $$@ -endef - -define assemble_library_windows #(target, configuration, platform) -build/${1}/${2}-${3}/${TARGET_NAME_${1}}.a: ${THIN_BINARIES_${1}_${2}_${3}} - mkdir -p $${dir $$@} - cp $$^ $$@ -endef - -#Produces final library build targets -${foreach target,${LIBRARY_TARGETS}, \ - ${foreach configuration,${CONFIGURATIONS_${target}}, \ - ${foreach platform,${PLATFORMS_${target}}, \ - ${eval ${call assemble_library_${HOST_PLATFORM},${target},${configuration},${platform}}} \ - } \ - } \ -} - -define assemble_executable_macosx #(target, configuration, platform) -build/${1}/${2}-${3}/${TARGET_NAME_${1}}: ${THIN_BINARIES_${1}_${2}_${3}} - mkdir -p $${dir $$@} - lipo -create -output $$@ $$^ -endef - -define assemble_executable_linux #(target, configuration, platform) -build/${1}/${2}-${3}/${TARGET_NAME_${1}}: ${THIN_BINARIES_${1}_${2}_${3}} - mkdir -p $${dir $$@} - cp $$^ $$@ -endef - -define assemble_executable_windows #(target, configuration, platform) -build/${1}/${2}-${3}/${TARGET_NAME_${1}}.exe: ${THIN_BINARIES_${1}_${2}_${3}} - mkdir -p $${dir $$@} - cp $$^ $$@ -endef - -#Produces final executable build targets -${foreach target,${EXECUTABLE_TARGETS}, \ - ${foreach configuration,${CONFIGURATIONS_${target}}, \ - ${foreach platform,${PLATFORMS_${target}}, \ - ${eval ${call assemble_executable_${HOST_PLATFORM},${target},${configuration},${platform}}} \ - } \ - } \ -} - -PLIST_FILE_testharness_macosx = test_resources/Info_testharness_macosx.plist - -define assemble_application_macosx #(target, configuration, platform) -build/${1}/${2}-${3}/${TARGET_NAME_${1}}.app/Contents/MacOS/${TARGET_NAME_${1}}: ${THIN_BINARIES_${1}_${2}_${3}} - mkdir -p $${dir $$@} - mkdir -p $${dir $$@}../Resources - sed -e "s/\$$$${PRODUCT_NAME}/${TARGET_NAME_${1}}/g" \ - -e "s/\$$$${VERSION}/${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_TWEAK}/g" \ - -e "s/\$$$${COPYRIGHT_YEAR}/"`date +%Y`"/g" \ - -e "s/\$$$${BUILD_NUMBER}/0/g" \ - -e "s/\$$$${PLATFORM_CASED}/${PLIST_PLATFORM_CASED_${3}}/g" \ - -e "s/\$$$${PLATFORM_LOWER}/${PLIST_PLATFORM_LOWER_${3}}/g" \ - -e "s/\$$$${SDK}/${PLIST_SDK_NAME_${3}}/g" \ - ${PLIST_FILE_${1}_${3}} > $${dir $$@}/../Info.plist - echo "APPL????" > $${dir $$@}../PkgInfo - lipo -create -output $$@ $$^ -endef - -define assemble_application_linux #(target, configuration, platform) -build/${1}/${2}-${3}/${TARGET_NAME_${1}}: ${THIN_BINARIES_${1}_${2}_${3}} - mkdir -p $${dir $$@} - cp $$^ $$@ -endef - -define assemble_application_windows #(target, configuration, platform) -build/${1}/${2}-${3}/${TARGET_NAME_${1}}.exe: ${THIN_BINARIES_${1}_${2}_${3}} - mkdir -p $${dir $$@} - cp $$^ $$@ -endef - -#Produces final application build targets -${foreach target,${APPLICATION_TARGETS}, \ - ${foreach configuration,${CONFIGURATIONS_${target}}, \ - ${foreach platform,${PLATFORMS_${target}}, \ - ${eval ${call assemble_application_${HOST_PLATFORM},${target},${configuration},${platform}}} \ - } \ - } \ -} - -define library_target_template_macosx #(target) -.PHONY: ${1} -${1}: ${foreach configuration,${CONFIGURATIONS_${1}},${foreach platform,${PLATFORMS_${1}},build/${1}/${configuration}-${platform}/${TARGET_NAME_${1}}.a}} -endef - -define library_target_template_linux #(target) -.PHONY: ${1} -${1}: ${foreach configuration,${CONFIGURATIONS_${1}},${foreach platform,${PLATFORMS_${1}},build/${1}/${configuration}-${platform}/${TARGET_NAME_${1}}.a}} -endef - -define library_target_template_windows #(target) -.PHONY: ${1} -${1}: ${foreach configuration,${CONFIGURATIONS_${1}},${foreach platform,${PLATFORMS_${1}},build/${1}/${configuration}-${platform}/${TARGET_NAME_${1}}.a}} -endef - -define executable_target_template_macosx #(target) -.PHONY: ${1} -${1}: ${foreach configuration,${CONFIGURATIONS_${1}},${foreach platform,${PLATFORMS_${1}},build/${1}/${configuration}-${platform}/${TARGET_NAME_${1}}}} -endef - -define executable_target_template_linux #(target) -.PHONY: ${1} -${1}: ${foreach configuration,${CONFIGURATIONS_${1}},${foreach platform,${PLATFORMS_${1}},build/${1}/${configuration}-${platform}/${TARGET_NAME_${1}}}} -endef - -define executable_target_template_windows #(target) -.PHONY: ${1} -${1}: ${foreach configuration,${CONFIGURATIONS_${1}},${foreach platform,${PLATFORMS_${1}},build/${1}/${configuration}-${platform}/${TARGET_NAME_${1}}.exe}} -endef - -define application_target_template_macosx #(target) -.PHONY: ${1} -${1}: ${foreach configuration,${CONFIGURATIONS_${1}},${foreach platform,${PLATFORMS_${1}},build/${1}/${configuration}-${platform}/${TARGET_NAME_${1}}.app/Contents/MacOS/${TARGET_NAME_${1}}}} -endef - -define application_target_template_linux #(target) -.PHONY: ${1} -${1}: ${foreach configuration,${CONFIGURATIONS_${1}},${foreach platform,${PLATFORMS_${1}},build/${1}/${configuration}-${platform}/${TARGET_NAME_${1}}}} -endef - -define application_target_template_windows #(target) -.PHONY: ${1} -${1}: ${foreach configuration,${CONFIGURATIONS_${1}},${foreach platform,${PLATFORMS_${1}},build/${1}/${configuration}-${platform}/${TARGET_NAME_${1}}.exe}} -endef - -${foreach target,${LIBRARY_TARGETS}, \ - ${eval ${call library_target_template_${HOST_PLATFORM},${target}}} \ -} - -${foreach target,${EXECUTABLE_TARGETS}, \ - ${eval ${call executable_target_template_${HOST_PLATFORM},${target}}} \ -} - -${foreach target,${APPLICATION_TARGETS}, \ - ${eval ${call application_target_template_${HOST_PLATFORM},${target}}} \ -} - -.PHONY: test -test: unittest ${foreach platform,${PLATFORMS_unittest},run_unittests_${platform}} - -.PHONY: run_unittests_macosx -run_unittests_macosx: - ./build/unittest/debug-macosx/unittest - -.PHONY: run_unittests_linux -run_unittests_linux: - ./build/unittest/debug-linux/unittest - -.PHONY: run_unittests_windows -run_unittests_windows: - ./build/unittest/debug-windows/unittest.exe - -.PHONY: include -include: ${INCLUDES} - mkdir -p build/include - cp $^ build/include - -.PHONY: clean -clean: - rm -rf build - rm -rf dist - rm -rf dist_append - diff --git a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/include/shell/Shell.h b/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/include/shell/Shell.h deleted file mode 100644 index 4280d45e2..000000000 --- a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/include/shell/Shell.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef __SHELL_H__ -#define __SHELL_H__ - -void Shell_mainLoop(); -void Shell_redisplay(); - -double Shell_getCurrentTime(); -const char * Shell_getResourcePath(); - -#endif diff --git a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/include/shell/ShellKeyCodes.h b/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/include/shell/ShellKeyCodes.h deleted file mode 100644 index 70af572fd..000000000 --- a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/include/shell/ShellKeyCodes.h +++ /dev/null @@ -1,176 +0,0 @@ -// Adapted from GameShell (http://onesadcookie.com/svn/GameShell/Source/Common/GSKeyCodesInternal.h). Thanks Keith! - -#ifndef __SHELL_KEY_CODES_H__ -#define __SHELL_KEY_CODES_H__ - -#define KEYBOARD_A 0x04 -#define KEYBOARD_B 0x05 -#define KEYBOARD_C 0x06 -#define KEYBOARD_D 0x07 -#define KEYBOARD_E 0x08 -#define KEYBOARD_F 0x09 -#define KEYBOARD_G 0x0A -#define KEYBOARD_H 0x0B -#define KEYBOARD_I 0x0C -#define KEYBOARD_J 0x0D -#define KEYBOARD_K 0x0E -#define KEYBOARD_L 0x0F -#define KEYBOARD_M 0x10 -#define KEYBOARD_N 0x11 -#define KEYBOARD_O 0x12 -#define KEYBOARD_P 0x13 -#define KEYBOARD_Q 0x14 -#define KEYBOARD_R 0x15 -#define KEYBOARD_S 0x16 -#define KEYBOARD_T 0x17 -#define KEYBOARD_U 0x18 -#define KEYBOARD_V 0x19 -#define KEYBOARD_W 0x1A -#define KEYBOARD_X 0x1B -#define KEYBOARD_Y 0x1C -#define KEYBOARD_Z 0x1D -#define KEYBOARD_1 0x1E -#define KEYBOARD_2 0x1F -#define KEYBOARD_3 0x20 -#define KEYBOARD_4 0x21 -#define KEYBOARD_5 0x22 -#define KEYBOARD_6 0x23 -#define KEYBOARD_7 0x24 -#define KEYBOARD_8 0x25 -#define KEYBOARD_9 0x26 -#define KEYBOARD_0 0x27 -#define KEYBOARD_RETURN_OR_ENTER 0x28 -#define KEYBOARD_ESCAPE 0x29 -#define KEYBOARD_DELETE_OR_BACKSPACE 0x2A -#define KEYBOARD_TAB 0x2B -#define KEYBOARD_SPACEBAR 0x2C -#define KEYBOARD_HYPHEN 0x2D -#define KEYBOARD_EQUAL_SIGN 0x2E -#define KEYBOARD_OPEN_BRACKET 0x2F -#define KEYBOARD_CLOSE_BRACKET 0x30 -#define KEYBOARD_BACKSLASH 0x31 -#define KEYBOARD_NON_USPOUND 0x32 -#define KEYBOARD_SEMICOLON 0x33 -#define KEYBOARD_QUOTE 0x34 -#define KEYBOARD_GRAVE_ACCENT_AND_TILDE 0x35 -#define KEYBOARD_COMMA 0x36 -#define KEYBOARD_PERIOD 0x37 -#define KEYBOARD_SLASH 0x38 -#define KEYBOARD_CAPS_LOCK 0x39 -#define KEYBOARD_F1 0x3A -#define KEYBOARD_F2 0x3B -#define KEYBOARD_F3 0x3C -#define KEYBOARD_F4 0x3D -#define KEYBOARD_F5 0x3E -#define KEYBOARD_F6 0x3F -#define KEYBOARD_F7 0x40 -#define KEYBOARD_F8 0x41 -#define KEYBOARD_F9 0x42 -#define KEYBOARD_F10 0x43 -#define KEYBOARD_F11 0x44 -#define KEYBOARD_F12 0x45 -#define KEYBOARD_PRINT_SCREEN 0x46 -#define KEYBOARD_SCROLL_LOCK 0x47 -#define KEYBOARD_PAUSE 0x48 -#define KEYBOARD_INSERT 0x49 -#define KEYBOARD_HOME 0x4A -#define KEYBOARD_PAGE_UP 0x4B -#define KEYBOARD_DELETE_FORWARD 0x4C -#define KEYBOARD_END 0x4D -#define KEYBOARD_PAGE_DOWN 0x4E -#define KEYBOARD_RIGHT_ARROW 0x4F -#define KEYBOARD_LEFT_ARROW 0x50 -#define KEYBOARD_DOWN_ARROW 0x51 -#define KEYBOARD_UP_ARROW 0x52 -#define KEYPAD_NUM_LOCK 0x53 -#define KEYPAD_SLASH 0x54 -#define KEYPAD_ASTERISK 0x55 -#define KEYPAD_HYPHEN 0x56 -#define KEYPAD_PLUS 0x57 -#define KEYPAD_ENTER 0x58 -#define KEYPAD_1 0x59 -#define KEYPAD_2 0x5A -#define KEYPAD_3 0x5B -#define KEYPAD_4 0x5C -#define KEYPAD_5 0x5D -#define KEYPAD_6 0x5E -#define KEYPAD_7 0x5F -#define KEYPAD_8 0x60 -#define KEYPAD_9 0x61 -#define KEYPAD_0 0x62 -#define KEYPAD_PERIOD 0x63 -#define KEYBOARD_NON_US_BACKSLASH 0x64 -#define KEYBOARD_APPLICATION 0x65 -#define KEYBOARD_POWER 0x66 -#define KEYPAD_EQUAL_SIGN 0x67 -#define KEYBOARD_F13 0x68 -#define KEYBOARD_F14 0x69 -#define KEYBOARD_F15 0x6A -#define KEYBOARD_F16 0x6B -#define KEYBOARD_F17 0x6C -#define KEYBOARD_F18 0x6D -#define KEYBOARD_F19 0x6E -#define KEYBOARD_F20 0x6F -#define KEYBOARD_F21 0x70 -#define KEYBOARD_F22 0x71 -#define KEYBOARD_F23 0x72 -#define KEYBOARD_F24 0x73 -#define KEYBOARD_EXECUTE 0x74 -#define KEYBOARD_HELP 0x75 -#define KEYBOARD_MENU 0x76 -#define KEYBOARD_SELECT 0x77 -#define KEYBOARD_STOP 0x78 -#define KEYBOARD_AGAIN 0x79 -#define KEYBOARD_UNDO 0x7A -#define KEYBOARD_CUT 0x7B -#define KEYBOARD_COPY 0x7C -#define KEYBOARD_PASTE 0x7D -#define KEYBOARD_FIND 0x7E -#define KEYBOARD_MUTE 0x7F -#define KEYBOARD_VOLUME_UP 0x80 -#define KEYBOARD_VOLUME_DOWN 0x81 -#define KEYBOARD_LOCKING_CAPS_LOCK 0x82 -#define KEYBOARD_LOCKING_NUM_LOCK 0x83 -#define KEYBOARD_LOCKING_SCROLL_LOCK 0x84 -#define KEYPAD_COMMA 0x85 -#define KEYPAD_EQUAL_SIGN_AS400 0x86 -#define KEYBOARD_INTERNATIONAL_1 0x87 -#define KEYBOARD_INTERNATIONAL_2 0x88 -#define KEYBOARD_INTERNATIONAL_3 0x89 -#define KEYBOARD_INTERNATIONAL_4 0x8A -#define KEYBOARD_INTERNATIONAL_5 0x8B -#define KEYBOARD_INTERNATIONAL_6 0x8C -#define KEYBOARD_INTERNATIONAL_7 0x8D -#define KEYBOARD_INTERNATIONAL_8 0x8E -#define KEYBOARD_INTERNATIONAL_9 0x8F -#define KEYBOARD_LANG1 0x90 -#define KEYBOARD_LANG2 0x91 -#define KEYBOARD_LANG3 0x92 -#define KEYBOARD_LANG4 0x93 -#define KEYBOARD_LANG5 0x94 -#define KEYBOARD_LANG6 0x95 -#define KEYBOARD_LANG7 0x96 -#define KEYBOARD_LANG8 0x97 -#define KEYBOARD_LANG9 0x98 -#define KEYBOARD_ALTERNATE_ERASE 0x99 -#define KEYBOARD_SYS_REQ_OR_ATTENTION 0x9A -#define KEYBOARD_CANCEL 0x9B -#define KEYBOARD_CLEAR 0x9C -#define KEYBOARD_PRIOR 0x9D -#define KEYBOARD_RETURN 0x9E -#define KEYBOARD_SEPARATOR 0x9F -#define KEYBOARD_OUT 0xA0 -#define KEYBOARD_OPER 0xA1 -#define KEYBOARD_CLEAR_OR_AGAIN 0xA2 -#define KEYBOARD_CR_SEL_OR_PROPS 0xA3 -#define KEYBOARD_EX_SEL 0xA4 -#define KEYBOARD_LEFT_CONTROL 0xE0 -#define KEYBOARD_LEFT_SHIFT 0xE1 -#define KEYBOARD_LEFT_ALT 0xE2 -#define KEYBOARD_LEFT_GUI 0xE3 -#define KEYBOARD_RIGHT_CONTROL 0xE4 -#define KEYBOARD_RIGHT_SHIFT 0xE5 -#define KEYBOARD_RIGHT_ALT 0xE6 -#define KEYBOARD_RIGHT_GUI 0xE7 - -#endif diff --git a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/include/shell/Target.h b/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/include/shell/Target.h deleted file mode 100644 index 93e002c52..000000000 --- a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/include/shell/Target.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef __TARGET_H__ -#define __TARGET_H__ - -const char * Target_getName(); - -void Target_init(int argc, char ** argv); -void Target_draw(); - -void Target_keyDown(int charCode, int keyCode); -void Target_keyUp(int charCode, int keyCode); -void Target_mouseDown(int buttonNumber, float x, float y); -void Target_mouseUp(int buttonNumber, float x, float y); -void Target_mouseMoved(float x, float y); -void Target_mouseDragged(int buttonMask, float x, float y); - -void Target_resized(int newWidth, int newHeight); - -#endif diff --git a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/source/gamepad/Gamepad.h b/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/source/gamepad/Gamepad.h deleted file mode 100644 index ed2b4c206..000000000 --- a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/source/gamepad/Gamepad.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - Copyright (c) 2010 Alex Diener - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Alex Diener adiener@sacredsoftware.net -*/ - -#ifndef __GAMEPAD_H__ -#define __GAMEPAD_H__ - -#include -#include "utilities/EventDispatcher.h" - -// eventData -> struct Gamepad_device -#define GAMEPAD_EVENT_DEVICE_ATTACHED "GAMEPAD_EVENT_DEVICE_ATTACHED" // Only dispatched when Gamepad_init or Gamepad_detectDevices is called -#define GAMEPAD_EVENT_DEVICE_REMOVED "GAMEPAD_EVENT_DEVICE_REMOVED" // Can be dispatched at any time - -// eventData -> struct Gamepad_buttonEvent -#define GAMEPAD_EVENT_BUTTON_DOWN "GAMEPAD_EVENT_BUTTON_DOWN" // Only dispatched when Gamepad_processEvents is called -#define GAMEPAD_EVENT_BUTTON_UP "GAMEPAD_EVENT_BUTTON_UP" // Only dispatched when Gamepad_processEvents is called - -// eventData -> struct Gamepad_axisEvent -#define GAMEPAD_EVENT_AXIS_MOVED "GAMEPAD_EVENT_AXIS_MOVED" // Only dispatched when Gamepad_processEvents is called - -struct Gamepad_buttonEvent { - // Device that generated the event - struct Gamepad_device * device; - - // Relative time of the event, in seconds - double timestamp; - - // Button being pushed or released - unsigned int buttonID; - - // True if button is down - bool down; -}; - -struct Gamepad_axisEvent { - // Device that generated the event - struct Gamepad_device * device; - - // Relative time of the event, in seconds - double timestamp; - - // Axis being moved - unsigned int axisID; - - // Axis position value, in the range [-1..1] - float value; -}; - -struct Gamepad_device { - // Unique device identifier for application session. If a device is removed and subsequently reattached during the same application session, it will have a new deviceID. - unsigned int deviceID; - - // Human-readable device name - const char * description; - - // USB vendor/product IDs as returned by the driver. Can be used to determine the particular model of device represented. - int vendorID; - int productID; - - // Number of axis elements belonging to the device - unsigned int numAxes; - - // Number of button elements belonging to the device - unsigned int numButtons; - - // Array[numAxes] of values representing the current state of each axis, in the range [-1..1] - float * axisStates; - - // Array[numButtons] of values representing the current state of each button - bool * buttonStates; - - // Broadcasts GAMEPAD_EVENT_BUTTON_DOWN, GAMEPAD_EVENT_BUTTON_UP, and GAMEPAD_EVENT_AXIS_MOVED - EventDispatcher * eventDispatcher; - - // Platform-specific device data storage; don't mess with it - void * privateData; -}; - -/* Initializes gamepad library and detects initial devices. Call this before any other Gamepad_*() - function, EXCEPT Gamepad_eventDispatcher(). In order to get receive GAMEPAD_EVENT_DEVICE_ATTACHED - events from devices detected in Gamepad_init(), you must register handlers for those events before - calling Gamepad_init(). */ -void Gamepad_init(); - -/* Tears down all data structures created by the gamepad library and releases any memory that was - allocated. It is not necessary to call this function at application termination. */ -void Gamepad_shutdown(); - -/* EventDispatcher used by gamepad library to broadcast GAMEPAD_EVENT_DEVICE_ATTACHED and - GAMEPAD_EVENT_DEVICE_REMOVED events. */ -EventDispatcher * Gamepad_eventDispatcher(); - -/* Returns the number of currently attached gamepad devices. */ -unsigned int Gamepad_numDevices(); - -/* Returns the specified Gamepad_device struct, or NULL if deviceIndex is out of bounds. */ -struct Gamepad_device * Gamepad_deviceAtIndex(unsigned int deviceIndex); - -/* Polls for any devices that have been attached since the last call to Gamepad_detectDevices() or - Gamepad_init(). If any new devices are found, a GAMEPAD_EVENT_DEVICE_ATTACHED event will be - broadcast via Gamepad_eventDispatcher() for each one. */ -void Gamepad_detectDevices(); - -/* Reads pending input from all attached devices and broadcasts GAMEPAD_EVENT_BUTTON_DOWN, - GAMEPAD_EVENT_BUTTON_UP, and GAMEPAD_EVENT_AXIS_MOVED events through the eventDispatcher of the - device that generated the event. */ -void Gamepad_processEvents(); - -#endif diff --git a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/source/utilities/EventDispatcher.c b/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/source/utilities/EventDispatcher.c deleted file mode 100644 index dff59d906..000000000 --- a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/source/utilities/EventDispatcher.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - Copyright (c) 2010 Alex Diener - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Alex Diener adiener@sacredsoftware.net -*/ - -#include "utilities/EventDispatcher.h" - -#include -#include - -struct EventTarget { - char * eventID; - EventDispatcherCallback callback; - void * context; -}; - -EventDispatcher * EventDispatcher_create(void * owner) { - EventDispatcher * self; - - self = malloc(sizeof(EventDispatcher)); - EventDispatcher_init(self, owner); - return self; -} - -void EventDispatcher_init(EventDispatcher * self, void * owner) { - self->dispose = EventDispatcher_dispose; - self->registerForEvent = EventDispatcher_registerForEvent; - self->unregisterForEvent = EventDispatcher_unregisterForEvent; - self->dispatchEvent = EventDispatcher_dispatchEvent; - - self->owner = owner; - self->numberOfTargets = 0; - self->targetListSize = 1; - self->targets = (struct EventTarget *) malloc(sizeof(struct EventTarget) * self->targetListSize); -} - -void EventDispatcher_dispose(void * selfPtr) { - EventDispatcher * self = selfPtr; - int targetIndex; - - for (targetIndex = 0; targetIndex < self->numberOfTargets; targetIndex++) { - free(self->targets[targetIndex].eventID); - } - free(self->targets); -} - -void EventDispatcher_registerForEvent(void * selfPtr, const char * eventID, EventDispatcherCallback callback, void * context) { - EventDispatcher * self = selfPtr; - size_t length; - - if (self->numberOfTargets >= self->targetListSize) { - self->targetListSize *= 2; - self->targets = (struct EventTarget *) realloc(self->targets, sizeof(struct EventTarget) * self->targetListSize); - } - - length = strlen(eventID); - self->targets[self->numberOfTargets].eventID = malloc(length + 1); - strncpy(self->targets[self->numberOfTargets].eventID, eventID, length + 1); - self->targets[self->numberOfTargets].callback = callback; - self->targets[self->numberOfTargets].context = context; - self->numberOfTargets++; -} - -void EventDispatcher_unregisterForEvent(void * selfPtr, const char * eventID, EventDispatcherCallback callback) { - EventDispatcher * self = selfPtr; - int targetIndex; - - for (targetIndex = 0; targetIndex < self->numberOfTargets; targetIndex++) { - if (!strcmp(eventID, self->targets[targetIndex].eventID) && self->targets[targetIndex].callback == callback) { - free(self->targets[targetIndex].eventID); - self->numberOfTargets--; - for (; targetIndex < self->numberOfTargets; targetIndex++) { - self->targets[targetIndex] = self->targets[targetIndex + 1]; - } - break; - } - } -} - -bool EventDispatcher_dispatchEvent(void * selfPtr, const char * eventID, void * eventData) { - EventDispatcher * self = selfPtr; - int targetIndex; - int numberOfTargetsCopy; - struct EventTarget * targetsCopy; - bool eventHandled, anyEventsHandled; - - numberOfTargetsCopy = self->numberOfTargets; - targetsCopy = malloc(sizeof(struct EventTarget) * numberOfTargetsCopy); - memcpy(targetsCopy, self->targets, sizeof(struct EventTarget) * numberOfTargetsCopy); - - anyEventsHandled = false; - for (targetIndex = 0; targetIndex < numberOfTargetsCopy; targetIndex++) { - if (!strcmp(eventID, self->targets[targetIndex].eventID)) { - eventHandled = targetsCopy[targetIndex].callback(self->owner, eventID, eventData, targetsCopy[targetIndex].context); - - if (eventHandled) { - anyEventsHandled = true; - } - } - } - - free(targetsCopy); - - return anyEventsHandled; -} diff --git a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/source/utilities/EventDispatcher.h b/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/source/utilities/EventDispatcher.h deleted file mode 100644 index 34de874dd..000000000 --- a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/source/utilities/EventDispatcher.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - Copyright (c) 2010 Alex Diener - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Alex Diener adiener@sacredsoftware.net -*/ - -#ifndef __EVENT_DISPATCHER_H__ -#define __EVENT_DISPATCHER_H__ - -#include - -typedef struct EventDispatcher EventDispatcher; - -/* Signature for event handler callbacks. - - sender: Object that dispatched the event. More specifically, the object passed to EventDispatcher_create. - eventID: Name of event that was triggered - eventData: Arbitrary data passed by dispatcher. Its format is known by convention depending on the event ID being dispatched. - context: Value passed as context to registerForEvent - - This function should return true if the event was handled, or false if it was ignored. */ -typedef bool (* EventDispatcherCallback)(void * sender, const char * eventID, void * eventData, void * context); - -struct EventTarget; - -#define EventDispatcher_structContents \ - void * owner; \ - \ - int numberOfTargets; \ - int targetListSize; \ - struct EventTarget * targets; \ - \ - void (* dispose)(void * self); \ - void (* registerForEvent)(void * self, const char * eventID, EventDispatcherCallback callback, void * context); \ - void (* unregisterForEvent)(void * self, const char * eventID, EventDispatcherCallback callback); \ - bool (* dispatchEvent)(void * self, const char * eventID, void * eventData); - -struct EventDispatcher { - EventDispatcher_structContents -}; - -/* Allocate and initialize a new EventDispatcher object. owner will be passed to event callbacks as - the sender parameter. */ -EventDispatcher * EventDispatcher_create(void * owner); - -/* Initialize an already allocated EventDispatcher. owner will be passed to event callbacks as the - sender parameter. */ -void EventDispatcher_init(EventDispatcher * self, void * owner); - -/* Free all memory allocated by EventDispatcher and remove all registered listeners. Does NOT free - the EventDispatcher itself. */ -void EventDispatcher_dispose(void * selfPtr); - -/* Register for notification of events of type eventID */ -void EventDispatcher_registerForEvent(void * selfPtr, const char * eventID, EventDispatcherCallback callback, void * context); - -/* Remove a previous registration for events of type eventID */ -void EventDispatcher_unregisterForEvent(void * selfPtr, const char * eventID, EventDispatcherCallback callback); - -/* Dispatch an event to all registered listeners for that event ID. Returns true if any listener is - registered and returns true from its handler callback. */ -bool EventDispatcher_dispatchEvent(void * selfPtr, const char * eventID, void * eventData); - -#endif diff --git a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_source/testharness/TestHarness_main.c b/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_source/testharness/TestHarness_main.c deleted file mode 100644 index abbf83323..000000000 --- a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_source/testharness/TestHarness_main.c +++ /dev/null @@ -1,242 +0,0 @@ -#include -#include -#include -#include "gamepad/Gamepad.h" -#include "shell/Shell.h" -#include "shell/ShellKeyCodes.h" -#include - -#ifdef __APPLE__ -#include -#include -#else -#include -#include -#endif - -static bool verbose = false; - -bool onButtonDown(void * sender, const char * eventID, void * eventData, void * context) { - struct Gamepad_buttonEvent * event; - - event = eventData; - if (verbose) { - printf("Button %u down (%d) on device %u at %f\n", event->buttonID, (int) event->down, event->device->deviceID, event->timestamp); - } - return true; -} - -bool onButtonUp(void * sender, const char * eventID, void * eventData, void * context) { - struct Gamepad_buttonEvent * event; - - event = eventData; - if (verbose) { - printf("Button %u up (%d) on device %u at %f\n", event->buttonID, (int) event->down, event->device->deviceID, event->timestamp); - } - return true; -} - -bool onAxisMoved(void * sender, const char * eventID, void * eventData, void * context) { - struct Gamepad_axisEvent * event; - - event = eventData; - if (verbose) { - printf("Axis %u moved to %f on device %u at %f\n", event->axisID, event->value, event->device->deviceID, event->timestamp); - } - return true; -} - -bool onDeviceAttached(void * sender, const char * eventID, void * eventData, void * context) { - struct Gamepad_device * device; - - device = eventData; - if (verbose) { - printf("Device ID %u attached (vendor = 0x%X; product = 0x%X)\n", device->deviceID, device->vendorID, device->productID); - } - device->eventDispatcher->registerForEvent(device->eventDispatcher, GAMEPAD_EVENT_BUTTON_DOWN, onButtonDown, device); - device->eventDispatcher->registerForEvent(device->eventDispatcher, GAMEPAD_EVENT_BUTTON_UP, onButtonUp, device); - device->eventDispatcher->registerForEvent(device->eventDispatcher, GAMEPAD_EVENT_AXIS_MOVED, onAxisMoved, device); - return true; -} - -bool onDeviceRemoved(void * sender, const char * eventID, void * eventData, void * context) { - struct Gamepad_device * device; - - device = eventData; - if (verbose) { - printf("Device ID %u removed\n", device->deviceID); - } - return true; -} - -const char * Target_getName() { - return "Gamepad test harness"; -} - -static unsigned int windowWidth = 800, windowHeight = 600; - -static void initGamepad() { - Gamepad_eventDispatcher()->registerForEvent(Gamepad_eventDispatcher(), GAMEPAD_EVENT_DEVICE_ATTACHED, onDeviceAttached, NULL); - Gamepad_eventDispatcher()->registerForEvent(Gamepad_eventDispatcher(), GAMEPAD_EVENT_DEVICE_REMOVED, onDeviceRemoved, NULL); - Gamepad_init(); -} - -void Target_init(int argc, char ** argv) { - int argIndex; - - for (argIndex = 1; argIndex < argc; argIndex++) { - if (!strcmp(argv[argIndex], "-v")) { - verbose = true; - } - } - - initGamepad(); - - glClearColor(1.0f, 1.0f, 1.0f, 0.0f); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0f, windowWidth, windowHeight, 0.0f, -1.0f, 1.0f); - glMatrixMode(GL_MODELVIEW); - - Shell_mainLoop(); -} - -static void drawGlutString(int rasterPosX, int rasterPosY, const char * string) { - size_t length, charIndex; - - glRasterPos2i(rasterPosX, rasterPosY); - length = strlen(string); - for (charIndex = 0; charIndex < length; charIndex++) { - glutBitmapCharacter(GLUT_BITMAP_8_BY_13, string[charIndex]); - } -} - -#define POLL_ITERATION_INTERVAL 30 - -void Target_draw() { - unsigned int gamepadIndex; - struct Gamepad_device * device; - unsigned int axesPerRow, buttonsPerRow; - unsigned int axisRowIndex, axisIndex; - unsigned int buttonRowIndex, buttonIndex; - float axisState; - char indexString[16]; - static unsigned int iterationsToNextPoll = POLL_ITERATION_INTERVAL; - char descriptionString[256]; - - iterationsToNextPoll--; - if (iterationsToNextPoll == 0) { - Gamepad_detectDevices(); - iterationsToNextPoll = POLL_ITERATION_INTERVAL; - } - Gamepad_processEvents(); - - axesPerRow = (windowWidth - 10) / 60; - buttonsPerRow = (windowWidth - 10) / 30; - - glClear(GL_COLOR_BUFFER_BIT); - glLoadIdentity(); - glTranslatef(5.0f, 20.0f, 0.0f); - for (gamepadIndex = 0; gamepadIndex < Gamepad_numDevices(); gamepadIndex++) { - device = Gamepad_deviceAtIndex(gamepadIndex); - - glColor3f(0.0f, 0.0f, 0.0f); - snprintf(descriptionString, 256, "%s (0x%X 0x%X %u)", device->description, device->vendorID, device->productID, device->deviceID); - drawGlutString(0, 0, descriptionString); - - for (axisRowIndex = 0; axisRowIndex <= device->numAxes / axesPerRow; axisRowIndex++) { - glPushMatrix(); - for (axisIndex = axisRowIndex * axesPerRow; axisIndex < (axisRowIndex + 1) * axesPerRow && axisIndex < device->numAxes; axisIndex++) { - axisState = device->axisStates[axisIndex]; - - sprintf(indexString, "a%d", axisIndex); - glColor3f(0.0f, 0.0f, 0.0f); - drawGlutString(2, 28, indexString); - - glBegin(GL_QUADS); - glVertex2f(2.0f, 5.0f); - glVertex2f(58.0f, 5.0f); - glVertex2f(58.0f, 15.0f); - glVertex2f(2.0f, 15.0f); - glColor3f(0.5f, 1.0f, 0.5f); - glVertex2f(29.0f + axisState * 26, 6.0f); - glVertex2f(31.0f + axisState * 26, 6.0f); - glVertex2f(31.0f + axisState * 26, 14.0f); - glVertex2f(29.0f + axisState * 26, 14.0f); - glEnd(); - glTranslatef(60.0f, 0.0f, 0.0f); - } - glPopMatrix(); - glTranslatef(0.0f, 32.0f, 0.0f); - } - - for (buttonRowIndex = 0; buttonRowIndex <= device->numButtons / buttonsPerRow; buttonRowIndex++) { - glPushMatrix(); - for (buttonIndex = buttonRowIndex * buttonsPerRow; buttonIndex < (buttonRowIndex + 1) * buttonsPerRow && buttonIndex < device->numButtons; buttonIndex++) { - sprintf(indexString, "b%d", buttonIndex); - glColor3f(0.0f, 0.0f, 0.0f); - drawGlutString(2, 32, indexString); - - glBegin(GL_QUADS); - glColor3f(0.0f, 0.0f, 0.0f); - glVertex2f(2.0f, 2.0f); - glVertex2f(28.0f, 2.0f); - glVertex2f(28.0f, 18.0f); - glVertex2f(2.0f, 18.0f); - if (device->buttonStates[buttonIndex]) { - glColor3f(0.5f, 1.0f, 0.5f); - glVertex2f(3.0f, 3.0f); - glVertex2f(27.0f, 3.0f); - glVertex2f(27.0f, 17.0f); - glVertex2f(3.0f, 17.0f); - } - glEnd(); - glTranslatef(30.0f, 0.0f, 0.0f); - } - glPopMatrix(); - glTranslatef(0.0f, 38.0f, 0.0f); - } - glTranslatef(0.0f, 40.0f, 0.0f); - } - - if (gamepadIndex == 0) { - glLoadIdentity(); - glTranslatef(5.0f, 20.0f, 0.0f); - glColor3f(0.0f, 0.0f, 0.0f); - drawGlutString(0, 0, "No devices found; plug in a USB gamepad and it will be detected automatically"); - } - - Shell_redisplay(); -} - -void Target_keyDown(int charCode, int keyCode) { - if (keyCode == KEYBOARD_R) { - Gamepad_shutdown(); - initGamepad(); - } -} - -void Target_keyUp(int charCode, int keyCode) { -} - -void Target_mouseDown(int buttonNumber, float x, float y) { -} - -void Target_mouseUp(int buttonNumber, float x, float y) { -} - -void Target_mouseMoved(float x, float y) { -} - -void Target_mouseDragged(int buttonMask, float x, float y) { -} - -void Target_resized(int newWidth, int newHeight) { - windowWidth = newWidth; - windowHeight = newHeight; - glViewport(0, 0, newWidth, newHeight); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0f, windowWidth, windowHeight, 0.0f, -1.0f, 1.0f); - glMatrixMode(GL_MODELVIEW); -} diff --git a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_source/unittest/framework/TestList.c b/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_source/unittest/framework/TestList.c deleted file mode 100644 index 13f5439a3..000000000 --- a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_source/unittest/framework/TestList.c +++ /dev/null @@ -1,41 +0,0 @@ -#define _GNU_SOURCE -#include -#include - -#if defined(WIN32) -#include -#else -#include -#endif - -#include "unittest/framework/TestSuite.h" - -TestSuite ** getTestSuites() { - static char * testFileNames[] = {SUITE_FILE_LIST}; - static TestSuite * testSuites[sizeof(testFileNames) / sizeof(char *)]; - char suiteFunctionName[256]; - unsigned int suiteIndex; - struct TestSuite * (* suiteFunction)(); -#if defined(WIN32) - HMODULE moduleHandle; - - moduleHandle = GetModuleHandle(NULL); -#endif - - for (suiteIndex = 0; suiteIndex < sizeof(testFileNames) / sizeof(char *) - 1; suiteIndex++) { - snprintf(suiteFunctionName, 256, "%s_suite", testFileNames[suiteIndex]); -#if defined(WIN32) - suiteFunction = (struct TestSuite * (*)()) GetProcAddress(moduleHandle, suiteFunctionName); -#else - suiteFunction = (struct TestSuite * (*)()) dlsym(RTLD_DEFAULT, suiteFunctionName); -#endif - if (suiteFunction == NULL) { - fprintf(stderr, "Couldn't load test suite %s (no symbol named %s found)\n", testFileNames[suiteIndex], suiteFunctionName); - abort(); - } - testSuites[suiteIndex] = suiteFunction(); - } - testSuites[suiteIndex] = NULL; - - return testSuites; -} diff --git a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_source/unittest/framework/TestSuite.h b/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_source/unittest/framework/TestSuite.h deleted file mode 100644 index 76449a496..000000000 --- a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_source/unittest/framework/TestSuite.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef __TEST_SUITE_H__ -#define __TEST_SUITE_H__ - -#include -#include -#include - -typedef struct TestSuite TestSuite; - -extern void (* g_unitTestFailureCallback)(const char * file, const char * function, int line, const char * format, ...) __attribute__((__noreturn__)) __attribute__((format(printf, 4, 5))); - -struct TestSuite { - char * description; - unsigned int numberOfTestCases; - void (** testCases)(); -}; - -#define TestCase_assert(condition, ...) \ - if (!(condition)) { \ - (*g_unitTestFailureCallback)(__FILE__, __FUNCTION__, __LINE__, __VA_ARGS__); \ - } - -#ifdef WIN32 -#define DLLEXPORT __declspec(dllexport) -#else -#define DLLEXPORT -#endif -#define TEST_SUITE(name, ...) \ -DLLEXPORT TestSuite * name##_suite() { \ - return testSuite(#name, __VA_ARGS__, NULL); \ -} - -static inline TestSuite * testSuite(const char * description, ...) __attribute__((sentinel)); -static inline TestSuite * testSuite(const char * description, ...) { - TestSuite * suite; - va_list args; - unsigned int testCaseIndex; - - suite = malloc(sizeof(TestSuite)); - - va_start(args, description); - for (suite->numberOfTestCases = 0; va_arg(args, void (*)()) != NULL; suite->numberOfTestCases++); - va_end(args); - - suite->description = malloc(strlen(description) + 1); - strcpy(suite->description, description); - suite->testCases = malloc(sizeof(void (*)()) * suite->numberOfTestCases); - va_start(args, description); - for (testCaseIndex = 0; testCaseIndex < suite->numberOfTestCases; testCaseIndex++) { - suite->testCases[testCaseIndex] = va_arg(args, void (*)()); - } - va_end(args); - - return suite; -} - -#endif diff --git a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_source/unittest/framework/unittest_main.c b/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_source/unittest/framework/unittest_main.c deleted file mode 100644 index cc39d9a35..000000000 --- a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_source/unittest/framework/unittest_main.c +++ /dev/null @@ -1,84 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "unittest/framework/TestSuite.h" - -extern TestSuite ** getTestSuites(); - -void (* g_unitTestFailureCallback)(const char * file, const char * function, int line, const char * format, ...) __attribute__((__noreturn__)) __attribute__((format(printf, 4, 5))); - -static int failures = 0; -static jmp_buf jmpEnv; - -static int lengthOfSharedPrefix(const char * string1, const char * string2) { - int charIndex; - - for (charIndex = 0; string1[charIndex] != '\x00' && string2[charIndex] != '\x00'; charIndex++) { - if (string1[charIndex] != string2[charIndex]) break; - } - return charIndex; -} - -static void assertFailureCallback(const char * file, const char * function, int line, const char * format, ...) __attribute__((__noreturn__)) __attribute__((format(printf, 4, 5))); -static void assertFailureCallback(const char * file, const char * function, int line, const char * format, ...) { - va_list args; - static char * sourceRoot = NULL; - - if (sourceRoot == NULL) { - int charIndex; - int length; - - length = strlen(__FILE__); - sourceRoot = malloc(length + 1); - strcpy(sourceRoot, __FILE__); - for (charIndex = 0; charIndex < length; charIndex++) { - if (!strcmp(sourceRoot + charIndex, "unittest/unittest_main.c")) { - sourceRoot[charIndex] = '\x00'; - break; - } - } - } - - file += lengthOfSharedPrefix(sourceRoot, file); - printf(" FAILURE in %s (%s:%d):\n ", function, file, line); - va_start(args, format); - vprintf(format, args); - va_end(args); - putchar('\n'); - - failures++; - longjmp(jmpEnv, 1); -} - -int main(int argc, char ** argv) { - TestSuite ** testSuites; - unsigned int numberOfTestSuites; - unsigned int testSuiteIndex, testCaseIndex; - - if (argc > 1) { - chdir(argv[1]); - } - - g_unitTestFailureCallback = assertFailureCallback; - - testSuites = getTestSuites(); - for (numberOfTestSuites = 0; testSuites[numberOfTestSuites] != NULL; numberOfTestSuites++); - - putchar('\n'); - for (testSuiteIndex = 0; testSuiteIndex < numberOfTestSuites; testSuiteIndex++) { - printf("%s (%d/%d) running %d test%s...\n", testSuites[testSuiteIndex]->description, testSuiteIndex + 1, numberOfTestSuites, testSuites[testSuiteIndex]->numberOfTestCases, testSuites[testSuiteIndex]->numberOfTestCases == 1 ? "" : "s"); - for (testCaseIndex = 0; testCaseIndex < testSuites[testSuiteIndex]->numberOfTestCases; testCaseIndex++) { - if (setjmp(jmpEnv) != 0) { - continue; - } - testSuites[testSuiteIndex]->testCases[testCaseIndex](); - } - } - printf("\nTests completed (%d failure%s)\n\n", failures, failures == 1 ? "" : "s"); - - return failures ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_source/unittest/suites/GamepadTest.c b/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_source/unittest/suites/GamepadTest.c deleted file mode 100644 index b8debf806..000000000 --- a/internal/c/parts/input/game_controller/download/gamepad_standalone_source_1.1.0/test_source/unittest/suites/GamepadTest.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "unittest/framework/TestSuite.h" - -static void blank() { - // No tests -} - -TEST_SUITE(GamepadTest, blank) \ No newline at end of file diff --git a/internal/c/parts/input/game_controller/notes.txt b/internal/c/parts/input/game_controller/notes.txt new file mode 100644 index 000000000..64b00c0e3 --- /dev/null +++ b/internal/c/parts/input/game_controller/notes.txt @@ -0,0 +1 @@ +http://forums.tigsource.com/index.php?topic=10675.80 \ No newline at end of file diff --git a/internal/c/parts/input/game_controller/os/lnx/build.sh b/internal/c/parts/input/game_controller/os/lnx/build.sh new file mode 100644 index 000000000..cf0309f74 --- /dev/null +++ b/internal/c/parts/input/game_controller/os/lnx/build.sh @@ -0,0 +1,14 @@ +#!/bin/sh +gcc -s -c -w -Wall ../../src/Gamepad_linux.c -o temp/Gamepad_linux.o +gcc -s -c -w -Wall ../../src/Gamepad_private.c -o temp/Gamepad_private.o +ar rcs src.a temp/Gamepad_private.o temp/Gamepad_linux.o +echo "Press any key to continue..." +Pause() +{ +OLDCONFIG=`stty -g` +stty -icanon -echo min 1 time 0 +dd count=1 2>/dev/null +stty $OLDCONFIG +} +Pause + diff --git a/internal/c/parts/input/game_controller/os/lnx/temp/ignore.bin b/internal/c/parts/input/game_controller/os/lnx/temp/ignore.bin new file mode 100644 index 000000000..e69de29bb diff --git a/internal/c/parts/input/game_controller/os/osx/build.command b/internal/c/parts/input/game_controller/os/osx/build.command new file mode 100644 index 000000000..204f63593 --- /dev/null +++ b/internal/c/parts/input/game_controller/os/osx/build.command @@ -0,0 +1,14 @@ +cd "$(dirname "$0")" +gcc -s -c -w -Wall ../../src/Gamepad_macosx.c -o temp/Gamepad_macosx.o +gcc -s -c -w -Wall ../../src/Gamepad_private.c -o temp/Gamepad_private.o +ar rcs src.a temp/Gamepad_private.o temp/Gamepad_macosx.o +echo "Press any key to continue..." +Pause() +{ +OLDCONFIG=`stty -g` +stty -icanon -echo min 1 time 0 +dd count=1 2>/dev/null +stty $OLDCONFIG +} +Pause + diff --git a/internal/c/parts/input/game_controller/os/osx/temp/ignore.bin b/internal/c/parts/input/game_controller/os/osx/temp/ignore.bin new file mode 100644 index 000000000..e69de29bb diff --git a/internal/c/parts/input/game_controller/os/win/build.bat b/internal/c/parts/input/game_controller/os/win/build.bat new file mode 100644 index 000000000..96bc45fc3 --- /dev/null +++ b/internal/c/parts/input/game_controller/os/win/build.bat @@ -0,0 +1,4 @@ +..\..\..\..\..\c_compiler\bin\gcc -s -c -w -Wall ..\..\src\Gamepad_windows_mm.c -o temp\Gamepad_windows_mm.o +..\..\..\..\..\c_compiler\bin\gcc -s -c -w -Wall ..\..\src\Gamepad_private.c -o temp\Gamepad_private.o +..\..\..\..\..\c_compiler\bin\ar rcs src.a temp\Gamepad_private.o temp\Gamepad_windows_mm.o +pause \ No newline at end of file diff --git a/internal/c/parts/input/game_controller/os/win/temp/ignore.bin b/internal/c/parts/input/game_controller/os/win/temp/ignore.bin new file mode 100644 index 000000000..e69de29bb diff --git a/internal/c/parts/input/game_controller/src.c b/internal/c/parts/input/game_controller/src.c index 7e12bcd0d..9f34cef36 100644 --- a/internal/c/parts/input/game_controller/src.c +++ b/internal/c/parts/input/game_controller/src.c @@ -1,299 +1,333 @@ -#include -#include -#include -#include +#include "src/gamepad/Gamepad.h" -#include "../../../os.h" - -#ifdef QB64_MACOSX - #include - #include -#else - #include - #include -#endif - -#ifdef QB64_WINDOWS - #include "src/Gamepad_windows.c" -#else - #ifdef QB64_MACOSX - #include "src/Gamepad_macosx.c" - #else - //assume LINUX - #include "src/Gamepad_linux.c" - #endif -#endif - -/* these shouldn't be necessary -#include "src/shell/Shell.h" -#include "src/shell/ShellKeyCodes.h" +/* +struct Gamepad_device { + // Unique device identifier for application session, starting at 0 for the first device attached and + // incrementing by 1 for each additional device. If a device is removed and subsequently reattached + // during the same application session, it will have a new deviceID. + unsigned int deviceID; + + // Human-readable device name + const char * description; + + // USB vendor/product IDs as returned by the driver. Can be used to determine the particular model of device represented. + int vendorID; + int productID; + + // Number of axis elements belonging to the device + unsigned int numAxes; + + // Number of button elements belonging to the device + unsigned int numButtons; + + // Array[numAxes] of values representing the current state of each axis, in the range [-1..1] + float * axisStates; + + // Array[numButtons] of values representing the current state of each button + bool * buttonStates; + + // Platform-specific device data storage. Don't touch unless you know what you're doing and don't + // mind your code breaking in future versions of this library. + void * privateData; +}; */ -static int verbose = 1; //whether printf prints out information about events -//"It appears to define some callbacks here" +bool verbose = false; + +char *verboseMessage=(char*)malloc(1000); + +void onButtonDown(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context) { + //buttonId is base 0 + + if (verbose) { + sprintf(verboseMessage,"Button %u down on device %u at %f with context %p\n", buttonID, device->deviceID, timestamp, context); + cout<used==1){ + if (d->type==1){ + controller++; + if (d->handle_int==device->deviceID){ + + //ON STRIG event + static int32 i; + if (controller<=256&&button<=255){//within supported range + i=(controller-1)*256+button; + if (onstrig[i].active){ + if (onstrig[i].id){ + if (onstrig[i].active==1){//(1)ON + onstrig[i].state++; + }else{//(2)STOP + onstrig[i].state=1; + } + qbevent=1; + } + } + }//within supported range + + uint8 *cp,*cp2; + if (d->queued_events==d->max_events){//expand/shift event buffer + if (d->max_events>=QUEUED_EVENTS_LIMIT){ + //discard base message + memmove(d->events,d->events+d->event_size,(d->queued_events-1)*d->event_size); + d->queued_events--; + }else{ + cp=(uint8*)calloc(d->max_events*2,d->event_size); + memcpy(cp,d->events,d->queued_events*d->event_size);//copy existing events + cp2=d->events; + d->events=cp; + free(cp2); + d->max_events*=2; + } + } + memmove(d->events+d->queued_events*d->event_size,d->events+(d->queued_events-1)*d->event_size,d->event_size);//duplicate last event + *(int64*)(d->events+(d->queued_events*d->event_size)+(d->event_size-8))=device_event_index++;//store global event index + //make required changes + *(d->events+(d->queued_events*d->event_size)+button)=1; + d->queued_events++; + //set STRIG_button_pressed for button + if (button>=0&&button<=255){ + d->STRIG_button_pressed[button]=1; + } + }//js index + }//type==1 + }//used + }//di -bool onButtonDown(void * sender, const char * eventID, void * eventData, void * context) { - struct Gamepad_buttonEvent * event; - - event = eventData; - if (verbose) { - printf("Button %u down (%d) on device %u at %f\n", event->buttonID, (int) event->down, event->device->deviceID, event->timestamp); - } - return true; } -bool onButtonUp(void * sender, const char * eventID, void * eventData, void * context) { - struct Gamepad_buttonEvent * event; - - event = eventData; - if (verbose) { - printf("Button %u up (%d) on device %u at %f\n", event->buttonID, (int) event->down, event->device->deviceID, event->timestamp); - } - return true; +void onButtonUp(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context) { + if (verbose) { + sprintf(verboseMessage,"Button %u up on device %u at %f with context %p\n", buttonID, device->deviceID, timestamp, context); + cout<used==1){ + if (d->type==1){ + if (d->handle_int==device->deviceID){ + uint8 *cp,*cp2; + if (d->queued_events==d->max_events){//expand/shift event buffer + if (d->max_events>=QUEUED_EVENTS_LIMIT){ + //discard base message + memmove(d->events,d->events+d->event_size,(d->queued_events-1)*d->event_size); + d->queued_events--; + }else{ + cp=(uint8*)calloc(d->max_events*2,d->event_size); + memcpy(cp,d->events,d->queued_events*d->event_size);//copy existing events + cp2=d->events; + d->events=cp; + free(cp2); + d->max_events*=2; + } + } + memmove(d->events+d->queued_events*d->event_size,d->events+(d->queued_events-1)*d->event_size,d->event_size);//duplicate last event + *(int64*)(d->events+(d->queued_events*d->event_size)+(d->event_size-8))=device_event_index++;//store global event index + //make required changes + *(d->events+(d->queued_events*d->event_size)+button)=0; + d->queued_events++; + }//js index + }//type==1 + }//used + }//di + } -bool onAxisMoved(void * sender, const char * eventID, void * eventData, void * context) { - struct Gamepad_axisEvent * event; - - event = eventData; - if (verbose) { - printf("Axis %u moved to %f on device %u at %f\n", event->axisID, event->value, event->device->deviceID, event->timestamp); - } - return true; +void onAxisMoved(struct Gamepad_device * device, unsigned int axisID, float value, float lastValue, double timestamp, void * context) { + if (verbose) { + sprintf(verboseMessage,"Axis %u moved from %f to %f on device %u at %f with context %p\n", axisID, lastValue, value, device->deviceID, timestamp, context); + cout<used==1){ + if (d->type==1){ + if (d->handle_int==device->deviceID){ + uint8 *cp,*cp2; + if (d->queued_events==d->max_events){//expand/shift event buffer + if (d->max_events>=QUEUED_EVENTS_LIMIT){ + //discard base message + memmove(d->events,d->events+d->event_size,(d->queued_events-1)*d->event_size); + d->queued_events--; + }else{ + cp=(uint8*)calloc(d->max_events*2,d->event_size); + memcpy(cp,d->events,d->queued_events*d->event_size);//copy existing events + cp2=d->events; + d->events=cp; + free(cp2); + d->max_events*=2; + } + } + memmove(d->events+d->queued_events*d->event_size,d->events+(d->queued_events-1)*d->event_size,d->event_size);//duplicate last event + *(int64*)(d->events+(d->queued_events*d->event_size)+(d->event_size-8))=device_event_index++;//store global event index + //make required changes + float f; + f=value; + /* + if (f==-32768) f=-32767; + f/=32767.0; + */ + if (f>1.0) f=1.0; + if (f<-1.0) f=-1.0; + int32 o; + o=d->lastbutton+axis*4; + *(float*)(d->events+(d->queued_events*d->event_size)+o)=f; + d->queued_events++; + }//js index + }//type==1 + }//used + }//di + } -bool onDeviceAttached(void * sender, const char * eventID, void * eventData, void * context) { - struct Gamepad_device * device; - - device = eventData; - if (verbose) { - printf("Device ID %u attached (vendor = 0x%X; product = 0x%X)\n", device->deviceID, device->vendorID, device->productID); - } - device->eventDispatcher->registerForEvent(device->eventDispatcher, GAMEPAD_EVENT_BUTTON_DOWN, onButtonDown, device); - device->eventDispatcher->registerForEvent(device->eventDispatcher, GAMEPAD_EVENT_BUTTON_UP, onButtonUp, device); - device->eventDispatcher->registerForEvent(device->eventDispatcher, GAMEPAD_EVENT_AXIS_MOVED, onAxisMoved, device); - return true; +void onDeviceAttached(struct Gamepad_device * device, void * context) { + if (verbose) { + sprintf(verboseMessage,"Device ID %u attached (vendor = 0x%X; product = 0x%X) with context %p\n", device->deviceID, device->vendorID, device->productID, context); + cout<productID==devices[i].product_id){ +if (device->vendorID==devices[i].vendor_id){ +if (device->numAxes==devices[i].axes){ +if (device->numButtons==devices[i].buttons){ +//(sometimes when gamepads are re-plugged they receieve a generic name) +//if (strlen(device->description)==strlen(devices[i].description)){//same name length +//if (strcmp(device->description,devices[i].description)==0){//same name content + //re-acquire device + devices[i].handle_int=device->deviceID; + if (!devices[i].connected){ + devices[i].connected=1; + devices[i].name[strlen(devices[i].name)-14]=0;//Remove "[DISCONNECTED]" + } + devices[i].used=1; + return; +//} +//} +} +} +} +} } -bool onDeviceRemoved(void * sender, const char * eventID, void * eventData, void * context) { - struct Gamepad_device * device; - - device = eventData; - if (verbose) { - printf("Device ID %u removed\n", device->deviceID); - } - return true; +} +} +}//i + +//add new device +i=device_last+1; +if (i>device_max){ + device_struct *devices=(device_struct*)realloc(devices,(device_max*2+1)*sizeof(device_struct)); + device_max*=2; +} +memset(&devices[i],0,sizeof(device_struct)); +devices[i].type=1; +devices[i].description=strdup(device->description); +devices[i].handle_int=device->deviceID; +devices[i].buttons=device->numButtons; +devices[i].lastbutton=devices[i].buttons; +devices[i].axes=device->numAxes; +devices[i].lastaxis=devices[i].axes; +devices[i].product_id=device->productID; +devices[i].vendor_id=device->vendorID; +char name[1000]; +strcpy (name,"[CONTROLLER][[NAME]["); +strcat (name,devices[i].description); +strcat (name,"]]"); +if (devices[i].lastbutton) strcat (name,"[BUTTON]"); +if (devices[i].lastaxis) strcat (name,"[AXIS]"); +if (devices[i].lastwheel) strcat (name,"[WHEEL]"); +devices[i].name=strdup(name); +//calculate queue message size +x=devices[i].lastbutton+(devices[i].lastaxis+devices[i].lastwheel)*4+8; +devices[i].event_size=x; +//create initial 'current' and 'previous' events +devices[i].events=(uint8*)calloc(2,x); +devices[i].max_events=2; +devices[i].queued_events=2; +devices[i].connected=1; +devices[i].used=1; +device_last=i; + } -const char * Target_getName() { - return "Gamepad test harness"; +void onDeviceRemoved(struct Gamepad_device * device, void * context) { + if (verbose) { + sprintf(verboseMessage,"Device ID %u removed with context %p\n", device->deviceID, context); + cout<deviceID){ + + char name[1000]; + strcpy(name,devices[i].name); + strcat(name,"[DISCONNECTED]"); + char *oldname=devices[i].name; + devices[i].name=strdup(name); + free(oldname); + devices[i].connected=0; +} +} +} +}//i + + } -//"initialization code is here, in its own function..." static void initGamepad() { - Gamepad_eventDispatcher()->registerForEvent(Gamepad_eventDispatcher(), GAMEPAD_EVENT_DEVICE_ATTACHED, onDeviceAttached, NULL); - Gamepad_eventDispatcher()->registerForEvent(Gamepad_eventDispatcher(), GAMEPAD_EVENT_DEVICE_REMOVED, onDeviceRemoved, NULL); - Gamepad_init(); + Gamepad_deviceAttachFunc(onDeviceAttached, (void *) 0x1); + Gamepad_deviceRemoveFunc(onDeviceRemoved, (void *) 0x2); + Gamepad_buttonDownFunc(onButtonDown, (void *) 0x3); + Gamepad_buttonUpFunc(onButtonUp, (void *) 0x4); + Gamepad_axisMoveFunc(onAxisMoved, (void *) 0x5); + Gamepad_init(); } - -void QB64_GameControllerInit(){ -initGamepad(); +void QB64_GAMEPAD_INIT(){ + initGamepad(); } - - - - - - - - - - - - - - - - - - - - - - - - - - - -//----------------------EVERYTHING PAST HERE IS FLUFF ---------------------------- -// (and has been commented out!) -/* - -//"OMG wtf is this function????? some kind of glutty, shelly thing-a-me-bob" -static unsigned int windowWidth = 800, windowHeight = 600; - -void Target_init(int argc, char ** argv) { - int argIndex; - - for (argIndex = 1; argIndex < argc; argIndex++) { - if (!strcmp(argv[argIndex], "-v")) { - verbose = true; - } - } - - initGamepad(); - - glClearColor(1.0f, 1.0f, 1.0f, 0.0f); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0f, windowWidth, windowHeight, 0.0f, -1.0f, 1.0f); - glMatrixMode(GL_MODELVIEW); - - Shell_mainLoop(); +void QB64_GAMEPAD_POLL(){ + Gamepad_detectDevices(); + Gamepad_processEvents(); } -//"why is this even here?" -static void drawGlutString(int rasterPosX, int rasterPosY, const char * string) { - size_t length, charIndex; - - glRasterPos2i(rasterPosX, rasterPosY); - length = strlen(string); - for (charIndex = 0; charIndex < length; charIndex++) { - glutBitmapCharacter(GLUT_BITMAP_8_BY_13, string[charIndex]); - } +void QB64_GAMEPAD_SHUTDOWN(){ + Gamepad_deviceAttachFunc(NULL, (void *) 0x1); + Gamepad_deviceRemoveFunc(NULL, (void *) 0x2); + Gamepad_buttonDownFunc(NULL, (void *) 0x3); + Gamepad_buttonUpFunc(NULL, (void *) 0x4); + Gamepad_axisMoveFunc(NULL, (void *) 0x5); + Gamepad_shutdown(); } - -#define POLL_ITERATION_INTERVAL 30 - -void Target_draw() { - unsigned int gamepadIndex; - struct Gamepad_device * device; - unsigned int axesPerRow, buttonsPerRow; - unsigned int axisRowIndex, axisIndex; - unsigned int buttonRowIndex, buttonIndex; - float axisState; - char indexString[16]; - static unsigned int iterationsToNextPoll = POLL_ITERATION_INTERVAL; - char descriptionString[256]; - - iterationsToNextPoll--; - if (iterationsToNextPoll == 0) { - Gamepad_detectDevices(); - iterationsToNextPoll = POLL_ITERATION_INTERVAL; - } - Gamepad_processEvents(); - - axesPerRow = (windowWidth - 10) / 60; - buttonsPerRow = (windowWidth - 10) / 30; - - glClear(GL_COLOR_BUFFER_BIT); - glLoadIdentity(); - glTranslatef(5.0f, 20.0f, 0.0f); - for (gamepadIndex = 0; gamepadIndex < Gamepad_numDevices(); gamepadIndex++) { - device = Gamepad_deviceAtIndex(gamepadIndex); - - glColor3f(0.0f, 0.0f, 0.0f); - snprintf(descriptionString, 256, "%s (0x%X 0x%X %u)", device->description, device->vendorID, device->productID, device->deviceID); - drawGlutString(0, 0, descriptionString); - - for (axisRowIndex = 0; axisRowIndex <= device->numAxes / axesPerRow; axisRowIndex++) { - glPushMatrix(); - for (axisIndex = axisRowIndex * axesPerRow; axisIndex < (axisRowIndex + 1) * axesPerRow && axisIndex < device->numAxes; axisIndex++) { - axisState = device->axisStates[axisIndex]; - - sprintf(indexString, "a%d", axisIndex); - glColor3f(0.0f, 0.0f, 0.0f); - drawGlutString(2, 28, indexString); - - glBegin(GL_QUADS); - glVertex2f(2.0f, 5.0f); - glVertex2f(58.0f, 5.0f); - glVertex2f(58.0f, 15.0f); - glVertex2f(2.0f, 15.0f); - glColor3f(0.5f, 1.0f, 0.5f); - glVertex2f(29.0f + axisState * 26, 6.0f); - glVertex2f(31.0f + axisState * 26, 6.0f); - glVertex2f(31.0f + axisState * 26, 14.0f); - glVertex2f(29.0f + axisState * 26, 14.0f); - glEnd(); - glTranslatef(60.0f, 0.0f, 0.0f); - } - glPopMatrix(); - glTranslatef(0.0f, 32.0f, 0.0f); - } - - for (buttonRowIndex = 0; buttonRowIndex <= device->numButtons / buttonsPerRow; buttonRowIndex++) { - glPushMatrix(); - for (buttonIndex = buttonRowIndex * buttonsPerRow; buttonIndex < (buttonRowIndex + 1) * buttonsPerRow && buttonIndex < device->numButtons; buttonIndex++) { - sprintf(indexString, "b%d", buttonIndex); - glColor3f(0.0f, 0.0f, 0.0f); - drawGlutString(2, 32, indexString); - - glBegin(GL_QUADS); - glColor3f(0.0f, 0.0f, 0.0f); - glVertex2f(2.0f, 2.0f); - glVertex2f(28.0f, 2.0f); - glVertex2f(28.0f, 18.0f); - glVertex2f(2.0f, 18.0f); - if (device->buttonStates[buttonIndex]) { - glColor3f(0.5f, 1.0f, 0.5f); - glVertex2f(3.0f, 3.0f); - glVertex2f(27.0f, 3.0f); - glVertex2f(27.0f, 17.0f); - glVertex2f(3.0f, 17.0f); - } - glEnd(); - glTranslatef(30.0f, 0.0f, 0.0f); - } - glPopMatrix(); - glTranslatef(0.0f, 38.0f, 0.0f); - } - glTranslatef(0.0f, 40.0f, 0.0f); - } - - if (gamepadIndex == 0) { - glLoadIdentity(); - glTranslatef(5.0f, 20.0f, 0.0f); - glColor3f(0.0f, 0.0f, 0.0f); - drawGlutString(0, 0, "No devices found; plug in a USB gamepad and it will be detected automatically"); - } - - Shell_redisplay(); -} - -void Target_keyDown(int charCode, int keyCode) { - if (keyCode == KEYBOARD_R) { - Gamepad_shutdown(); - initGamepad(); - } -} - -void Target_keyUp(int charCode, int keyCode) { -} - -void Target_mouseDown(int buttonNumber, float x, float y) { -} - -void Target_mouseUp(int buttonNumber, float x, float y) { -} - -void Target_mouseMoved(float x, float y) { -} - -void Target_mouseDragged(int buttonMask, float x, float y) { -} - -void Target_resized(int newWidth, int newHeight) { - windowWidth = newWidth; - windowHeight = newHeight; - glViewport(0, 0, newWidth, newHeight); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0f, windowWidth, windowHeight, 0.0f, -1.0f, 1.0f); - glMatrixMode(GL_MODELVIEW); -} -*/ \ No newline at end of file diff --git a/internal/c/parts/input/game_controller/src/Gamepad_linux.c b/internal/c/parts/input/game_controller/src/Gamepad_linux.c index 7135586d9..ca5d53dfa 100644 --- a/internal/c/parts/input/game_controller/src/Gamepad_linux.c +++ b/internal/c/parts/input/game_controller/src/Gamepad_linux.c @@ -1,440 +1,454 @@ -/* - Copyright (c) 2010 Alex Diener - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Alex Diener adiener@sacredsoftware.net -*/ - -#include "gamepad/Gamepad.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct Gamepad_devicePrivate { - pthread_t thread; - int fd; - char * path; - int buttonMap[KEY_CNT - BTN_MISC]; - int axisMap[ABS_CNT]; - struct input_absinfo axisInfo[ABS_CNT]; -}; - -struct Gamepad_queuedEvent { - EventDispatcher * dispatcher; - const char * eventType; - void * eventData; -}; - -static struct Gamepad_device ** devices = NULL; -static unsigned int numDevices = 0; -static unsigned int nextDeviceID = 0; -static pthread_mutex_t devicesMutex; - -static struct Gamepad_queuedEvent * eventQueue = NULL; -static size_t eventQueueSize = 0; -static size_t eventCount = 0; -static pthread_mutex_t eventQueueMutex; - -static EventDispatcher * eventDispatcher = NULL; -static bool inited = false; - -#define test_bit(bitIndex, array) \ - ((array[(bitIndex) / (sizeof(int) * 8)] >> ((bitIndex) % (sizeof(int) * 8))) & 0x1) - -static char ** findGamepadPaths(unsigned int * outNumGamepads) { - DIR * dev_input; - struct dirent * entity; - unsigned int numGamepads = 0; - char ** gamepadDevs = NULL; - unsigned int charsConsumed; - int num; - int fd; - int evCapBits[(EV_CNT - 1) / sizeof(int) * 8 + 1]; - int evKeyBits[(KEY_CNT - 1) / sizeof(int) * 8 + 1]; - int evAbsBits[(ABS_CNT - 1) / sizeof(int) * 8 + 1]; - char fileName[PATH_MAX]; - - dev_input = opendir("/dev/input"); - if (dev_input != NULL) { - for (entity = readdir(dev_input); entity != NULL; entity = readdir(dev_input)) { - charsConsumed = 0; - if (sscanf(entity->d_name, "event%d%n", &num, &charsConsumed) && charsConsumed == strlen(entity->d_name)) { - snprintf(fileName, PATH_MAX, "/dev/input/%s", entity->d_name); - fd = open(fileName, O_RDONLY, 0); - memset(evCapBits, 0, sizeof(evCapBits)); - memset(evKeyBits, 0, sizeof(evKeyBits)); - memset(evAbsBits, 0, sizeof(evAbsBits)); - if (ioctl(fd, EVIOCGBIT(0, sizeof(evCapBits)), evCapBits) < 0 || - ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(evKeyBits)), evKeyBits) < 0 || - ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(evAbsBits)), evAbsBits) < 0) { - close(fd); - continue; - } - if (!test_bit(EV_KEY, evCapBits) || !test_bit(EV_ABS, evCapBits) || - !test_bit(ABS_X, evAbsBits) || !test_bit(ABS_Y, evAbsBits) || - (!test_bit(BTN_TRIGGER, evKeyBits) && !test_bit(BTN_A, evKeyBits) && !test_bit(BTN_1, evKeyBits))) { - close(fd); - continue; - } - close(fd); - - numGamepads++; - gamepadDevs = realloc(gamepadDevs, sizeof(char *) * numGamepads); - gamepadDevs[numGamepads - 1] = malloc(strlen(fileName) + 1); - strcpy(gamepadDevs[numGamepads - 1], fileName); - } - } - closedir(dev_input); - } - - *outNumGamepads = numGamepads; - return gamepadDevs; -} - -void Gamepad_init() { - if (!inited) { - pthread_mutex_init(&devicesMutex, NULL); - pthread_mutex_init(&eventQueueMutex, NULL); - inited = true; - Gamepad_detectDevices(); - } -} - -static void disposeDevice(struct Gamepad_device * device) { - device->eventDispatcher->dispose(device->eventDispatcher); - - close(((struct Gamepad_devicePrivate *) device->privateData)->fd); - free(((struct Gamepad_devicePrivate *) device->privateData)->path); - free(device->privateData); - - free((void *) device->description); - free(device->axisStates); - free(device->buttonStates); - free(device->eventDispatcher); - - free(device); -} - -void Gamepad_shutdown() { - if (inited) { - unsigned int eventIndex; - unsigned int devicesLeft; - unsigned int gamepadIndex; - - do { - pthread_mutex_lock(&devicesMutex); - devicesLeft = numDevices; - if (devicesLeft > 0) { - pthread_t thread; - - thread = ((struct Gamepad_devicePrivate *) devices[0]->privateData)->thread; - pthread_cancel(thread); - pthread_join(thread, NULL); - - numDevices--; - for (gamepadIndex = 0; gamepadIndex < numDevices; gamepadIndex++) { - devices[gamepadIndex] = devices[gamepadIndex + 1]; - } - } - pthread_mutex_unlock(&devicesMutex); - } while (devicesLeft > 0); - - pthread_mutex_destroy(&devicesMutex); - pthread_mutex_destroy(&eventQueueMutex); - free(devices); - devices = NULL; - - for (eventIndex = 0; eventIndex < eventCount; eventIndex++) { - if (!strcmp(eventQueue[eventIndex].eventType, GAMEPAD_EVENT_DEVICE_REMOVED)) { - disposeDevice(eventQueue[eventIndex].eventData); - } - } - - eventQueueSize = 0; - eventCount = 0; - free(eventQueue); - eventQueue = NULL; - - if (eventDispatcher != NULL) { - eventDispatcher->dispose(eventDispatcher); - free(eventDispatcher); - eventDispatcher = NULL; - } - - inited = false; - } -} - -EventDispatcher * Gamepad_eventDispatcher() { - if (eventDispatcher == NULL) { - eventDispatcher = EventDispatcher_create(NULL); - } - return eventDispatcher; -} - -unsigned int Gamepad_numDevices() { - unsigned int result; - - pthread_mutex_lock(&devicesMutex); - result = numDevices; - pthread_mutex_unlock(&devicesMutex); - return result; -} - -struct Gamepad_device * Gamepad_deviceAtIndex(unsigned int deviceIndex) { - struct Gamepad_device * result; - - pthread_mutex_lock(&devicesMutex); - if (deviceIndex >= numDevices) { - result = NULL; - } else { - result = devices[deviceIndex]; - } - pthread_mutex_unlock(&devicesMutex); - - return result; -} - -static void queueEvent(EventDispatcher * dispatcher, const char * eventType, void * eventData) { - struct Gamepad_queuedEvent queuedEvent; - - queuedEvent.dispatcher = dispatcher; - queuedEvent.eventType = eventType; - queuedEvent.eventData = eventData; - - pthread_mutex_lock(&eventQueueMutex); - if (eventCount >= eventQueueSize) { - eventQueueSize = eventQueueSize == 0 ? 1 : eventQueueSize * 2; - eventQueue = realloc(eventQueue, sizeof(struct Gamepad_queuedEvent) * eventQueueSize); - } - eventQueue[eventCount++] = queuedEvent; - pthread_mutex_unlock(&eventQueueMutex); -} - -static void queueAxisEvent(struct Gamepad_device * device, double timestamp, unsigned int axisID, float value) { - struct Gamepad_axisEvent * axisEvent; - - axisEvent = malloc(sizeof(struct Gamepad_axisEvent)); - axisEvent->device = device; - axisEvent->timestamp = timestamp; - axisEvent->axisID = axisID; - axisEvent->value = value; - - queueEvent(device->eventDispatcher, GAMEPAD_EVENT_AXIS_MOVED, axisEvent); -} - -static void queueButtonEvent(struct Gamepad_device * device, double timestamp, unsigned int buttonID, bool down) { - struct Gamepad_buttonEvent * buttonEvent; - - buttonEvent = malloc(sizeof(struct Gamepad_buttonEvent)); - buttonEvent->device = device; - buttonEvent->timestamp = timestamp; - buttonEvent->buttonID = buttonID; - buttonEvent->down = down; - - queueEvent(device->eventDispatcher, down ? GAMEPAD_EVENT_BUTTON_DOWN : GAMEPAD_EVENT_BUTTON_UP, buttonEvent); -} - -static void * deviceThread(void * context) { - unsigned int gamepadIndex; - struct Gamepad_device * device; - struct Gamepad_devicePrivate * devicePrivate; - struct input_event event; - - device = context; - devicePrivate = device->privateData; - - while (read(devicePrivate->fd, &event, sizeof(struct input_event)) > 0) { - if (event.type == EV_ABS) { - float value; - - if (event.code > ABS_MAX || devicePrivate->axisMap[event.code] == -1) { - continue; - } - - value = (event.value - devicePrivate->axisInfo[event.code].minimum) / (float) (devicePrivate->axisInfo[event.code].maximum - devicePrivate->axisInfo[event.code].minimum) * 2.0f - 1.0f; - queueAxisEvent(device, - event.time.tv_sec + event.time.tv_usec * 0.000001, - devicePrivate->axisMap[event.code], - value); - - device->axisStates[devicePrivate->axisMap[event.code]] = value; - - } else if (event.type == EV_KEY) { - if (event.code < BTN_MISC || event.code > KEY_MAX || devicePrivate->buttonMap[event.code - BTN_MISC] == -1) { - continue; - } - - queueButtonEvent(device, - event.time.tv_sec + event.time.tv_usec * 0.000001, - devicePrivate->buttonMap[event.code - BTN_MISC], - !!event.value); - - device->buttonStates[devicePrivate->buttonMap[event.code - BTN_MISC]] = !!event.value; - } - } - - queueEvent(eventDispatcher, GAMEPAD_EVENT_DEVICE_REMOVED, device); - - pthread_mutex_lock(&devicesMutex); - for (gamepadIndex = 0; gamepadIndex < numDevices; gamepadIndex++) { - if (devices[gamepadIndex] == device) { - unsigned int gamepadIndex2; - - numDevices--; - for (gamepadIndex2 = gamepadIndex; gamepadIndex2 < numDevices; gamepadIndex2++) { - devices[gamepadIndex2] = devices[gamepadIndex2 + 1]; - } - gamepadIndex--; - } - } - pthread_mutex_unlock(&devicesMutex); - - return NULL; -} - -void Gamepad_detectDevices() { - unsigned int numPaths; - char ** gamepadPaths; - bool duplicate; - unsigned int pathIndex, gamepadIndex; - struct stat statBuf; - struct Gamepad_device * deviceRecord; - struct Gamepad_devicePrivate * deviceRecordPrivate; - int fd; - char name[128]; - char * description; - int evKeyBits[(KEY_CNT - 1) / sizeof(int) * 8 + 1]; - int evAbsBits[(ABS_CNT - 1) / sizeof(int) * 8 + 1]; - int bit; - struct input_id id; - - if (!inited) { - return; - } - - gamepadPaths = findGamepadPaths(&numPaths); - - pthread_mutex_lock(&devicesMutex); - for (pathIndex = 0; pathIndex < numPaths; pathIndex++) { - duplicate = false; - for (gamepadIndex = 0; gamepadIndex < numDevices; gamepadIndex++) { - if (!strcmp(((struct Gamepad_devicePrivate *) devices[gamepadIndex]->privateData)->path, gamepadPaths[pathIndex])) { - duplicate = true; - break; - } - } - if (duplicate) { - free(gamepadPaths[pathIndex]); - continue; - } - - if (!stat(gamepadPaths[pathIndex], &statBuf)) { - deviceRecord = malloc(sizeof(struct Gamepad_device)); - deviceRecord->deviceID = nextDeviceID++; - deviceRecord->eventDispatcher = EventDispatcher_create(deviceRecord); - devices = realloc(devices, sizeof(struct Gamepad_device *) * (numDevices + 1)); - devices[numDevices++] = deviceRecord; - - fd = open(gamepadPaths[pathIndex], O_RDONLY, 0); - - deviceRecordPrivate = malloc(sizeof(struct Gamepad_devicePrivate)); - deviceRecordPrivate->fd = fd; - deviceRecordPrivate->path = gamepadPaths[pathIndex]; - memset(deviceRecordPrivate->buttonMap, 0xFF, sizeof(deviceRecordPrivate->buttonMap)); - memset(deviceRecordPrivate->axisMap, 0xFF, sizeof(deviceRecordPrivate->axisMap)); - deviceRecord->privateData = deviceRecordPrivate; - - if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) > 0) { - description = malloc(strlen(name + 1)); - strcpy(description, name); - } else { - description = malloc(strlen(gamepadPaths[pathIndex] + 1)); - strcpy(description, gamepadPaths[pathIndex]); - } - deviceRecord->description = description; - - if (!ioctl(fd, EVIOCGID, &id)) { - deviceRecord->vendorID = id.vendor; - deviceRecord->productID = id.product; - } else { - deviceRecord->vendorID = deviceRecord->productID = 0; - } - - memset(evKeyBits, 0, sizeof(evKeyBits)); - memset(evAbsBits, 0, sizeof(evAbsBits)); - ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(evKeyBits)), evKeyBits); - ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(evAbsBits)), evAbsBits); - - deviceRecord->numAxes = 0; - for (bit = 0; bit < ABS_CNT; bit++) { - if (test_bit(bit, evAbsBits)) { - if (ioctl(fd, EVIOCGABS(bit), &deviceRecordPrivate->axisInfo[bit]) < 0 || - deviceRecordPrivate->axisInfo[bit].minimum == deviceRecordPrivate->axisInfo[bit].maximum) { - continue; - } - deviceRecordPrivate->axisMap[bit] = deviceRecord->numAxes; - deviceRecord->numAxes++; - } - } - deviceRecord->numButtons = 0; - for (bit = BTN_MISC; bit < KEY_CNT; bit++) { - if (test_bit(bit, evKeyBits)) { - deviceRecordPrivate->buttonMap[bit - BTN_MISC] = deviceRecord->numButtons; - deviceRecord->numButtons++; - } - } - - deviceRecord->axisStates = calloc(sizeof(float), deviceRecord->numAxes); - deviceRecord->buttonStates = calloc(sizeof(bool), deviceRecord->numButtons); - - Gamepad_eventDispatcher()->dispatchEvent(Gamepad_eventDispatcher(), GAMEPAD_EVENT_DEVICE_ATTACHED, deviceRecord); - - pthread_create(&deviceRecordPrivate->thread, NULL, deviceThread, deviceRecord); - } - } - pthread_mutex_unlock(&devicesMutex); -} - -void Gamepad_processEvents() { - unsigned int eventIndex; - - if (!inited) { - return; - } - - pthread_mutex_lock(&eventQueueMutex); - for (eventIndex = 0; eventIndex < eventCount; eventIndex++) { - eventQueue[eventIndex].dispatcher->dispatchEvent(eventQueue[eventIndex].dispatcher, eventQueue[eventIndex].eventType, eventQueue[eventIndex].eventData); - if (!strcmp(eventQueue[eventIndex].eventType, GAMEPAD_EVENT_DEVICE_REMOVED)) { - disposeDevice(eventQueue[eventIndex].eventData); - } - } - eventCount = 0; - pthread_mutex_unlock(&eventQueueMutex); -} - +/* + Copyright (c) 2014 Alex Diener + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Alex Diener alex@ludobloom.com +*/ + +#include "gamepad/Gamepad.h" +#include "gamepad/Gamepad_private.h" +#include +#include +#include +#include +#include +#define __USE_UNIX98 +#include +#include +#include +#include +#include +#include +#include + +struct Gamepad_devicePrivate { + pthread_t thread; + int fd; + char * path; + int buttonMap[KEY_CNT - BTN_MISC]; + int axisMap[ABS_CNT]; + struct input_absinfo axisInfo[ABS_CNT]; +}; + +struct Gamepad_queuedEvent { + unsigned int deviceID; + enum Gamepad_eventType eventType; + void * eventData; +}; + +static struct Gamepad_device ** devices = NULL; +static unsigned int numDevices = 0; +static unsigned int nextDeviceID = 0; +static pthread_mutex_t devicesMutex; + +static struct Gamepad_queuedEvent * eventQueue = NULL; +static size_t eventQueueSize = 0; +static size_t eventCount = 0; +static pthread_mutex_t eventQueueMutex; + +static bool inited = false; + +#define test_bit(bitIndex, array) \ + ((array[(bitIndex) / (sizeof(int) * 8)] >> ((bitIndex) % (sizeof(int) * 8))) & 0x1) + +void Gamepad_init() { + if (!inited) { + pthread_mutexattr_t recursiveLock; + pthread_mutexattr_init(&recursiveLock); + pthread_mutexattr_settype(&recursiveLock, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&devicesMutex, &recursiveLock); + pthread_mutex_init(&eventQueueMutex, &recursiveLock); + inited = true; + Gamepad_detectDevices(); + } +} + +static void disposeDevice(struct Gamepad_device * device) { + close(((struct Gamepad_devicePrivate *) device->privateData)->fd); + free(((struct Gamepad_devicePrivate *) device->privateData)->path); + free(device->privateData); + + free((void *) device->description); + free(device->axisStates); + free(device->buttonStates); + + free(device); +} + +void Gamepad_shutdown() { + if (inited) { + unsigned int eventIndex; + unsigned int devicesLeft; + unsigned int gamepadIndex; + + do { + pthread_mutex_lock(&devicesMutex); + devicesLeft = numDevices; + if (devicesLeft > 0) { + pthread_t thread; + + thread = ((struct Gamepad_devicePrivate *) devices[0]->privateData)->thread; + pthread_cancel(thread); + pthread_join(thread, NULL); + + numDevices--; + for (gamepadIndex = 0; gamepadIndex < numDevices; gamepadIndex++) { + devices[gamepadIndex] = devices[gamepadIndex + 1]; + } + } + pthread_mutex_unlock(&devicesMutex); + } while (devicesLeft > 0); + + pthread_mutex_destroy(&devicesMutex); + pthread_mutex_destroy(&eventQueueMutex); + free(devices); + devices = NULL; + + for (eventIndex = 0; eventIndex < eventCount; eventIndex++) { + if (eventQueue[eventIndex].eventType == GAMEPAD_EVENT_DEVICE_REMOVED) { + disposeDevice(eventQueue[eventIndex].eventData); + } + } + + eventQueueSize = 0; + eventCount = 0; + free(eventQueue); + eventQueue = NULL; + + inited = false; + } +} + +unsigned int Gamepad_numDevices() { + unsigned int result; + + pthread_mutex_lock(&devicesMutex); + result = numDevices; + pthread_mutex_unlock(&devicesMutex); + return result; +} + +struct Gamepad_device * Gamepad_deviceAtIndex(unsigned int deviceIndex) { + struct Gamepad_device * result; + + pthread_mutex_lock(&devicesMutex); + if (deviceIndex >= numDevices) { + result = NULL; + } else { + result = devices[deviceIndex]; + } + pthread_mutex_unlock(&devicesMutex); + + return result; +} + +static void queueEvent(unsigned int deviceID, enum Gamepad_eventType eventType, void * eventData) { + struct Gamepad_queuedEvent queuedEvent; + + queuedEvent.deviceID = deviceID; + queuedEvent.eventType = eventType; + queuedEvent.eventData = eventData; + + pthread_mutex_lock(&eventQueueMutex); + if (eventCount >= eventQueueSize) { + eventQueueSize = eventQueueSize == 0 ? 1 : eventQueueSize * 2; + eventQueue = realloc(eventQueue, sizeof(struct Gamepad_queuedEvent) * eventQueueSize); + } + eventQueue[eventCount++] = queuedEvent; + pthread_mutex_unlock(&eventQueueMutex); +} + +static void queueAxisEvent(struct Gamepad_device * device, double timestamp, unsigned int axisID, float value, float lastValue) { + struct Gamepad_axisEvent * axisEvent; + + axisEvent = malloc(sizeof(struct Gamepad_axisEvent)); + axisEvent->device = device; + axisEvent->timestamp = timestamp; + axisEvent->axisID = axisID; + axisEvent->value = value; + axisEvent->lastValue = lastValue; + + queueEvent(device->deviceID, GAMEPAD_EVENT_AXIS_MOVED, axisEvent); +} + +static void queueButtonEvent(struct Gamepad_device * device, double timestamp, unsigned int buttonID, bool down) { + struct Gamepad_buttonEvent * buttonEvent; + + buttonEvent = malloc(sizeof(struct Gamepad_buttonEvent)); + buttonEvent->device = device; + buttonEvent->timestamp = timestamp; + buttonEvent->buttonID = buttonID; + buttonEvent->down = down; + + queueEvent(device->deviceID, down ? GAMEPAD_EVENT_BUTTON_DOWN : GAMEPAD_EVENT_BUTTON_UP, buttonEvent); +} + +static void * deviceThread(void * context) { + unsigned int gamepadIndex; + struct Gamepad_device * device; + struct Gamepad_devicePrivate * devicePrivate; + struct input_event event; + + device = context; + devicePrivate = device->privateData; + + while (read(devicePrivate->fd, &event, sizeof(struct input_event)) > 0) { + if (event.type == EV_ABS) { + float value; + + if (event.code > ABS_MAX || devicePrivate->axisMap[event.code] == -1) { + continue; + } + + value = (event.value - devicePrivate->axisInfo[event.code].minimum) / (float) (devicePrivate->axisInfo[event.code].maximum - devicePrivate->axisInfo[event.code].minimum) * 2.0f - 1.0f; + queueAxisEvent(device, + event.time.tv_sec + event.time.tv_usec * 0.000001, + devicePrivate->axisMap[event.code], + value, + device->axisStates[devicePrivate->axisMap[event.code]]); + + device->axisStates[devicePrivate->axisMap[event.code]] = value; + + } else if (event.type == EV_KEY) { + if (event.code < BTN_MISC || event.code > KEY_MAX || devicePrivate->buttonMap[event.code - BTN_MISC] == -1) { + continue; + } + + queueButtonEvent(device, + event.time.tv_sec + event.time.tv_usec * 0.000001, + devicePrivate->buttonMap[event.code - BTN_MISC], + !!event.value); + + device->buttonStates[devicePrivate->buttonMap[event.code - BTN_MISC]] = !!event.value; + } + } + + queueEvent(device->deviceID, GAMEPAD_EVENT_DEVICE_REMOVED, device); + + pthread_mutex_lock(&devicesMutex); + for (gamepadIndex = 0; gamepadIndex < numDevices; gamepadIndex++) { + if (devices[gamepadIndex] == device) { + unsigned int gamepadIndex2; + + numDevices--; + for (gamepadIndex2 = gamepadIndex; gamepadIndex2 < numDevices; gamepadIndex2++) { + devices[gamepadIndex2] = devices[gamepadIndex2 + 1]; + } + gamepadIndex--; + } + } + pthread_mutex_unlock(&devicesMutex); + + return NULL; +} + +void Gamepad_detectDevices() { + struct input_id id; + DIR * dev_input; + struct dirent * entity; + unsigned int charsConsumed; + int num; + int fd; + int evCapBits[(EV_CNT - 1) / sizeof(int) * 8 + 1]; + int evKeyBits[(KEY_CNT - 1) / sizeof(int) * 8 + 1]; + int evAbsBits[(ABS_CNT - 1) / sizeof(int) * 8 + 1]; + char fileName[PATH_MAX]; + bool duplicate; + unsigned int gamepadIndex; + struct stat statBuf; + struct Gamepad_device * deviceRecord; + struct Gamepad_devicePrivate * deviceRecordPrivate; + char name[128]; + char * description; + int bit; + time_t currentTime; + static time_t lastInputStatTime; + + if (!inited) { + return; + } + + pthread_mutex_lock(&devicesMutex); + + dev_input = opendir("/dev/input"); + currentTime = time(NULL); + if (dev_input != NULL) { + while ((entity = readdir(dev_input)) != NULL) { + charsConsumed = 0; + if (sscanf(entity->d_name, "event%d%n", &num, &charsConsumed) && charsConsumed == strlen(entity->d_name)) { + snprintf(fileName, PATH_MAX, "/dev/input/%s", entity->d_name); + if (stat(fileName, &statBuf) || statBuf.st_mtime < lastInputStatTime) { + continue; + } + + duplicate = false; + for (gamepadIndex = 0; gamepadIndex < numDevices; gamepadIndex++) { + if (!strcmp(((struct Gamepad_devicePrivate *) devices[gamepadIndex]->privateData)->path, fileName)) { + duplicate = true; + break; + } + } + if (duplicate) { + continue; + } + + fd = open(fileName, O_RDONLY, 0); + memset(evCapBits, 0, sizeof(evCapBits)); + memset(evKeyBits, 0, sizeof(evKeyBits)); + memset(evAbsBits, 0, sizeof(evAbsBits)); + if (ioctl(fd, EVIOCGBIT(0, sizeof(evCapBits)), evCapBits) < 0 || + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(evKeyBits)), evKeyBits) < 0 || + ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(evAbsBits)), evAbsBits) < 0) { + close(fd); + continue; + } + if (!test_bit(EV_KEY, evCapBits) || !test_bit(EV_ABS, evCapBits) || + !test_bit(ABS_X, evAbsBits) || !test_bit(ABS_Y, evAbsBits) || + (!test_bit(BTN_TRIGGER, evKeyBits) && !test_bit(BTN_A, evKeyBits) && !test_bit(BTN_1, evKeyBits))) { + close(fd); + continue; + } + + deviceRecord = malloc(sizeof(struct Gamepad_device)); + deviceRecord->deviceID = nextDeviceID++; + devices = realloc(devices, sizeof(struct Gamepad_device *) * (numDevices + 1)); + devices[numDevices++] = deviceRecord; + + deviceRecordPrivate = malloc(sizeof(struct Gamepad_devicePrivate)); + deviceRecordPrivate->fd = fd; + deviceRecordPrivate->path = malloc(strlen(fileName) + 1); + strcpy(deviceRecordPrivate->path, fileName); + memset(deviceRecordPrivate->buttonMap, 0xFF, sizeof(deviceRecordPrivate->buttonMap)); + memset(deviceRecordPrivate->axisMap, 0xFF, sizeof(deviceRecordPrivate->axisMap)); + deviceRecord->privateData = deviceRecordPrivate; + + if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) > 0) { + description = malloc(strlen(name) + 1); + strcpy(description, name); + } else { + description = malloc(strlen(fileName) + 1); + strcpy(description, fileName); + } + deviceRecord->description = description; + + if (!ioctl(fd, EVIOCGID, &id)) { + deviceRecord->vendorID = id.vendor; + deviceRecord->productID = id.product; + } else { + deviceRecord->vendorID = deviceRecord->productID = 0; + } + + memset(evKeyBits, 0, sizeof(evKeyBits)); + memset(evAbsBits, 0, sizeof(evAbsBits)); + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(evKeyBits)), evKeyBits); + ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(evAbsBits)), evAbsBits); + + deviceRecord->numAxes = 0; + for (bit = 0; bit < ABS_CNT; bit++) { + if (test_bit(bit, evAbsBits)) { + if (ioctl(fd, EVIOCGABS(bit), &deviceRecordPrivate->axisInfo[bit]) < 0 || + deviceRecordPrivate->axisInfo[bit].minimum == deviceRecordPrivate->axisInfo[bit].maximum) { + continue; + } + deviceRecordPrivate->axisMap[bit] = deviceRecord->numAxes; + deviceRecord->numAxes++; + } + } + deviceRecord->numButtons = 0; + for (bit = BTN_MISC; bit < KEY_CNT; bit++) { + if (test_bit(bit, evKeyBits)) { + deviceRecordPrivate->buttonMap[bit - BTN_MISC] = deviceRecord->numButtons; + deviceRecord->numButtons++; + } + } + + deviceRecord->axisStates = calloc(sizeof(float), deviceRecord->numAxes); + deviceRecord->buttonStates = calloc(sizeof(bool), deviceRecord->numButtons); + + if (Gamepad_deviceAttachCallback != NULL) { + Gamepad_deviceAttachCallback(deviceRecord, Gamepad_deviceAttachContext); + } + + pthread_create(&deviceRecordPrivate->thread, NULL, deviceThread, deviceRecord); + } + } + closedir(dev_input); + } + + lastInputStatTime = currentTime; + pthread_mutex_unlock(&devicesMutex); +} + +static void processQueuedEvent(struct Gamepad_queuedEvent event) { + switch (event.eventType) { + case GAMEPAD_EVENT_DEVICE_ATTACHED: + if (Gamepad_deviceAttachCallback != NULL) { + Gamepad_deviceAttachCallback(event.eventData, Gamepad_deviceAttachContext); + } + break; + + case GAMEPAD_EVENT_DEVICE_REMOVED: + if (Gamepad_deviceRemoveCallback != NULL) { + Gamepad_deviceRemoveCallback(event.eventData, Gamepad_deviceRemoveContext); + } + break; + + case GAMEPAD_EVENT_BUTTON_DOWN: + if (Gamepad_buttonDownCallback != NULL) { + struct Gamepad_buttonEvent * buttonEvent = event.eventData; + Gamepad_buttonDownCallback(buttonEvent->device, buttonEvent->buttonID, buttonEvent->timestamp, Gamepad_buttonDownContext); + } + break; + + case GAMEPAD_EVENT_BUTTON_UP: + if (Gamepad_buttonUpCallback != NULL) { + struct Gamepad_buttonEvent * buttonEvent = event.eventData; + Gamepad_buttonUpCallback(buttonEvent->device, buttonEvent->buttonID, buttonEvent->timestamp, Gamepad_buttonUpContext); + } + break; + + case GAMEPAD_EVENT_AXIS_MOVED: + if (Gamepad_axisMoveCallback != NULL) { + struct Gamepad_axisEvent * axisEvent = event.eventData; + Gamepad_axisMoveCallback(axisEvent->device, axisEvent->axisID, axisEvent->value, axisEvent->lastValue, axisEvent->timestamp, Gamepad_axisMoveContext); + } + break; + } +} + +void Gamepad_processEvents() { + unsigned int eventIndex; + static bool inProcessEvents; + + if (!inited || inProcessEvents) { + return; + } + + inProcessEvents = true; + pthread_mutex_lock(&eventQueueMutex); + for (eventIndex = 0; eventIndex < eventCount; eventIndex++) { + processQueuedEvent(eventQueue[eventIndex]); + if (eventQueue[eventIndex].eventType == GAMEPAD_EVENT_DEVICE_REMOVED) { + disposeDevice(eventQueue[eventIndex].eventData); + } + } + eventCount = 0; + pthread_mutex_unlock(&eventQueueMutex); + inProcessEvents = false; +} + diff --git a/internal/c/parts/input/game_controller/src/Gamepad_macosx.c b/internal/c/parts/input/game_controller/src/Gamepad_macosx.c index 3903c1946..a5ff09126 100644 --- a/internal/c/parts/input/game_controller/src/Gamepad_macosx.c +++ b/internal/c/parts/input/game_controller/src/Gamepad_macosx.c @@ -1,525 +1,562 @@ -/* - Copyright (c) 2010 Alex Diener - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Alex Diener adiener@sacredsoftware.net -*/ - -#include "gamepad/Gamepad.h" -#include -#include -#include -#include - -struct HIDGamepadAxis { - IOHIDElementCookie cookie; - CFIndex logicalMin; - CFIndex logicalMax; - bool hasNullState; - bool isHatSwitch; - bool isHatSwitchSecondAxis; -}; - -struct HIDGamepadButton { - IOHIDElementCookie cookie; -}; - -struct Gamepad_devicePrivate { - IOHIDDeviceRef deviceRef; - struct HIDGamepadAxis * axisElements; - struct HIDGamepadButton * buttonElements; -}; - -struct Gamepad_queuedEvent { - EventDispatcher * dispatcher; - const char * eventType; - void * eventData; -}; - -static IOHIDManagerRef hidManager = NULL; -static struct Gamepad_device ** devices = NULL; -static unsigned int numDevices = 0; -static unsigned int nextDeviceID = 0; - -static struct Gamepad_queuedEvent * inputEventQueue = NULL; -static size_t inputEventQueueSize = 0; -static size_t inputEventCount = 0; - -static struct Gamepad_queuedEvent * deviceEventQueue = NULL; -static size_t deviceEventQueueSize = 0; -static size_t deviceEventCount = 0; - -static EventDispatcher * eventDispatcher = NULL; - -static void hatValueToXY(CFIndex value, CFIndex range, int * outX, int * outY) { - if (value == range) { - *outX = *outY = 0; - - } else { - if (value > 0 && value < range / 2) { - *outX = 1; - - } else if (value > range / 2) { - *outX = -1; - - } else { - *outX = 0; - } - - if (value > range / 4 * 3 || value < range / 4) { - *outY = -1; - - } else if (value > range / 4 && value < range / 4 * 3) { - *outY = 1; - - } else { - *outY = 0; - } - } -} - -static void queueInputEvent(EventDispatcher * dispatcher, const char * eventType, void * eventData) { - struct Gamepad_queuedEvent queuedEvent; - - queuedEvent.dispatcher = dispatcher; - queuedEvent.eventType = eventType; - queuedEvent.eventData = eventData; - - if (inputEventCount >= inputEventQueueSize) { - inputEventQueueSize = inputEventQueueSize == 0 ? 1 : inputEventQueueSize * 2; - inputEventQueue = realloc(inputEventQueue, sizeof(struct Gamepad_queuedEvent) * inputEventQueueSize); - } - inputEventQueue[inputEventCount++] = queuedEvent; -} - -static void queueAxisEvent(struct Gamepad_device * device, double timestamp, unsigned int axisID, float value) { - struct Gamepad_axisEvent * axisEvent; - - axisEvent = malloc(sizeof(struct Gamepad_axisEvent)); - axisEvent->device = device; - axisEvent->timestamp = timestamp; - axisEvent->axisID = axisID; - axisEvent->value = value; - - queueInputEvent(device->eventDispatcher, GAMEPAD_EVENT_AXIS_MOVED, axisEvent); -} - -static void queueButtonEvent(struct Gamepad_device * device, double timestamp, unsigned int buttonID, bool down) { - struct Gamepad_buttonEvent * buttonEvent; - - buttonEvent = malloc(sizeof(struct Gamepad_buttonEvent)); - buttonEvent->device = device; - buttonEvent->timestamp = timestamp; - buttonEvent->buttonID = buttonID; - buttonEvent->down = down; - - queueInputEvent(device->eventDispatcher, down ? GAMEPAD_EVENT_BUTTON_DOWN : GAMEPAD_EVENT_BUTTON_UP, buttonEvent); -} - -static void onDeviceValueChanged(void * context, IOReturn result, void * sender, IOHIDValueRef value) { - struct Gamepad_device * deviceRecord; - struct Gamepad_devicePrivate * hidDeviceRecord; - IOHIDElementRef element; - IOHIDElementCookie cookie; - unsigned int axisIndex, buttonIndex; - static mach_timebase_info_data_t timebaseInfo; - - if (timebaseInfo.denom == 0) { - mach_timebase_info(&timebaseInfo); - } - - deviceRecord = context; - hidDeviceRecord = deviceRecord->privateData; - element = IOHIDValueGetElement(value); - cookie = IOHIDElementGetCookie(element); - - for (axisIndex = 0; axisIndex < deviceRecord->numAxes; axisIndex++) { - if (!hidDeviceRecord->axisElements[axisIndex].isHatSwitchSecondAxis && - hidDeviceRecord->axisElements[axisIndex].cookie == cookie) { - CFIndex integerValue; - - if (IOHIDValueGetLength(value) > 4) { - // Workaround for a strange crash that occurs with PS3 controller; was getting lengths of 39 (!) - continue; - } - integerValue = IOHIDValueGetIntegerValue(value); - - if (hidDeviceRecord->axisElements[axisIndex].isHatSwitch) { - int x, y; - - // Fix for Saitek X52 - hidDeviceRecord->axisElements[axisIndex].hasNullState = false; - if (!hidDeviceRecord->axisElements[axisIndex].hasNullState) { - if (integerValue < hidDeviceRecord->axisElements[axisIndex].logicalMin) { - integerValue = hidDeviceRecord->axisElements[axisIndex].logicalMax - hidDeviceRecord->axisElements[axisIndex].logicalMin + 1; - } else { - integerValue--; - } - } - - hatValueToXY(integerValue, hidDeviceRecord->axisElements[axisIndex].logicalMax - hidDeviceRecord->axisElements[axisIndex].logicalMin + 1, &x, &y); - - if (x != deviceRecord->axisStates[axisIndex]) { - queueAxisEvent(deviceRecord, - IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001, - axisIndex, - x); - - deviceRecord->axisStates[axisIndex] = x; - } - - if (y != deviceRecord->axisStates[axisIndex + 1]) { - queueAxisEvent(deviceRecord, - IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001, - axisIndex + 1, - y); - - deviceRecord->axisStates[axisIndex + 1] = y; - } - - } else { - float floatValue; - - if (integerValue < hidDeviceRecord->axisElements[axisIndex].logicalMin) { - hidDeviceRecord->axisElements[axisIndex].logicalMin = integerValue; - } - if (integerValue > hidDeviceRecord->axisElements[axisIndex].logicalMax) { - hidDeviceRecord->axisElements[axisIndex].logicalMax = integerValue; - } - floatValue = (integerValue - hidDeviceRecord->axisElements[axisIndex].logicalMin) / (float) (hidDeviceRecord->axisElements[axisIndex].logicalMax - hidDeviceRecord->axisElements[axisIndex].logicalMin) * 2.0f - 1.0f; - - queueAxisEvent(deviceRecord, - IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001, - axisIndex, - floatValue); - - deviceRecord->axisStates[axisIndex] = floatValue; - } - - return; - } - } - - for (buttonIndex = 0; buttonIndex < deviceRecord->numButtons; buttonIndex++) { - if (hidDeviceRecord->buttonElements[buttonIndex].cookie == cookie) { - bool down; - - down = IOHIDValueGetIntegerValue(value); - queueButtonEvent(deviceRecord, - IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001, - buttonIndex, - down); - - deviceRecord->buttonStates[buttonIndex] = down; - - return; - } - } -} - -static int IOHIDDeviceGetIntProperty(IOHIDDeviceRef deviceRef, CFStringRef key) { - CFTypeRef typeRef; - int value; - - typeRef = IOHIDDeviceGetProperty(deviceRef, key); - if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID()) { - return 0; - } - - CFNumberGetValue((CFNumberRef) typeRef, kCFNumberSInt32Type, &value); - return value; -} - -static int IOHIDDeviceGetVendorID(IOHIDDeviceRef deviceRef) { - return IOHIDDeviceGetIntProperty(deviceRef, CFSTR(kIOHIDVendorIDKey)); -} - -static int IOHIDDeviceGetProductID(IOHIDDeviceRef deviceRef) { - return IOHIDDeviceGetIntProperty(deviceRef, CFSTR(kIOHIDProductIDKey)); -} - -static void onDeviceMatched(void * context, IOReturn result, void * sender, IOHIDDeviceRef device) { - CFArrayRef elements; - CFIndex elementIndex; - IOHIDElementRef element; - CFStringRef cfProductName; - struct Gamepad_device * deviceRecord; - struct Gamepad_devicePrivate * hidDeviceRecord; - IOHIDElementType type; - char * description; - struct Gamepad_queuedEvent queuedEvent; - - deviceRecord = malloc(sizeof(struct Gamepad_device)); - deviceRecord->deviceID = nextDeviceID++; - deviceRecord->vendorID = IOHIDDeviceGetVendorID(device); - deviceRecord->productID = IOHIDDeviceGetProductID(device); - deviceRecord->numAxes = 0; - deviceRecord->numButtons = 0; - deviceRecord->eventDispatcher = EventDispatcher_create(deviceRecord); - devices = realloc(devices, sizeof(struct Gamepad_device *) * (numDevices + 1)); - devices[numDevices++] = deviceRecord; - - hidDeviceRecord = malloc(sizeof(struct Gamepad_devicePrivate)); - hidDeviceRecord->deviceRef = device; - hidDeviceRecord->axisElements = NULL; - hidDeviceRecord->buttonElements = NULL; - deviceRecord->privateData = hidDeviceRecord; - - cfProductName = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); - if (cfProductName == NULL || CFGetTypeID(cfProductName) != CFStringGetTypeID()) { - description = malloc(strlen("[Unknown]" + 1)); - strcpy(description, "[Unknown]"); - - } else { - const char * cStringPtr; - - cStringPtr = CFStringGetCStringPtr(cfProductName, CFStringGetSmallestEncoding(cfProductName)); - description = malloc(strlen(cStringPtr + 1)); - strcpy(description, cStringPtr); - } - deviceRecord->description = description; - - elements = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone); - for (elementIndex = 0; elementIndex < CFArrayGetCount(elements); elementIndex++) { - element = (IOHIDElementRef) CFArrayGetValueAtIndex(elements, elementIndex); - type = IOHIDElementGetType(element); - - // All of the axis elements I've ever detected have been kIOHIDElementTypeInput_Misc. kIOHIDElementTypeInput_Axis is only included for good faith... - if (type == kIOHIDElementTypeInput_Misc || - type == kIOHIDElementTypeInput_Axis) { - - hidDeviceRecord->axisElements = realloc(hidDeviceRecord->axisElements, sizeof(struct HIDGamepadAxis) * (deviceRecord->numAxes + 1)); - hidDeviceRecord->axisElements[deviceRecord->numAxes].cookie = IOHIDElementGetCookie(element); - hidDeviceRecord->axisElements[deviceRecord->numAxes].logicalMin = IOHIDElementGetLogicalMin(element); - hidDeviceRecord->axisElements[deviceRecord->numAxes].logicalMax = IOHIDElementGetLogicalMax(element); - hidDeviceRecord->axisElements[deviceRecord->numAxes].hasNullState = !!IOHIDElementHasNullState(element); - hidDeviceRecord->axisElements[deviceRecord->numAxes].isHatSwitch = IOHIDElementGetUsage(element) == kHIDUsage_GD_Hatswitch; - hidDeviceRecord->axisElements[deviceRecord->numAxes].isHatSwitchSecondAxis = false; - deviceRecord->numAxes++; - - if (hidDeviceRecord->axisElements[deviceRecord->numAxes - 1].isHatSwitch) { - hidDeviceRecord->axisElements = realloc(hidDeviceRecord->axisElements, sizeof(struct HIDGamepadAxis) * (deviceRecord->numAxes + 1)); - hidDeviceRecord->axisElements[deviceRecord->numAxes].isHatSwitchSecondAxis = true; - deviceRecord->numAxes++; - } - - } else if (type == kIOHIDElementTypeInput_Button) { - hidDeviceRecord->buttonElements = realloc(hidDeviceRecord->buttonElements, sizeof(struct HIDGamepadButton) * (deviceRecord->numButtons + 1)); - hidDeviceRecord->buttonElements[deviceRecord->numButtons].cookie = IOHIDElementGetCookie(element); - deviceRecord->numButtons++; - } - } - CFRelease(elements); - - deviceRecord->axisStates = calloc(sizeof(float), deviceRecord->numAxes); - deviceRecord->buttonStates = calloc(sizeof(bool), deviceRecord->numButtons); - - IOHIDDeviceRegisterInputValueCallback(device, onDeviceValueChanged, deviceRecord); - - queuedEvent.dispatcher = Gamepad_eventDispatcher(); - queuedEvent.eventType = GAMEPAD_EVENT_DEVICE_ATTACHED; - queuedEvent.eventData = deviceRecord; - - if (deviceEventCount >= deviceEventQueueSize) { - deviceEventQueueSize = deviceEventQueueSize == 0 ? 1 : deviceEventQueueSize * 2; - deviceEventQueue = realloc(deviceEventQueue, sizeof(struct Gamepad_queuedEvent) * deviceEventQueueSize); - } - deviceEventQueue[deviceEventCount++] = queuedEvent; -} - -static void disposeDevice(struct Gamepad_device * deviceRecord) { - unsigned int inputEventIndex, deviceEventIndex; - - IOHIDDeviceRegisterInputValueCallback(((struct Gamepad_devicePrivate *) deviceRecord->privateData)->deviceRef, NULL, NULL); - - for (inputEventIndex = 0; inputEventIndex < inputEventCount; inputEventIndex++) { - if (inputEventQueue[inputEventIndex].dispatcher == deviceRecord->eventDispatcher) { - unsigned int inputEventIndex2; - - free(inputEventQueue[inputEventIndex].eventData); - inputEventCount--; - for (inputEventIndex2 = inputEventIndex; inputEventIndex2 < inputEventCount; inputEventIndex2++) { - inputEventQueue[inputEventIndex2] = inputEventQueue[inputEventIndex2 + 1]; - } - inputEventIndex--; - } - } - - for (deviceEventIndex = 0; deviceEventIndex < deviceEventCount; deviceEventIndex++) { - if (deviceEventQueue[deviceEventIndex].dispatcher == deviceRecord->eventDispatcher) { - unsigned int deviceEventIndex2; - - deviceEventCount--; - for (deviceEventIndex2 = deviceEventIndex; deviceEventIndex2 < deviceEventCount; deviceEventIndex2++) { - deviceEventQueue[deviceEventIndex2] = deviceEventQueue[deviceEventIndex2 + 1]; - } - deviceEventIndex--; - } - } - - deviceRecord->eventDispatcher->dispose(deviceRecord->eventDispatcher); - - free(((struct Gamepad_devicePrivate *) deviceRecord->privateData)->axisElements); - free(((struct Gamepad_devicePrivate *) deviceRecord->privateData)->buttonElements); - free(deviceRecord->privateData); - - free((void *) deviceRecord->description); - free(deviceRecord->axisStates); - free(deviceRecord->buttonStates); - free(deviceRecord->eventDispatcher); - - free(deviceRecord); -} - -static void onDeviceRemoved(void * context, IOReturn result, void * sender, IOHIDDeviceRef device) { - unsigned int deviceIndex; - - for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) { - if (((struct Gamepad_devicePrivate *) devices[deviceIndex]->privateData)->deviceRef == device) { - Gamepad_eventDispatcher()->dispatchEvent(Gamepad_eventDispatcher(), GAMEPAD_EVENT_DEVICE_REMOVED, devices[deviceIndex]); - - disposeDevice(devices[deviceIndex]); - numDevices--; - for (; deviceIndex < numDevices; deviceIndex++) { - devices[deviceIndex] = devices[deviceIndex + 1]; - } - return; - } - } -} - -void Gamepad_init() { - if (hidManager == NULL) { - CFStringRef keys[2]; - int value; - CFNumberRef values[2]; - CFDictionaryRef dictionaries[3]; - CFArrayRef array; - - hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); - IOHIDManagerOpen(hidManager, kIOHIDOptionsTypeNone); - IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); - - keys[0] = CFSTR(kIOHIDDeviceUsagePageKey); - keys[1] = CFSTR(kIOHIDDeviceUsageKey); - - value = kHIDPage_GenericDesktop; - values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); - value = kHIDUsage_GD_Joystick; - values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); - dictionaries[0] = CFDictionaryCreate(kCFAllocatorDefault, (const void **) keys, (const void **) values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFRelease(values[0]); - CFRelease(values[1]); - - value = kHIDPage_GenericDesktop; - values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); - value = kHIDUsage_GD_GamePad; - values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); - dictionaries[1] = CFDictionaryCreate(kCFAllocatorDefault, (const void **) keys, (const void **) values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFRelease(values[0]); - CFRelease(values[1]); - - value = kHIDPage_GenericDesktop; - values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); - value = kHIDUsage_GD_MultiAxisController; - values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); - dictionaries[2] = CFDictionaryCreate(kCFAllocatorDefault, (const void **) keys, (const void **) values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFRelease(values[0]); - CFRelease(values[1]); - - array = CFArrayCreate(kCFAllocatorDefault, (const void **) dictionaries, 3, &kCFTypeArrayCallBacks); - CFRelease(dictionaries[0]); - CFRelease(dictionaries[1]); - CFRelease(dictionaries[2]); - IOHIDManagerSetDeviceMatchingMultiple(hidManager, array); - CFRelease(array); - - IOHIDManagerRegisterDeviceMatchingCallback(hidManager, onDeviceMatched, NULL); - IOHIDManagerRegisterDeviceRemovalCallback(hidManager, onDeviceRemoved, NULL); - } -} - -void Gamepad_shutdown() { - if (hidManager != NULL) { - unsigned int deviceIndex; - - IOHIDManagerUnscheduleFromRunLoop(hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); - IOHIDManagerClose(hidManager, 0); - CFRelease(hidManager); - hidManager = NULL; - - for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) { - disposeDevice(devices[deviceIndex]); - } - free(devices); - devices = NULL; - numDevices = 0; - - if (eventDispatcher != NULL) { - eventDispatcher->dispose(eventDispatcher); - free(eventDispatcher); - eventDispatcher = NULL; - } - } -} - -EventDispatcher * Gamepad_eventDispatcher() { - if (eventDispatcher == NULL) { - eventDispatcher = EventDispatcher_create(NULL); - } - return eventDispatcher; -} - -unsigned int Gamepad_numDevices() { - return numDevices; -} - -struct Gamepad_device * Gamepad_deviceAtIndex(unsigned int deviceIndex) { - if (deviceIndex >= numDevices) { - return NULL; - } - return devices[deviceIndex]; -} - -void Gamepad_detectDevices() { - unsigned int eventIndex; - - if (hidManager == NULL) { - return; - } - - for (eventIndex = 0; eventIndex < deviceEventCount; eventIndex++) { - deviceEventQueue[eventIndex].dispatcher->dispatchEvent(deviceEventQueue[eventIndex].dispatcher, deviceEventQueue[eventIndex].eventType, deviceEventQueue[eventIndex].eventData); - } - deviceEventCount = 0; -} - -void Gamepad_processEvents() { - unsigned int eventIndex; - - if (hidManager == NULL) { - return; - } - - for (eventIndex = 0; eventIndex < inputEventCount; eventIndex++) { - inputEventQueue[eventIndex].dispatcher->dispatchEvent(inputEventQueue[eventIndex].dispatcher, inputEventQueue[eventIndex].eventType, inputEventQueue[eventIndex].eventData); - free(inputEventQueue[eventIndex].eventData); - } - inputEventCount = 0; -} - +/* + Copyright (c) 2014 Alex Diener + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Alex Diener alex@ludobloom.com +*/ + +#include "gamepad/Gamepad.h" +#include "gamepad/Gamepad_private.h" +#include +#include +#include +#include + +#define GAMEPAD_RUN_LOOP_MODE CFSTR("GamepadRunLoopMode") + +struct HIDGamepadAxis { + IOHIDElementCookie cookie; + CFIndex logicalMin; + CFIndex logicalMax; + bool hasNullState; + bool isHatSwitch; + bool isHatSwitchSecondAxis; +}; + +struct HIDGamepadButton { + IOHIDElementCookie cookie; +}; + +struct Gamepad_devicePrivate { + IOHIDDeviceRef deviceRef; + struct HIDGamepadAxis * axisElements; + struct HIDGamepadButton * buttonElements; +}; + +struct Gamepad_queuedEvent { + unsigned int deviceID; + enum Gamepad_eventType eventType; + void * eventData; +}; + +static IOHIDManagerRef hidManager = NULL; +static struct Gamepad_device ** devices = NULL; +static unsigned int numDevices = 0; +static unsigned int nextDeviceID = 0; + +static struct Gamepad_queuedEvent * inputEventQueue = NULL; +static size_t inputEventQueueSize = 0; +static size_t inputEventCount = 0; + +static struct Gamepad_queuedEvent * deviceEventQueue = NULL; +static size_t deviceEventQueueSize = 0; +static size_t deviceEventCount = 0; + +static void hatValueToXY(CFIndex value, CFIndex range, int * outX, int * outY) { + if (value == range) { + *outX = *outY = 0; + + } else { + if (value > 0 && value < range / 2) { + *outX = 1; + + } else if (value > range / 2) { + *outX = -1; + + } else { + *outX = 0; + } + + if (value > range / 4 * 3 || value < range / 4) { + *outY = -1; + + } else if (value > range / 4 && value < range / 4 * 3) { + *outY = 1; + + } else { + *outY = 0; + } + } +} + +static void queueInputEvent(unsigned int deviceID, enum Gamepad_eventType eventType, void * eventData) { + struct Gamepad_queuedEvent queuedEvent; + + queuedEvent.deviceID = deviceID; + queuedEvent.eventType = eventType; + queuedEvent.eventData = eventData; + + if (inputEventCount >= inputEventQueueSize) { + inputEventQueueSize = inputEventQueueSize == 0 ? 1 : inputEventQueueSize * 2; + inputEventQueue = realloc(inputEventQueue, sizeof(struct Gamepad_queuedEvent) * inputEventQueueSize); + } + inputEventQueue[inputEventCount++] = queuedEvent; +} + +static void queueAxisEvent(struct Gamepad_device * device, double timestamp, unsigned int axisID, float value, float lastValue) { + struct Gamepad_axisEvent * axisEvent; + + axisEvent = malloc(sizeof(struct Gamepad_axisEvent)); + axisEvent->device = device; + axisEvent->timestamp = timestamp; + axisEvent->axisID = axisID; + axisEvent->value = value; + axisEvent->lastValue = lastValue; + + queueInputEvent(device->deviceID, GAMEPAD_EVENT_AXIS_MOVED, axisEvent); +} + +static void queueButtonEvent(struct Gamepad_device * device, double timestamp, unsigned int buttonID, bool down) { + struct Gamepad_buttonEvent * buttonEvent; + + buttonEvent = malloc(sizeof(struct Gamepad_buttonEvent)); + buttonEvent->device = device; + buttonEvent->timestamp = timestamp; + buttonEvent->buttonID = buttonID; + buttonEvent->down = down; + + queueInputEvent(device->deviceID, down ? GAMEPAD_EVENT_BUTTON_DOWN : GAMEPAD_EVENT_BUTTON_UP, buttonEvent); +} + +static void onDeviceValueChanged(void * context, IOReturn result, void * sender, IOHIDValueRef value) { + struct Gamepad_device * deviceRecord; + struct Gamepad_devicePrivate * hidDeviceRecord; + IOHIDElementRef element; + IOHIDElementCookie cookie; + unsigned int axisIndex, buttonIndex; + static mach_timebase_info_data_t timebaseInfo; + + if (timebaseInfo.denom == 0) { + mach_timebase_info(&timebaseInfo); + } + + deviceRecord = context; + hidDeviceRecord = deviceRecord->privateData; + element = IOHIDValueGetElement(value); + cookie = IOHIDElementGetCookie(element); + + for (axisIndex = 0; axisIndex < deviceRecord->numAxes; axisIndex++) { + if (!hidDeviceRecord->axisElements[axisIndex].isHatSwitchSecondAxis && + hidDeviceRecord->axisElements[axisIndex].cookie == cookie) { + CFIndex integerValue; + + if (IOHIDValueGetLength(value) > 4) { + // Workaround for a strange crash that occurs with PS3 controller; was getting lengths of 39 (!) + continue; + } + integerValue = IOHIDValueGetIntegerValue(value); + + if (hidDeviceRecord->axisElements[axisIndex].isHatSwitch) { + int x, y; + + // Fix for Saitek X52 + if (!hidDeviceRecord->axisElements[axisIndex].hasNullState) { + if (integerValue < hidDeviceRecord->axisElements[axisIndex].logicalMin) { + integerValue = hidDeviceRecord->axisElements[axisIndex].logicalMax - hidDeviceRecord->axisElements[axisIndex].logicalMin + 1; + } else { + integerValue--; + } + } + + hatValueToXY(integerValue, hidDeviceRecord->axisElements[axisIndex].logicalMax - hidDeviceRecord->axisElements[axisIndex].logicalMin + 1, &x, &y); + + if (x != deviceRecord->axisStates[axisIndex]) { + queueAxisEvent(deviceRecord, + IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001, + axisIndex, + x, + deviceRecord->axisStates[axisIndex]); + + deviceRecord->axisStates[axisIndex] = x; + } + + if (y != deviceRecord->axisStates[axisIndex + 1]) { + queueAxisEvent(deviceRecord, + IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001, + axisIndex + 1, + y, + deviceRecord->axisStates[axisIndex + 1]); + + deviceRecord->axisStates[axisIndex + 1] = y; + } + + } else { + float floatValue; + + if (integerValue < hidDeviceRecord->axisElements[axisIndex].logicalMin) { + hidDeviceRecord->axisElements[axisIndex].logicalMin = integerValue; + } + if (integerValue > hidDeviceRecord->axisElements[axisIndex].logicalMax) { + hidDeviceRecord->axisElements[axisIndex].logicalMax = integerValue; + } + floatValue = (integerValue - hidDeviceRecord->axisElements[axisIndex].logicalMin) / (float) (hidDeviceRecord->axisElements[axisIndex].logicalMax - hidDeviceRecord->axisElements[axisIndex].logicalMin) * 2.0f - 1.0f; + + queueAxisEvent(deviceRecord, + IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001, + axisIndex, + floatValue, + deviceRecord->axisStates[axisIndex]); + + deviceRecord->axisStates[axisIndex] = floatValue; + } + + return; + } + } + + for (buttonIndex = 0; buttonIndex < deviceRecord->numButtons; buttonIndex++) { + if (hidDeviceRecord->buttonElements[buttonIndex].cookie == cookie) { + bool down; + + down = IOHIDValueGetIntegerValue(value); + queueButtonEvent(deviceRecord, + IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001, + buttonIndex, + down); + + deviceRecord->buttonStates[buttonIndex] = down; + + return; + } + } +} + +static int IOHIDDeviceGetIntProperty(IOHIDDeviceRef deviceRef, CFStringRef key) { + CFTypeRef typeRef; + int value; + + typeRef = IOHIDDeviceGetProperty(deviceRef, key); + if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID()) { + return 0; + } + + CFNumberGetValue((CFNumberRef) typeRef, kCFNumberSInt32Type, &value); + return value; +} + +static int IOHIDDeviceGetVendorID(IOHIDDeviceRef deviceRef) { + return IOHIDDeviceGetIntProperty(deviceRef, CFSTR(kIOHIDVendorIDKey)); +} + +static int IOHIDDeviceGetProductID(IOHIDDeviceRef deviceRef) { + return IOHIDDeviceGetIntProperty(deviceRef, CFSTR(kIOHIDProductIDKey)); +} + +static void onDeviceMatched(void * context, IOReturn result, void * sender, IOHIDDeviceRef device) { + CFArrayRef elements; + CFIndex elementIndex; + IOHIDElementRef element; + CFStringRef cfProductName; + struct Gamepad_device * deviceRecord; + struct Gamepad_devicePrivate * hidDeviceRecord; + IOHIDElementType type; + char * description; + struct Gamepad_queuedEvent queuedEvent; + + deviceRecord = malloc(sizeof(struct Gamepad_device)); + deviceRecord->deviceID = nextDeviceID++; + deviceRecord->vendorID = IOHIDDeviceGetVendorID(device); + deviceRecord->productID = IOHIDDeviceGetProductID(device); + deviceRecord->numAxes = 0; + deviceRecord->numButtons = 0; + devices = realloc(devices, sizeof(struct Gamepad_device *) * (numDevices + 1)); + devices[numDevices++] = deviceRecord; + + hidDeviceRecord = malloc(sizeof(struct Gamepad_devicePrivate)); + hidDeviceRecord->deviceRef = device; + hidDeviceRecord->axisElements = NULL; + hidDeviceRecord->buttonElements = NULL; + deviceRecord->privateData = hidDeviceRecord; + + cfProductName = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); + if (cfProductName == NULL || CFGetTypeID(cfProductName) != CFStringGetTypeID()) { + description = malloc(strlen("[Unknown]" + 1)); + strcpy(description, "[Unknown]"); + + } else { + CFIndex length; + + CFStringGetBytes(cfProductName, CFRangeMake(0, CFStringGetLength(cfProductName)), kCFStringEncodingUTF8, '?', false, NULL, 100, &length); + description = malloc(length + 1); + CFStringGetBytes(cfProductName, CFRangeMake(0, CFStringGetLength(cfProductName)), kCFStringEncodingUTF8, '?', false, (UInt8 *) description, length + 1, NULL); + description[length] = '\x00'; + } + deviceRecord->description = description; + + elements = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone); + for (elementIndex = 0; elementIndex < CFArrayGetCount(elements); elementIndex++) { + element = (IOHIDElementRef) CFArrayGetValueAtIndex(elements, elementIndex); + type = IOHIDElementGetType(element); + + // All of the axis elements I've ever detected have been kIOHIDElementTypeInput_Misc. kIOHIDElementTypeInput_Axis is only included for good faith... + if (type == kIOHIDElementTypeInput_Misc || + type == kIOHIDElementTypeInput_Axis) { + + hidDeviceRecord->axisElements = realloc(hidDeviceRecord->axisElements, sizeof(struct HIDGamepadAxis) * (deviceRecord->numAxes + 1)); + hidDeviceRecord->axisElements[deviceRecord->numAxes].cookie = IOHIDElementGetCookie(element); + hidDeviceRecord->axisElements[deviceRecord->numAxes].logicalMin = IOHIDElementGetLogicalMin(element); + hidDeviceRecord->axisElements[deviceRecord->numAxes].logicalMax = IOHIDElementGetLogicalMax(element); + hidDeviceRecord->axisElements[deviceRecord->numAxes].hasNullState = !!IOHIDElementHasNullState(element); + hidDeviceRecord->axisElements[deviceRecord->numAxes].isHatSwitch = IOHIDElementGetUsage(element) == kHIDUsage_GD_Hatswitch; + hidDeviceRecord->axisElements[deviceRecord->numAxes].isHatSwitchSecondAxis = false; + deviceRecord->numAxes++; + + if (hidDeviceRecord->axisElements[deviceRecord->numAxes - 1].isHatSwitch) { + hidDeviceRecord->axisElements = realloc(hidDeviceRecord->axisElements, sizeof(struct HIDGamepadAxis) * (deviceRecord->numAxes + 1)); + hidDeviceRecord->axisElements[deviceRecord->numAxes].isHatSwitchSecondAxis = true; + deviceRecord->numAxes++; + } + + } else if (type == kIOHIDElementTypeInput_Button) { + hidDeviceRecord->buttonElements = realloc(hidDeviceRecord->buttonElements, sizeof(struct HIDGamepadButton) * (deviceRecord->numButtons + 1)); + hidDeviceRecord->buttonElements[deviceRecord->numButtons].cookie = IOHIDElementGetCookie(element); + deviceRecord->numButtons++; + } + } + CFRelease(elements); + + deviceRecord->axisStates = calloc(sizeof(float), deviceRecord->numAxes); + deviceRecord->buttonStates = calloc(sizeof(bool), deviceRecord->numButtons); + + IOHIDDeviceRegisterInputValueCallback(device, onDeviceValueChanged, deviceRecord); + + queuedEvent.deviceID = deviceRecord->deviceID; + queuedEvent.eventType = GAMEPAD_EVENT_DEVICE_ATTACHED; + queuedEvent.eventData = deviceRecord; + + if (deviceEventCount >= deviceEventQueueSize) { + deviceEventQueueSize = deviceEventQueueSize == 0 ? 1 : deviceEventQueueSize * 2; + deviceEventQueue = realloc(deviceEventQueue, sizeof(struct Gamepad_queuedEvent) * deviceEventQueueSize); + } + deviceEventQueue[deviceEventCount++] = queuedEvent; +} + +static void disposeDevice(struct Gamepad_device * deviceRecord) { + unsigned int inputEventIndex, deviceEventIndex; + + IOHIDDeviceRegisterInputValueCallback(((struct Gamepad_devicePrivate *) deviceRecord->privateData)->deviceRef, NULL, NULL); + + for (inputEventIndex = 0; inputEventIndex < inputEventCount; inputEventIndex++) { + if (inputEventQueue[inputEventIndex].deviceID == deviceRecord->deviceID) { + unsigned int inputEventIndex2; + + free(inputEventQueue[inputEventIndex].eventData); + inputEventCount--; + for (inputEventIndex2 = inputEventIndex; inputEventIndex2 < inputEventCount; inputEventIndex2++) { + inputEventQueue[inputEventIndex2] = inputEventQueue[inputEventIndex2 + 1]; + } + inputEventIndex--; + } + } + + for (deviceEventIndex = 0; deviceEventIndex < deviceEventCount; deviceEventIndex++) { + if (deviceEventQueue[deviceEventIndex].deviceID == deviceRecord->deviceID) { + unsigned int deviceEventIndex2; + + deviceEventCount--; + for (deviceEventIndex2 = deviceEventIndex; deviceEventIndex2 < deviceEventCount; deviceEventIndex2++) { + deviceEventQueue[deviceEventIndex2] = deviceEventQueue[deviceEventIndex2 + 1]; + } + deviceEventIndex--; + } + } + + free(((struct Gamepad_devicePrivate *) deviceRecord->privateData)->axisElements); + free(((struct Gamepad_devicePrivate *) deviceRecord->privateData)->buttonElements); + free(deviceRecord->privateData); + + free((void *) deviceRecord->description); + free(deviceRecord->axisStates); + free(deviceRecord->buttonStates); + + free(deviceRecord); +} + +static void onDeviceRemoved(void * context, IOReturn result, void * sender, IOHIDDeviceRef device) { + unsigned int deviceIndex; + + for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) { + if (((struct Gamepad_devicePrivate *) devices[deviceIndex]->privateData)->deviceRef == device) { + if (Gamepad_deviceRemoveCallback != NULL) { + Gamepad_deviceRemoveCallback(devices[deviceIndex], Gamepad_deviceRemoveContext); + } + + disposeDevice(devices[deviceIndex]); + numDevices--; + for (; deviceIndex < numDevices; deviceIndex++) { + devices[deviceIndex] = devices[deviceIndex + 1]; + } + return; + } + } +} + +void Gamepad_init() { + if (hidManager == NULL) { + CFStringRef keys[2]; + int value; + CFNumberRef values[2]; + CFDictionaryRef dictionaries[3]; + CFArrayRef array; + + hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + + keys[0] = CFSTR(kIOHIDDeviceUsagePageKey); + keys[1] = CFSTR(kIOHIDDeviceUsageKey); + + value = kHIDPage_GenericDesktop; + values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); + value = kHIDUsage_GD_Joystick; + values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); + dictionaries[0] = CFDictionaryCreate(kCFAllocatorDefault, (const void **) keys, (const void **) values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFRelease(values[0]); + CFRelease(values[1]); + + value = kHIDPage_GenericDesktop; + values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); + value = kHIDUsage_GD_GamePad; + values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); + dictionaries[1] = CFDictionaryCreate(kCFAllocatorDefault, (const void **) keys, (const void **) values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFRelease(values[0]); + CFRelease(values[1]); + + value = kHIDPage_GenericDesktop; + values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); + value = kHIDUsage_GD_MultiAxisController; + values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); + dictionaries[2] = CFDictionaryCreate(kCFAllocatorDefault, (const void **) keys, (const void **) values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFRelease(values[0]); + CFRelease(values[1]); + + array = CFArrayCreate(kCFAllocatorDefault, (const void **) dictionaries, 3, &kCFTypeArrayCallBacks); + CFRelease(dictionaries[0]); + CFRelease(dictionaries[1]); + CFRelease(dictionaries[2]); + IOHIDManagerSetDeviceMatchingMultiple(hidManager, array); + CFRelease(array); + + IOHIDManagerRegisterDeviceMatchingCallback(hidManager, onDeviceMatched, NULL); + IOHIDManagerRegisterDeviceRemovalCallback(hidManager, onDeviceRemoved, NULL); + + IOHIDManagerOpen(hidManager, kIOHIDOptionsTypeNone); + + // Force gamepads to be recognized immediately. The normal run loop mode takes a few frames, + // but we can run one iteration with a custom mode to do it without a delay. + IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetCurrent(), GAMEPAD_RUN_LOOP_MODE); + CFRunLoopRunInMode(GAMEPAD_RUN_LOOP_MODE, 0, true); + } +} + +void Gamepad_shutdown() { + if (hidManager != NULL) { + unsigned int deviceIndex; + + IOHIDManagerUnscheduleFromRunLoop(hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + + for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) { + disposeDevice(devices[deviceIndex]); + } + free(devices); + devices = NULL; + numDevices = 0; + + IOHIDManagerClose(hidManager, 0); + CFRelease(hidManager); + hidManager = NULL; + } +} + +unsigned int Gamepad_numDevices() { + return numDevices; +} + +struct Gamepad_device * Gamepad_deviceAtIndex(unsigned int deviceIndex) { + if (deviceIndex >= numDevices) { + return NULL; + } + return devices[deviceIndex]; +} + +static void processQueuedEvent(struct Gamepad_queuedEvent event) { + switch (event.eventType) { + case GAMEPAD_EVENT_DEVICE_ATTACHED: + if (Gamepad_deviceAttachCallback != NULL) { + Gamepad_deviceAttachCallback(event.eventData, Gamepad_deviceAttachContext); + } + break; + + case GAMEPAD_EVENT_DEVICE_REMOVED: + if (Gamepad_deviceRemoveCallback != NULL) { + Gamepad_deviceRemoveCallback(event.eventData, Gamepad_deviceRemoveContext); + } + break; + + case GAMEPAD_EVENT_BUTTON_DOWN: + if (Gamepad_buttonDownCallback != NULL) { + struct Gamepad_buttonEvent * buttonEvent = event.eventData; + Gamepad_buttonDownCallback(buttonEvent->device, buttonEvent->buttonID, buttonEvent->timestamp, Gamepad_buttonDownContext); + } + break; + + case GAMEPAD_EVENT_BUTTON_UP: + if (Gamepad_buttonUpCallback != NULL) { + struct Gamepad_buttonEvent * buttonEvent = event.eventData; + Gamepad_buttonUpCallback(buttonEvent->device, buttonEvent->buttonID, buttonEvent->timestamp, Gamepad_buttonUpContext); + } + break; + + case GAMEPAD_EVENT_AXIS_MOVED: + if (Gamepad_axisMoveCallback != NULL) { + struct Gamepad_axisEvent * axisEvent = event.eventData; + Gamepad_axisMoveCallback(axisEvent->device, axisEvent->axisID, axisEvent->value, axisEvent->lastValue, axisEvent->timestamp, Gamepad_axisMoveContext); + } + break; + } +} + +void Gamepad_detectDevices() { + unsigned int eventIndex; + + if (hidManager == NULL) { + return; + } + + CFRunLoopRunInMode(GAMEPAD_RUN_LOOP_MODE, 0, true); + for (eventIndex = 0; eventIndex < deviceEventCount; eventIndex++) { + processQueuedEvent(deviceEventQueue[eventIndex]); + } + deviceEventCount = 0; +} + +void Gamepad_processEvents() { + unsigned int eventIndex; + static bool inProcessEvents; + + if (hidManager == NULL || inProcessEvents) { + return; + } + + inProcessEvents = true; + CFRunLoopRunInMode(GAMEPAD_RUN_LOOP_MODE, 0, true); + for (eventIndex = 0; eventIndex < inputEventCount; eventIndex++) { + processQueuedEvent(inputEventQueue[eventIndex]); + free(inputEventQueue[eventIndex].eventData); + } + inputEventCount = 0; + inProcessEvents = false; +} diff --git a/internal/c/parts/input/game_controller/src/Gamepad_private.c b/internal/c/parts/input/game_controller/src/Gamepad_private.c new file mode 100644 index 000000000..55ee159a6 --- /dev/null +++ b/internal/c/parts/input/game_controller/src/Gamepad_private.c @@ -0,0 +1,61 @@ +/* + Copyright (c) 2014 Alex Diener + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Alex Diener alex@ludobloom.com +*/ + +#include "gamepad/Gamepad.h" +#include "gamepad/Gamepad_private.h" +#include + +void (* Gamepad_deviceAttachCallback)(struct Gamepad_device * device, void * context) = NULL; +void (* Gamepad_deviceRemoveCallback)(struct Gamepad_device * device, void * context) = NULL; +void (* Gamepad_buttonDownCallback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context) = NULL; +void (* Gamepad_buttonUpCallback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context) = NULL; +void (* Gamepad_axisMoveCallback)(struct Gamepad_device * device, unsigned int buttonID, float value, float lastValue, double timestamp, void * context) = NULL; +void * Gamepad_deviceAttachContext = NULL; +void * Gamepad_deviceRemoveContext = NULL; +void * Gamepad_buttonDownContext = NULL; +void * Gamepad_buttonUpContext = NULL; +void * Gamepad_axisMoveContext = NULL; + +void Gamepad_deviceAttachFunc(void (* callback)(struct Gamepad_device * device, void * context), void * context) { + Gamepad_deviceAttachCallback = callback; + Gamepad_deviceAttachContext = context; +} + +void Gamepad_deviceRemoveFunc(void (* callback)(struct Gamepad_device * device, void * context), void * context) { + Gamepad_deviceRemoveCallback = callback; + Gamepad_deviceRemoveContext = context; +} + +void Gamepad_buttonDownFunc(void (* callback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context), void * context) { + Gamepad_buttonDownCallback = callback; + Gamepad_buttonDownContext = context; +} + +void Gamepad_buttonUpFunc(void (* callback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context), void * context) { + Gamepad_buttonUpCallback = callback; + Gamepad_buttonUpContext = context; +} + +void Gamepad_axisMoveFunc(void (* callback)(struct Gamepad_device * device, unsigned int axisID, float value, float lastValue, double timestamp, void * context), void * context) { + Gamepad_axisMoveCallback = callback; + Gamepad_axisMoveContext = context; +} diff --git a/internal/c/parts/input/game_controller/src/Gamepad_windows.c b/internal/c/parts/input/game_controller/src/Gamepad_windows.c deleted file mode 100644 index f9dbe402e..000000000 --- a/internal/c/parts/input/game_controller/src/Gamepad_windows.c +++ /dev/null @@ -1,416 +0,0 @@ -/* - Copyright (c) 2010 Alex Diener - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Alex Diener adiener@sacredsoftware.net -*/ - -#include "gamepad/Gamepad.h" -#include -#include -#include -#include - -struct Gamepad_devicePrivate { - UINT joystickID; - JOYINFOEX lastState; - int xAxisIndex; - int yAxisIndex; - int zAxisIndex; - int rAxisIndex; - int uAxisIndex; - int vAxisIndex; - int povXAxisIndex; - int povYAxisIndex; - UINT (* axisRanges)[2]; -}; - -static struct Gamepad_device ** devices = NULL; -static unsigned int numDevices = 0; -static unsigned int nextDeviceID = 0; - -static EventDispatcher * eventDispatcher = NULL; -static bool inited = false; - -void Gamepad_init() { - if (!inited) { - inited = true; - Gamepad_detectDevices(); - } -} - -static void disposeDevice(struct Gamepad_device * deviceRecord) { - deviceRecord->eventDispatcher->dispose(deviceRecord->eventDispatcher); - - free(((struct Gamepad_devicePrivate *) deviceRecord->privateData)->axisRanges); - free(deviceRecord->privateData); - - free((void *) deviceRecord->description); - free(deviceRecord->axisStates); - free(deviceRecord->buttonStates); - free(deviceRecord->eventDispatcher); - - free(deviceRecord); -} - -void Gamepad_shutdown() { - unsigned int deviceIndex; - - if (inited) { - for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) { - disposeDevice(devices[deviceIndex]); - } - free(devices); - devices = NULL; - numDevices = 0; - if (eventDispatcher != NULL) { - eventDispatcher->dispose(eventDispatcher); - free(eventDispatcher); - eventDispatcher = NULL; - } - inited = false; - } -} - -EventDispatcher * Gamepad_eventDispatcher() { - if (eventDispatcher == NULL) { - eventDispatcher = EventDispatcher_create(NULL); - } - return eventDispatcher; -} - -unsigned int Gamepad_numDevices() { - return numDevices; -} - -struct Gamepad_device * Gamepad_deviceAtIndex(unsigned int deviceIndex) { - if (deviceIndex >= numDevices) { - return NULL; - } - return devices[deviceIndex]; -} - -#define REG_STRING_MAX 256 - -static char * getDeviceDescription(UINT joystickID, JOYCAPS caps) { - char * description; - char subkey[REG_STRING_MAX]; - HKEY topKey, key; - LONG result; - - snprintf(subkey, REG_STRING_MAX, "%s\\%s\\%s", REGSTR_PATH_JOYCONFIG, caps.szRegKey, REGSTR_KEY_JOYCURR); - result = RegOpenKeyEx(topKey = HKEY_LOCAL_MACHINE, subkey, 0, KEY_READ, &key); - if (result != ERROR_SUCCESS) { - result = RegOpenKeyEx(topKey = HKEY_CURRENT_USER, subkey, 0, KEY_READ, &key); - } - if (result == ERROR_SUCCESS) { - char value[REG_STRING_MAX]; - char name[REG_STRING_MAX]; - DWORD nameSize; - - snprintf(value, REG_STRING_MAX, "Joystick%d%s", joystickID + 1, REGSTR_VAL_JOYOEMNAME); - nameSize = sizeof(name); - result = RegQueryValueEx(key, value, NULL, NULL, (LPBYTE) name, &nameSize); - RegCloseKey(key); - - if (result == ERROR_SUCCESS) { - snprintf(subkey, REG_STRING_MAX, "%s\\%s", REGSTR_PATH_JOYOEM, name); - result = RegOpenKeyEx(topKey, subkey, 0, KEY_READ, &key); - - if (result == ERROR_SUCCESS) { - nameSize = sizeof(name); - result = RegQueryValueEx(key, REGSTR_VAL_JOYOEMNAME, NULL, NULL, NULL, &nameSize); - - if (result == ERROR_SUCCESS) { - description = malloc(nameSize); - result = RegQueryValueEx(key, REGSTR_VAL_JOYOEMNAME, NULL, NULL, (LPBYTE) description, &nameSize); - } - RegCloseKey(key); - - if (result == ERROR_SUCCESS) { - return description; - } - free(description); - } - } - } - - description = malloc(strlen(caps.szPname) + 1); - strcpy(description, caps.szPname); - - return description; -} - -void Gamepad_detectDevices() { - unsigned int numPadsSupported; - unsigned int deviceIndex, deviceIndex2; - JOYINFOEX info; - JOYCAPS caps; - bool duplicate; - struct Gamepad_device * deviceRecord; - struct Gamepad_devicePrivate * deviceRecordPrivate; - UINT joystickID; - int axisIndex; - - if (!inited) { - return; - } - - numPadsSupported = joyGetNumDevs(); - for (deviceIndex = 0; deviceIndex < numPadsSupported; deviceIndex++) { - info.dwSize = sizeof(info); - info.dwFlags = JOY_RETURNALL; - joystickID = JOYSTICKID1 + deviceIndex; - if (joyGetPosEx(joystickID, &info) == JOYERR_NOERROR && - joyGetDevCaps(joystickID, &caps, sizeof(JOYCAPS)) == JOYERR_NOERROR) { - - duplicate = false; - for (deviceIndex2 = 0; deviceIndex2 < numDevices; deviceIndex2++) { - if (((struct Gamepad_devicePrivate *) devices[deviceIndex2]->privateData)->joystickID == joystickID) { - duplicate = true; - break; - } - } - if (duplicate) { - continue; - } - - deviceRecord = malloc(sizeof(struct Gamepad_device)); - deviceRecord->deviceID = nextDeviceID++; - deviceRecord->description = getDeviceDescription(joystickID, caps); - deviceRecord->vendorID = caps.wMid; - deviceRecord->productID = caps.wPid; - deviceRecord->numAxes = caps.wNumAxes + ((caps.wCaps & JOYCAPS_HASPOV) ? 2 : 0); - deviceRecord->numButtons = caps.wNumButtons; - deviceRecord->axisStates = calloc(sizeof(float), deviceRecord->numAxes); - deviceRecord->buttonStates = calloc(sizeof(bool), deviceRecord->numButtons); - deviceRecord->eventDispatcher = EventDispatcher_create(deviceRecord); - devices = realloc(devices, sizeof(struct Gamepad_device *) * (numDevices + 1)); - devices[numDevices++] = deviceRecord; - - deviceRecordPrivate = malloc(sizeof(struct Gamepad_devicePrivate)); - deviceRecordPrivate->joystickID = joystickID; - deviceRecordPrivate->lastState = info; - - deviceRecordPrivate->xAxisIndex = 0; - deviceRecordPrivate->yAxisIndex = 1; - axisIndex = 2; - deviceRecordPrivate->zAxisIndex = (caps.wCaps & JOYCAPS_HASZ) ? axisIndex++ : -1; - deviceRecordPrivate->rAxisIndex = (caps.wCaps & JOYCAPS_HASR) ? axisIndex++ : -1; - deviceRecordPrivate->uAxisIndex = (caps.wCaps & JOYCAPS_HASU) ? axisIndex++ : -1; - deviceRecordPrivate->vAxisIndex = (caps.wCaps & JOYCAPS_HASV) ? axisIndex++ : -1; - - deviceRecordPrivate->axisRanges = malloc(sizeof(UINT[2]) * axisIndex); - deviceRecordPrivate->axisRanges[0][0] = caps.wXmin; - deviceRecordPrivate->axisRanges[0][1] = caps.wXmax; - deviceRecordPrivate->axisRanges[1][0] = caps.wYmin; - deviceRecordPrivate->axisRanges[1][1] = caps.wYmax; - if (deviceRecordPrivate->zAxisIndex != -1) { - deviceRecordPrivate->axisRanges[deviceRecordPrivate->zAxisIndex][0] = caps.wZmin; - deviceRecordPrivate->axisRanges[deviceRecordPrivate->zAxisIndex][1] = caps.wZmax; - } - if (deviceRecordPrivate->rAxisIndex != -1) { - deviceRecordPrivate->axisRanges[deviceRecordPrivate->rAxisIndex][0] = caps.wRmin; - deviceRecordPrivate->axisRanges[deviceRecordPrivate->rAxisIndex][1] = caps.wRmax; - } - if (deviceRecordPrivate->uAxisIndex != -1) { - deviceRecordPrivate->axisRanges[deviceRecordPrivate->uAxisIndex][0] = caps.wUmin; - deviceRecordPrivate->axisRanges[deviceRecordPrivate->uAxisIndex][1] = caps.wUmax; - } - if (deviceRecordPrivate->vAxisIndex != -1) { - deviceRecordPrivate->axisRanges[deviceRecordPrivate->vAxisIndex][0] = caps.wVmin; - deviceRecordPrivate->axisRanges[deviceRecordPrivate->vAxisIndex][1] = caps.wVmax; - } - - deviceRecordPrivate->povXAxisIndex = (caps.wCaps & JOYCAPS_HASPOV) ? axisIndex++ : -1; - deviceRecordPrivate->povYAxisIndex = (caps.wCaps & JOYCAPS_HASPOV) ? axisIndex++ : -1; - - deviceRecord->privateData = deviceRecordPrivate; - - Gamepad_eventDispatcher()->dispatchEvent(Gamepad_eventDispatcher(), GAMEPAD_EVENT_DEVICE_ATTACHED, deviceRecord); - } - } -} - -static double currentTime() { - // HACK: No timestamp data from joyGetInfoEx, so we make it up - static LARGE_INTEGER frequency; - LARGE_INTEGER currentTime; - - if (frequency.QuadPart == 0) { - QueryPerformanceFrequency(&frequency); - } - QueryPerformanceCounter(¤tTime); - - return (double) currentTime.QuadPart / frequency.QuadPart; -} - -static void handleAxisChange(struct Gamepad_device * device, int axisIndex, DWORD value) { - struct Gamepad_axisEvent axisEvent; - struct Gamepad_devicePrivate * devicePrivate; - - if (axisIndex < 0 || axisIndex >= (int) device->numAxes) { - return; - } - - devicePrivate = device->privateData; - - axisEvent.device = device; - axisEvent.timestamp = currentTime(); - axisEvent.axisID = axisIndex; - axisEvent.value = (value - devicePrivate->axisRanges[axisIndex][0]) / (float) (devicePrivate->axisRanges[axisIndex][1] - devicePrivate->axisRanges[axisIndex][0]) * 2.0f - 1.0f; - - device->axisStates[axisIndex] = axisEvent.value; - device->eventDispatcher->dispatchEvent(device->eventDispatcher, GAMEPAD_EVENT_AXIS_MOVED, &axisEvent); -} - -static void handleButtonChange(struct Gamepad_device * device, DWORD lastValue, DWORD value) { - struct Gamepad_buttonEvent buttonEvent; - unsigned int buttonIndex; - - for (buttonIndex = 0; buttonIndex < device->numButtons; buttonIndex++) { - if ((lastValue ^ value) & (1 << buttonIndex)) { - buttonEvent.device = device; - buttonEvent.timestamp = currentTime(); - buttonEvent.buttonID = buttonIndex; - buttonEvent.down = !!(value & (1 << buttonIndex)); - - device->buttonStates[buttonIndex] = buttonEvent.down; - device->eventDispatcher->dispatchEvent(device->eventDispatcher, buttonEvent.down ? GAMEPAD_EVENT_BUTTON_DOWN : GAMEPAD_EVENT_BUTTON_UP, &buttonEvent); - } - } -} - -static void povToXY(DWORD pov, int * outX, int * outY) { - if (pov == JOY_POVCENTERED) { - *outX = *outY = 0; - - } else { - if (pov > JOY_POVFORWARD && pov < JOY_POVBACKWARD) { - *outX = 1; - - } else if (pov > JOY_POVBACKWARD) { - *outX = -1; - - } else { - *outX = 0; - } - - if (pov > JOY_POVLEFT || pov < JOY_POVRIGHT) { - *outY = -1; - - } else if (pov > JOY_POVRIGHT && pov < JOY_POVLEFT) { - *outY = 1; - - } else { - *outY = 0; - } - } -} - -static void handlePOVChange(struct Gamepad_device * device, DWORD lastValue, DWORD value) { - struct Gamepad_devicePrivate * devicePrivate; - int lastX, lastY, newX, newY; - struct Gamepad_axisEvent axisEvent; - - devicePrivate = device->privateData; - - if (devicePrivate->povXAxisIndex == -1 || devicePrivate->povYAxisIndex == -1) { - return; - } - - povToXY(lastValue, &lastX, &lastY); - povToXY(value, &newX, &newY); - - if (newX != lastX) { - axisEvent.device = device; - axisEvent.timestamp = currentTime(); - axisEvent.axisID = devicePrivate->povXAxisIndex; - axisEvent.value = newX; - - device->axisStates[devicePrivate->povXAxisIndex] = axisEvent.value; - device->eventDispatcher->dispatchEvent(device->eventDispatcher, GAMEPAD_EVENT_AXIS_MOVED, &axisEvent); - } - if (newY != lastY) { - axisEvent.device = device; - axisEvent.timestamp = currentTime(); - axisEvent.axisID = devicePrivate->povYAxisIndex; - axisEvent.value = newY; - - device->axisStates[devicePrivate->povYAxisIndex] = axisEvent.value; - device->eventDispatcher->dispatchEvent(device->eventDispatcher, GAMEPAD_EVENT_AXIS_MOVED, &axisEvent); - } -} - -void Gamepad_processEvents() { - unsigned int deviceIndex; - JOYINFOEX info; - MMRESULT result; - struct Gamepad_device * device; - struct Gamepad_devicePrivate * devicePrivate; - - if (!inited) { - return; - } - - for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) { - device = devices[deviceIndex]; - devicePrivate = device->privateData; - - info.dwSize = sizeof(info); - info.dwFlags = JOY_RETURNALL; - result = joyGetPosEx(devicePrivate->joystickID, &info); - if (result == JOYERR_UNPLUGGED) { - Gamepad_eventDispatcher()->dispatchEvent(Gamepad_eventDispatcher(), GAMEPAD_EVENT_DEVICE_REMOVED, device); - - disposeDevice(device); - numDevices--; - for (; deviceIndex < numDevices; deviceIndex++) { - devices[deviceIndex] = devices[deviceIndex + 1]; - } - - } else if (result == JOYERR_NOERROR) { - if (info.dwXpos != devicePrivate->lastState.dwXpos) { - handleAxisChange(device, devicePrivate->xAxisIndex, info.dwXpos); - } - if (info.dwYpos != devicePrivate->lastState.dwYpos) { - handleAxisChange(device, devicePrivate->yAxisIndex, info.dwYpos); - } - if (info.dwZpos != devicePrivate->lastState.dwZpos) { - handleAxisChange(device, devicePrivate->zAxisIndex, info.dwZpos); - } - if (info.dwRpos != devicePrivate->lastState.dwRpos) { - handleAxisChange(device, devicePrivate->rAxisIndex, info.dwRpos); - } - if (info.dwUpos != devicePrivate->lastState.dwUpos) { - handleAxisChange(device, devicePrivate->uAxisIndex, info.dwUpos); - } - if (info.dwVpos != devicePrivate->lastState.dwVpos) { - handleAxisChange(device, devicePrivate->vAxisIndex, info.dwVpos); - } - if (info.dwPOV != devicePrivate->lastState.dwPOV) { - handlePOVChange(device, devicePrivate->lastState.dwPOV, info.dwPOV); - } - if (info.dwButtons != devicePrivate->lastState.dwButtons) { - handleButtonChange(device, devicePrivate->lastState.dwButtons, info.dwButtons); - } - devicePrivate->lastState = info; - } - } -} - diff --git a/internal/c/parts/input/game_controller/src/Gamepad_windows_dinput.c b/internal/c/parts/input/game_controller/src/Gamepad_windows_dinput.c new file mode 100644 index 000000000..052ea95be --- /dev/null +++ b/internal/c/parts/input/game_controller/src/Gamepad_windows_dinput.c @@ -0,0 +1,1055 @@ +/* + Copyright (c) 2014 Alex Diener + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Alex Diener alex@ludobloom.com +*/ + +// Special thanks to SDL2 for portions of DirectInput and XInput code used in this implementation + +#define _WIN32_WINNT 0x0501 +#define INITGUID +#define DIRECTINPUT_VERSION 0x0800 +#ifdef _MSC_VER +#define strdup _strdup +#undef UNICODE +#else +#define __in +#define __out +#define __reserved +#endif + +#include "gamepad/Gamepad.h" +#include "gamepad/Gamepad_private.h" +#include +#include + +#include +#include +#include +#include + +// Copy from MinGW-w64 to MinGW, along with wbemcli.h, wbemprov.h, wbemdisp.h, and wbemtran.h +#ifndef __MINGW_EXTENSION +#define __MINGW_EXTENSION +#endif +#define COBJMACROS 1 +#include +#include +// Super helpful info: http://www.wreckedgames.com/forum/index.php?topic=2584.0 + +#define INPUT_QUEUE_SIZE 32 +#define XINPUT_GAMEPAD_GUIDE 0x400 + +typedef struct { + WORD wButtons; + BYTE bLeftTrigger; + BYTE bRightTrigger; + SHORT sThumbLX; + SHORT sThumbLY; + SHORT sThumbRX; + SHORT sThumbRY; + DWORD dwPaddingReserved; +} XINPUT_GAMEPAD_EX; + +typedef struct { + DWORD dwPacketNumber; + XINPUT_GAMEPAD_EX Gamepad; +} XINPUT_STATE_EX; + +struct diAxisInfo { + DWORD offset; + bool isPOV; + bool isPOVSecondAxis; +}; + +struct Gamepad_devicePrivate { + bool isXInput; + + // DInput only + GUID guidInstance; + IDirectInputDevice8 * deviceInterface; + bool buffered; + unsigned int sliderCount; + unsigned int povCount; + struct diAxisInfo * axisInfo; + DWORD * buttonOffsets; + + // XInput only + unsigned int playerIndex; +}; + +static struct Gamepad_device ** devices = NULL; +static unsigned int numDevices = 0; +static unsigned int nextDeviceID = 0; +static struct Gamepad_device * registeredXInputDevices[4]; +static const char * xInputDeviceNames[4] = { + "XInput Controller 1", + "XInput Controller 2", + "XInput Controller 3", + "XInput Controller 4" +}; +static DWORD (WINAPI * XInputGetStateEx_proc)(DWORD dwUserIndex, XINPUT_STATE_EX * pState); +static DWORD (WINAPI * XInputGetState_proc)(DWORD dwUserIndex, XINPUT_STATE * pState); +static DWORD (WINAPI * XInputGetCapabilities_proc)(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES * pCapabilities); + +static LPDIRECTINPUT directInputInterface; +static bool inited = false; +static bool xInputAvailable; + +void Gamepad_init() { + if (!inited) { + HRESULT result; + HMODULE module; + HRESULT (WINAPI * DirectInput8Create_proc)(HINSTANCE, DWORD, REFIID, LPVOID *, LPUNKNOWN); + + module = LoadLibrary("XInput1_4.dll"); + if (module == NULL) { + module = LoadLibrary("XInput1_3.dll"); + } + if (module == NULL) { + module = LoadLibrary("bin\\XInput1_3.dll"); + } + if (module == NULL) { + fprintf(stderr, "Gamepad_init couldn't load XInput1_4.dll or XInput1_3.dll; proceeding with DInput only\n"); + xInputAvailable = false; + } else { + xInputAvailable = true; + XInputGetStateEx_proc = (DWORD (WINAPI *)(DWORD, XINPUT_STATE_EX *)) GetProcAddress(module, (LPCSTR) 100); + XInputGetState_proc = (DWORD (WINAPI *)(DWORD, XINPUT_STATE *)) GetProcAddress(module, "XInputGetState"); + XInputGetCapabilities_proc = (DWORD (WINAPI *)(DWORD, DWORD, XINPUT_CAPABILITIES *)) GetProcAddress(module, "XInputGetCapabilities"); + } + + //result = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8, (void **) &directInputInterface, NULL); + // Calling DirectInput8Create directly crashes in 64-bit builds for some reason. Loading it with GetProcAddress works though! + + module = LoadLibrary("DINPUT8.dll"); + if (module == NULL) { + fprintf(stderr, "Gamepad_init fatal error: Couldn't load DINPUT8.dll\n"); + abort(); + } + + + DirectInput8Create_proc = (HRESULT (WINAPI *)(HINSTANCE, DWORD, REFIID, LPVOID *, LPUNKNOWN)) GetProcAddress(module, "DirectInput8Create"); + + result = DirectInput8Create_proc(GetModuleHandle(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8, (void **) &directInputInterface, NULL); + + if (result != DI_OK) { + fprintf(stderr, "Warning: DirectInput8Create returned 0x%X\n", (unsigned int) result); + } + + inited = true; + Gamepad_detectDevices(); + } +} + +static void disposeDevice(struct Gamepad_device * deviceRecord) { + struct Gamepad_devicePrivate * deviceRecordPrivate = deviceRecord->privateData; + + if (!deviceRecordPrivate->isXInput) { + IDirectInputDevice8_Release(deviceRecordPrivate->deviceInterface); + free(deviceRecordPrivate->axisInfo); + free(deviceRecordPrivate->buttonOffsets); + free((void *) deviceRecord->description); + } + free(deviceRecordPrivate); + + free(deviceRecord->axisStates); + free(deviceRecord->buttonStates); + + free(deviceRecord); +} + +void Gamepad_shutdown() { + unsigned int deviceIndex; + + if (inited) { + for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) { + disposeDevice(devices[deviceIndex]); + } + free(devices); + devices = NULL; + numDevices = 0; + inited = false; + } +} + +unsigned int Gamepad_numDevices() { + return numDevices; +} + +struct Gamepad_device * Gamepad_deviceAtIndex(unsigned int deviceIndex) { + if (deviceIndex >= numDevices) { + return NULL; + } + return devices[deviceIndex]; +} + +static double currentTime() { + static LARGE_INTEGER frequency; + LARGE_INTEGER currentTime; + + if (frequency.QuadPart == 0) { + QueryPerformanceFrequency(&frequency); + } + QueryPerformanceCounter(¤tTime); + + return (double) currentTime.QuadPart / frequency.QuadPart; +} + +#if 0 +// This code from http://msdn.microsoft.com/en-us/library/windows/desktop/ee417014(v=vs.85).aspx is really really really slow +static bool isXInputDevice(const GUID * pGuidProductFromDirectInput) { + IWbemLocator * pIWbemLocator = NULL; + IEnumWbemClassObject * pEnumDevices = NULL; + IWbemClassObject * pDevices[20] = {0}; + IWbemServices * pIWbemServices = NULL; + BSTR bstrNamespace = NULL; + BSTR bstrDeviceID = NULL; + BSTR bstrClassName = NULL; + DWORD uReturned = 0; + bool bIsXinputDevice = false; + UINT iDevice = 0; + VARIANT var; + HRESULT hr; + + hr = CoInitialize(NULL); + bool bCleanupCOM = SUCCEEDED(hr); + + hr = CoCreateInstance(&CLSID_WbemLocator, + NULL, + CLSCTX_INPROC_SERVER, + &IID_IWbemLocator, + (LPVOID *) &pIWbemLocator); + if (FAILED(hr) || pIWbemLocator == NULL) { + goto LCleanup; + } + + bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2"); if (bstrNamespace == NULL) {goto LCleanup;} + bstrClassName = SysAllocString(L"Win32_PNPEntity"); if (bstrClassName == NULL) {goto LCleanup;} + bstrDeviceID = SysAllocString(L"DeviceID"); if (bstrDeviceID == NULL) {goto LCleanup;} + + hr = IWbemLocator_ConnectServer(pIWbemLocator, bstrNamespace, NULL, NULL, 0L, + 0L, NULL, NULL, &pIWbemServices); + if (FAILED(hr) || pIWbemServices == NULL) { + goto LCleanup; + } + + CoSetProxyBlanket((IUnknown *) pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, + RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE); + + hr = IWbemServices_CreateInstanceEnum(pIWbemServices, bstrClassName, 0, NULL, &pEnumDevices); + if (FAILED(hr) || pEnumDevices == NULL) { + goto LCleanup; + } + + for (;;) { + hr = IEnumWbemClassObject_Next(pEnumDevices, 10000, 20, pDevices, &uReturned); + if (FAILED(hr)) { + goto LCleanup; + } + if (uReturned == 0) { + break; + } + for (iDevice = 0; iDevice < uReturned; iDevice++) { + hr = IWbemClassObject_Get(pDevices[iDevice], bstrDeviceID, 0L, &var, NULL, NULL); + if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != NULL) { + if (wcsstr(var.bstrVal, L"IG_")) { + DWORD dwPid = 0, dwVid = 0; + WCHAR * strVid = wcsstr(var.bstrVal, L"VID_"); + if (strVid && swscanf(strVid, L"VID_%4X", &dwVid) != 1) { + dwVid = 0; + } + WCHAR * strPid = wcsstr(var.bstrVal, L"PID_"); + if (strPid != NULL && swscanf(strPid, L"PID_%4X", &dwPid) != 1) { + dwPid = 0; + } + DWORD dwVidPid = MAKELONG(dwVid, dwPid); + if (dwVidPid == pGuidProductFromDirectInput->Data1) { + bIsXinputDevice = true; + goto LCleanup; + } + } + } + if (pDevices[iDevice] != NULL) { + IWbemClassObject_Release(pDevices[iDevice]); + pDevices[iDevice] = NULL; + } + } + } + +LCleanup: + if (bstrNamespace != NULL) { + SysFreeString(bstrNamespace); + } + if (bstrDeviceID != NULL) { + SysFreeString(bstrDeviceID); + } + if (bstrClassName != NULL) { + SysFreeString(bstrClassName); + } + for (iDevice = 0; iDevice < uReturned; iDevice++) { + if (pDevices[iDevice] != NULL) { + IWbemClassObject_Release(pDevices[iDevice]); + } + } + if (pEnumDevices != NULL) { + IEnumWbemClassObject_Release(pEnumDevices); + } + if (pIWbemLocator != NULL) { + IWbemLocator_Release(pIWbemLocator); + } + if (pIWbemServices != NULL) { + IWbemServices_Release(pIWbemServices); + } + + if (bCleanupCOM) { + CoUninitialize(); + } + + return bIsXinputDevice; +} +#else +// This code from SDL2 is much faster + +DEFINE_GUID(IID_ValveStreamingGamepad, MAKELONG(0x28DE, 0x11FF),0x0000,0x0000,0x00,0x00,0x50,0x49,0x44,0x56,0x49,0x44); +DEFINE_GUID(IID_X360WiredGamepad, MAKELONG(0x045E, 0x02A1),0x0000,0x0000,0x00,0x00,0x50,0x49,0x44,0x56,0x49,0x44); +DEFINE_GUID(IID_X360WirelessGamepad, MAKELONG(0x045E, 0x028E),0x0000,0x0000,0x00,0x00,0x50,0x49,0x44,0x56,0x49,0x44); + +static PRAWINPUTDEVICELIST rawDevList = NULL; +static UINT rawDevListCount = 0; + +static bool isXInputDevice(const GUID * pGuidProductFromDirectInput) { + static const GUID * s_XInputProductGUID[] = { + &IID_ValveStreamingGamepad, + &IID_X360WiredGamepad, // Microsoft's wired X360 controller for Windows + &IID_X360WirelessGamepad // Microsoft's wireless X360 controller for Windows + }; + + size_t iDevice; + UINT i; + + // Check for well known XInput device GUIDs + // This lets us skip RAWINPUT for popular devices. Also, we need to do this for the Valve Streaming Gamepad because it's virtualized and doesn't show up in the device list. + for (iDevice = 0; iDevice < sizeof(s_XInputProductGUID) / sizeof(s_XInputProductGUID[0]); ++iDevice) { + if (!memcmp(pGuidProductFromDirectInput, s_XInputProductGUID[iDevice], sizeof(GUID))) { + return true; + } + } + + // Go through RAWINPUT (WinXP and later) to find HID devices. + // Cache this if we end up using it. + if (rawDevList == NULL) { + if ((GetRawInputDeviceList(NULL, &rawDevListCount, sizeof(RAWINPUTDEVICELIST)) == (UINT) -1) || rawDevListCount == 0) { + return false; + } + + rawDevList = malloc(sizeof(RAWINPUTDEVICELIST) * rawDevListCount); + + if (GetRawInputDeviceList(rawDevList, &rawDevListCount, sizeof(RAWINPUTDEVICELIST)) == (UINT) -1) { + free(rawDevList); + rawDevList = NULL; + return false; + } + } + + for (i = 0; i < rawDevListCount; i++) { + RID_DEVICE_INFO rdi; + char devName[128]; + UINT rdiSize = sizeof(rdi); + UINT nameSize = sizeof(devName); + + rdi.cbSize = sizeof(rdi); + if (rawDevList[i].dwType == RIM_TYPEHID && + GetRawInputDeviceInfoA(rawDevList[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) != (UINT) -1 && + MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == (LONG) pGuidProductFromDirectInput->Data1 && + GetRawInputDeviceInfoA(rawDevList[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) != (UINT) -1 && + strstr(devName, "IG_") != NULL) { + return true; + } + } + + return false; +} +#endif + +static BOOL CALLBACK countAxesCallback(LPCDIDEVICEOBJECTINSTANCE instance, LPVOID context) { + struct Gamepad_device * deviceRecord = context; + + deviceRecord->numAxes++; + if (instance->dwType & DIDFT_POV) { + deviceRecord->numAxes++; + } + return DIENUM_CONTINUE; +} + +static BOOL CALLBACK countButtonsCallback(LPCDIDEVICEOBJECTINSTANCE instance, LPVOID context) { + struct Gamepad_device * deviceRecord = context; + + deviceRecord->numButtons++; + return DIENUM_CONTINUE; +} + +#define AXIS_MIN -32768 +#define AXIS_MAX 32767 + +static BOOL CALLBACK enumAxesCallback(LPCDIDEVICEOBJECTINSTANCE instance, LPVOID context) { + struct Gamepad_device * deviceRecord = context; + struct Gamepad_devicePrivate * deviceRecordPrivate = deviceRecord->privateData; + DWORD offset; + + deviceRecord->numAxes++; + if (instance->dwType & DIDFT_POV) { + offset = DIJOFS_POV(deviceRecordPrivate->povCount); + deviceRecordPrivate->axisInfo[deviceRecord->numAxes - 1].offset = offset; + deviceRecordPrivate->axisInfo[deviceRecord->numAxes - 1].isPOV = true; + deviceRecord->numAxes++; + deviceRecordPrivate->axisInfo[deviceRecord->numAxes - 1].offset = offset; + deviceRecordPrivate->axisInfo[deviceRecord->numAxes - 1].isPOV = true; + deviceRecordPrivate->povCount++; + + } else { + DIPROPRANGE range; + DIPROPDWORD deadZone; + HRESULT result; + + if (!memcmp(&instance->guidType, &GUID_XAxis, sizeof(instance->guidType))) { + offset = DIJOFS_X; + } else if (!memcmp(&instance->guidType, &GUID_YAxis, sizeof(instance->guidType))) { + offset = DIJOFS_Y; + } else if (!memcmp(&instance->guidType, &GUID_ZAxis, sizeof(instance->guidType))) { + offset = DIJOFS_Z; + } else if (!memcmp(&instance->guidType, &GUID_RxAxis, sizeof(instance->guidType))) { + offset = DIJOFS_RX; + } else if (!memcmp(&instance->guidType, &GUID_RyAxis, sizeof(instance->guidType))) { + offset = DIJOFS_RY; + } else if (!memcmp(&instance->guidType, &GUID_RzAxis, sizeof(instance->guidType))) { + offset = DIJOFS_RZ; + } else if (!memcmp(&instance->guidType, &GUID_Slider, sizeof(instance->guidType))) { + offset = DIJOFS_SLIDER(deviceRecordPrivate->sliderCount++); + } else { + offset = -1; + } + deviceRecordPrivate->axisInfo[deviceRecord->numAxes - 1].offset = offset; + deviceRecordPrivate->axisInfo[deviceRecord->numAxes - 1].isPOV = false; + + range.diph.dwSize = sizeof(range); + range.diph.dwHeaderSize = sizeof(range.diph); + range.diph.dwObj = instance->dwType; + range.diph.dwHow = DIPH_BYID; + range.lMin = AXIS_MIN; + range.lMax = AXIS_MAX; + + result = IDirectInputDevice8_SetProperty(deviceRecordPrivate->deviceInterface, DIPROP_RANGE, &range.diph); + if (result != DI_OK) { + fprintf(stderr, "Warning: IDIrectInputDevice8_SetProperty returned 0x%X\n", (unsigned int) result); + } + + deadZone.diph.dwSize = sizeof(deadZone); + deadZone.diph.dwHeaderSize = sizeof(deadZone.diph); + deadZone.diph.dwObj = instance->dwType; + deadZone.diph.dwHow = DIPH_BYID; + deadZone.dwData = 0; + result = IDirectInputDevice8_SetProperty(deviceRecordPrivate->deviceInterface, DIPROP_DEADZONE, &deadZone.diph); + if (result != DI_OK) { + fprintf(stderr, "Warning: IDIrectInputDevice8_SetProperty returned 0x%X\n", (unsigned int) result); + } + } + return DIENUM_CONTINUE; +} + +static BOOL CALLBACK enumButtonsCallback(LPCDIDEVICEOBJECTINSTANCE instance, LPVOID context) { + struct Gamepad_device * deviceRecord = context; + struct Gamepad_devicePrivate * deviceRecordPrivate = deviceRecord->privateData; + + deviceRecordPrivate->buttonOffsets[deviceRecord->numButtons] = DIJOFS_BUTTON(deviceRecord->numButtons); + deviceRecord->numButtons++; + return DIENUM_CONTINUE; +} + +#ifdef _MSC_VER +#ifndef DIDFT_OPTIONAL +#define DIDFT_OPTIONAL 0x80000000 +#endif + +/* Taken from Wine - Thanks! */ +DIOBJECTDATAFORMAT dfDIJoystick2[] = { + { &GUID_XAxis, DIJOFS_X, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_YAxis, DIJOFS_Y, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_ZAxis, DIJOFS_Z, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RxAxis, DIJOFS_RX, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RyAxis, DIJOFS_RY, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RzAxis, DIJOFS_RZ, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, DIJOFS_SLIDER(0), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, DIJOFS_SLIDER(1), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_POV, DIJOFS_POV(0), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 }, + { &GUID_POV, DIJOFS_POV(1), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 }, + { &GUID_POV, DIJOFS_POV(2), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 }, + { &GUID_POV, DIJOFS_POV(3), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(0), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(1), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(2), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(3), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(4), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(5), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(6), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(7), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(8), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(9), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(10), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(11), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(12), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(13), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(14), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(15), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(16), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(17), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(18), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(19), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(20), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(21), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(22), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(23), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(24), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(25), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(26), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(27), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(28), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(29), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(30), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(31), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(32), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(33), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(34), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(35), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(36), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(37), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(38), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(39), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(40), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(41), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(42), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(43), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(44), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(45), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(46), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(47), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(48), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(49), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(50), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(51), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(52), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(53), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(54), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(55), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(56), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(57), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(58), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(59), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(60), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(61), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(62), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(63), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(64), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(65), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(66), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(67), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(68), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(69), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(70), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(71), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(72), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(73), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(74), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(75), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(76), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(77), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(78), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(79), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(80), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(81), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(82), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(83), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(84), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(85), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(86), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(87), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(88), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(89), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(90), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(91), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(92), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(93), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(94), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(95), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(96), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(97), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(98), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(99), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(100), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(101), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(102), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(103), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(104), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(105), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(106), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(107), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(108), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(109), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(110), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(111), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(112), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(113), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(114), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(115), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(116), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(117), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(118), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(119), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(120), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(121), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(122), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(123), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(124), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(125), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(126), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(127), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lVX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lVY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lVZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lVRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lVRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lVRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglVSlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglVSlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lAX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lAY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lAZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lARx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lARy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lARz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglASlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglASlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lFX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lFY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lFZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lFRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lFRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lFRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglFSlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglFSlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, +}; + +const DIDATAFORMAT c_dfDIJoystick2 = { + sizeof(DIDATAFORMAT), + sizeof(DIOBJECTDATAFORMAT), + DIDF_ABSAXIS, + sizeof(DIJOYSTATE2), + sizeof(dfDIJoystick2) / sizeof(dfDIJoystick2[0]), + dfDIJoystick2 +}; +#endif + +static BOOL CALLBACK enumDevicesCallback(const DIDEVICEINSTANCE * instance, LPVOID context) { + struct Gamepad_device * deviceRecord; + struct Gamepad_devicePrivate * deviceRecordPrivate; + unsigned int deviceIndex; + IDirectInputDevice * diDevice; + IDirectInputDevice8 * di8Device; + HRESULT result; + DIPROPDWORD bufferSizeProp; + bool buffered = true; + + if (xInputAvailable && isXInputDevice(&instance->guidProduct)) { + return DIENUM_CONTINUE; + } + + for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) { + if (!memcmp(&((struct Gamepad_devicePrivate *) devices[deviceIndex]->privateData)->guidInstance, &instance->guidInstance, sizeof(GUID))) { + return DIENUM_CONTINUE; + } + } + + result = IDirectInput8_CreateDevice(directInputInterface, &instance->guidInstance, &diDevice, NULL); + if (result != DI_OK) { + fprintf(stderr, "Warning: IDirectInput8_CreateDevice returned 0x%X\n", (unsigned int) result); + } + result = IDirectInputDevice8_QueryInterface(diDevice, &IID_IDirectInputDevice8, (LPVOID *) &di8Device); + if (result != DI_OK) { + fprintf(stderr, "Warning: IDirectInputDevice8_QueryInterface returned 0x%X\n", (unsigned int) result); + } + IDirectInputDevice8_Release(diDevice); + + result = IDirectInputDevice8_SetCooperativeLevel(di8Device, GetActiveWindow(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); + if (result != DI_OK) { + fprintf(stderr, "Warning: IDirectInputDevice8_SetCooperativeLevel returned 0x%X\n", (unsigned int) result); + } + + result = IDirectInputDevice8_SetDataFormat(di8Device, &c_dfDIJoystick2); + if (result != DI_OK) { + fprintf(stderr, "Warning: IDirectInputDevice8_SetDataFormat returned 0x%X\n", (unsigned int) result); + } + + bufferSizeProp.diph.dwSize = sizeof(DIPROPDWORD); + bufferSizeProp.diph.dwHeaderSize = sizeof(DIPROPHEADER); + bufferSizeProp.diph.dwObj = 0; + bufferSizeProp.diph.dwHow = DIPH_DEVICE; + bufferSizeProp.dwData = INPUT_QUEUE_SIZE; + result = IDirectInputDevice8_SetProperty(di8Device, DIPROP_BUFFERSIZE, &bufferSizeProp.diph); + if (result == DI_POLLEDDEVICE) { + buffered = false; + } else if (result != DI_OK) { + fprintf(stderr, "Warning: IDirectInputDevice8_SetProperty returned 0x%X\n", (unsigned int) result); + } + + deviceRecord = malloc(sizeof(struct Gamepad_device)); + deviceRecordPrivate = malloc(sizeof(struct Gamepad_devicePrivate)); + deviceRecordPrivate->guidInstance = instance->guidInstance; + deviceRecordPrivate->isXInput = false; + deviceRecordPrivate->deviceInterface = di8Device; + deviceRecordPrivate->buffered = buffered; + deviceRecordPrivate->sliderCount = 0; + deviceRecordPrivate->povCount = 0; + deviceRecord->privateData = deviceRecordPrivate; + deviceRecord->deviceID = nextDeviceID++; + deviceRecord->description = strdup(instance->tszProductName); + deviceRecord->vendorID = instance->guidProduct.Data1 & 0xFFFF; + deviceRecord->productID = instance->guidProduct.Data1 >> 16 & 0xFFFF; + deviceRecord->numAxes = 0; + IDirectInputDevice_EnumObjects(di8Device, countAxesCallback, deviceRecord, DIDFT_AXIS | DIDFT_POV); + deviceRecord->numButtons = 0; + IDirectInputDevice_EnumObjects(di8Device, countButtonsCallback, deviceRecord, DIDFT_BUTTON); + deviceRecord->axisStates = calloc(sizeof(float), deviceRecord->numAxes); + deviceRecord->buttonStates = calloc(sizeof(bool), deviceRecord->numButtons); + deviceRecordPrivate->axisInfo = calloc(sizeof(struct diAxisInfo), deviceRecord->numAxes); + deviceRecordPrivate->buttonOffsets = calloc(sizeof(DWORD), deviceRecord->numButtons); + deviceRecord->numAxes = 0; + IDirectInputDevice_EnumObjects(di8Device, enumAxesCallback, deviceRecord, DIDFT_AXIS | DIDFT_POV); + deviceRecord->numButtons = 0; + IDirectInputDevice_EnumObjects(di8Device, enumButtonsCallback, deviceRecord, DIDFT_BUTTON); + devices = realloc(devices, sizeof(struct Gamepad_device *) * (numDevices + 1)); + devices[numDevices++] = deviceRecord; + + return DIENUM_CONTINUE; +} + +static void removeDevice(unsigned int deviceIndex) { + if (Gamepad_deviceRemoveCallback != NULL) { + Gamepad_deviceRemoveCallback(devices[deviceIndex], Gamepad_deviceRemoveContext); + } + + disposeDevice(devices[deviceIndex]); + numDevices--; + for (; deviceIndex < numDevices; deviceIndex++) { + devices[deviceIndex] = devices[deviceIndex + 1]; + } +} + +void Gamepad_detectDevices() { + HRESULT result; + DWORD xResult; + XINPUT_CAPABILITIES capabilities; + unsigned int playerIndex, deviceIndex; + + if (!inited) { + return; + } + + result = IDirectInput_EnumDevices(directInputInterface, DI8DEVCLASS_GAMECTRL, enumDevicesCallback, NULL, DIEDFL_ALLDEVICES); + if (result != DI_OK) { + fprintf(stderr, "Warning: IDirectInput_EnumDevices returned 0x%X\n", (unsigned int) result); + } + + if (xInputAvailable) { + for (playerIndex = 0; playerIndex < 4; playerIndex++) { + xResult = XInputGetCapabilities_proc(playerIndex, 0, &capabilities); + if (xResult == ERROR_SUCCESS && registeredXInputDevices[playerIndex] == NULL) { + struct Gamepad_device * deviceRecord; + struct Gamepad_devicePrivate * deviceRecordPrivate; + + deviceRecord = malloc(sizeof(struct Gamepad_device)); + deviceRecordPrivate = malloc(sizeof(struct Gamepad_devicePrivate)); + deviceRecordPrivate->isXInput = true; + deviceRecordPrivate->playerIndex = playerIndex; + deviceRecord->privateData = deviceRecordPrivate; + deviceRecord->deviceID = nextDeviceID++; + deviceRecord->description = xInputDeviceNames[playerIndex]; + // HACK: XInput doesn't provide any way to get vendor and product ID, nor any way to map player index to + // DirectInput device enumeration. All we can do is assume all XInput devices are XBox 360 controllers. + deviceRecord->vendorID = 0x45E; + deviceRecord->productID = 0x28E; + deviceRecord->numAxes = 6; + deviceRecord->numButtons = 15; + deviceRecord->axisStates = calloc(sizeof(float), deviceRecord->numAxes); + deviceRecord->buttonStates = calloc(sizeof(bool), deviceRecord->numButtons); + devices = realloc(devices, sizeof(struct Gamepad_device *) * (numDevices + 1)); + devices[numDevices++] = deviceRecord; + registeredXInputDevices[playerIndex] = deviceRecord; + + } else if (xResult != ERROR_SUCCESS && registeredXInputDevices[playerIndex] != NULL) { + for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) { + if (devices[deviceIndex] == registeredXInputDevices[playerIndex]) { + removeDevice(deviceIndex); + break; + } + } + registeredXInputDevices[playerIndex] = NULL; + } + } + } +} + +static void updateButtonValue(struct Gamepad_device * device, unsigned int buttonIndex, bool down, double timestamp) { + if (down != device->buttonStates[buttonIndex]) { + device->buttonStates[buttonIndex] = down; + if (down && Gamepad_buttonDownCallback != NULL) { + Gamepad_buttonDownCallback(device, buttonIndex, timestamp, Gamepad_buttonDownContext); + } else if (!down && Gamepad_buttonUpCallback != NULL) { + Gamepad_buttonUpCallback(device, buttonIndex, timestamp, Gamepad_buttonUpContext); + } + } +} + +static void updateAxisValueFloat(struct Gamepad_device * device, unsigned int axisIndex, float value, double timestamp) { + float lastValue; + + lastValue = device->axisStates[axisIndex]; + device->axisStates[axisIndex] = value; + if (value != lastValue && Gamepad_axisMoveCallback != NULL) { + Gamepad_axisMoveCallback(device, axisIndex, value, lastValue, timestamp, Gamepad_axisMoveContext); + } +} + +static void updateAxisValue(struct Gamepad_device * device, unsigned int axisIndex, LONG ivalue, double timestamp) { + updateAxisValueFloat(device, axisIndex, (ivalue - AXIS_MIN) / (float) (AXIS_MAX - AXIS_MIN) * 2.0f - 1.0f, timestamp); +} + +#define POV_UP 0 +#define POV_RIGHT 9000 +#define POV_DOWN 18000 +#define POV_LEFT 27000 + +static void povToXY(DWORD pov, float * outX, float * outY) { + if (LOWORD(pov) == 0xFFFF) { + *outX = *outY = 0.0f; + + } else { + if (pov > POV_UP && pov < POV_DOWN) { + *outX = 1.0f; + + } else if (pov > POV_DOWN) { + *outX = -1.0f; + + } else { + *outX = 0.0f; + } + + if (pov > POV_LEFT || pov < POV_RIGHT) { + *outY = -1.0f; + + } else if (pov > POV_RIGHT && pov < POV_LEFT) { + *outY = 1.0f; + + } else { + *outY = 0.0f; + } + } +} + +static void updatePOVAxisValues(struct Gamepad_device * device, unsigned int axisIndex, DWORD ivalue, double timestamp) { + float x = 0.0f, y = 0.0f; + + povToXY(ivalue, &x, &y); + updateAxisValueFloat(device, axisIndex, x, timestamp); + updateAxisValueFloat(device, axisIndex + 1, y, timestamp); +} + +void Gamepad_processEvents() { + static bool inProcessEvents; + unsigned int deviceIndex, buttonIndex, axisIndex; + struct Gamepad_device * device; + struct Gamepad_devicePrivate * devicePrivate; + HRESULT result; + + if (!inited || inProcessEvents) { + return; + } + + inProcessEvents = true; + for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) { + device = devices[deviceIndex]; + devicePrivate = device->privateData; + + if (devicePrivate->isXInput) { + XINPUT_STATE state; + DWORD xResult; + + if (XInputGetStateEx_proc != NULL) { + XINPUT_STATE_EX stateEx; + + xResult = XInputGetStateEx_proc(devicePrivate->playerIndex, &stateEx); + state.Gamepad.wButtons = stateEx.Gamepad.wButtons; + state.Gamepad.sThumbLX = stateEx.Gamepad.sThumbLX; + state.Gamepad.sThumbLY = stateEx.Gamepad.sThumbLY; + state.Gamepad.sThumbRX = stateEx.Gamepad.sThumbRX; + state.Gamepad.sThumbRY = stateEx.Gamepad.sThumbRY; + state.Gamepad.bLeftTrigger = stateEx.Gamepad.bLeftTrigger; + state.Gamepad.bRightTrigger = stateEx.Gamepad.bRightTrigger; + } else { + xResult = XInputGetState_proc(devicePrivate->playerIndex, &state); + } + if (xResult == ERROR_SUCCESS) { + updateButtonValue(device, 0, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP), currentTime()); + updateButtonValue(device, 1, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN), currentTime()); + updateButtonValue(device, 2, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT), currentTime()); + updateButtonValue(device, 3, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT), currentTime()); + updateButtonValue(device, 4, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_START), currentTime()); + updateButtonValue(device, 5, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK), currentTime()); + updateButtonValue(device, 6, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB), currentTime()); + updateButtonValue(device, 7, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB), currentTime()); + updateButtonValue(device, 8, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER), currentTime()); + updateButtonValue(device, 9, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER), currentTime()); + updateButtonValue(device, 10, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_A), currentTime()); + updateButtonValue(device, 11, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_B), currentTime()); + updateButtonValue(device, 12, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_X), currentTime()); + updateButtonValue(device, 13, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_Y), currentTime()); + updateButtonValue(device, 14, !!(state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE), currentTime()); + updateAxisValue(device, 0, state.Gamepad.sThumbLX, currentTime()); + updateAxisValue(device, 1, state.Gamepad.sThumbLY, currentTime()); + updateAxisValue(device, 2, state.Gamepad.sThumbRX, currentTime()); + updateAxisValue(device, 3, state.Gamepad.sThumbRY, currentTime()); + updateAxisValueFloat(device, 4, state.Gamepad.bLeftTrigger / 127.5f - 1.0f, currentTime()); + updateAxisValueFloat(device, 5, state.Gamepad.bRightTrigger / 127.5f - 1.0f, currentTime()); + + } else { + registeredXInputDevices[devicePrivate->playerIndex] = NULL; + removeDevice(deviceIndex); + deviceIndex--; + continue; + } + + } else { + result = IDirectInputDevice8_Poll(devicePrivate->deviceInterface); + if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) { + IDirectInputDevice8_Acquire(devicePrivate->deviceInterface); + IDirectInputDevice8_Poll(devicePrivate->deviceInterface); + } + + if (devicePrivate->buffered) { + DWORD eventCount = INPUT_QUEUE_SIZE; + DIDEVICEOBJECTDATA events[INPUT_QUEUE_SIZE]; + unsigned int eventIndex; + + result = IDirectInputDevice8_GetDeviceData(devicePrivate->deviceInterface, sizeof(DIDEVICEOBJECTDATA), events, &eventCount, 0); + if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) { + IDirectInputDevice8_Acquire(devicePrivate->deviceInterface); + result = IDirectInputDevice8_GetDeviceData(devicePrivate->deviceInterface, sizeof(DIDEVICEOBJECTDATA), events, &eventCount, 0); + } + if (result != DI_OK) { + removeDevice(deviceIndex); + deviceIndex--; + continue; + } + + for (eventIndex = 0; eventIndex < eventCount; eventIndex++) { + for (buttonIndex = 0; buttonIndex < device->numButtons; buttonIndex++) { + if (events[eventIndex].dwOfs == devicePrivate->buttonOffsets[buttonIndex]) { + updateButtonValue(device, buttonIndex, !!events[eventIndex].dwData, events[eventIndex].dwTimeStamp / 1000.0); + } + } + for (axisIndex = 0; axisIndex < device->numAxes; axisIndex++) { + if (events[eventIndex].dwOfs == devicePrivate->axisInfo[axisIndex].offset) { + if (devicePrivate->axisInfo[axisIndex].isPOV) { + updatePOVAxisValues(device, axisIndex, events[eventIndex].dwData, events[eventIndex].dwTimeStamp / 1000.0); + axisIndex++; + } else { + updateAxisValue(device, axisIndex, events[eventIndex].dwData, events[eventIndex].dwTimeStamp / 1000.0); + } + } + } + } + + } else { + DIJOYSTATE2 state; + + result = IDirectInputDevice8_GetDeviceState(devicePrivate->deviceInterface, sizeof(DIJOYSTATE2), &state); + if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) { + IDirectInputDevice8_Acquire(devicePrivate->deviceInterface); + result = IDirectInputDevice8_GetDeviceState(devicePrivate->deviceInterface, sizeof(DIJOYSTATE2), &state); + } + + if (result != DI_OK) { + removeDevice(deviceIndex); + deviceIndex--; + continue; + } + + for (buttonIndex = 0; buttonIndex < device->numButtons; buttonIndex++) { + updateButtonValue(device, buttonIndex, !!state.rgbButtons[buttonIndex], currentTime()); + } + + for (axisIndex = 0; axisIndex < device->numAxes; axisIndex++) { + switch (devicePrivate->axisInfo[axisIndex].offset) { + case DIJOFS_X: + updateAxisValue(device, axisIndex, state.lX, currentTime()); + break; + case DIJOFS_Y: + updateAxisValue(device, axisIndex, state.lY, currentTime()); + break; + case DIJOFS_Z: + updateAxisValue(device, axisIndex, state.lZ, currentTime()); + break; + case DIJOFS_RX: + updateAxisValue(device, axisIndex, state.lRx, currentTime()); + break; + case DIJOFS_RY: + updateAxisValue(device, axisIndex, state.lRy, currentTime()); + break; + case DIJOFS_RZ: + updateAxisValue(device, axisIndex, state.lRz, currentTime()); + break; + case DIJOFS_SLIDER(0): + updateAxisValue(device, axisIndex, state.rglSlider[0], currentTime()); + break; + case DIJOFS_SLIDER(1): + updateAxisValue(device, axisIndex, state.rglSlider[1], currentTime()); + break; + case DIJOFS_POV(0): + updatePOVAxisValues(device, axisIndex, state.rgdwPOV[0], currentTime()); + axisIndex++; + break; + case DIJOFS_POV(1): + updatePOVAxisValues(device, axisIndex, state.rgdwPOV[1], currentTime()); + axisIndex++; + break; + case DIJOFS_POV(2): + updatePOVAxisValues(device, axisIndex, state.rgdwPOV[2], currentTime()); + axisIndex++; + break; + case DIJOFS_POV(3): + updatePOVAxisValues(device, axisIndex, state.rgdwPOV[3], currentTime()); + axisIndex++; + break; + } + } + } + } + } + inProcessEvents = false; +} + diff --git a/internal/c/parts/input/game_controller/src/Gamepad_windows_mm.c b/internal/c/parts/input/game_controller/src/Gamepad_windows_mm.c new file mode 100644 index 000000000..fcf93b46c --- /dev/null +++ b/internal/c/parts/input/game_controller/src/Gamepad_windows_mm.c @@ -0,0 +1,400 @@ +/* + Copyright (c) 2014 Alex Diener + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Alex Diener alex@ludobloom.com +*/ + +#include "gamepad/Gamepad.h" +#include "gamepad/Gamepad_private.h" +#include +#include +#include +#include + +struct Gamepad_devicePrivate { + UINT joystickID; + JOYINFOEX lastState; + int xAxisIndex; + int yAxisIndex; + int zAxisIndex; + int rAxisIndex; + int uAxisIndex; + int vAxisIndex; + int povXAxisIndex; + int povYAxisIndex; + UINT (* axisRanges)[2]; +}; + +static struct Gamepad_device ** devices = NULL; +static unsigned int numDevices = 0; +static unsigned int nextDeviceID = 0; + +static bool inited = false; + +void Gamepad_init() { + if (!inited) { + inited = true; + Gamepad_detectDevices(); + } +} + +static void disposeDevice(struct Gamepad_device * deviceRecord) { + free(((struct Gamepad_devicePrivate *) deviceRecord->privateData)->axisRanges); + free(deviceRecord->privateData); + + free((void *) deviceRecord->description); + free(deviceRecord->axisStates); + free(deviceRecord->buttonStates); + + free(deviceRecord); +} + +void Gamepad_shutdown() { + unsigned int deviceIndex; + + if (inited) { + for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) { + disposeDevice(devices[deviceIndex]); + } + free(devices); + devices = NULL; + numDevices = 0; + inited = false; + } +} + +unsigned int Gamepad_numDevices() { + return numDevices; +} + +struct Gamepad_device * Gamepad_deviceAtIndex(unsigned int deviceIndex) { + if (deviceIndex >= numDevices) { + return NULL; + } + return devices[deviceIndex]; +} + +#define REG_STRING_MAX 256 + +static char * getDeviceDescription(UINT joystickID, JOYCAPS caps) { + char * description = NULL; + char subkey[REG_STRING_MAX]; + HKEY topKey, key; + LONG result; + + snprintf(subkey, REG_STRING_MAX, "%s\\%s\\%s", REGSTR_PATH_JOYCONFIG, caps.szRegKey, REGSTR_KEY_JOYCURR); + result = RegOpenKeyEx(topKey = HKEY_LOCAL_MACHINE, subkey, 0, KEY_READ, &key); + if (result != ERROR_SUCCESS) { + result = RegOpenKeyEx(topKey = HKEY_CURRENT_USER, subkey, 0, KEY_READ, &key); + } + if (result == ERROR_SUCCESS) { + char value[REG_STRING_MAX]; + char name[REG_STRING_MAX]; + DWORD nameSize; + + snprintf(value, REG_STRING_MAX, "Joystick%d%s", joystickID + 1, REGSTR_VAL_JOYOEMNAME); + nameSize = sizeof(name); + result = RegQueryValueEx(key, value, NULL, NULL, (LPBYTE) name, &nameSize); + RegCloseKey(key); + + if (result == ERROR_SUCCESS) { + snprintf(subkey, REG_STRING_MAX, "%s\\%s", REGSTR_PATH_JOYOEM, name); + result = RegOpenKeyEx(topKey, subkey, 0, KEY_READ, &key); + + if (result == ERROR_SUCCESS) { + nameSize = sizeof(name); + result = RegQueryValueEx(key, REGSTR_VAL_JOYOEMNAME, NULL, NULL, NULL, &nameSize); + + if (result == ERROR_SUCCESS) { + description = malloc(nameSize); + result = RegQueryValueEx(key, REGSTR_VAL_JOYOEMNAME, NULL, NULL, (LPBYTE) description, &nameSize); + } + RegCloseKey(key); + + if (result == ERROR_SUCCESS) { + return description; + } + free(description); + } + } + } + + description = malloc(strlen(caps.szPname) + 1); + strcpy(description, caps.szPname); + + return description; +} + +void Gamepad_detectDevices() { + unsigned int numPadsSupported; + unsigned int deviceIndex, deviceIndex2; + JOYINFOEX info; + JOYCAPS caps; + bool duplicate; + struct Gamepad_device * deviceRecord; + struct Gamepad_devicePrivate * deviceRecordPrivate; + UINT joystickID; + int axisIndex; + + if (!inited) { + return; + } + + numPadsSupported = joyGetNumDevs(); + for (deviceIndex = 0; deviceIndex < numPadsSupported; deviceIndex++) { + info.dwSize = sizeof(info); + info.dwFlags = JOY_RETURNALL; + joystickID = JOYSTICKID1 + deviceIndex; + if (joyGetPosEx(joystickID, &info) == JOYERR_NOERROR && + joyGetDevCaps(joystickID, &caps, sizeof(JOYCAPS)) == JOYERR_NOERROR) { + + duplicate = false; + for (deviceIndex2 = 0; deviceIndex2 < numDevices; deviceIndex2++) { + if (((struct Gamepad_devicePrivate *) devices[deviceIndex2]->privateData)->joystickID == joystickID) { + duplicate = true; + break; + } + } + if (duplicate) { + continue; + } + + deviceRecord = malloc(sizeof(struct Gamepad_device)); + deviceRecord->deviceID = nextDeviceID++; + deviceRecord->description = getDeviceDescription(joystickID, caps); + deviceRecord->vendorID = caps.wMid; + deviceRecord->productID = caps.wPid; + deviceRecord->numAxes = caps.wNumAxes + ((caps.wCaps & JOYCAPS_HASPOV) ? 2 : 0); + deviceRecord->numButtons = caps.wNumButtons; + deviceRecord->axisStates = calloc(sizeof(float), deviceRecord->numAxes); + deviceRecord->buttonStates = calloc(sizeof(bool), deviceRecord->numButtons); + devices = realloc(devices, sizeof(struct Gamepad_device *) * (numDevices + 1)); + devices[numDevices++] = deviceRecord; + + deviceRecordPrivate = malloc(sizeof(struct Gamepad_devicePrivate)); + deviceRecordPrivate->joystickID = joystickID; + deviceRecordPrivate->lastState = info; + + deviceRecordPrivate->xAxisIndex = 0; + deviceRecordPrivate->yAxisIndex = 1; + axisIndex = 2; + deviceRecordPrivate->zAxisIndex = (caps.wCaps & JOYCAPS_HASZ) ? axisIndex++ : -1; + deviceRecordPrivate->rAxisIndex = (caps.wCaps & JOYCAPS_HASR) ? axisIndex++ : -1; + deviceRecordPrivate->uAxisIndex = (caps.wCaps & JOYCAPS_HASU) ? axisIndex++ : -1; + deviceRecordPrivate->vAxisIndex = (caps.wCaps & JOYCAPS_HASV) ? axisIndex++ : -1; + + deviceRecordPrivate->axisRanges = malloc(sizeof(UINT[2]) * axisIndex); + deviceRecordPrivate->axisRanges[0][0] = caps.wXmin; + deviceRecordPrivate->axisRanges[0][1] = caps.wXmax; + deviceRecordPrivate->axisRanges[1][0] = caps.wYmin; + deviceRecordPrivate->axisRanges[1][1] = caps.wYmax; + if (deviceRecordPrivate->zAxisIndex != -1) { + deviceRecordPrivate->axisRanges[deviceRecordPrivate->zAxisIndex][0] = caps.wZmin; + deviceRecordPrivate->axisRanges[deviceRecordPrivate->zAxisIndex][1] = caps.wZmax; + } + if (deviceRecordPrivate->rAxisIndex != -1) { + deviceRecordPrivate->axisRanges[deviceRecordPrivate->rAxisIndex][0] = caps.wRmin; + deviceRecordPrivate->axisRanges[deviceRecordPrivate->rAxisIndex][1] = caps.wRmax; + } + if (deviceRecordPrivate->uAxisIndex != -1) { + deviceRecordPrivate->axisRanges[deviceRecordPrivate->uAxisIndex][0] = caps.wUmin; + deviceRecordPrivate->axisRanges[deviceRecordPrivate->uAxisIndex][1] = caps.wUmax; + } + if (deviceRecordPrivate->vAxisIndex != -1) { + deviceRecordPrivate->axisRanges[deviceRecordPrivate->vAxisIndex][0] = caps.wVmin; + deviceRecordPrivate->axisRanges[deviceRecordPrivate->vAxisIndex][1] = caps.wVmax; + } + + deviceRecordPrivate->povXAxisIndex = (caps.wCaps & JOYCAPS_HASPOV) ? axisIndex++ : -1; + deviceRecordPrivate->povYAxisIndex = (caps.wCaps & JOYCAPS_HASPOV) ? axisIndex++ : -1; + + deviceRecord->privateData = deviceRecordPrivate; + + if (Gamepad_deviceAttachCallback != NULL) { + Gamepad_deviceAttachCallback(deviceRecord, Gamepad_deviceAttachContext); + } + } + } +} + +static double currentTime() { + // HACK: No timestamp data from joyGetInfoEx, so we make it up + static LARGE_INTEGER frequency; + LARGE_INTEGER currentTime; + + if (frequency.QuadPart == 0) { + QueryPerformanceFrequency(&frequency); + } + QueryPerformanceCounter(¤tTime); + + return (double) currentTime.QuadPart / frequency.QuadPart; +} + +static void handleAxisChange(struct Gamepad_device * device, int axisIndex, DWORD ivalue) { + float value, lastValue; + struct Gamepad_devicePrivate * devicePrivate; + + if (axisIndex < 0 || axisIndex >= (int) device->numAxes) { + return; + } + + devicePrivate = device->privateData; + value = (ivalue - devicePrivate->axisRanges[axisIndex][0]) / (float) (devicePrivate->axisRanges[axisIndex][1] - devicePrivate->axisRanges[axisIndex][0]) * 2.0f - 1.0f; + + lastValue = device->axisStates[axisIndex]; + device->axisStates[axisIndex] = value; + if (Gamepad_axisMoveCallback != NULL) { + Gamepad_axisMoveCallback(device, axisIndex, value, lastValue, currentTime(), Gamepad_axisMoveContext); + } +} + +static void handleButtonChange(struct Gamepad_device * device, DWORD lastValue, DWORD value) { + bool down; + unsigned int buttonIndex; + + for (buttonIndex = 0; buttonIndex < device->numButtons; buttonIndex++) { + if ((lastValue ^ value) & (1 << buttonIndex)) { + down = !!(value & (1 << buttonIndex)); + + device->buttonStates[buttonIndex] = down; + if (down && Gamepad_buttonDownCallback != NULL) { + Gamepad_buttonDownCallback(device, buttonIndex, currentTime(), Gamepad_buttonDownContext); + } else if (!down && Gamepad_buttonUpCallback != NULL) { + Gamepad_buttonUpCallback(device, buttonIndex, currentTime(), Gamepad_buttonUpContext); + } + } + } +} + +static void povToXY(DWORD pov, int * outX, int * outY) { + if (pov == JOY_POVCENTERED) { + *outX = *outY = 0; + + } else { + if (pov > JOY_POVFORWARD && pov < JOY_POVBACKWARD) { + *outX = 1; + + } else if (pov > JOY_POVBACKWARD) { + *outX = -1; + + } else { + *outX = 0; + } + + if (pov > JOY_POVLEFT || pov < JOY_POVRIGHT) { + *outY = -1; + + } else if (pov > JOY_POVRIGHT && pov < JOY_POVLEFT) { + *outY = 1; + + } else { + *outY = 0; + } + } +} + +static void handlePOVChange(struct Gamepad_device * device, DWORD lastValue, DWORD value) { + struct Gamepad_devicePrivate * devicePrivate; + int lastX, lastY, newX, newY; + + devicePrivate = device->privateData; + + if (devicePrivate->povXAxisIndex == -1 || devicePrivate->povYAxisIndex == -1) { + return; + } + + povToXY(lastValue, &lastX, &lastY); + povToXY(value, &newX, &newY); + + if (newX != lastX) { + device->axisStates[devicePrivate->povXAxisIndex] = newX; + if (Gamepad_axisMoveCallback != NULL) { + Gamepad_axisMoveCallback(device, devicePrivate->povXAxisIndex, newX, lastX, currentTime(), Gamepad_axisMoveContext); + } + } + if (newY != lastY) { + device->axisStates[devicePrivate->povYAxisIndex] = newY; + if (Gamepad_axisMoveCallback != NULL) { + Gamepad_axisMoveCallback(device, devicePrivate->povYAxisIndex, newY, lastY, currentTime(), Gamepad_axisMoveContext); + } + } +} + +void Gamepad_processEvents() { + unsigned int deviceIndex; + static bool inProcessEvents; + JOYINFOEX info; + MMRESULT result; + struct Gamepad_device * device; + struct Gamepad_devicePrivate * devicePrivate; + + if (!inited || inProcessEvents) { + return; + } + + inProcessEvents = true; + for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) { + device = devices[deviceIndex]; + devicePrivate = device->privateData; + + info.dwSize = sizeof(info); + info.dwFlags = JOY_RETURNALL; + result = joyGetPosEx(devicePrivate->joystickID, &info); + if (result == JOYERR_UNPLUGGED) { + if (Gamepad_deviceRemoveCallback != NULL) { + Gamepad_deviceRemoveCallback(device, Gamepad_deviceRemoveContext); + } + + disposeDevice(device); + numDevices--; + for (; deviceIndex < numDevices; deviceIndex++) { + devices[deviceIndex] = devices[deviceIndex + 1]; + } + + } else if (result == JOYERR_NOERROR) { + if (info.dwXpos != devicePrivate->lastState.dwXpos) { + handleAxisChange(device, devicePrivate->xAxisIndex, info.dwXpos); + } + if (info.dwYpos != devicePrivate->lastState.dwYpos) { + handleAxisChange(device, devicePrivate->yAxisIndex, info.dwYpos); + } + if (info.dwZpos != devicePrivate->lastState.dwZpos) { + handleAxisChange(device, devicePrivate->zAxisIndex, info.dwZpos); + } + if (info.dwRpos != devicePrivate->lastState.dwRpos) { + handleAxisChange(device, devicePrivate->rAxisIndex, info.dwRpos); + } + if (info.dwUpos != devicePrivate->lastState.dwUpos) { + handleAxisChange(device, devicePrivate->uAxisIndex, info.dwUpos); + } + if (info.dwVpos != devicePrivate->lastState.dwVpos) { + handleAxisChange(device, devicePrivate->vAxisIndex, info.dwVpos); + } + if (info.dwPOV != devicePrivate->lastState.dwPOV) { + handlePOVChange(device, devicePrivate->lastState.dwPOV, info.dwPOV); + } + if (info.dwButtons != devicePrivate->lastState.dwButtons) { + handleButtonChange(device, devicePrivate->lastState.dwButtons, info.dwButtons); + } + devicePrivate->lastState = info; + } + } + inProcessEvents = false; +} + diff --git a/internal/c/parts/input/game_controller/src/gamepad/Gamepad.h b/internal/c/parts/input/game_controller/src/gamepad/Gamepad.h index bff69f747..7c8994ee1 100644 --- a/internal/c/parts/input/game_controller/src/gamepad/Gamepad.h +++ b/internal/c/parts/input/game_controller/src/gamepad/Gamepad.h @@ -1,128 +1,128 @@ -/* - Copyright (c) 2010 Alex Diener - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Alex Diener adiener@sacredsoftware.net -*/ - -#ifndef __GAMEPAD_H__ -#define __GAMEPAD_H__ - -#include -#include "utilities/EventDispatcher.h" - -// eventData -> struct Gamepad_device -#define GAMEPAD_EVENT_DEVICE_ATTACHED "GAMEPAD_EVENT_DEVICE_ATTACHED" // Only dispatched when Gamepad_init or Gamepad_detectDevices is called -#define GAMEPAD_EVENT_DEVICE_REMOVED "GAMEPAD_EVENT_DEVICE_REMOVED" // Can be dispatched at any time - -// eventData -> struct Gamepad_buttonEvent -#define GAMEPAD_EVENT_BUTTON_DOWN "GAMEPAD_EVENT_BUTTON_DOWN" // Only dispatched when Gamepad_processEvents is called -#define GAMEPAD_EVENT_BUTTON_UP "GAMEPAD_EVENT_BUTTON_UP" // Only dispatched when Gamepad_processEvents is called - -// eventData -> struct Gamepad_axisEvent -#define GAMEPAD_EVENT_AXIS_MOVED "GAMEPAD_EVENT_AXIS_MOVED" // Only dispatched when Gamepad_processEvents is called - -struct Gamepad_buttonEvent { - // Device that generated the event - struct Gamepad_device * device; - - // Relative time of the event, in seconds - double timestamp; - - // Button being pushed or released - unsigned int buttonID; - - // True if button is down - bool down; -}; - -struct Gamepad_axisEvent { - // Device that generated the event - struct Gamepad_device * device; - - // Relative time of the event, in seconds - double timestamp; - - // Axis being moved - unsigned int axisID; - - // Axis position value, in the range [-1..1] - float value; -}; - -struct Gamepad_device { - // Unique device identifier for application session. If a device is removed and subsequently reattached during the same application session, it will have a new deviceID. - unsigned int deviceID; - - // Human-readable device name - const char * description; - - // USB vendor/product IDs as returned by the driver. Can be used to determine the particular model of device represented. - int vendorID; - int productID; - - // Number of axis elements belonging to the device - unsigned int numAxes; - - // Number of button elements belonging to the device - unsigned int numButtons; - - // Array[numAxes] of values representing the current state of each axis, in the range [-1..1] - float * axisStates; - - // Array[numButtons] of values representing the current state of each button - bool * buttonStates; - - // Broadcasts GAMEPAD_EVENT_BUTTON_DOWN, GAMEPAD_EVENT_BUTTON_UP, and GAMEPAD_EVENT_AXIS_MOVED - EventDispatcher * eventDispatcher; - - // Platform-specific device data storage; don't mess with it - void * privateData; -}; - -/* Initializes gamepad library and detects initial devices. Call this before any other Gamepad_*() - function, EXCEPT Gamepad_eventDispatcher(). In order to get receive GAMEPAD_EVENT_DEVICE_ATTACHED - events from devices detected in Gamepad_init(), you must register handlers for those events before - calling Gamepad_init(). */ -void Gamepad_init(); - -/* Tears down all data structures created by the gamepad library and releases any memory that was - allocated. It is not necessary to call this function at application termination. */ -void Gamepad_shutdown(); - -/* EventDispatcher used by gamepad library to broadcast GAMEPAD_EVENT_DEVICE_ATTACHED and - GAMEPAD_EVENT_DEVICE_REMOVED events. */ -EventDispatcher * Gamepad_eventDispatcher(); - -/* Returns the number of currently attached gamepad devices. */ -unsigned int Gamepad_numDevices(); - -/* Returns the specified Gamepad_device struct, or NULL if deviceIndex is out of bounds. */ -struct Gamepad_device * Gamepad_deviceAtIndex(unsigned int deviceIndex); - -/* Polls for any devices that have been attached since the last call to Gamepad_detectDevices() or - Gamepad_init(). If any new devices are found, a GAMEPAD_EVENT_DEVICE_ATTACHED event will be - broadcast via Gamepad_eventDispatcher() for each one. */ -void Gamepad_detectDevices(); - -/* Reads pending input from all attached devices and broadcasts GAMEPAD_EVENT_BUTTON_DOWN, - GAMEPAD_EVENT_BUTTON_UP, and GAMEPAD_EVENT_AXIS_MOVED events through the eventDispatcher of the - device that generated the event. */ -void Gamepad_processEvents(); - -#endif +/* + Copyright (c) 2014 Alex Diener + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Alex Diener alex@ludobloom.com +*/ + +#ifndef __GAMEPAD_H__ +#define __GAMEPAD_H__ +#ifdef __cplusplus +extern "C" { +#endif + +#include + +struct Gamepad_device { + // Unique device identifier for application session, starting at 0 for the first device attached and + // incrementing by 1 for each additional device. If a device is removed and subsequently reattached + // during the same application session, it will have a new deviceID. + unsigned int deviceID; + + // Human-readable device name + const char * description; + + // USB vendor/product IDs as returned by the driver. Can be used to determine the particular model of device represented. + int vendorID; + int productID; + + // Number of axis elements belonging to the device + unsigned int numAxes; + + // Number of button elements belonging to the device + unsigned int numButtons; + + // Array[numAxes] of values representing the current state of each axis, in the range [-1..1] + float * axisStates; + + // Array[numButtons] of values representing the current state of each button + bool * buttonStates; + + // Platform-specific device data storage. Don't touch unless you know what you're doing and don't + // mind your code breaking in future versions of this library. + void * privateData; +}; + +/* Initializes gamepad library and detects initial devices. Call this before any other Gamepad_*() + function, other than callback registration functions. If you want to receive deviceAttachFunc + callbacks from devices detected in Gamepad_init(), you must call Gamepad_deviceAttachFunc() + before calling Gamepad_init(). + + This function must be called from the same thread that will be calling Gamepad_processEvents() + and Gamepad_detectDevices(). */ +void Gamepad_init(); + +/* Tears down all data structures created by the gamepad library and releases any memory that was + allocated. It is not necessary to call this function at application termination, but it's + provided in case you want to free memory associated with gamepads at some earlier time. */ +void Gamepad_shutdown(); + +/* Returns the number of currently attached gamepad devices. */ +unsigned int Gamepad_numDevices(); + +/* Returns the specified Gamepad_device struct, or NULL if deviceIndex is out of bounds. */ +struct Gamepad_device * Gamepad_deviceAtIndex(unsigned int deviceIndex); + +/* Polls for any devices that have been attached since the last call to Gamepad_detectDevices() or + Gamepad_init(). If any new devices are found, the callback registered with + Gamepad_deviceAttachFunc() (if any) will be called once per newly detected device. + + Note that depending on implementation, you may receive button and axis event callbacks for + devices that have not yet been detected with Gamepad_detectDevices(). You can safely ignore + these events, but be aware that your callbacks might receive a device ID that hasn't been seen + by your deviceAttachFunc. */ +void Gamepad_detectDevices(); + +/* Reads pending input from all attached devices and calls the appropriate input callbacks, if any + have been registered. */ +void Gamepad_processEvents(); + +/* Registers a function to be called whenever a device is attached. The specified function will be + called only during calls to Gamepad_init() and Gamepad_detectDevices(), in the thread from + which those functions were called. Calling this function with a NULL argument will stop any + previously registered callback from being called subsequently. */ +void Gamepad_deviceAttachFunc(void (* callback)(struct Gamepad_device * device, void * context), void * context); + +/* Registers a function to be called whenever a device is detached. The specified function can be + called at any time, and will not necessarily be called from the main thread. Calling this + function with a NULL argument will stop any previously registered callback from being called + subsequently. */ +void Gamepad_deviceRemoveFunc(void (* callback)(struct Gamepad_device * device, void * context), void * context); + +/* Registers a function to be called whenever a button on any attached device is pressed. The + specified function will be called only during calls to Gamepad_processEvents(), in the + thread from which Gamepad_processEvents() was called. Calling this function with a NULL + argument will stop any previously registered callback from being called subsequently. */ +void Gamepad_buttonDownFunc(void (* callback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context), void * context); + +/* Registers a function to be called whenever a button on any attached device is released. The + specified function will be called only during calls to Gamepad_processEvents(), in the + thread from which Gamepad_processEvents() was called. Calling this function with a NULL + argument will stop any previously registered callback from being called subsequently. */ +void Gamepad_buttonUpFunc(void (* callback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context), void * context); + +/* Registers a function to be called whenever an axis on any attached device is moved. The + specified function will be called only during calls to Gamepad_processEvents(), in the + thread from which Gamepad_processEvents() was called. Calling this function with a NULL + argument will stop any previously registered callback from being called subsequently. */ +void Gamepad_axisMoveFunc(void (* callback)(struct Gamepad_device * device, unsigned int axisID, float value, float lastValue, double timestamp, void * context), void * context); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/internal/c/parts/input/game_controller/src/gamepad/Gamepad_private.h b/internal/c/parts/input/game_controller/src/gamepad/Gamepad_private.h new file mode 100644 index 000000000..9d10c2bac --- /dev/null +++ b/internal/c/parts/input/game_controller/src/gamepad/Gamepad_private.h @@ -0,0 +1,76 @@ +/* + Copyright (c) 2014 Alex Diener + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Alex Diener alex@ludobloom.com +*/ + +#ifndef __GAMEPAD_PRIVATE_H__ +#define __GAMEPAD_PRIVATE_H__ + +enum Gamepad_eventType { + GAMEPAD_EVENT_DEVICE_ATTACHED, + GAMEPAD_EVENT_DEVICE_REMOVED, + GAMEPAD_EVENT_BUTTON_DOWN, + GAMEPAD_EVENT_BUTTON_UP, + GAMEPAD_EVENT_AXIS_MOVED +}; + +struct Gamepad_buttonEvent { + // Device that generated the event + struct Gamepad_device * device; + + // Relative time of the event, in seconds + double timestamp; + + // Button being pushed or released + unsigned int buttonID; + + // True if button is down + bool down; +}; + +struct Gamepad_axisEvent { + // Device that generated the event + struct Gamepad_device * device; + + // Relative time of the event, in seconds + double timestamp; + + // Axis being moved + unsigned int axisID; + + // Axis position value, in the range [-1..1] + float value; + + // Previous axis position value, in the range [-1..1] + float lastValue; +}; + +extern void (* Gamepad_deviceAttachCallback)(struct Gamepad_device * device, void * context); +extern void (* Gamepad_deviceRemoveCallback)(struct Gamepad_device * device, void * context); +extern void (* Gamepad_buttonDownCallback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context); +extern void (* Gamepad_buttonUpCallback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context); +extern void (* Gamepad_axisMoveCallback)(struct Gamepad_device * device, unsigned int axisID, float value, float lastValue, double timestamp, void * context); +extern void * Gamepad_deviceAttachContext; +extern void * Gamepad_deviceRemoveContext; +extern void * Gamepad_buttonDownContext; +extern void * Gamepad_buttonUpContext; +extern void * Gamepad_axisMoveContext; + +#endif diff --git a/internal/c/parts/input/game_controller/src/shell/Shell.h b/internal/c/parts/input/game_controller/src/shell/Shell.h deleted file mode 100644 index 4280d45e2..000000000 --- a/internal/c/parts/input/game_controller/src/shell/Shell.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef __SHELL_H__ -#define __SHELL_H__ - -void Shell_mainLoop(); -void Shell_redisplay(); - -double Shell_getCurrentTime(); -const char * Shell_getResourcePath(); - -#endif diff --git a/internal/c/parts/input/game_controller/src/shell/ShellKeyCodes.h b/internal/c/parts/input/game_controller/src/shell/ShellKeyCodes.h deleted file mode 100644 index 70af572fd..000000000 --- a/internal/c/parts/input/game_controller/src/shell/ShellKeyCodes.h +++ /dev/null @@ -1,176 +0,0 @@ -// Adapted from GameShell (http://onesadcookie.com/svn/GameShell/Source/Common/GSKeyCodesInternal.h). Thanks Keith! - -#ifndef __SHELL_KEY_CODES_H__ -#define __SHELL_KEY_CODES_H__ - -#define KEYBOARD_A 0x04 -#define KEYBOARD_B 0x05 -#define KEYBOARD_C 0x06 -#define KEYBOARD_D 0x07 -#define KEYBOARD_E 0x08 -#define KEYBOARD_F 0x09 -#define KEYBOARD_G 0x0A -#define KEYBOARD_H 0x0B -#define KEYBOARD_I 0x0C -#define KEYBOARD_J 0x0D -#define KEYBOARD_K 0x0E -#define KEYBOARD_L 0x0F -#define KEYBOARD_M 0x10 -#define KEYBOARD_N 0x11 -#define KEYBOARD_O 0x12 -#define KEYBOARD_P 0x13 -#define KEYBOARD_Q 0x14 -#define KEYBOARD_R 0x15 -#define KEYBOARD_S 0x16 -#define KEYBOARD_T 0x17 -#define KEYBOARD_U 0x18 -#define KEYBOARD_V 0x19 -#define KEYBOARD_W 0x1A -#define KEYBOARD_X 0x1B -#define KEYBOARD_Y 0x1C -#define KEYBOARD_Z 0x1D -#define KEYBOARD_1 0x1E -#define KEYBOARD_2 0x1F -#define KEYBOARD_3 0x20 -#define KEYBOARD_4 0x21 -#define KEYBOARD_5 0x22 -#define KEYBOARD_6 0x23 -#define KEYBOARD_7 0x24 -#define KEYBOARD_8 0x25 -#define KEYBOARD_9 0x26 -#define KEYBOARD_0 0x27 -#define KEYBOARD_RETURN_OR_ENTER 0x28 -#define KEYBOARD_ESCAPE 0x29 -#define KEYBOARD_DELETE_OR_BACKSPACE 0x2A -#define KEYBOARD_TAB 0x2B -#define KEYBOARD_SPACEBAR 0x2C -#define KEYBOARD_HYPHEN 0x2D -#define KEYBOARD_EQUAL_SIGN 0x2E -#define KEYBOARD_OPEN_BRACKET 0x2F -#define KEYBOARD_CLOSE_BRACKET 0x30 -#define KEYBOARD_BACKSLASH 0x31 -#define KEYBOARD_NON_USPOUND 0x32 -#define KEYBOARD_SEMICOLON 0x33 -#define KEYBOARD_QUOTE 0x34 -#define KEYBOARD_GRAVE_ACCENT_AND_TILDE 0x35 -#define KEYBOARD_COMMA 0x36 -#define KEYBOARD_PERIOD 0x37 -#define KEYBOARD_SLASH 0x38 -#define KEYBOARD_CAPS_LOCK 0x39 -#define KEYBOARD_F1 0x3A -#define KEYBOARD_F2 0x3B -#define KEYBOARD_F3 0x3C -#define KEYBOARD_F4 0x3D -#define KEYBOARD_F5 0x3E -#define KEYBOARD_F6 0x3F -#define KEYBOARD_F7 0x40 -#define KEYBOARD_F8 0x41 -#define KEYBOARD_F9 0x42 -#define KEYBOARD_F10 0x43 -#define KEYBOARD_F11 0x44 -#define KEYBOARD_F12 0x45 -#define KEYBOARD_PRINT_SCREEN 0x46 -#define KEYBOARD_SCROLL_LOCK 0x47 -#define KEYBOARD_PAUSE 0x48 -#define KEYBOARD_INSERT 0x49 -#define KEYBOARD_HOME 0x4A -#define KEYBOARD_PAGE_UP 0x4B -#define KEYBOARD_DELETE_FORWARD 0x4C -#define KEYBOARD_END 0x4D -#define KEYBOARD_PAGE_DOWN 0x4E -#define KEYBOARD_RIGHT_ARROW 0x4F -#define KEYBOARD_LEFT_ARROW 0x50 -#define KEYBOARD_DOWN_ARROW 0x51 -#define KEYBOARD_UP_ARROW 0x52 -#define KEYPAD_NUM_LOCK 0x53 -#define KEYPAD_SLASH 0x54 -#define KEYPAD_ASTERISK 0x55 -#define KEYPAD_HYPHEN 0x56 -#define KEYPAD_PLUS 0x57 -#define KEYPAD_ENTER 0x58 -#define KEYPAD_1 0x59 -#define KEYPAD_2 0x5A -#define KEYPAD_3 0x5B -#define KEYPAD_4 0x5C -#define KEYPAD_5 0x5D -#define KEYPAD_6 0x5E -#define KEYPAD_7 0x5F -#define KEYPAD_8 0x60 -#define KEYPAD_9 0x61 -#define KEYPAD_0 0x62 -#define KEYPAD_PERIOD 0x63 -#define KEYBOARD_NON_US_BACKSLASH 0x64 -#define KEYBOARD_APPLICATION 0x65 -#define KEYBOARD_POWER 0x66 -#define KEYPAD_EQUAL_SIGN 0x67 -#define KEYBOARD_F13 0x68 -#define KEYBOARD_F14 0x69 -#define KEYBOARD_F15 0x6A -#define KEYBOARD_F16 0x6B -#define KEYBOARD_F17 0x6C -#define KEYBOARD_F18 0x6D -#define KEYBOARD_F19 0x6E -#define KEYBOARD_F20 0x6F -#define KEYBOARD_F21 0x70 -#define KEYBOARD_F22 0x71 -#define KEYBOARD_F23 0x72 -#define KEYBOARD_F24 0x73 -#define KEYBOARD_EXECUTE 0x74 -#define KEYBOARD_HELP 0x75 -#define KEYBOARD_MENU 0x76 -#define KEYBOARD_SELECT 0x77 -#define KEYBOARD_STOP 0x78 -#define KEYBOARD_AGAIN 0x79 -#define KEYBOARD_UNDO 0x7A -#define KEYBOARD_CUT 0x7B -#define KEYBOARD_COPY 0x7C -#define KEYBOARD_PASTE 0x7D -#define KEYBOARD_FIND 0x7E -#define KEYBOARD_MUTE 0x7F -#define KEYBOARD_VOLUME_UP 0x80 -#define KEYBOARD_VOLUME_DOWN 0x81 -#define KEYBOARD_LOCKING_CAPS_LOCK 0x82 -#define KEYBOARD_LOCKING_NUM_LOCK 0x83 -#define KEYBOARD_LOCKING_SCROLL_LOCK 0x84 -#define KEYPAD_COMMA 0x85 -#define KEYPAD_EQUAL_SIGN_AS400 0x86 -#define KEYBOARD_INTERNATIONAL_1 0x87 -#define KEYBOARD_INTERNATIONAL_2 0x88 -#define KEYBOARD_INTERNATIONAL_3 0x89 -#define KEYBOARD_INTERNATIONAL_4 0x8A -#define KEYBOARD_INTERNATIONAL_5 0x8B -#define KEYBOARD_INTERNATIONAL_6 0x8C -#define KEYBOARD_INTERNATIONAL_7 0x8D -#define KEYBOARD_INTERNATIONAL_8 0x8E -#define KEYBOARD_INTERNATIONAL_9 0x8F -#define KEYBOARD_LANG1 0x90 -#define KEYBOARD_LANG2 0x91 -#define KEYBOARD_LANG3 0x92 -#define KEYBOARD_LANG4 0x93 -#define KEYBOARD_LANG5 0x94 -#define KEYBOARD_LANG6 0x95 -#define KEYBOARD_LANG7 0x96 -#define KEYBOARD_LANG8 0x97 -#define KEYBOARD_LANG9 0x98 -#define KEYBOARD_ALTERNATE_ERASE 0x99 -#define KEYBOARD_SYS_REQ_OR_ATTENTION 0x9A -#define KEYBOARD_CANCEL 0x9B -#define KEYBOARD_CLEAR 0x9C -#define KEYBOARD_PRIOR 0x9D -#define KEYBOARD_RETURN 0x9E -#define KEYBOARD_SEPARATOR 0x9F -#define KEYBOARD_OUT 0xA0 -#define KEYBOARD_OPER 0xA1 -#define KEYBOARD_CLEAR_OR_AGAIN 0xA2 -#define KEYBOARD_CR_SEL_OR_PROPS 0xA3 -#define KEYBOARD_EX_SEL 0xA4 -#define KEYBOARD_LEFT_CONTROL 0xE0 -#define KEYBOARD_LEFT_SHIFT 0xE1 -#define KEYBOARD_LEFT_ALT 0xE2 -#define KEYBOARD_LEFT_GUI 0xE3 -#define KEYBOARD_RIGHT_CONTROL 0xE4 -#define KEYBOARD_RIGHT_SHIFT 0xE5 -#define KEYBOARD_RIGHT_ALT 0xE6 -#define KEYBOARD_RIGHT_GUI 0xE7 - -#endif diff --git a/internal/c/parts/input/game_controller/src/shell/Target.h b/internal/c/parts/input/game_controller/src/shell/Target.h deleted file mode 100644 index 93e002c52..000000000 --- a/internal/c/parts/input/game_controller/src/shell/Target.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef __TARGET_H__ -#define __TARGET_H__ - -const char * Target_getName(); - -void Target_init(int argc, char ** argv); -void Target_draw(); - -void Target_keyDown(int charCode, int keyCode); -void Target_keyUp(int charCode, int keyCode); -void Target_mouseDown(int buttonNumber, float x, float y); -void Target_mouseUp(int buttonNumber, float x, float y); -void Target_mouseMoved(float x, float y); -void Target_mouseDragged(int buttonMask, float x, float y); - -void Target_resized(int newWidth, int newHeight); - -#endif diff --git a/internal/c/parts/input/game_controller/src/utilities/EventDispatcher.c b/internal/c/parts/input/game_controller/src/utilities/EventDispatcher.c deleted file mode 100644 index dff59d906..000000000 --- a/internal/c/parts/input/game_controller/src/utilities/EventDispatcher.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - Copyright (c) 2010 Alex Diener - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Alex Diener adiener@sacredsoftware.net -*/ - -#include "utilities/EventDispatcher.h" - -#include -#include - -struct EventTarget { - char * eventID; - EventDispatcherCallback callback; - void * context; -}; - -EventDispatcher * EventDispatcher_create(void * owner) { - EventDispatcher * self; - - self = malloc(sizeof(EventDispatcher)); - EventDispatcher_init(self, owner); - return self; -} - -void EventDispatcher_init(EventDispatcher * self, void * owner) { - self->dispose = EventDispatcher_dispose; - self->registerForEvent = EventDispatcher_registerForEvent; - self->unregisterForEvent = EventDispatcher_unregisterForEvent; - self->dispatchEvent = EventDispatcher_dispatchEvent; - - self->owner = owner; - self->numberOfTargets = 0; - self->targetListSize = 1; - self->targets = (struct EventTarget *) malloc(sizeof(struct EventTarget) * self->targetListSize); -} - -void EventDispatcher_dispose(void * selfPtr) { - EventDispatcher * self = selfPtr; - int targetIndex; - - for (targetIndex = 0; targetIndex < self->numberOfTargets; targetIndex++) { - free(self->targets[targetIndex].eventID); - } - free(self->targets); -} - -void EventDispatcher_registerForEvent(void * selfPtr, const char * eventID, EventDispatcherCallback callback, void * context) { - EventDispatcher * self = selfPtr; - size_t length; - - if (self->numberOfTargets >= self->targetListSize) { - self->targetListSize *= 2; - self->targets = (struct EventTarget *) realloc(self->targets, sizeof(struct EventTarget) * self->targetListSize); - } - - length = strlen(eventID); - self->targets[self->numberOfTargets].eventID = malloc(length + 1); - strncpy(self->targets[self->numberOfTargets].eventID, eventID, length + 1); - self->targets[self->numberOfTargets].callback = callback; - self->targets[self->numberOfTargets].context = context; - self->numberOfTargets++; -} - -void EventDispatcher_unregisterForEvent(void * selfPtr, const char * eventID, EventDispatcherCallback callback) { - EventDispatcher * self = selfPtr; - int targetIndex; - - for (targetIndex = 0; targetIndex < self->numberOfTargets; targetIndex++) { - if (!strcmp(eventID, self->targets[targetIndex].eventID) && self->targets[targetIndex].callback == callback) { - free(self->targets[targetIndex].eventID); - self->numberOfTargets--; - for (; targetIndex < self->numberOfTargets; targetIndex++) { - self->targets[targetIndex] = self->targets[targetIndex + 1]; - } - break; - } - } -} - -bool EventDispatcher_dispatchEvent(void * selfPtr, const char * eventID, void * eventData) { - EventDispatcher * self = selfPtr; - int targetIndex; - int numberOfTargetsCopy; - struct EventTarget * targetsCopy; - bool eventHandled, anyEventsHandled; - - numberOfTargetsCopy = self->numberOfTargets; - targetsCopy = malloc(sizeof(struct EventTarget) * numberOfTargetsCopy); - memcpy(targetsCopy, self->targets, sizeof(struct EventTarget) * numberOfTargetsCopy); - - anyEventsHandled = false; - for (targetIndex = 0; targetIndex < numberOfTargetsCopy; targetIndex++) { - if (!strcmp(eventID, self->targets[targetIndex].eventID)) { - eventHandled = targetsCopy[targetIndex].callback(self->owner, eventID, eventData, targetsCopy[targetIndex].context); - - if (eventHandled) { - anyEventsHandled = true; - } - } - } - - free(targetsCopy); - - return anyEventsHandled; -} diff --git a/internal/c/parts/input/game_controller/src/utilities/EventDispatcher.h b/internal/c/parts/input/game_controller/src/utilities/EventDispatcher.h deleted file mode 100644 index 34de874dd..000000000 --- a/internal/c/parts/input/game_controller/src/utilities/EventDispatcher.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - Copyright (c) 2010 Alex Diener - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Alex Diener adiener@sacredsoftware.net -*/ - -#ifndef __EVENT_DISPATCHER_H__ -#define __EVENT_DISPATCHER_H__ - -#include - -typedef struct EventDispatcher EventDispatcher; - -/* Signature for event handler callbacks. - - sender: Object that dispatched the event. More specifically, the object passed to EventDispatcher_create. - eventID: Name of event that was triggered - eventData: Arbitrary data passed by dispatcher. Its format is known by convention depending on the event ID being dispatched. - context: Value passed as context to registerForEvent - - This function should return true if the event was handled, or false if it was ignored. */ -typedef bool (* EventDispatcherCallback)(void * sender, const char * eventID, void * eventData, void * context); - -struct EventTarget; - -#define EventDispatcher_structContents \ - void * owner; \ - \ - int numberOfTargets; \ - int targetListSize; \ - struct EventTarget * targets; \ - \ - void (* dispose)(void * self); \ - void (* registerForEvent)(void * self, const char * eventID, EventDispatcherCallback callback, void * context); \ - void (* unregisterForEvent)(void * self, const char * eventID, EventDispatcherCallback callback); \ - bool (* dispatchEvent)(void * self, const char * eventID, void * eventData); - -struct EventDispatcher { - EventDispatcher_structContents -}; - -/* Allocate and initialize a new EventDispatcher object. owner will be passed to event callbacks as - the sender parameter. */ -EventDispatcher * EventDispatcher_create(void * owner); - -/* Initialize an already allocated EventDispatcher. owner will be passed to event callbacks as the - sender parameter. */ -void EventDispatcher_init(EventDispatcher * self, void * owner); - -/* Free all memory allocated by EventDispatcher and remove all registered listeners. Does NOT free - the EventDispatcher itself. */ -void EventDispatcher_dispose(void * selfPtr); - -/* Register for notification of events of type eventID */ -void EventDispatcher_registerForEvent(void * selfPtr, const char * eventID, EventDispatcherCallback callback, void * context); - -/* Remove a previous registration for events of type eventID */ -void EventDispatcher_unregisterForEvent(void * selfPtr, const char * eventID, EventDispatcherCallback callback); - -/* Dispatch an event to all registered listeners for that event ID. Returns true if any listener is - registered and returns true from its handler callback. */ -bool EventDispatcher_dispatchEvent(void * selfPtr, const char * eventID, void * eventData); - -#endif diff --git a/source/qb64.bas b/source/qb64.bas index af74848d3..9a3e0d8d4 100644 --- a/source/qb64.bas +++ b/source/qb64.bas @@ -51,7 +51,6 @@ END IF DIM SHARED Include_GDB_Debugging_Info 'set using "options.bin" - DIM SHARED DEPENDENCY_LAST CONST DEPENDENCY_LOADFONT = 1: DEPENDENCY_LAST = DEPENDENCY_LAST + 1 CONST DEPENDENCY_AUDIO_CONVERSION = 2: DEPENDENCY_LAST = DEPENDENCY_LAST + 1 @@ -65,14 +64,16 @@ CONST DEPENDENCY_SOCKETS = 9: DEPENDENCY_LAST = DEPENDENCY_LAST + 1 CONST DEPENDENCY_PRINTER = 10: DEPENDENCY_LAST = DEPENDENCY_LAST + 1 CONST DEPENDENCY_ICON = 11: DEPENDENCY_LAST = DEPENDENCY_LAST + 1 CONST DEPENDENCY_SCREENIMAGE = 12: DEPENDENCY_LAST = DEPENDENCY_LAST + 1 +CONST DEPENDENCY_DEVICEINPUT = 13: DEPENDENCY_LAST = DEPENDENCY_LAST + 1 'removes support for gamepad input if not present + + + DIM SHARED DEPENDENCY(1 TO DEPENDENCY_LAST) DIM SHARED UseGL 'declared SUB _GL (no params) - - DIM SHARED OS_BITS AS LONG OS_BITS = 64: IF INSTR(_OS$, "[32BIT]") THEN OS_BITS = 32 @@ -944,12 +945,12 @@ sendc$ = "" 'no initial message IF CMDLineFile <> "" THEN sendc$ = CHR$(1) + CMDLineFile sendcommand: idecommand$ = sendc$ -c = ide(0) +C = ide(0) ideerror = 0 -IF c = 0 THEN idemode = 0: GOTO noide +IF C = 0 THEN idemode = 0: GOTO noide c$ = idereturn$ -IF c = 2 THEN 'begin +IF C = 2 THEN 'begin ideerrorline = 0 'addresses invalid prepass error line numbers being reported idepass = 1 GOTO fullrecompile @@ -961,7 +962,7 @@ IF c = 2 THEN 'begin GOTO sendcommand END IF -IF c = 4 THEN 'next line +IF C = 4 THEN 'next line IF idepass = 1 THEN wholeline$ = c$ GOTO ideprepass @@ -976,7 +977,7 @@ IF c = 4 THEN 'next line GOTO sendcommand END IF -IF c = 5 THEN 'end of program reached +IF C = 5 THEN 'end of program reached IF idepass = 1 THEN 'prepass complete idepass = 2 @@ -994,7 +995,7 @@ IF c = 5 THEN 'end of program reached GOTO sendcommand END IF -IF c = 9 THEN 'run +IF C = 9 THEN 'run IF idecompiled = 0 THEN 'exe needs to be compiled file$ = c$ @@ -5862,6 +5863,7 @@ DO IF n >= 2 THEN IF firstelement$ = "ON" AND secondelement$ = "STRIG" THEN + DEPENDENCY(DEPENDENCY_DEVICEINPUT) = 1 i = 3 IF i > n THEN a$ = "Expected (": GOTO errmes a2$ = getelement$(ca$, i): i = i + 1 @@ -10769,6 +10771,10 @@ IF DEPENDENCY(DEPENDENCY_GL) THEN defines$ = defines$ + defines_header$ + "DEPENDENCY_GL" END IF +IF DEPENDENCY(DEPENDENCY_SCREENIMAGE) THEN + DEPENDENCY(DEPENDENCY_IMAGE_CODEC) = 1 'used by OSX to read in screen capture files +END IF + IF DEPENDENCY(DEPENDENCY_IMAGE_CODEC) THEN defines$ = defines$ + defines_header$ + "DEPENDENCY_IMAGE_CODEC" END IF @@ -10789,7 +10795,6 @@ ELSE defines$ = defines$ + defines_header$ + "DEPENDENCY_NO_PRINTER" END IF - IF DEPENDENCY(DEPENDENCY_ICON) THEN defines$ = defines$ + defines_header$ + "DEPENDENCY_ICON" ELSE @@ -10812,6 +10817,17 @@ IF DEPENDENCY(DEPENDENCY_LOADFONT) THEN libs$ = libs$ + " " + "parts\video\font\ttf\os\" + o$ + "\src.o" END IF +localpath$ = "internal\c\" + +IF DEPENDENCY(DEPENDENCY_DEVICEINPUT) THEN + defines$ = defines$ + defines_header$ + "DEPENDENCY_DEVICEINPUT" + libname$ = "input\game_controller" + libpath$ = "parts\" + libname$ + "\os\" + o$ + libfile$ = libpath$ + "\src.a" + IF _FILEEXISTS(localpath$ + libfile$) = 0 THEN Build localpath$ + libpath$ 'rebuild? + libs$ = libs$ + " " + libfile$ +END IF + IF DEPENDENCY(DEPENDENCY_AUDIO_DECODE) THEN DEPENDENCY(DEPENDENCY_AUDIO_CONVERSION) = 1 IF DEPENDENCY(DEPENDENCY_AUDIO_CONVERSION) THEN DEPENDENCY(DEPENDENCY_AUDIO_OUT) = 1 IF DEPENDENCY(DEPENDENCY_AUDIO_DECODE) THEN DEPENDENCY(DEPENDENCY_AUDIO_OUT) = 1 @@ -11630,7 +11646,7 @@ tpos = 1 DO token$ = MID$(cmdline$, tpos, 2) ')) SELECT CASE token$ - CASE "-g" 'non-GUI environment ($CONSOLE:ONLY in effect) + CASE "-g" 'non-GUI environment (uses $CONSOLE:ONLY) DEPENDENCY(DEPENDENCY_CONSOLE_ONLY) = DEPENDENCY(DEPENDENCY_CONSOLE_ONLY) OR 2 NoIDEMode = 1 'Implies -c Console = 1 diff --git a/source/subs_functions/subs_functions.bas b/source/subs_functions/subs_functions.bas index 2f393292c..ad0b60ecd 100644 --- a/source/subs_functions/subs_functions.bas +++ b/source/subs_functions/subs_functions.bas @@ -289,7 +289,7 @@ regid 'QB64 DEVICE interface clearid -id.n = "STICK" +id.n = "STICK": id.Dependency=DEPENDENCY_DEVICEINPUT id.subfunc = 1 id.callname = "func_stick" id.args = 2 @@ -299,7 +299,7 @@ id.specialformat = "?[,?]" regid clearid -id.n = "STRIG" +id.n = "STRIG": id.Dependency=DEPENDENCY_DEVICEINPUT id.subfunc = 1 id.callname = "func_strig" id.args = 2 @@ -309,7 +309,7 @@ id.specialformat = "?[,?]" regid clearid -id.n = "STRIG" +id.n = "STRIG": id.Dependency=DEPENDENCY_DEVICEINPUT id.subfunc = 2 id.callname = "sub_strig" id.args = 3 @@ -321,14 +321,14 @@ regid clearid -id.n = "_DEVICES" +id.n = "_DEVICES": id.Dependency=DEPENDENCY_DEVICEINPUT id.subfunc = 1 id.callname = "func__devices" id.ret = LONGTYPE - ISPOINTER regid clearid -id.n = "_DEVICE" +id.n = "_DEVICE": id.Dependency=DEPENDENCY_DEVICEINPUT id.musthave = "$" id.subfunc = 1 id.callname = "func__device" @@ -339,7 +339,7 @@ id.specialformat = "[?]" regid clearid -id.n = "_DEVICEINPUT" +id.n = "_DEVICEINPUT": id.Dependency=DEPENDENCY_DEVICEINPUT id.subfunc = 1 id.callname = "func__deviceinput" id.args = 1 @@ -349,7 +349,7 @@ id.specialformat = "[?]" regid clearid -id.n = "_LASTBUTTON" +id.n = "_LASTBUTTON": id.Dependency=DEPENDENCY_DEVICEINPUT id.subfunc = 1 id.callname = "func__lastbutton" id.args = 1 @@ -359,7 +359,7 @@ id.specialformat = "[?]" regid clearid -id.n = "_LASTAXIS" +id.n = "_LASTAXIS": id.Dependency=DEPENDENCY_DEVICEINPUT id.subfunc = 1 id.callname = "func__lastaxis" id.args = 1 @@ -369,7 +369,7 @@ id.specialformat = "[?]" regid clearid -id.n = "_LASTWHEEL" +id.n = "_LASTWHEEL": id.Dependency=DEPENDENCY_DEVICEINPUT id.subfunc = 1 id.callname = "func__lastwheel" id.args = 1 @@ -379,7 +379,7 @@ id.specialformat = "[?]" regid clearid -id.n = "_BUTTON" +id.n = "_BUTTON": id.Dependency=DEPENDENCY_DEVICEINPUT id.subfunc = 1 id.callname = "func__button" id.args = 1 @@ -389,7 +389,7 @@ id.specialformat = "[?]" regid clearid -id.n = "_BUTTONCHANGE" +id.n = "_BUTTONCHANGE": id.Dependency=DEPENDENCY_DEVICEINPUT id.subfunc = 1 id.callname = "func__buttonchange" id.args = 1 @@ -399,7 +399,7 @@ id.specialformat = "[?]" regid clearid -id.n = "_AXIS" +id.n = "_AXIS": id.Dependency=DEPENDENCY_DEVICEINPUT id.subfunc = 1 id.callname = "func__axis" id.args = 1 @@ -410,7 +410,7 @@ regid clearid -id.n = "_WHEEL" +id.n = "_WHEEL": id.Dependency=DEPENDENCY_DEVICEINPUT id.subfunc = 1 id.callname = "func__wheel" id.args = 1 @@ -559,7 +559,6 @@ id.args = 4 id.arg = MKL$(LONGTYPE - ISPOINTER) + MKL$(LONGTYPE - ISPOINTER) + MKL$(LONGTYPE - ISPOINTER) + MKL$(LONGTYPE - ISPOINTER) id.specialformat = "[?,?,?,?]" id.ret = LONGTYPE - ISPOINTER -id.Dependency = DEPENDENCY_IMAGE_CODEC 'used by OSX to read in screen capture files id.NoCloud = 1 regid @@ -735,7 +734,6 @@ id.subfunc = 2 id.callname = "sub__autodisplay" regid - clearid id.n = "_LIMIT" id.subfunc = 2 @@ -744,6 +742,15 @@ id.args = 1 id.arg = MKL$(DOUBLETYPE - ISPOINTER) regid +clearid +id.n = "_FPS" +id.subfunc = 2 +id.callname = "sub__fps" +id.args = 1 +id.arg = MKL$(DOUBLETYPE - ISPOINTER) +id.specialformat = "[{_AUTO}][?]" +regid + clearid id.n = "_DELAY" id.subfunc = 2 @@ -793,14 +800,13 @@ id.ret = LONGTYPE - ISPOINTER regid clearid -id.n = "_LOADIMAGE" +id.n = "_LOADIMAGE": id.Dependency = DEPENDENCY_IMAGE_CODEC id.subfunc = 1 id.callname = "func__loadimage" id.args = 2 id.arg = MKL$(STRINGTYPE - ISPOINTER) + MKL$(LONGTYPE - ISPOINTER) id.specialformat = "?[,?]" id.ret = LONGTYPE - ISPOINTER -id.Dependency = DEPENDENCY_IMAGE_CODEC regid clearid @@ -1035,14 +1041,13 @@ regid 'FONT SUPPORT clearid -id.n = "_LOADFONT" +id.n = "_LOADFONT": id.Dependency = DEPENDENCY_LOADFONT id.subfunc = 1 id.callname = "func__loadfont" id.args = 3 id.arg = MKL$(STRINGTYPE - ISPOINTER) + MKL$(DOUBLETYPE - ISPOINTER) + MKL$(STRINGTYPE - ISPOINTER) id.specialformat = "?,?[,?]" id.ret = LONGTYPE - ISPOINTER -id.Dependency = DEPENDENCY_LOADFONT regid clearid