1
1
Fork 0
mirror of https://github.com/QB64Official/qb64.git synced 2024-07-03 07:41:21 +00:00

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
This commit is contained in:
Galleon 2015-01-18 04:20:57 -08:00
parent e86f50a3cf
commit 3928325615
57 changed files with 6561 additions and 3962 deletions

View file

@ -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

View file

@ -23,6 +23,7 @@
#include <X11/Xutil.h>
#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(){

View file

@ -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, <olszta@sourceforge.net>
* 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 <errno.h>
#endif
#include <stdarg.h>
#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, <olszta@sourceforge.net>
* 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 <errno.h>
#endif
#include <stdarg.h>
#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

View file

@ -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

View file

@ -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
Alex Diener alex@ludobloom.com

View file

@ -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

View file

@ -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.

View file

@ -0,0 +1,162 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>testharness</RootNamespace>
<ProjectName>testharness</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\source;C:\Program Files\Common Files\MSVC\freeglut\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>C:\Projects\StemLibProjects\gamepad_trunk\msvc\Debug;C:\Program Files\Common Files\MSVC\freeglut\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
<AdditionalDependencies>libstem_gamepad.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\source;C:\Program Files\Common Files\MSVC\freeglut\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>C:\Projects\StemLibProjects\gamepad_trunk\msvc\x64\Debug;C:\Program Files\Common Files\MSVC\freeglut\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
<AdditionalDependencies>libstem_gamepad.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>C:\Program Files\Common Files\MSVC\freeglut\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalLibraryDirectories>C:\Program Files\Common Files\MSVC\freeglut\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>C:\Program Files\Common Files\MSVC\freeglut\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalLibraryDirectories>C:\Program Files\Common Files\MSVC\freeglut\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\source\testharness\TestHarness_main.c" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\source\testharness\TestHarness_main.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -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

View file

@ -0,0 +1,142 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{4579D2EA-39EB-4766-9EB6-497500F379D6}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>libstem_gamepad</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>../../source</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>../../source</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\source\gamepad\Gamepad_private.c" />
<ClCompile Include="..\..\source\gamepad\Gamepad_windows_dinput.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\source\gamepad\Gamepad.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\source\gamepad\Gamepad_windows_dinput.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\source\gamepad\Gamepad_private.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\source\gamepad\Gamepad.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -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 <stdbool.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;
};
/* 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

View file

@ -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 <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <linux/input.h>
#define __USE_UNIX98
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
@ -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;
}

View file

@ -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 <IOKit/hid/IOHIDLib.h>
#include <limits.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#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;
}

View file

@ -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 <stdlib.h>
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;
}

View file

@ -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

View file

@ -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 <stdio.h>
#include <stdlib.h>
#include <windows.h>
@ -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;
}

View file

@ -0,0 +1,214 @@
#include "gamepad/Gamepad.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef __APPLE__
#include <GLUT/glut.h>
#include <OpenGL/gl.h>
#else
#include <GL/glut.h>
#include <GL/gl.h>
#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();
}

View file

@ -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

View file

@ -1,10 +0,0 @@
#ifndef __SHELL_H__
#define __SHELL_H__
void Shell_mainLoop();
void Shell_redisplay();
double Shell_getCurrentTime();
const char * Shell_getResourcePath();
#endif

View file

@ -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

View file

@ -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

View file

@ -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 <stdbool.h>
#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

View file

@ -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 <stdlib.h>
#include <string.h>
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;
}

View file

@ -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 <stdbool.h>
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

View file

@ -1,242 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "gamepad/Gamepad.h"
#include "shell/Shell.h"
#include "shell/ShellKeyCodes.h"
#include <string.h>
#ifdef __APPLE__
#include <GLUT/glut.h>
#include <OpenGL/gl.h>
#else
#include <GL/glut.h>
#include <GL/gl.h>
#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);
}

View file

@ -1,41 +0,0 @@
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#if defined(WIN32)
#include <windows.h>
#else
#include <dlfcn.h>
#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;
}

View file

@ -1,57 +0,0 @@
#ifndef __TEST_SUITE_H__
#define __TEST_SUITE_H__
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
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

View file

@ -1,84 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <setjmp.h>
#include <unistd.h>
#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;
}

View file

@ -1,7 +0,0 @@
#include "unittest/framework/TestSuite.h"
static void blank() {
// No tests
}
TEST_SUITE(GamepadTest, blank)

View file

@ -0,0 +1 @@
http://forums.tigsource.com/index.php?topic=10675.80

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -1,299 +1,333 @@
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "src/gamepad/Gamepad.h"
#include "../../../os.h"
#ifdef QB64_MACOSX
#include <GLUT/glut.h>
#include <OpenGL/gl.h>
#else
#include <GL/glut.h>
#include <GL/gl.h>
#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<<verboseMessage;
}
int button = buttonID;
int32 di,controller;
controller=0;
for(di=1;di<=device_last;di++){
static device_struct *d;
d=&devices[di];
if (d->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<<verboseMessage;
}
int button = buttonID;
int32 di;
for(di=1;di<=device_last;di++){
static device_struct *d;
d=&devices[di];
if (d->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<<verboseMessage;
}
int axis=axisID;
int32 di;
for(di=1;di<=device_last;di++){
static device_struct *d;
d=&devices[di];
if (d->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<<verboseMessage;
}
int i,x,x2;
//re-aquire a potentially dropped device in its original index
for (i=1;i<=device_last;i++){
if (devices[i].used){
if (devices[i].type==1){//it's a joystick/gamepad
if (!devices[i].connected){
if (device->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<<verboseMessage;
}
int i;
for (i=1;i<=device_last;i++){
if (devices[i].used){
if (devices[i].type==1){//it's a joystick/gamepad
if (devices[i].handle_int==device->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);
}
*/

View file

@ -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 <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <linux/input.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
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 <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <linux/input.h>
#define __USE_UNIX98
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
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;
}

File diff suppressed because it is too large Load diff

View file

@ -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 <stdlib.h>
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;
}

View file

@ -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 <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <regstr.h>
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(&currentTime);
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;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -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 <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <regstr.h>
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(&currentTime);
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;
}

View file

@ -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 <stdbool.h>
#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 <stdbool.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;
};
/* 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

View file

@ -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

View file

@ -1,10 +0,0 @@
#ifndef __SHELL_H__
#define __SHELL_H__
void Shell_mainLoop();
void Shell_redisplay();
double Shell_getCurrentTime();
const char * Shell_getResourcePath();
#endif

View file

@ -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

View file

@ -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

View file

@ -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 <stdlib.h>
#include <string.h>
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;
}

View file

@ -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 <stdbool.h>
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

View file

@ -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

View file

@ -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