If anyone is interested in how I have done my configurable inputs for my PGDAnnual entry, Day Of Destruction!, this is how:

First, I have these types/constants:

[pascal]
{...}Const
cLeftButton = 1;
cMiddleButton = 2;
cRightButton = 3;

cButton1 = 1;
cButton2 = 2;
cButton3 = 3;
cButton4 = 4;
cButton5 = 5;
cButton6 = 6;
cButton7 = 7;
cButton8 = 8;
cButton9 = 9;
cButton10 = 10;
{...}
Type
{................................................. .............................}
TControl = (
eControl_InsertCredit,
eControl_StartGame,
eControl_PrimaryWeapon,
eControl_SecondaryWeapon,
eControl_HighScoreClick,
eControl_TakeScreenshot
);
{................................................. .............................}

{................................................. .............................}
TInputType = (
eInputType_Mouse,
eInputType_Joystick,
eInputType_Keyboard
);
{................................................. .............................}

{................................................. .............................}
Const
cControlStr : Array[TControl] Of AnsiString = (
'insert_credit',
'start_game',
'primary_weapon',
'secondary_weapon',
'highscore_click',
'take_screenshot'
);
[/pascal]

I have this array in my game which uses the control and input types from above:

[pascal] FControls : Array[TControl,TInputType] Of Integer;[/pascal]

I have these routines in my game to get/set the FControls array:

[pascal]Function TGame.GetControl(Const AControl : TControl;
Const AInputType : TInputType) : Integer;
Begin
Result := FControls[AControl,AInputType];
End;
{................................................. .............................}

{................................................. .............................}
Procedure TGame.SetControl(Const AControl : TControl;
Const AInputType : TInputType;
Const AValue : Integer);

Begin
FControls[AControl,AInputType] := AValue;
End;
[/pascal]

I have these routines that do the actual parsing of my control input type strings and call SetControl():

[pascal]
Function SDLKeyFromStr(Const Str : AnsiString) : Integer;
Var
i : Integer;
Name : AnsiString;
Begin
Result := -1;
For i := 0 To High(Word) Do
Begin
Name := StringReplace(LowerCase(SDL_GetKeyName(i)),'sdlk_' ,'',[]);
If Name = LowerCase(Str) Then
Begin
Result := i;
Exit;
End;
End;
End;
{................................................. .............................}

{................................................. .............................}
Procedure TGame.ParseControlInput(Const AControl : TControl; AValueStr : AnsiString);
Begin
If Pos('mb_',AValueStr) = 1 Then
Begin
Delete(AValueStr,1,Length('mb_'));
If AValueStr = 'left' Then
SetControl(AControl,eInputType_Mouse,cLeftButton)
Else
If AValueStr = 'middle' Then
SetControl(AControl,eInputType_Mouse,cMiddleButton )
Else
If AValueStr = 'right' Then
SetControl(AControl,eInputType_Mouse,cRightButton)
End
Else
If Pos('jb_',AValueStr) = 1 Then
Begin
Delete(AValueStr,1,Length('jb_'));
Try
SetControl(AControl,eInputType_Joystick,cButton1 + (StrToInt(AValueStr) - cButton1));
Except
SetControl(AControl,eInputType_Joystick,cButton1);
End;
End
Else
If Pos('kb_',AValueStr) = 1 Then
Begin
Delete(AValueStr,1,Length('kb_'));
SetControl(AControl,eInputType_Keyboard,SDLKeyFrom Str(AValueStr));
End;
End;[/pascal]

I have this code below that reads in the .ini file control inputs and uses the above routines:

[pascal]Type
{................................................. .............................}
TParseControlInput = Procedure(Const AControl : TControl; AValueStr : AnsiString) Of Object;
{................................................. .............................}

{................................................. .............................}

{................................................. .............................}
Procedure GetControlInputs(Const AIniFile : TIniFile;
Const AControl : TControl;
Const ADefault : AnsiString;
Const AParseInput : TParseControlInput);
Var
InputTypeStr : AnsiString;
sl : TStringList;
i : Integer;
Begin
sl := TStringList.Create;
Try
sl.CommaText := LowerCase(AIniFile.ReadString('controls',cControlS tr[AControl],ADefault));
For i := 0 To sl.Count - 1 Do
Begin
InputTypeStr := sl.Strings[i];
AParseInput(AControl,InputTypeStr);
End;
Finally
sl.Free;
End;
End;[/pascal]

Here is the final bits of code to use the GetControlInput() routine:

[pascal]
ConfigFile := TIniFile.Create(ModulePath + ChangeFileExt(ModuleName,'.ini'));
Try
SDL_Init(SDL_INIT_VIDEO Or SDL_INIT_AUDIO Or SDL_INIT_JOYSTICK);

FullScreen := ConfigFile.ReadBool('settings','fullscreen' ,False);
Game.MusicEnabled := ConfigFile.ReadBool('settings','music_enabled',Tru e);
Game.SoundEnabled := ConfigFile.ReadBool('settings','sound_enabled',Tru e);
Game.FPSEnabled := ConfigFile.ReadBool('settings','fps_enabled' ,True);

GetControlInputs(ConfigFile,eControl_HighscoreClic k ,'kb_lctrl,kb_rctrl,left_jb_,jb_1' ,Game.ParseControlInput);

GetControlInputs(ConfigFile,eControl_PrimaryWeapon ,'mb_left,jb_1' ,Game.ParseControlInput);
GetControlInputs(ConfigFile,eControl_SecondaryWeap on,'mb_right,jb_2' ,Game.ParseControlInput);

GetControlInputs(ConfigFile,eControl_InsertCredit ,'kb_c,mb_right,jb_9' ,Game.ParseControlInput);
GetControlInputs(ConfigFile,eControl_StartGame ,'kb_1,mb_left,jb_10' ,Game.ParseControlInput);
GetControlInputs(ConfigFile,eControl_TakeScreensho t ,'kb_s,jb_5' ,Game.ParseControlInput);

SDL_Quit;
Finally
ConfigFile.Free;
End;[/pascal]

Here is an example of me checking the Controls array to see if the input(s) match to do different actions:

[pascal]Procedure TState_PlayGame.OnJoystickUp(Which: UInt8; Button: UInt8; State: SInt16);
Const
cInputType = eInputType_Joystick;
Var
Game : TGame;
x,y : Single;
Begin
Game := TGame(FOwner);
x := FCrossHairs.x;
y := FCrossHairs.y;

If Button = Game.Control[eControl_PrimaryWeapon,cInputType] Then
FirePrimaryWeapon(x,y,Button,Ord(cInputType))
Else
If Button = Game.Control[eControl_SecondaryWeapon,cInputType] Then
FireSecondaryWeapon(x,y,Button,Ord(cInputType));
End;[/pascal]

Maybe I will encapsulate some of that code into a TControl (or similar) class to make it a bit neater, but for now it works just fine

I hope this helps someone else

cheers,
Paul