SDL2 Input Implementation (#131)

* initial SDL2 input stub restored

only light button presses, bumpers, d-pad works
axis, triggers, haptics and long presses haven't been implemented yet

* fixed bumpers, buttons and hats

cross, circle, square, triangle,
L1, R1, L3, R3 and D-PAD
should now work well

* axes and triggers support added

Left and Right stick should now work along with L2 and R2

* minor oopsie daisy

fixed dpad down button

* +

controller info console output added
+
led set to pink (for goofy reasons)

* Clipped headers from "SDL2-for-Pascal"

* sdl2 made dynamically loadable

* xinput made dynamically loadable

* Detached headers

* sce_pad_interface prototype

* +

* keyboard/mouse interface

* touchpad

* +

* xinput interface

* add LightBar interface

* SDL_PollEvent

* select_pad_interface

* select_led_color

* +

---------

Co-authored-by: red-prig <vdpasha@mail.ru>
This commit is contained in:
Addy2004 2023-06-18 23:41:25 +05:30 committed by GitHub
parent d39bc5d685
commit 929e72b83a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 2586 additions and 561 deletions

View File

@ -31,7 +31,7 @@
<PackageName Value="LCL"/>
</Item1>
</RequiredPackages>
<Units Count="139">
<Units Count="144">
<Unit0>
<Filename Value="fpPS4.lpr"/>
<IsPartOfProject Value="True"/>
@ -640,6 +640,26 @@
<IsPartOfProject Value="True"/>
<UnitName Value="ps4_libSceNpAuth"/>
</Unit138>
<Unit139>
<Filename Value="src\inputs\sce_pad_types.pas"/>
<IsPartOfProject Value="True"/>
</Unit139>
<Unit140>
<Filename Value="src\inputs\sce_pad_interface.pas"/>
<IsPartOfProject Value="True"/>
</Unit140>
<Unit141>
<Filename Value="src\inputs\sdl2_pad_interface.pas"/>
<IsPartOfProject Value="True"/>
</Unit141>
<Unit142>
<Filename Value="src\inputs\kbm_pad_interface.pas"/>
<IsPartOfProject Value="True"/>
</Unit142>
<Unit143>
<Filename Value="src\inputs\xinput_pad_interface.pas"/>
<IsPartOfProject Value="True"/>
</Unit143>
</Units>
</ProjectOptions>
<CompilerOptions>

View File

@ -94,13 +94,15 @@ begin
Writeln('Copyright (c) 2021-2023 by red-prig');
Writeln('PS4 compatibility layer (emulator) written with Free Pascal '+{$I %FPCVERSION%});
Writeln(' Parameters:');
Writeln(' -e <name> //Decrypted ELF or SELF file name');
Writeln(' -f <name> //Folder of app (/app0)');
Writeln(' -p <name> //Folder of patch (/app1)');
Writeln(' -s <name> //Savedata path');
Writeln(' -w //Fullscreen mode');
Writeln(' -e <name> //Decrypted ELF or SELF file name');
Writeln(' -f <name> //Folder of app (/app0)');
Writeln(' -p <name> //Folder of patch (/app1)');
Writeln(' -s <name> //Savedata path');
Writeln(' -w //Fullscreen mode');
Writeln(' -pad <name> //Gamepad interface selection (xinput,sdl2,keyboard) default:xinput');
Writeln(' -led <clr> //Initial LED color of Gamepad ($rrggbb)');
Writeln(' -h <name> //enable hack');
Writeln(' -h <name> //enable hack');
Writeln(' DEPTH_DISABLE_HACK //Disables depth buffer');
Writeln(' COMPUTE_DISABLE_HACK //Disables compute shaders');
Writeln(' MEMORY_BOUND_HACK //Limits the amount of GPU allocated memory (iGPU)');
@ -117,12 +119,14 @@ begin
For i:=1 to ParamCount do
begin
case LowerCase(ParamStr(i)) of
'-e':n:=0;
'-f':n:=1;
'-p':n:=2;
'-s':n:=3;
'-h':n:=4;
'-w':ps4_libSceVideoOut.FULLSCREEN_MODE:=True;
'-e':n:=0;
'-f':n:=1;
'-p':n:=2;
'-s':n:=3;
'-h':n:=4;
'-w':ps4_libSceVideoOut.FULLSCREEN_MODE:=True;
'-pad':n:=5;
'-led':n:=6;
else
if (n<>-1) then
begin
@ -147,6 +151,12 @@ begin
3:begin
ps4_app.save_path:=Trim(ParamStr(i));
end;
5:begin
select_pad_interface(Trim(ParamStr(i)));
end;
6:begin
select_led_color(Trim(ParamStr(i)));
end;
4:begin
case UpperCase(ParamStr(i)) of
'DEPTH_DISABLE_HACK' :ps4_videodrv.DEPTH_DISABLE_HACK:=True;
@ -475,7 +485,6 @@ begin
_pthread_run_entry(@main,GetSceUserMainThreadName,GetSceUserMainThreadStackSize);
ps4_libSceVideoOut.App_Run;
//KillALLThreads TODO
//readln;
end.

View File

@ -0,0 +1,183 @@
unit kbm_pad_interface;
{$mode ObjFPC}{$H+}
interface
uses
Windows,
sysutils,
spinlock,
sce_pad_types,
sce_pad_interface;
type
TKbmPadHandle=class(TScePadHandle)
function ReadState(data:PScePadData):Integer; override;
end;
TKbmPadInterface=class(TScePadInterface)
class function Open(index:Integer;var handle:TScePadHandle):Integer; override;
end;
TMouseAsTouchpad=class
class var
last_mouse_lock :Pointer;
last_mouse_point:TPoint;
last_mouse_init :Integer;
class function ReadState(data:PScePadData):Integer;
end;
implementation
class function TKbmPadInterface.Open(index:Integer;var handle:TScePadHandle):Integer;
begin
Result:=0;
if (index<0) or (index>15) then Exit(SCE_PAD_ERROR_INVALID_ARG);
if (pad_opened[index]<>nil) then Exit(SCE_PAD_ERROR_ALREADY_OPENED);
handle:=TKbmPadHandle.Create;
TKbmPadHandle(handle).index:=index;
pad_opened[index]:=handle;
end;
function GetAsyncKeyState(vKey:longint):Boolean; inline;
begin
Result:=(Windows.GetKeyState(vKey) and $8000)<>0;
end;
class function TMouseAsTouchpad.ReadState(data:PScePadData):Integer;
var
mPoint,delta:TPoint;
begin
Result:=0;
//mouse as touch pad
spin_lock(last_mouse_lock);
GetCursorPos(mPoint);
if (last_mouse_init=0) then
begin
last_mouse_init :=1;
last_mouse_point:=mPoint;
end else
if QWORD(mPoint)<>QWORD(last_mouse_point) then
begin
data^.touchData.touchNum:=1;
data^.touchData.touch[0].id:=0;
delta:=mPoint;
if (delta.X<0) then delta.X:=0;
if (delta.Y<0) then delta.Y:=0;
if (delta.X>1919) then delta.X:=1919;
if (delta.Y>941) then delta.Y:=941;
data^.touchData.touch[0].x:=delta.X;
data^.touchData.touch[0].y:=delta.Y;
last_mouse_point:=mPoint;
end;
spin_unlock(last_mouse_lock);
if GetAsyncKeyState(VK_LBUTTON) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_TOUCH_PAD;
end;
function TKbmPadHandle.ReadState(data:PScePadData):Integer;
begin
Result:=0;
TMouseAsTouchpad.ReadState(data);
//keymapping
if GetAsyncKeyState(VK_W) then
data^.leftStick.y:=0;
if GetAsyncKeyState(VK_S) then
data^.leftStick.y:=$FF;
if GetAsyncKeyState(VK_A) then
data^.leftStick.x:=0;
if GetAsyncKeyState(VK_D) then
data^.leftStick.x:=$FF;
//
if GetAsyncKeyState(VK_I) then
data^.rightStick.y:=0;
if GetAsyncKeyState(VK_K) then
data^.rightStick.y:=$FF;
if GetAsyncKeyState(VK_J) then
data^.rightStick.x:=0;
if GetAsyncKeyState(VK_L) then
data^.rightStick.x:=$FF;
//
if GetAsyncKeyState(VK_RETURN) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_OPTIONS;
if GetAsyncKeyState(VK_UP) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_UP;
if GetAsyncKeyState(VK_RIGHT) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_RIGHT;
if GetAsyncKeyState(VK_DOWN) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_DOWN;
if GetAsyncKeyState(VK_LEFT) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_LEFT;
if GetAsyncKeyState(VK_NUMPAD8) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_TRIANGLE;
if GetAsyncKeyState(VK_NUMPAD6) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_CIRCLE;
if GetAsyncKeyState(VK_NUMPAD2) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_CROSS;
if GetAsyncKeyState(VK_NUMPAD4) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_SQUARE;
if GetAsyncKeyState(VK_Q) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_L1;
if GetAsyncKeyState(VK_1) then
begin
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_L2;
data^.analogButtons.l2:=255;
end;
if GetAsyncKeyState(VK_Z) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_L3;
if GetAsyncKeyState(VK_E) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_R1;
if GetAsyncKeyState(VK_4) then
begin
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_R2;
data^.analogButtons.r2:=255;
end;
if GetAsyncKeyState(VK_C) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_R3;
end;
end.

View File

@ -0,0 +1,95 @@
unit sce_pad_interface;
{$mode ObjFPC}{$H+}
interface
uses
sce_pad_types,
ps4_handles;
type
TScePadHandle=class(TClassHandle)
var
handle:Integer;
index:Integer;
function ReadState(data:PScePadData):Integer; virtual;
function SetLightBar(data:pScePadLightBarParam):Integer; virtual;
function ResetLightBar():Integer; virtual;
destructor Destroy; override;
end;
TScePadInterface=class
class function Load:Boolean; virtual;
class procedure Unload; virtual;
class function Init:Integer; virtual;
class function Done:Integer; virtual;
class function Open(index:Integer;var handle:TScePadHandle):Integer; virtual;
end;
TAbstractScePadInterface=class of TScePadInterface;
var
pad_handles:TIntegerHandles;
pad_opened :array[0..15] of TScePadHandle;
implementation
function TScePadHandle.ReadState(data:PScePadData):Integer;
begin
Result:=SCE_PAD_ERROR_INVALID_HANDLE;
end;
function TScePadHandle.SetLightBar(data:pScePadLightBarParam):Integer;
begin
Result:=0;
end;
function TScePadHandle.ResetLightBar():Integer;
begin
Result:=0;
end;
destructor TScePadHandle.Destroy;
begin
if (index>=0) and (index<16) then
begin
pad_opened[index]:=nil;
end;
inherited;
end;
class function TScePadInterface.Load:Boolean;
begin
Result:=True;
end;
class procedure TScePadInterface.Unload;
begin
//
end;
class function TScePadInterface.Init:Integer;
begin
Result:=0;
end;
class function TScePadInterface.Done:Integer;
begin
Result:=0;
end;
class function TScePadInterface.Open(index:Integer;var handle:TScePadHandle):Integer;
begin
handle:=nil;
Result:=SCE_PAD_ERROR_NOT_INITIALIZED;
end;
initialization
pad_handles:=TIntegerHandles.Create(0);
pad_handles.max_key:=16;
end.

View File

@ -0,0 +1,283 @@
unit sce_pad_types;
{$mode ObjFPC}{$H+}
interface
const
SCE_PAD_ERROR_INVALID_ARG =-2137915391; // 0x80920001
SCE_PAD_ERROR_INVALID_PORT =-2137915390; // 0x80920002
SCE_PAD_ERROR_INVALID_HANDLE =-2137915389; // 0x80920003
SCE_PAD_ERROR_ALREADY_OPENED =-2137915388; // 0x80920004
SCE_PAD_ERROR_NOT_INITIALIZED =-2137915387; // 0x80920005
SCE_PAD_ERROR_INVALID_LIGHTBAR_SETTING=-2137915386; // 0x80920006
SCE_PAD_ERROR_DEVICE_NOT_CONNECTED =-2137915385; // 0x80920007
SCE_PAD_ERROR_NO_HANDLE =-2137915384; // 0x80920008
SCE_PAD_ERROR_FATAL =-2137915137; // 0x809200FF
//ScePadButtonDataOffset
SCE_PAD_BUTTON_L3 = $00000002;
SCE_PAD_BUTTON_R3 = $00000004;
SCE_PAD_BUTTON_OPTIONS = $00000008;
SCE_PAD_BUTTON_UP = $00000010;
SCE_PAD_BUTTON_RIGHT = $00000020;
SCE_PAD_BUTTON_DOWN = $00000040;
SCE_PAD_BUTTON_LEFT = $00000080;
SCE_PAD_BUTTON_L2 = $00000100;
SCE_PAD_BUTTON_R2 = $00000200;
SCE_PAD_BUTTON_L1 = $00000400;
SCE_PAD_BUTTON_R1 = $00000800;
SCE_PAD_BUTTON_TRIANGLE = $00001000;
SCE_PAD_BUTTON_CIRCLE = $00002000;
SCE_PAD_BUTTON_CROSS = $00004000;
SCE_PAD_BUTTON_SQUARE = $00008000;
SCE_PAD_BUTTON_TOUCH_PAD = $00100000;
SCE_PAD_BUTTON_INTERCEPTED = $80000000;
// This definietion is alias for support old style.
SCE_PAD_BUTTON_START=SCE_PAD_BUTTON_OPTIONS;
//Maximum number of touch points.
SCE_PAD_MAX_TOUCH_NUM=2;
//device unique data size
SCE_PAD_MAX_DEVICE_UNIQUE_DATA_SIZE=12;
SCE_PAD_CONNECTION_TYPE_LOCAL =0;
SCE_PAD_CONNECTION_TYPE_REMOTE =1;
SCE_PAD_CONNECTION_TYPE_REMOTE_VITA =SCE_PAD_CONNECTION_TYPE_REMOTE;
SCE_PAD_CONNECTION_TYPE_REMOTE_DUALSHOCK4=2;
//ScePadDeviceClass
SCE_PAD_DEVICE_CLASS_INVALID = -1;
SCE_PAD_DEVICE_CLASS_STANDARD = 0;
SCE_PAD_DEVICE_CLASS_GUITAR = 1;
SCE_PAD_DEVICE_CLASS_DRUM = 2;
SCE_PAD_DEVICE_CLASS_DJ_TURNTABLE = 3;
SCE_PAD_DEVICE_CLASS_DANCEMAT = 4;
SCE_PAD_DEVICE_CLASS_NAVIGATION = 5;
SCE_PAD_DEVICE_CLASS_STEERING_WHEEL = 6;
SCE_PAD_DEVICE_CLASS_STICK = 7;
SCE_PAD_DEVICE_CLASS_FLIGHT_STICK = 8;
SCE_PAD_DEVICE_CLASS_GUN = 9;
// Personal user
SCE_PAD_PORT_TYPE_STANDARD=0; // for standard controller
SCE_PAD_PORT_TYPE_SPECIAL =2; // for special controller
//SYSTEM user(SCE_USER_SERVICE_USER_ID_SYSTEM)
SCE_PAD_PORT_TYPE_REMOTE_CONTROL=16; // for remote control(CEC remote control)
type
Tvec_float3=packed record
x,y,z:Single;
end;
Tvec_float4=packed record
x,y,z,w:Single;
end;
ScePadAnalogStick=packed record
x,y:Byte;
end;
ScePadAnalogButtons=packed record
l2,r2:Byte;
padding:Word;
end;
ScePadTouch=packed record
x:Word;
y:Word;
id:Byte;
reserve:array[0..2] of Byte;
end;
ScePadTouchData=packed record
touchNum:Byte;
reserve:array[0..2] of Byte;
reserve1:DWORD;
touch:array[0..SCE_PAD_MAX_TOUCH_NUM-1] of ScePadTouch;
end;
ScePadExtensionUnitData=packed record
extensionUnitId:DWORD;
reserve:Byte;
dataLength:Byte;
data:array[0..9] of Byte;
end;
PScePadData=^ScePadData;
ScePadData=packed record
buttons :DWORD;
leftStick :ScePadAnalogStick;
rightStick :ScePadAnalogStick;
analogButtons :ScePadAnalogButtons;
orientation :Tvec_float4;
acceleration :Tvec_float3;
angularVelocity :Tvec_float3;
touchData :ScePadTouchData;
connected :Boolean;
_align:array[0..2] of Byte;
timestamp :QWORD;
extensionUnitData:ScePadExtensionUnitData;
connectedCount :Byte;
reserve:array[0..1] of Byte;
deviceUniqueDataLen:Byte;
deviceUniqueData:array[0..SCE_PAD_MAX_DEVICE_UNIQUE_DATA_SIZE-1] of Byte;
end;
ScePadOpenParam=packed record
reserve:array[0..7] of Byte;
end;
TPadColor=packed record
r,g,b,a:Byte;
end;
PScePadVibrationParam=^ScePadVibrationParam;
ScePadVibrationParam=packed record
largeMotor:Byte;
smallMotor:Byte;
end;
ScePadColor=packed record
r:Byte;
g:Byte;
b:Byte;
reserve:Byte;
end;
ScePadLightBarParam=ScePadColor;
PScePadLightBarParam=^ScePadLightBarParam;
ScePadTouchPadInformation=packed record
pixelDensity:Single;
resolution:packed record
x,y:Word;
end;
end;
ScePadStickInformation=packed record
deadZoneLeft:Byte;
deadZoneRight:Byte;
end;
PScePadControllerInformation=^ScePadControllerInformation;
ScePadControllerInformation=packed record
touchPadInfo:ScePadTouchPadInformation;
stickInfo:ScePadStickInformation;
connectionType:Byte;
connectedCount:Byte;
connected:Boolean;
_align:array[0..2] of Byte;
deviceClass:DWORD; //ScePadDeviceClass
reserve:array[0..7] of Byte;
end;
pScePadDeviceClassExtendedInformation=^ScePadDeviceClassExtendedInformation;
ScePadDeviceClassExtendedInformation=packed record
deviceClass:DWORD; //ScePadDeviceClass
reserved:DWORD;
classData:packed record
Case Byte of
0:(steeringWheel:packed record
capability:Byte;
reserved1:Byte;
maxPhysicalWheelAngle:Word;
reserved2:QWORD;
end);
1:(guitar:packed record
capability:Byte;
quantityOfSelectorSwitch:Byte;
reserved1:Word;
reserved2:QWORD;
end);
2:(drum:packed record
capability:Byte;
reserved1:Byte;
reserved2:Word;
reserved3:QWORD;
end);
3:(flightStick:packed record
capability:Byte;
reserved1:Byte;
reserved2:Word;
reserved3:QWORD;
end);
4:(data:array[0..11] of Byte);
end;
end;
pScePadDeviceClassData=^ScePadDeviceClassData;
ScePadDeviceClassData=packed record
deviceClass:DWORD; //ScePadDeviceClass
bDataValid :Boolean;
_align:array[0..2] of Byte;
classData:packed record
Case Byte of
0:(steeringWheel:packed record
steeringWheelAngle:Single;
steeringWheel :Word;
acceleratorPedal :Word;
brakePedal :Word;
clutchPedal :Word;
handBlake :Word;
gear :Byte;
reserved :Byte;
end); //SCE_PAD_DEVICE_CLASS_STEERING_WHEEL
1:(guitar:packed record
toneNumber:Byte;
whammyBar :Byte;
tilt :Byte;
fret :Byte;
fretSolo :Byte;
reserved:array[0..10] of Byte;
end); //SCE_PAD_DEVICE_CLASS_GUITAR
2:(drum:packed record
snare :Byte;
tom1 :Byte;
tom2 :Byte;
floorTom :Byte;
hihatCymbal:Byte;
rideCymbal :Byte;
crashCymbal:Byte;
reserved:array[0..8] of Byte;
end); //SCE_PAD_DEVICE_CLASS_DRUM
3:(flightStick:packed record
stickAxisX :Word;
stickAxisY :Word;
stickTwist :Byte;
throttle :Byte;
trigger :Byte;
rudderPedal :Byte;
brakePedalLeft :Byte;
brakePedalRight:Byte;
antennaKnob :Byte;
rangeKnob :Byte;
reserved:array[0..3] of Byte;
end); //SCE_PAD_DEVICE_CLASS_FLIGHT_STICK
4:(others:packed record
dataLen:Byte;
reserved:array[0..2] of Byte;
data:array[0..SCE_PAD_MAX_DEVICE_UNIQUE_DATA_SIZE-1] of Byte;
end); //Not Supported device
end;
end;
implementation
end.

1091
src/inputs/sdl2.pas Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,395 @@
unit sdl2_pad_interface;
{$mode ObjFPC}{$H+}
interface
uses
sysutils,
sdl2,
sce_pad_types,
sce_pad_interface,
kbm_pad_interface;
type
TSdl2PadHandle=class(TScePadHandle)
var
connectedCount:Integer;
game_controller:PSDL_GameController;
LED:ScePadLightBarParam;
function SetLightBar(data:pScePadLightBarParam):Integer; override;
function ResetLightBar():Integer; override;
function ReadState(data:PScePadData):Integer; override;
end;
TSdl2PadInterface=class(TScePadInterface)
class var
sdl2_init:Boolean;
class function Load:Boolean; override;
class procedure Unload; override;
class function Init:Integer; override;
class function Done:Integer; override;
class function Open(index:Integer;var handle:TScePadHandle):Integer; override;
class function FindOpened(device_index:Integer;prev:PSDL_GameController):Boolean;
class function FindDevice(prev:PSDL_GameController):PSDL_GameController;
end;
implementation
class function TSdl2PadInterface.Load:Boolean;
var
i:Integer;
begin
i:=SDL_InitSubSystem(SDL_INIT_JOYSTICK or SDL_INIT_GAMECONTROLLER);
sdl2_init:=(i=0);
if sdl2_init then
begin
Writeln('SDL2 Game-Controller subsystem initialized!');
end else
begin
Writeln('SDL2 Game-Controller not initialized!');
end;
Result:=sdl2_init;
end;
class procedure TSdl2PadInterface.Unload;
begin
if not sdl2_init then Exit;
SDL_QuitSubSystem(SDL_INIT_JOYSTICK or SDL_INIT_GAMECONTROLLER);
sdl2_init:=False;
Writeln('SDL2 Game-Controller subsystem quit!');
end;
class function TSdl2PadInterface.Init:Integer;
begin
if (sdl2_init) then
begin
Result:=0;
end else
begin
Result:=SCE_PAD_ERROR_NOT_INITIALIZED;
end;
end;
class function TSdl2PadInterface.Done:Integer;
begin
Result:=0;
end;
function Compare(g1,g2:TSDL_JoystickGUID):Boolean; inline;
begin
Result:=CompareByte(g1,g2,SizeOf(TSDL_JoystickGUID))=0;
end;
function Compare(g1,g2:PSDL_GameController):Boolean;
begin
Result:=Compare(SDL_JoystickGetGUID(SDL_GameControllerGetJoystick(g1)),
SDL_JoystickGetGUID(SDL_GameControllerGetJoystick(g2))
);
end;
function Compare(device_index:Integer;g2:PSDL_GameController):Boolean;
begin
Result:=Compare(SDL_JoystickGetDeviceGUID(device_index),
SDL_JoystickGetGUID(SDL_GameControllerGetJoystick(g2))
);
end;
class function TSdl2PadInterface.FindOpened(device_index:Integer;prev:PSDL_GameController):Boolean;
var
i:Integer;
h:TSdl2PadHandle;
begin
Result:=False;
For i:=0 to 15 do
if (pad_opened[i]<>nil) then
if (pad_opened[i].InheritsFrom(TSdl2PadHandle)) then
begin
h:=TSdl2PadHandle(pad_opened[i]);
if (h.game_controller<>nil) then
if (h.game_controller<>prev) then
begin
Result:=Compare(device_index,h.game_controller);
if Result then Break;
end;
end;
end;
class function TSdl2PadInterface.FindDevice(prev:PSDL_GameController):PSDL_GameController;
var
i,c:Integer;
first,compared:Integer;
begin
Result:=nil;
first:=-1;
compared:=-1;
SDL_LockJoysticks;
c:=SDL_NumJoysticks();
if (c<>0) then
For i:=0 to c-1 do
if SDL_IsGameController(i) then
if not FindOpened(i,prev) then
begin
if (first=-1) then first:=i;
if (prev=nil) then
begin
compared:=i;
Break;
end else
if Compare(i,prev) then
begin
compared:=i;
Break;
end;
end;
//
if (compared=-1) then compared:=first;
if (compared<>-1) then
begin
Result:=SDL_GameControllerOpen(compared);
end;
SDL_UnlockJoysticks;
end;
class function TSdl2PadInterface.Open(index:Integer;var handle:TScePadHandle):Integer;
var
game_controller:PSDL_GameController;
begin
Result:=0;
if not sdl2_init then Exit(SCE_PAD_ERROR_NOT_INITIALIZED);
if (index<0) or (index>15) then Exit(SCE_PAD_ERROR_INVALID_ARG);
if (pad_opened[index]<>nil) then Exit(SCE_PAD_ERROR_ALREADY_OPENED);
game_controller:=FindDevice(nil);
handle:=TSdl2PadHandle.Create;
TSdl2PadHandle(handle).index:=index;
TSdl2PadHandle(handle).game_controller:=game_controller;
pad_opened[index]:=handle;
Writeln('----------------------------------------------------------------------------------------');
Writeln('----------------------------------------------------------------------------------------');
Writeln('SDL2: Game Controller loaded!');
Writeln('----------------------------------------------------------------------------------------');
Writeln('----------------------------------------------------------------------------------------');
Writeln('Controller Name: ', SDL_GameControllerName(game_controller));
Writeln('----------------------------------------------------------------------------------------');
Writeln('----------------------------------------------------------------------------------------');
Writeln('USB Vendor ID: ', SDL_GameControllerGetVendor(game_controller));
Writeln('----------------------------------------------------------------------------------------');
Writeln('----------------------------------------------------------------------------------------');
Writeln('USB Product ID: ', SDL_GameControllerGetProduct(game_controller));
Writeln('----------------------------------------------------------------------------------------');
Writeln('----------------------------------------------------------------------------------------');
Writeln('Product Version: ', SDL_GameControllerGetProductVersion(game_controller));
Writeln('----------------------------------------------------------------------------------------');
Writeln('----------------------------------------------------------------------------------------');
Writeln('Firmware Version: ', SDL_GameControllerGetFirmwareVersion(game_controller));
Writeln('----------------------------------------------------------------------------------------');
Writeln('----------------------------------------------------------------------------------------');
Writeln('Serial Number: ', SDL_GameControllerGetSerial(game_controller));
Writeln('----------------------------------------------------------------------------------------');
Writeln('----------------------------------------------------------------------------------------');
Writeln('Controller Path: ', SDL_GameControllerPath(game_controller));
Writeln('----------------------------------------------------------------------------------------');
Writeln('----------------------------------------------------------------------------------------');
Writeln('Controller GUID: ', GUIDToString(TGUID(SDL_JoystickGetGUID(SDL_GameControllerGetJoystick(game_controller)))));
Writeln('----------------------------------------------------------------------------------------');
Writeln('----------------------------------------------------------------------------------------');
Writeln('Modifiable LED: ', SDL_GameControllerHasLED(game_controller));
Writeln('----------------------------------------------------------------------------------------');
Writeln('----------------------------------------------------------------------------------------');
Writeln('Modifiable Rumble: ', SDL_GameControllerHasRumble(game_controller));
Writeln('----------------------------------------------------------------------------------------');
Writeln('----------------------------------------------------------------------------------------');
end;
function AxisToAnalog(i:Integer):Byte;
begin
if (i>0) then
begin
i:=(i*127) div 32767;
end else
begin
i:=(i*128) div 32768;
end;
Result:=Byte(i+128);
end;
function AxisToTrigger(i:Integer):Byte;
begin
Result:=Byte((abs(i)*255) div 32767);
end;
function TSdl2PadHandle.SetLightBar(data:pScePadLightBarParam):Integer;
begin
LED:=data^;
if (game_controller<>nil) then
begin
SDL_GameControllerSetLED(game_controller, LED.r, LED.g, LED.b);
end;
end;
function TSdl2PadHandle.ResetLightBar():Integer;
begin
LED:=Default(ScePadLightBarParam);
if (game_controller<>nil) then
begin
SDL_GameControllerSetLED(game_controller, LED.r, LED.g, LED.b);
end;
end;
function TSdl2PadHandle.ReadState(data:PScePadData):Integer;
var
i,f,t,n:Integer;
x,y,pressure:Single;
state:Byte;
new:PSDL_GameController;
event:TSDL_Event;
begin
Result:=0;
//loop events
while (SDL_PollEvent(@event)<>0) do;
if not SDL_GameControllerGetAttached(game_controller) then
begin
new:=TSdl2PadInterface.FindDevice(game_controller);
if (new=nil) then
begin
data^.connected:=False;
data^.connectedCount:=Byte(connectedCount);
Exit(0);
end else
begin
Writeln('Reconnect:',index);
SDL_GameControllerClose(game_controller);
game_controller:=new;
Inc(connectedCount);
//
SDL_GameControllerSetLED(game_controller, LED.r, LED.g, LED.b);
end;
end;
data^.connected:=True;
data^.connectedCount:=Byte(connectedCount);
t:=SDL_GameControllerGetNumTouchpads(game_controller);
if (t=0) then
begin
//no touchpad? use mouse
TMouseAsTouchpad.ReadState(data);
end else
begin
//use ony first touchpad
f:=SDL_GameControllerGetNumTouchpadFingers(game_controller,0);
n:=0;
data^.touchData.touchNum:=0;
if (f<>0) then
For i:=0 to f-1 do
begin
if SDL_GameControllerGetTouchpadFinger(
game_controller,
0,i,
@state,
@x,@y,@pressure)=0 then
begin
if (x>1919) then x:=1919;
if (y>941) then y:=941;
data^.touchData.touch[n].id:=n;
data^.touchData.touch[n].x :=Word(Trunc(x));
data^.touchData.touch[n].y :=Word(Trunc(y));
Inc(n);
data^.touchData.touchNum:=n;
if (n=SCE_PAD_MAX_TOUCH_NUM) then Break;
end;
end;
end;
//Options and Touchpad
if SDL_GameControllerGetButton(game_controller, SDL_CONTROLLER_BUTTON_START) = 1 then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_OPTIONS;
if SDL_GameControllerGetButton(game_controller, SDL_CONTROLLER_BUTTON_TOUCHPAD) = 1 then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_TOUCH_PAD;
//Hats(D-PAD)
if SDL_GameControllerGetButton(game_controller, SDL_CONTROLLER_BUTTON_DPAD_UP) = 1 then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_UP;
if SDL_GameControllerGetButton(game_controller, SDL_CONTROLLER_BUTTON_DPAD_DOWN) = 1 then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_DOWN;
if SDL_GameControllerGetButton(game_controller, SDL_CONTROLLER_BUTTON_DPAD_LEFT) = 1 then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_LEFT;
if SDL_GameControllerGetButton(game_controller, SDL_CONTROLLER_BUTTON_DPAD_RIGHT) = 1 then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_RIGHT;
//Cross, Circle, Square and Triangle
if SDL_GameControllerGetButton(game_controller, SDL_CONTROLLER_BUTTON_A) = 1 then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_CROSS;
if SDL_GameControllerGetButton(game_controller, SDL_CONTROLLER_BUTTON_B) = 1 then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_CIRCLE;
if SDL_GameControllerGetButton(game_controller, SDL_CONTROLLER_BUTTON_X) = 1 then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_SQUARE;
if SDL_GameControllerGetButton(game_controller, SDL_CONTROLLER_BUTTON_Y) = 1 then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_TRIANGLE;
//L1, R1 and L3, R3
if SDL_GameControllerGetButton(game_controller, SDL_CONTROLLER_BUTTON_LEFTSHOULDER) = 1 then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_L1;
if SDL_GameControllerGetButton(game_controller, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) = 1 then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_R1;
if SDL_GameControllerGetButton(game_controller, SDL_CONTROLLER_BUTTON_LEFTSTICK) = 1 then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_L3;
if SDL_GameControllerGetButton(game_controller, SDL_CONTROLLER_BUTTON_RIGHTSTICK) = 1 then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_R3;
//Left and Right Axes
data^.leftStick.x:=AxisToAnalog(SDL_GameControllerGetAxis(game_controller, SDL_CONTROLLER_AXIS_LEFTX));
data^.leftStick.y:=AxisToAnalog(SDL_GameControllerGetAxis(game_controller, SDL_CONTROLLER_AXIS_LEFTY));
data^.rightStick.x:=AxisToAnalog(SDL_GameControllerGetAxis(game_controller, SDL_CONTROLLER_AXIS_RIGHTX));
data^.rightStick.y:=AxisToAnalog(SDL_GameControllerGetAxis(game_controller, SDL_CONTROLLER_AXIS_RIGHTY));
//Left and Right Triggers (L2 and R2)
data^.analogButtons.l2:=AxisToTrigger(SDL_GameControllerGetAxis(game_controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT ));
data^.analogButtons.r2:=AxisToTrigger(SDL_GameControllerGetAxis(game_controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT));
//Approx L2
if (data^.analogButtons.l2>1) then
begin
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_L2;
end;
//Approx R2
if (data^.analogButtons.r2>1) then
begin
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_R2;
end;
end;
initialization
//sdl2 needs to be initialized on the main thread, otherwise it just doesn't work. :(
TSdl2PadInterface.Load;
finalization
TSdl2PadInterface.Unload;
end.

View File

@ -202,9 +202,9 @@ begin
if not FileExists(filePath) then exit;
iniFile := TIniFile.Create(filePath);
Self.XInputEnabled := iniFile.ReadBool('XInput', 'XInputEnabled', True);
Self.XInputDeadzoneLeft := iniFile.ReadInteger('XInput', 'XInputDeadzoneLeft', XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
Self.XInputDeadzoneRight := iniFile.ReadInteger('XInput', 'XInputDeadzoneRight', XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
Self.XInputEnabled := iniFile.ReadBool ('XInput', 'XInputEnabled' , True);
Self.XInputDeadzoneLeft := iniFile.ReadInteger('XInput', 'XInputDeadzoneLeft' , XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
Self.XInputDeadzoneRight := iniFile.ReadInteger('XInput', 'XInputDeadzoneRight' , XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
Self.XInputDeadzoneTrigger := iniFile.ReadInteger('XInput', 'XInputDeadzoneTrigger', XINPUT_GAMEPAD_TRIGGER_THRESHOLD);
for i := 1 to NUM_PS4_BUTTONS do
begin
@ -225,9 +225,9 @@ begin
Result := False;
iniFile := TIniFile.Create(filePath);
iniFile.WriteBool('XInput', 'XInputEnabled', Self.XInputEnabled);
iniFile.WriteInteger('XInput', 'XInputDeadzoneLeft', Self.XInputDeadzoneLeft);
iniFile.WriteInteger('XInput', 'XInputDeadzoneRight', Self.XInputDeadzoneRight);
iniFile.WriteBool ('XInput', 'XInputEnabled' , Self.XInputEnabled);
iniFile.WriteInteger('XInput', 'XInputDeadzoneLeft' , Self.XInputDeadzoneLeft);
iniFile.WriteInteger('XInput', 'XInputDeadzoneRight' , Self.XInputDeadzoneRight);
iniFile.WriteInteger('XInput', 'XInputDeadzoneTrigger', Self.XInputDeadzoneTrigger);
for i := 1 to NUM_PS4_BUTTONS do
begin
@ -242,14 +242,14 @@ end;
function TMappableInputs.XInputIsTriggered(input: EnumXInputButtons; state: TXInputState): boolean;
begin
case (input) of
xiLJoyUp: Result := state.Gamepad.sThumbLY > self.XInputDeadzoneLeft;
xiLJoyDown: Result := state.Gamepad.sThumbLY < -self.XInputDeadzoneLeft;
xiLJoyLeft: Result := state.Gamepad.sThumbLX < -self.XInputDeadzoneLeft;
xiLJoyUp : Result := state.Gamepad.sThumbLY > self.XInputDeadzoneLeft;
xiLJoyDown : Result := state.Gamepad.sThumbLY < -self.XInputDeadzoneLeft;
xiLJoyLeft : Result := state.Gamepad.sThumbLX < -self.XInputDeadzoneLeft;
xiLJoyRight: Result := state.Gamepad.sThumbLX > self.XInputDeadzoneLeft;
xiRJoyUp: Result := state.Gamepad.sThumbRY > self.XInputDeadzoneRight;
xiRJoyDown: Result := state.Gamepad.sThumbRY < -self.XInputDeadzoneRight;
xiRJoyLeft: Result := state.Gamepad.sThumbRX < -self.XInputDeadzoneRight;
xiRJoyUp : Result := state.Gamepad.sThumbRY > self.XInputDeadzoneRight;
xiRJoyDown : Result := state.Gamepad.sThumbRY < -self.XInputDeadzoneRight;
xiRJoyLeft : Result := state.Gamepad.sThumbRX < -self.XInputDeadzoneRight;
xiRJoyRight: Result := state.Gamepad.sThumbRX > self.XInputDeadzoneRight;
xiA: Result := (state.Gamepad.wButtons and XINPUT_GAMEPAD_A) <> 0;
@ -257,14 +257,14 @@ begin
xiX: Result := (state.Gamepad.wButtons and XINPUT_GAMEPAD_X) <> 0;
xiY: Result := (state.Gamepad.wButtons and XINPUT_GAMEPAD_Y) <> 0;
xiDPadUp: Result := (state.Gamepad.wButtons and XINPUT_GAMEPAD_DPAD_UP) <> 0;
xiDPadDown: Result := (state.Gamepad.wButtons and XINPUT_GAMEPAD_DPAD_DOWN) <> 0;
xiDPadLeft: Result := (state.Gamepad.wButtons and XINPUT_GAMEPAD_DPAD_LEFT) <> 0;
xiDPadUp : Result := (state.Gamepad.wButtons and XINPUT_GAMEPAD_DPAD_UP) <> 0;
xiDPadDown : Result := (state.Gamepad.wButtons and XINPUT_GAMEPAD_DPAD_DOWN) <> 0;
xiDPadLeft : Result := (state.Gamepad.wButtons and XINPUT_GAMEPAD_DPAD_LEFT) <> 0;
xiDPadRight: Result := (state.Gamepad.wButtons and XINPUT_GAMEPAD_DPAD_RIGHT) <> 0;
xiSelect: Result := (state.Gamepad.wButtons and XINPUT_GAMEPAD_BACK) <> 0;
xiGuide: Result := (state.Gamepad.wButtons and XINPUT_GAMEPAD_GUIDE) <> 0;
xiStart: Result := (state.Gamepad.wButtons and XINPUT_GAMEPAD_START) <> 0;
xiGuide : Result := (state.Gamepad.wButtons and XINPUT_GAMEPAD_GUIDE) <> 0;
xiStart : Result := (state.Gamepad.wButtons and XINPUT_GAMEPAD_START) <> 0;
xiL1: Result := (state.Gamepad.wButtons and XINPUT_GAMEPAD_LEFT_SHOULDER) <> 0;
xiL2: Result := state.Gamepad.bLeftTrigger > self.XInputDeadzoneTrigger;
@ -280,20 +280,20 @@ initialization
MappableInputs := TMappableInputs.Create;
MappableInputs.XInputButtonsNames[Ord(xiUnbound)] := 'Unbound';
MappableInputs.XInputButtonsNames[Ord(xiLJoyUp)] := 'LJOY_UP';
MappableInputs.XInputButtonsNames[Ord(xiLJoyDown)] := 'LJOY_DOWN';
MappableInputs.XInputButtonsNames[Ord(xiLJoyLeft)] := 'LJOY_LEFT';
MappableInputs.XInputButtonsNames[Ord(xiUnbound )] := 'Unbound';
MappableInputs.XInputButtonsNames[Ord(xiLJoyUp )] := 'LJOY_UP';
MappableInputs.XInputButtonsNames[Ord(xiLJoyDown )] := 'LJOY_DOWN';
MappableInputs.XInputButtonsNames[Ord(xiLJoyLeft )] := 'LJOY_LEFT';
MappableInputs.XInputButtonsNames[Ord(xiLJoyRight)] := 'LJOY_RIGHT';
MappableInputs.XInputButtonsNames[Ord(xiRJoyUp)] := 'RJOY_UP';
MappableInputs.XInputButtonsNames[Ord(xiRJoyDown)] := 'RJOY_DOWN';
MappableInputs.XInputButtonsNames[Ord(xiRJoyLeft)] := 'RJOY_LEFT';
MappableInputs.XInputButtonsNames[Ord(xiRJoyUp )] := 'RJOY_UP';
MappableInputs.XInputButtonsNames[Ord(xiRJoyDown )] := 'RJOY_DOWN';
MappableInputs.XInputButtonsNames[Ord(xiRJoyLeft )] := 'RJOY_LEFT';
MappableInputs.XInputButtonsNames[Ord(xiRJoyRight)] := 'RJOY_RIGHT';
MappableInputs.XInputButtonsNames[Ord(xiDPadUp)] := 'DPAD_UP';
MappableInputs.XInputButtonsNames[Ord(xiDPadDown)] := 'DPAD_DOWN';
MappableInputs.XInputButtonsNames[Ord(xiDPadLeft)] := 'DPAD_LEFT';
MappableInputs.XInputButtonsNames[Ord(xiDPadUp )] := 'DPAD_UP';
MappableInputs.XInputButtonsNames[Ord(xiDPadDown )] := 'DPAD_DOWN';
MappableInputs.XInputButtonsNames[Ord(xiDPadLeft )] := 'DPAD_LEFT';
MappableInputs.XInputButtonsNames[Ord(xiDPadRight)] := 'DPAD_RIGHT';
MappableInputs.XInputButtonsNames[Ord(xiA)] := 'A';
@ -302,8 +302,8 @@ initialization
MappableInputs.XInputButtonsNames[Ord(xiY)] := 'Y';
MappableInputs.XInputButtonsNames[Ord(xiSelect)] := 'SELECT';
MappableInputs.XInputButtonsNames[Ord(xiGuide)] := 'GUIDE';
MappableInputs.XInputButtonsNames[Ord(xiStart)] := 'START';
MappableInputs.XInputButtonsNames[Ord(xiGuide )] := 'GUIDE';
MappableInputs.XInputButtonsNames[Ord(xiStart )] := 'START';
MappableInputs.XInputButtonsNames[Ord(xiL1)] := 'L1';
MappableInputs.XInputButtonsNames[Ord(xiL2)] := 'L2';
@ -315,35 +315,37 @@ initialization
// Default mapping
MappableInputs.XInputEnabled := True;
MappableInputs.XInputDeadzoneLeft := XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE;
MappableInputs.XInputDeadzoneRight := XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE;
MappableInputs.XInputDeadzoneLeft := XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE;
MappableInputs.XInputDeadzoneRight := XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE;
MappableInputs.XInputDeadzoneTrigger := XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
MappableInputs.SetXInputMapping(miUnbound, xiUnbound);
MappableInputs.SetXInputMapping(miLJoyUp, xiLJoyUp);
MappableInputs.SetXInputMapping(miLJoyDown, xiLJoyDown);
MappableInputs.SetXInputMapping(miLJoyLeft, xiLJoyLeft);
MappableInputs.SetXInputMapping(miUnbound , xiUnbound);
MappableInputs.SetXInputMapping(miLJoyUp , xiLJoyUp);
MappableInputs.SetXInputMapping(miLJoyDown , xiLJoyDown);
MappableInputs.SetXInputMapping(miLJoyLeft , xiLJoyLeft);
MappableInputs.SetXInputMapping(miLJoyRight, xiLJoyRight);
MappableInputs.SetXInputMapping(miRJoyUp, xiRJoyUp);
MappableInputs.SetXInputMapping(miRJoyDown, xiRJoyDown);
MappableInputs.SetXInputMapping(miRJoyLeft, xiRJoyLeft);
MappableInputs.SetXInputMapping(miRJoyUp , xiRJoyUp);
MappableInputs.SetXInputMapping(miRJoyDown , xiRJoyDown);
MappableInputs.SetXInputMapping(miRJoyLeft , xiRJoyLeft);
MappableInputs.SetXInputMapping(miRJoyRight, xiRJoyRight);
MappableInputs.SetXInputMapping(miDPadUp, xiDPadUp);
MappableInputs.SetXInputMapping(miDPadDown, xiDPadDown);
MappableInputs.SetXInputMapping(miDPadLeft, xiDPadLeft);
MappableInputs.SetXInputMapping(miDPadUp , xiDPadUp);
MappableInputs.SetXInputMapping(miDPadDown , xiDPadDown);
MappableInputs.SetXInputMapping(miDPadLeft , xiDPadLeft);
MappableInputs.SetXInputMapping(miDPadRight, xiDPadRight);
MappableInputs.SetXInputMapping(miCross, xiA);
MappableInputs.SetXInputMapping(miCircle, xiB);
MappableInputs.SetXInputMapping(miSquare, xiX);
MappableInputs.SetXInputMapping(miTriangle, xiY);
MappableInputs.SetXInputMapping(miShare, xiUnbound);
MappableInputs.SetXInputMapping(miTouchPad, xiSelect);
MappableInputs.SetXInputMapping(miOptions, xiStart);
MappableInputs.SetXInputMapping(miL1, xiL1);
MappableInputs.SetXInputMapping(miL2, xiL2);
MappableInputs.SetXInputMapping(miL3, xiL3);
MappableInputs.SetXInputMapping(miR1, xiR1);
MappableInputs.SetXInputMapping(miR2, xiR2);
MappableInputs.SetXInputMapping(miR3, xiR3);
MappableInputs.SetXInputMapping(miCross , xiA);
MappableInputs.SetXInputMapping(miCircle , xiB);
MappableInputs.SetXInputMapping(miSquare , xiX);
MappableInputs.SetXInputMapping(miTriangle , xiY);
MappableInputs.SetXInputMapping(miShare , xiUnbound);
MappableInputs.SetXInputMapping(miTouchPad , xiSelect);
MappableInputs.SetXInputMapping(miOptions , xiStart);
MappableInputs.SetXInputMapping(miL1 , xiL1);
MappableInputs.SetXInputMapping(miL2 , xiL2);
MappableInputs.SetXInputMapping(miL3 , xiL3);
MappableInputs.SetXInputMapping(miR1 , xiR1);
MappableInputs.SetXInputMapping(miR2 , xiR2);
MappableInputs.SetXInputMapping(miR3 , xiR3);
MappableInputs.LoadFromFile(XINPUT_CONFIG_FILE);
end.

View File

@ -2,11 +2,18 @@ unit XInput;
interface
uses
dynlibs;
type
BOOL = longbool;
const
XINPUT_DLL = 'xinput1_3.dll';
XINPUT_DLL:array[0..2] of PChar=(
'XInput1_4.dll',
'XInput1_3.dll',
'XInput9_1_0.dll'
);
// Device types available in XINPUT_CAPABILITIES
XINPUT_DEVTYPE_GAMEPAD = $01;
@ -162,44 +169,106 @@ type
HidCode: Byte;
end;
function XInputGetState(
function XInput_Init: Integer; stdcall;
procedure XInput_Quit; stdcall;
var
XInputGetState:function(
dwUserIndex: DWORD; // [in] Index of the gamer associated with the device
out pState: TXInputState // [out] Receives the current state
): DWORD; stdcall; external XINPUT_DLL;
): DWORD; stdcall;
function XInputSetState(
XInputSetState:function(
dwUserIndex: DWORD; // [in] Index of the gamer associated with the device
const pVibration: TXInputVibration // [in, out] The vibration information to send to the controller
): DWORD; stdcall; external XINPUT_DLL;
): DWORD; stdcall;
function XInputGetCapabilities(
XInputGetCapabilities:function(
dwUserIndex: DWORD; // [in] Index of the gamer associated with the device
dwFlags: DWORD; // [in] Input flags that identify the device type
out pCapabilities: TXInputCapabilities // [out] Receives the capabilities
): DWORD; stdcall; external XINPUT_DLL;
): DWORD; stdcall;
procedure XInputEnable(
XInputEnable:procedure(
enable: BOOL // [in] Indicates whether xinput is enabled or disabled.
); stdcall; external XINPUT_DLL;
); stdcall;
function XInputGetDSoundAudioDeviceGuids(
XInputGetDSoundAudioDeviceGuids:function(
dwUserIndex: DWORD; // [in] Index of the gamer associated with the device
out pDSoundRenderGuid: TGUID; // [out] DSound device ID for render
out pDSoundCaptureGuid: TGUID // [out] DSound device ID for capture
): DWORD; stdcall; external XINPUT_DLL;
): DWORD; stdcall;
function XInputGetBatteryInformation(
XInputGetBatteryInformation:function(
dwUserIndex: DWORD; // [in] Index of the gamer associated with the device
devType: Byte; // [in] Which device on this user index
out pBatteryInformation: TXInputBatteryInformation // [out] Contains the level and types of batteries
): DWORD; stdcall; external XINPUT_DLL;
): DWORD; stdcall;
function XInputGetKeystroke(
XInputGetKeystroke:function(
dwUserIndex: DWORD; // [in] Index of the gamer associated with the device
dwReserved: DWORD; // [in] Reserved for future use
var pKeystroke: TXInputKeystroke // [out] Pointer to an XINPUT_KEYSTROKE structure that receives an input event.
): DWORD; stdcall; external XINPUT_DLL;
): DWORD; stdcall;
implementation
var
lib_handle:TLibHandle=NilHandle;
function XInput_Init:Integer; stdcall;
var
i:Integer;
begin
if (lib_handle<>NilHandle) then Exit(0);
For i:=0 to High(XINPUT_DLL) do
begin
lib_handle:=SafeLoadLibrary(XINPUT_DLL[i]);
if (lib_handle<>NilHandle) then Break;
end;
if (lib_handle=NilHandle) then Exit(-1);
Pointer(XInputGetState):=GetProcedureAddress(lib_handle,100);
if (XInputGetState=nil) then
begin
Pointer(XInputGetState):=GetProcedureAddress(lib_handle,'XInputGetState');
end;
if (XInputGetState=nil) then
begin
UnloadLibrary(lib_handle);
lib_handle:=NilHandle;
Exit(-1);
end;
Pointer(XInputSetState ):=GetProcedureAddress(lib_handle,'XInputSetState');
Pointer(XInputGetCapabilities ):=GetProcedureAddress(lib_handle,'XInputGetCapabilities');
Pointer(XInputEnable ):=GetProcedureAddress(lib_handle,'XInputEnable');
Pointer(XInputGetDSoundAudioDeviceGuids):=GetProcedureAddress(lib_handle,'XInputGetDSoundAudioDeviceGuids');
Pointer(XInputGetBatteryInformation ):=GetProcedureAddress(lib_handle,'XInputGetBatteryInformation');
Pointer(XInputGetKeystroke ):=GetProcedureAddress(lib_handle,'XInputGetKeystroke');
end;
procedure XInput_Quit; stdcall;
begin
if (lib_handle=NilHandle) then Exit;
UnloadLibrary(lib_handle);
lib_handle:=NilHandle;
Pointer(XInputGetState ):=nil;
Pointer(XInputSetState ):=nil;
Pointer(XInputGetCapabilities ):=nil;
Pointer(XInputEnable ):=nil;
Pointer(XInputGetDSoundAudioDeviceGuids):=nil;
Pointer(XInputGetBatteryInformation ):=nil;
Pointer(XInputGetKeystroke ):=nil;
end;
end.

View File

@ -0,0 +1,185 @@
unit xinput_pad_interface;
{$mode ObjFPC}{$H+}
interface
uses
sysutils,
XInput,
sce_pad_types,
sce_pad_interface,
kbm_pad_interface;
type
TXInputPadHandle=class(TScePadHandle)
var
connected :Boolean;
connectedCount:Byte;
function ReadState(data:PScePadData):Integer; override;
end;
TXInputPadInterface=class(TScePadInterface)
class var
xinput_init:Boolean;
class function Load:Boolean; override;
class procedure Unload; override;
class function Init:Integer; override;
class function Done:Integer; override;
class function Open(index:Integer;var handle:TScePadHandle):Integer; override;
end;
implementation
uses
formController,
uMappableInputs;
class function TXInputPadInterface.Load:Boolean;
var
i:Integer;
begin
i:=XInput.XInput_Init();
xinput_init:=(i=0);
if xinput_init then
begin
Writeln('XInput initialized!');
end else
begin
Writeln('XInput not initialized!');
end;
Result:=xinput_init;
end;
class procedure TXInputPadInterface.Unload;
begin
if not xinput_init then Exit;
XInput_Quit;
xinput_init:=False;
Writeln('XInput quit!');
end;
class function TXInputPadInterface.Init:Integer;
begin
if (xinput_init) then
begin
Result:=0;
end else
begin
Result:=SCE_PAD_ERROR_NOT_INITIALIZED;
end;
end;
class function TXInputPadInterface.Done:Integer;
begin
Result:=0;
end;
class function TXInputPadInterface.Open(index:Integer;var handle:TScePadHandle):Integer;
begin
Result:=0;
if (index<0) or (index>15) then Exit(SCE_PAD_ERROR_INVALID_ARG);
if (pad_opened[index]<>nil) then Exit(SCE_PAD_ERROR_ALREADY_OPENED);
handle:=TXInputPadHandle.Create;
TXInputPadHandle(handle).index:=index;
pad_opened[index]:=handle;
end;
function TXInputPadHandle.ReadState(data:PScePadData):Integer;
var
cs:TXInputState;
controllerIndex:DWORD;
stateResult:DWORD;
begin
Result:=0;
if (index>=XUSER_MAX_COUNT) then
begin
data^.connected :=False;
data^.connectedCount:=0;
Exit;
end;
cs:=Default(TXInputState);
stateResult:=XInputGetState(index, cs);
if (stateResult=ERROR_DEVICE_NOT_CONNECTED) then
begin
//disconnect
connected:=False;
//
data^.connected :=connected;
data^.connectedCount:=connectedCount;
Exit;
end else
begin
//connect
if not connected then
begin
connected:=True;
Inc(connectedCount);
end;
end;
data^.connected :=connected;
data^.connectedCount:=connectedCount;
TMouseAsTouchpad.ReadState(data);
//if not MappableInputs.XInputEnabled then break; ?????
if MappableInputs.PS4IsPressed(miCross, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_CROSS;
if MappableInputs.PS4IsPressed(miCircle, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_CIRCLE;
if MappableInputs.PS4IsPressed(miSquare, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_SQUARE;
if MappableInputs.PS4IsPressed(miTriangle, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_TRIANGLE;
if MappableInputs.PS4IsPressed(miOptions, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_OPTIONS;
if MappableInputs.PS4IsPressed(miTouchPad, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_TOUCH_PAD;
if MappableInputs.PS4IsPressed(miL1, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_L1;
if MappableInputs.PS4IsPressed(miR1, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_R1;
if MappableInputs.PS4IsPressed(miL3, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_L3;
if MappableInputs.PS4IsPressed(miR3, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_R3;
if MappableInputs.PS4IsPressed(miDPadUp, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_UP;
if MappableInputs.PS4IsPressed(miDPadDown, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_DOWN;
if MappableInputs.PS4IsPressed(miDPadLeft, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_LEFT;
if MappableInputs.PS4IsPressed(miDPadRight, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_RIGHT;
data^.leftStick.x:=Trunc(128+(MappableInputs.GetAnalog(miLJoyRight, cs)-MappableInputs.GetAnalog(miLJoyLeft, cs))*127);
data^.leftStick.y:=Trunc(128+(MappableInputs.GetAnalog(miLJoyDown , cs)-MappableInputs.GetAnalog(miLJoyUp , cs))*127);
data^.rightStick.x:=Trunc(128+(MappableInputs.GetAnalog(miRJoyRight, cs)-MappableInputs.GetAnalog(miRJoyLeft, cs))*127);
data^.rightStick.y:=Trunc(128+(MappableInputs.GetAnalog(miRJoyDown , cs)-MappableInputs.GetAnalog(miRJoyUp , cs))*127);
data^.analogButtons.l2:=Trunc(MappableInputs.GetAnalog(miL2, cs)*255);
data^.analogButtons.r2:=Trunc(MappableInputs.GetAnalog(miR2, cs)*255);
if MappableInputs.PS4IsPressed(miL2, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_L2;
if MappableInputs.PS4IsPressed(miR2, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_R2;
end;
end.

View File

@ -7,529 +7,175 @@ interface
uses
windows,
ps4_program,
spinlock,
sys_signal,
Classes,
SysUtils,
xinput,
formController;
sce_pad_interface,
sdl2_pad_interface,
xinput_pad_interface,
kbm_pad_interface;
Procedure select_pad_interface(const name:RawByteString);
Procedure select_led_color (const name:RawByteString);
implementation
uses
ps4_libSceVideoOut, uMappableInputs, sys_kernel;
sce_pad_types,
ps4_libSceVideoOut,
sys_kernel;
const
SCE_PAD_ERROR_INVALID_ARG =-2137915391; // 0x80920001
SCE_PAD_ERROR_INVALID_PORT =-2137915390; // 0x80920002
SCE_PAD_ERROR_INVALID_HANDLE =-2137915389; // 0x80920003
SCE_PAD_ERROR_ALREADY_OPENED =-2137915388; // 0x80920004
SCE_PAD_ERROR_NOT_INITIALIZED =-2137915387; // 0x80920005
SCE_PAD_ERROR_INVALID_LIGHTBAR_SETTING=-2137915386; // 0x80920006
SCE_PAD_ERROR_DEVICE_NOT_CONNECTED =-2137915385; // 0x80920007
SCE_PAD_ERROR_NO_HANDLE =-2137915384; // 0x80920008
SCE_PAD_ERROR_FATAL =-2137915137; // 0x809200FF
SCE_PAD_BUTTON_L3 = $00000002;
SCE_PAD_BUTTON_R3 = $00000004;
SCE_PAD_BUTTON_OPTIONS = $00000008;
SCE_PAD_BUTTON_UP = $00000010;
SCE_PAD_BUTTON_RIGHT = $00000020;
SCE_PAD_BUTTON_DOWN = $00000040;
SCE_PAD_BUTTON_LEFT = $00000080;
SCE_PAD_BUTTON_L2 = $00000100;
SCE_PAD_BUTTON_R2 = $00000200;
SCE_PAD_BUTTON_L1 = $00000400;
SCE_PAD_BUTTON_R1 = $00000800;
SCE_PAD_BUTTON_TRIANGLE = $00001000;
SCE_PAD_BUTTON_CIRCLE = $00002000;
SCE_PAD_BUTTON_CROSS = $00004000;
SCE_PAD_BUTTON_SQUARE = $00008000;
SCE_PAD_BUTTON_TOUCH_PAD = $00100000;
SCE_PAD_BUTTON_INTERCEPTED = $80000000;
SCE_PAD_MAX_TOUCH_NUM=2;
SCE_PAD_MAX_DEVICE_UNIQUE_DATA_SIZE=12;
SCE_PAD_CONNECTION_TYPE_LOCAL=0;
SCE_PAD_CONNECTION_TYPE_REMOTE=1;
SCE_PAD_CONNECTION_TYPE_REMOTE_VITA=SCE_PAD_CONNECTION_TYPE_REMOTE;
SCE_PAD_CONNECTION_TYPE_REMOTE_DUALSHOCK4=2;
//ScePadDeviceClass
SCE_PAD_DEVICE_CLASS_INVALID = -1;
SCE_PAD_DEVICE_CLASS_STANDARD = 0;
SCE_PAD_DEVICE_CLASS_GUITAR = 1;
SCE_PAD_DEVICE_CLASS_DRUM = 2;
SCE_PAD_DEVICE_CLASS_DJ_TURNTABLE = 3;
SCE_PAD_DEVICE_CLASS_DANCEMAT = 4;
SCE_PAD_DEVICE_CLASS_NAVIGATION = 5;
SCE_PAD_DEVICE_CLASS_STEERING_WHEEL = 6;
SCE_PAD_DEVICE_CLASS_STICK = 7;
SCE_PAD_DEVICE_CLASS_FLIGHT_STICK = 8;
SCE_PAD_DEVICE_CLASS_GUN = 9;
type
Tvec_float3=packed record
x,y,z:Single;
end;
Tvec_float4=packed record
x,y,z,w:Single;
end;
ScePadAnalogStick=packed record
x,y:Byte;
end;
ScePadAnalogButtons=packed record
l2,r2:Byte;
padding:Word;
end;
ScePadTouch=packed record
x:Word;
y:Word;
id:Byte;
reserve:array[0..2] of Byte;
end;
ScePadTouchData=packed record
touchNum:Byte;
reserve:array[0..2] of Byte;
reserve1:DWORD;
touch:array[0..SCE_PAD_MAX_TOUCH_NUM-1] of ScePadTouch;
end;
ScePadExtensionUnitData=packed record
extensionUnitId:DWORD;
reserve:Byte;
dataLength:Byte;
data:array[0..9] of Byte;
end;
PScePadData=^ScePadData;
ScePadData=packed record
buttons :DWORD;
leftStick :ScePadAnalogStick;
rightStick :ScePadAnalogStick;
analogButtons :ScePadAnalogButtons;
orientation :Tvec_float4;
acceleration :Tvec_float3;
angularVelocity :Tvec_float3;
touchData :ScePadTouchData;
connected :Boolean;
_align:array[0..2] of Byte;
timestamp :QWORD;
extensionUnitData:ScePadExtensionUnitData;
connectedCount :Byte;
reserve:array[0..1] of Byte;
deviceUniqueDataLen:Byte;
deviceUniqueData:array[0..SCE_PAD_MAX_DEVICE_UNIQUE_DATA_SIZE-1] of Byte;
end;
TPadColor=packed record
r,g,b,a:Byte;
end;
PScePadVibrationParam=^ScePadVibrationParam;
ScePadVibrationParam=packed record
largeMotor:Byte;
smallMotor:Byte;
end;
ScePadColor=packed record
r:Byte;
g:Byte;
b:Byte;
reserve:Byte;
end;
ScePadLightBarParam=ScePadColor;
PScePadLightBarParam=^ScePadLightBarParam;
ScePadTouchPadInformation=packed record
pixelDensity:Single;
resolution:packed record
x,y:Word;
end;
end;
ScePadStickInformation=packed record
deadZoneLeft:Byte;
deadZoneRight:Byte;
end;
PScePadControllerInformation=^ScePadControllerInformation;
ScePadControllerInformation=packed record
touchPadInfo:ScePadTouchPadInformation;
stickInfo:ScePadStickInformation;
connectionType:Byte;
connectedCount:Byte;
connected:Boolean;
_align:array[0..2] of Byte;
deviceClass:DWORD; //ScePadDeviceClass
reserve:array[0..7] of Byte;
end;
pScePadDeviceClassExtendedInformation=^ScePadDeviceClassExtendedInformation;
ScePadDeviceClassExtendedInformation=packed record
deviceClass:DWORD; //ScePadDeviceClass
reserved:DWORD;
classData:packed record
Case Byte of
0:(steeringWheel:packed record
capability:Byte;
reserved1:Byte;
maxPhysicalWheelAngle:Word;
reserved2:QWORD;
end);
1:(guitar:packed record
capability:Byte;
quantityOfSelectorSwitch:Byte;
reserved1:Word;
reserved2:QWORD;
end);
2:(drum:packed record
capability:Byte;
reserved1:Byte;
reserved2:Word;
reserved3:QWORD;
end);
3:(flightStick:packed record
capability:Byte;
reserved1:Byte;
reserved2:Word;
reserved3:QWORD;
end);
4:(data:array[0..11] of Byte);
end;
end;
pScePadDeviceClassData=^ScePadDeviceClassData;
ScePadDeviceClassData=packed record
deviceClass:DWORD; //ScePadDeviceClass
bDataValid :Boolean;
_align:array[0..2] of Byte;
classData:packed record
Case Byte of
0:(steeringWheel:packed record
steeringWheelAngle:Single;
steeringWheel :Word;
acceleratorPedal :Word;
brakePedal :Word;
clutchPedal :Word;
handBlake :Word;
gear :Byte;
reserved :Byte;
end); //SCE_PAD_DEVICE_CLASS_STEERING_WHEEL
1:(guitar:packed record
toneNumber:Byte;
whammyBar :Byte;
tilt :Byte;
fret :Byte;
fretSolo :Byte;
reserved:array[0..10] of Byte;
end); //SCE_PAD_DEVICE_CLASS_GUITAR
2:(drum:packed record
snare :Byte;
tom1 :Byte;
tom2 :Byte;
floorTom :Byte;
hihatCymbal:Byte;
rideCymbal :Byte;
crashCymbal:Byte;
reserved:array[0..8] of Byte;
end); //SCE_PAD_DEVICE_CLASS_DRUM
3:(flightStick:packed record
stickAxisX :Word;
stickAxisY :Word;
stickTwist :Byte;
throttle :Byte;
trigger :Byte;
rudderPedal :Byte;
brakePedalLeft :Byte;
brakePedalRight:Byte;
antennaKnob :Byte;
rangeKnob :Byte;
reserved:array[0..3] of Byte;
end); //SCE_PAD_DEVICE_CLASS_FLIGHT_STICK
4:(others:packed record
dataLen:Byte;
reserved:array[0..2] of Byte;
data:array[0..SCE_PAD_MAX_DEVICE_UNIQUE_DATA_SIZE-1] of Byte;
end); //Not Supported device
end;
end;
Const
DefaultPadInterface:TAbstractScePadInterface=TXInputPadInterface;
var
last_mouse_lock:Pointer;
last_mouse_point:TPoint;
last_mouse_init:Integer=0;
xinput_last_poll:Long=0;
xinput_controllers_connected:Array[0..XUSER_MAX_COUNT-1] of Boolean;
ScePadInterface:TAbstractScePadInterface=nil;
DefaultPadLightBar:ScePadLightBarParam;
Procedure select_pad_interface(const name:RawByteString);
begin
case lowercase(name) of
'xinput' :ScePadInterface:=TXInputPadInterface;
'sdl2' :ScePadInterface:=TSdl2PadInterface;
'keyboard':ScePadInterface:=TKbmPadInterface;
else
ScePadInterface:=DefaultPadInterface; //default
end;
if not ScePadInterface.Load then
begin
ScePadInterface:=TKbmPadInterface; //reset to kbm
end;
end;
Procedure select_led_color(const name:RawByteString);
var
clr:DWord;
begin
if TryStrToDWord(name,clr) then
begin
DefaultPadLightBar.r:=(clr shr 16) and $FF;
DefaultPadLightBar.g:=(clr shr 8) and $FF;
DefaultPadLightBar.b:=(clr shr 0) and $FF;
end;
end;
function ps4_scePadInit():Integer; SysV_ABI_CDecl;
var
controllerIndex:Integer;
begin
Writeln(SysLogPrefix,'scePadInit');
// init xinput
for controllerIndex := 0 to XUSER_MAX_COUNT - 1 do
xinput_controllers_connected[controllerIndex] := false;
if (ScePadInterface=nil) then
begin
ScePadInterface:=DefaultPadInterface; //default
if not ScePadInterface.Load then
begin
ScePadInterface:=TKbmPadInterface; //reset to kbm
end;
end;
Result:=0;
Result:=ScePadInterface.Init;
end;
function ps4_scePadOpen(userID,_type,index:Integer;param:Pointer):Integer; SysV_ABI_CDecl;
var
sce_handle:TScePadHandle;
key:Integer;
begin
//Writeln('scePadOpen:',userID);
Result:=222;
if (ScePadInterface=nil) then Exit(SCE_PAD_ERROR_NOT_INITIALIZED);
if (_type<>SCE_PAD_PORT_TYPE_STANDARD) then Exit(SCE_PAD_ERROR_INVALID_ARG);
if (index<0) or (index>15) then Exit(SCE_PAD_ERROR_INVALID_ARG);
sce_handle:=nil;
Result:=ScePadInterface.Open(index,sce_handle);
if (Result<>0) then Exit;
sce_handle.SetLightBar(@DefaultPadLightBar);
key:=0;
if pad_handles.New(sce_handle,key) then
begin
sce_handle.handle:=key;
sce_handle.Release;
Result:=key;
end else
begin
Result:=SCE_PAD_ERROR_FATAL;
end;
end;
function ps4_scePadGetHandle(userID,_type,index:Integer):Integer; SysV_ABI_CDecl;
begin
Result:=222;
if (ScePadInterface=nil) then Exit(SCE_PAD_ERROR_NOT_INITIALIZED);
if (_type<>SCE_PAD_PORT_TYPE_STANDARD) then Exit(SCE_PAD_ERROR_INVALID_ARG);
if (index<0) or (index>15) then Exit(SCE_PAD_ERROR_INVALID_ARG);
if (pad_opened[index]=nil) then Exit(SCE_PAD_ERROR_NO_HANDLE);
Result:=pad_opened[index].handle;
end;
function ps4_scePadClose(handle:Integer):Integer; SysV_ABI_CDecl;
begin
if (ScePadInterface=nil) then Exit(SCE_PAD_ERROR_NOT_INITIALIZED);
if not pad_handles.Delete(handle) then Exit(SCE_PAD_ERROR_INVALID_HANDLE);
Result:=0;
end;
function ps4_scePadReadState(handle:Integer;data:PScePadData):Integer; SysV_ABI_CDecl;
var
mPoint,delta:TPoint;
cs:TXInputState;
controllerIndex,stateResult:DWORD;
function GetAsyncKeyState(vKey:longint):Boolean; inline;
begin
Result:=(Windows.GetKeyState(vKey) and $8000)<>0;
end;
sce_handle:TScePadHandle;
begin
Result:=SCE_PAD_ERROR_INVALID_ARG;
if (data=nil) then Exit;
//Writeln(SizeOf(TPadData)); //184
Result:=0;
if (data=nil) then Exit(SCE_PAD_ERROR_INVALID_ARG);
if (ScePadInterface=nil) then Exit(SCE_PAD_ERROR_NOT_INITIALIZED);
data^:=Default(ScePadData);
_sig_lock;
sce_handle:=TScePadHandle(pad_handles.Acqure(handle));
if (sce_handle=nil) then
begin
_sig_unlock;
Exit(SCE_PAD_ERROR_INVALID_HANDLE);
end;
//connect data
data^.timestamp:=Sysutils.GetTickCount64;
data^.connected:=True;
data^.connected :=True;
data^.connectedCount:=1;
data^.leftStick.x :=$80;
data^.leftStick.y :=$80;
data^.rightStick.x:=$80;
data^.rightStick.y:=$80;
data^.leftStick.x :=$80;
data^.leftStick.y :=$80;
data^.rightStick.x :=$80;
data^.rightStick.y :=$80;
data^.orientation.w :=1;
data^.acceleration.y:=1;
//only in active windows
if (MainWindows<>GetForegroundWindow) then
begin
sce_handle.Release;
_sig_unlock;
Result:=0;
Exit;
end;
// xinput - Check connected controllers every couple of seconds
if GetTickCount64 > xinput_last_poll + 10000 then
begin
for controllerIndex := 0 to XUSER_MAX_COUNT - 1 do
xinput_controllers_connected[controllerIndex] := XInputGetState(controllerIndex, cs) <> ERROR_DEVICE_NOT_CONNECTED;
xinput_last_poll := GetTickCount64;
end;
// xinput for controllers
for controllerIndex := 0 to XUSER_MAX_COUNT - 1 do
begin
if not MappableInputs.XInputEnabled then break;
if not xinput_controllers_connected[controllerIndex] then
continue;
ZeroMemory(@cs, SizeOf(cs));
stateResult := XInputGetState(controllerIndex, cs);
if stateResult = ERROR_SUCCESS then
begin
if MappableInputs.PS4IsPressed(miCross, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_CROSS;
if MappableInputs.PS4IsPressed(miCircle, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_CIRCLE;
if MappableInputs.PS4IsPressed(miSquare, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_SQUARE;
if MappableInputs.PS4IsPressed(miTriangle, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_TRIANGLE;
if MappableInputs.PS4IsPressed(miOptions, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_OPTIONS;
if MappableInputs.PS4IsPressed(miTouchPad, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_TOUCH_PAD;
if MappableInputs.PS4IsPressed(miL1, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_L1;
if MappableInputs.PS4IsPressed(miR1, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_R1;
if MappableInputs.PS4IsPressed(miL3, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_L3;
if MappableInputs.PS4IsPressed(miR3, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_R3;
if MappableInputs.PS4IsPressed(miDPadUp, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_UP;
if MappableInputs.PS4IsPressed(miDPadDown, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_DOWN;
if MappableInputs.PS4IsPressed(miDPadLeft, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_LEFT;
if MappableInputs.PS4IsPressed(miDPadRight, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_RIGHT;
data^.leftStick.x:=Trunc(128+(MappableInputs.GetAnalog(miLJoyRight, cs)-MappableInputs.GetAnalog(miLJoyLeft, cs))*127);
data^.leftStick.y:=Trunc(128+(MappableInputs.GetAnalog(miLJoyDown, cs)-MappableInputs.GetAnalog(miLJoyUp, cs))*127);
data^.rightStick.x:=Trunc(128+(MappableInputs.GetAnalog(miRJoyRight, cs)-MappableInputs.GetAnalog(miRJoyLeft, cs))*127);
data^.rightStick.y:=Trunc(128+(MappableInputs.GetAnalog(miRJoyDown, cs)-MappableInputs.GetAnalog(miRJoyUp, cs))*127);
data^.analogButtons.l2:=Trunc(MappableInputs.GetAnalog(miL2, cs)*255);
data^.analogButtons.r2:=Trunc(MappableInputs.GetAnalog(miR2, cs)*255);
if MappableInputs.PS4IsPressed(miL2, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_L2;
if MappableInputs.PS4IsPressed(miR2, cs) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_R2;
end;
end;
//mouse as touch pad
spin_lock(last_mouse_lock);
GetCursorPos(mPoint);
if (last_mouse_init=0) then
begin
last_mouse_init :=1;
last_mouse_point:=mPoint;
end else
if QWORD(mPoint)<>QWORD(last_mouse_point) then
begin
data^.touchData.touchNum:=1;
data^.touchData.touch[0].id:=0;
delta:=mPoint;
if (delta.X<0) then delta.X:=0;
if (delta.Y<0) then delta.Y:=0;
if (delta.X>1919) then delta.X:=1919;
if (delta.Y>941) then delta.Y:=941;
data^.touchData.touch[0].x:=delta.X;
data^.touchData.touch[0].y:=delta.Y;
last_mouse_point:=mPoint;
end;
spin_unlock(last_mouse_lock);
//keymapping
if GetAsyncKeyState(VK_W) then
data^.leftStick.y:=0;
if GetAsyncKeyState(VK_S) then
data^.leftStick.y:=$FF;
if GetAsyncKeyState(VK_A) then
data^.leftStick.x:=0;
if GetAsyncKeyState(VK_D) then
data^.leftStick.x:=$FF;
//
if GetAsyncKeyState(VK_I) then
data^.rightStick.y:=0;
if GetAsyncKeyState(VK_K) then
data^.rightStick.y:=$FF;
if GetAsyncKeyState(VK_J) then
data^.rightStick.x:=0;
if GetAsyncKeyState(VK_L) then
data^.rightStick.x:=$FF;
//
if GetAsyncKeyState(VK_LBUTTON) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_TOUCH_PAD;
if GetAsyncKeyState(VK_RETURN) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_OPTIONS;
if GetAsyncKeyState(VK_UP) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_UP;
if GetAsyncKeyState(VK_RIGHT) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_RIGHT;
if GetAsyncKeyState(VK_DOWN) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_DOWN;
if GetAsyncKeyState(VK_LEFT) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_LEFT;
if GetAsyncKeyState(VK_NUMPAD8) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_TRIANGLE;
if GetAsyncKeyState(VK_NUMPAD6) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_CIRCLE;
if GetAsyncKeyState(VK_NUMPAD2) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_CROSS;
if GetAsyncKeyState(VK_NUMPAD4) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_SQUARE;
if GetAsyncKeyState(VK_Q) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_L1;
if GetAsyncKeyState(VK_1) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_L2;
if GetAsyncKeyState(VK_Z) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_L3;
if GetAsyncKeyState(VK_E) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_R1;
if GetAsyncKeyState(VK_4) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_R2;
if GetAsyncKeyState(VK_C) then
data^.buttons:=data^.buttons or SCE_PAD_BUTTON_R3;
sce_handle.ReadState(data);
sce_handle.Release;
_sig_unlock;
Result:=0;
end;
@ -537,6 +183,9 @@ end;
function ps4_scePadRead(handle:Integer;data:PScePadData;num:Integer):Integer; SysV_ABI_CDecl;
begin
Result:=0;
if (data=nil) then Exit(SCE_PAD_ERROR_INVALID_ARG);
if (ScePadInterface=nil) then Exit(SCE_PAD_ERROR_NOT_INITIALIZED);
if (num<>0) then
begin
ps4_scePadReadState(handle,data);
@ -546,23 +195,66 @@ end;
function ps4_scePadSetVibration(handle:Integer;pParam:PScePadVibrationParam):Integer; SysV_ABI_CDecl;
begin
if (ScePadInterface=nil) then Exit(SCE_PAD_ERROR_NOT_INITIALIZED);
Result:=0;
end;
function ps4_scePadResetLightBar(handle:Integer):Integer; assembler; nostackframe;
asm
xor %rax,%rax
function ps4_scePadSetLightBar(handle:Integer;pParam:PScePadLightBarParam):Integer; SysV_ABI_CDecl;
var
sce_handle:TScePadHandle;
begin
if (ScePadInterface=nil) then Exit(SCE_PAD_ERROR_NOT_INITIALIZED);
if (pParam=nil) then Exit(SCE_PAD_ERROR_INVALID_ARG);
_sig_lock;
sce_handle:=TScePadHandle(pad_handles.Acqure(handle));
if (sce_handle=nil) then
begin
_sig_unlock;
Exit(SCE_PAD_ERROR_INVALID_HANDLE);
end;
Result:=sce_handle.SetLightBar(pParam);
sce_handle.Release;
_sig_unlock;
end;
function ps4_scePadResetLightBar(handle:Integer):Integer; SysV_ABI_CDecl;
var
sce_handle:TScePadHandle;
begin
if (ScePadInterface=nil) then Exit(SCE_PAD_ERROR_NOT_INITIALIZED);
_sig_lock;
sce_handle:=TScePadHandle(pad_handles.Acqure(handle));
if (sce_handle=nil) then
begin
_sig_unlock;
Exit(SCE_PAD_ERROR_INVALID_HANDLE);
end;
Result:=sce_handle.ResetLightBar();
sce_handle.Release;
_sig_unlock;
end;
function ps4_scePadGetControllerInformation(handle:Integer;
pInfo:PScePadControllerInformation
):Integer; SysV_ABI_CDecl;
begin
if (ScePadInterface=nil) then Exit(SCE_PAD_ERROR_NOT_INITIALIZED);
if (pInfo=nil) then Exit(SCE_PAD_ERROR_INVALID_ARG);
pInfo^:=Default(ScePadControllerInformation);
pInfo^.touchPadInfo.pixelDensity := 1;
pInfo^.touchPadInfo.resolution.x := 256;
pInfo^.touchPadInfo.resolution.y := 256;
pInfo^.touchPadInfo.resolution.x := 1920;
pInfo^.touchPadInfo.resolution.y := 950;
pInfo^.stickInfo.deadZoneLeft := 2;
pInfo^.stickInfo.deadZoneRight := 2;
pInfo^.connectionType := SCE_PAD_CONNECTION_TYPE_LOCAL;
@ -576,6 +268,7 @@ function ps4_scePadDeviceClassGetExtendedInformation(handle:Integer;
pExtInfo:pScePadDeviceClassExtendedInformation
):Integer; SysV_ABI_CDecl;
begin
if (ScePadInterface=nil) then Exit(SCE_PAD_ERROR_NOT_INITIALIZED);
if (pExtInfo=nil) then Exit(SCE_PAD_ERROR_INVALID_ARG);
pExtInfo^:=Default(ScePadDeviceClassExtendedInformation);
pExtInfo^.deviceClass:=SCE_PAD_DEVICE_CLASS_STANDARD;
@ -587,6 +280,7 @@ function ps4_scePadDeviceClassParseData(handle:Integer;
pDeviceClassData:pScePadDeviceClassData
):Integer; SysV_ABI_CDecl;
begin
if (ScePadInterface=nil) then Exit(SCE_PAD_ERROR_NOT_INITIALIZED);
if (pData=nil) then Exit(SCE_PAD_ERROR_INVALID_ARG);
if (pDeviceClassData=nil) then Exit(SCE_PAD_ERROR_INVALID_ARG);
pDeviceClassData^:=Default(ScePadDeviceClassData);
@ -597,26 +291,25 @@ end;
function ps4_scePadSetMotionSensorState(handle:Integer;bEnable:Boolean):Integer; SysV_ABI_CDecl;
begin
Result:=0;
end;
function ps4_scePadSetLightBar(handle:Integer;pParam:PScePadLightBarParam):Integer; SysV_ABI_CDecl;
begin
if (ScePadInterface=nil) then Exit(SCE_PAD_ERROR_NOT_INITIALIZED);
Result:=0;
end;
function ps4_scePadResetOrientation(handle:Integer):Integer; SysV_ABI_CDecl;
begin
if (ScePadInterface=nil) then Exit(SCE_PAD_ERROR_NOT_INITIALIZED);
Result:=0;
end;
function ps4_scePadSetTiltCorrectionState(handle:Integer;bEnable:BOOL):Integer; SysV_ABI_CDecl;
begin
if (ScePadInterface=nil) then Exit(SCE_PAD_ERROR_NOT_INITIALIZED);
Result:=0;
end;
function ps4_scePadSetAngularVelocityDeadbandState(handle:Integer;bEnable:BOOL):Integer; SysV_ABI_CDecl;
begin
if (ScePadInterface=nil) then Exit(SCE_PAD_ERROR_NOT_INITIALIZED);
Result:=0;
end;
@ -636,12 +329,12 @@ begin
lib^.set_proc($6277605EA41557B7,@ps4_scePadReadState);
lib^.set_proc($AB570735F1B270B2,@ps4_scePadRead);
lib^.set_proc($C8556739D1B1BD96,@ps4_scePadSetVibration);
lib^.set_proc($451E27A2F50410D6,@ps4_scePadSetLightBar);
lib^.set_proc($0EC703D62F475F5C,@ps4_scePadResetLightBar);
lib^.set_proc($8233FDFCA433A149,@ps4_scePadGetControllerInformation);
lib^.set_proc($01CB25A4DD631D1F,@ps4_scePadDeviceClassGetExtendedInformation);
lib^.set_proc($2073EA71B734CC20,@ps4_scePadDeviceClassParseData);
lib^.set_proc($72556F2F86439EDC,@ps4_scePadSetMotionSensorState);
lib^.set_proc($451E27A2F50410D6,@ps4_scePadSetLightBar);
lib^.set_proc($AC866747A792A6F9,@ps4_scePadResetOrientation);
lib^.set_proc($BC32CCA092DD7BC2,@ps4_scePadSetTiltCorrectionState);
lib^.set_proc($AF8E260317521BE5,@ps4_scePadSetAngularVelocityDeadbandState);