diff --git a/fpPS4.lpi b/fpPS4.lpi index 91cdbce9..8703b2d5 100644 --- a/fpPS4.lpi +++ b/fpPS4.lpi @@ -31,7 +31,7 @@ - + @@ -640,6 +640,26 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/fpPS4.lpr b/fpPS4.lpr index ecdf9a20..0d8e0bdb 100644 --- a/fpPS4.lpr +++ b/fpPS4.lpr @@ -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 //Decrypted ELF or SELF file name'); - Writeln(' -f //Folder of app (/app0)'); - Writeln(' -p //Folder of patch (/app1)'); - Writeln(' -s //Savedata path'); - Writeln(' -w //Fullscreen mode'); + Writeln(' -e //Decrypted ELF or SELF file name'); + Writeln(' -f //Folder of app (/app0)'); + Writeln(' -p //Folder of patch (/app1)'); + Writeln(' -s //Savedata path'); + Writeln(' -w //Fullscreen mode'); + Writeln(' -pad //Gamepad interface selection (xinput,sdl2,keyboard) default:xinput'); + Writeln(' -led //Initial LED color of Gamepad ($rrggbb)'); - Writeln(' -h //enable hack'); + Writeln(' -h //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. diff --git a/src/inputs/kbm_pad_interface.pas b/src/inputs/kbm_pad_interface.pas new file mode 100644 index 00000000..8d9a8eb1 --- /dev/null +++ b/src/inputs/kbm_pad_interface.pas @@ -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. + diff --git a/src/inputs/sce_pad_interface.pas b/src/inputs/sce_pad_interface.pas new file mode 100644 index 00000000..b2b8e992 --- /dev/null +++ b/src/inputs/sce_pad_interface.pas @@ -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. + + diff --git a/src/inputs/sce_pad_types.pas b/src/inputs/sce_pad_types.pas new file mode 100644 index 00000000..9f940c00 --- /dev/null +++ b/src/inputs/sce_pad_types.pas @@ -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. + diff --git a/src/inputs/sdl2.pas b/src/inputs/sdl2.pas new file mode 100644 index 00000000..b2a42083 --- /dev/null +++ b/src/inputs/sdl2.pas @@ -0,0 +1,1091 @@ +unit sdl2; + +{ + Clipped headers from "SDL2-for-Pascal" +} + +{$mode objfpc}{$H+} + +interface + + {$IFDEF WINDOWS} + uses + dynlibs, + ctypes, + Windows; + {$ENDIF} + + {$IF DEFINED(UNIX) AND NOT DEFINED(ANDROID)} + uses + {$IFDEF FPC} + ctypes, + UnixType, + {$ENDIF} + {$IFDEF DARWIN} + CocoaAll; + {$ELSE} + X, + XLib; + {$ENDIF} + {$ENDIF} + +const + + {$IFDEF WINDOWS} + SDL_LibName = 'SDL2.dll'; + {$ENDIF} + + {$IFDEF UNIX} + {$IFDEF DARWIN} + SDL_LibName = 'libSDL2.dylib'; + {$ELSE} + SDL_LibName = 'libSDL2.so'; + {$ENDIF} + {$ENDIF} + + {$IFDEF MACOS} + SDL_LibName = 'SDL2'; + {$ENDIF} + +type + PPSDL_Init = ^PSDL_Init; + PSDL_Init = ^TSDL_Init; + TSDL_Init = type cuint32; + +const + SDL_INIT_JOYSTICK = TSDL_Init($00000200); // SDL_INIT_JOYSTICK implies SDL_INIT_EVENTS + SDL_INIT_GAMECONTROLLER = TSDL_Init($00002000); //turn on game controller also implicitly does JOYSTICK + +type + PSDL_ScanCode = ^TSDL_ScanCode; + TSDL_ScanCode = type cint; + + PSDL_KeyCode = ^TSDL_KeyCode; + TSDL_KeyCode = type cint32; + + PPSDL_Keysym = ^PSDL_Keysym; + PSDL_Keysym = ^TSDL_Keysym; + TSDL_Keysym = record + scancode: TSDL_ScanCode; // SDL physical key code - see SDL_Scancode for details + sym: TSDL_KeyCode; // SDL virtual key code - see SDL_Keycode for details + mod_: cuint16; // current key modifiers + unicode: cuint32; // (deprecated) use SDL_TextInputEvent instead + end; + + PSDL_TouchID = ^TSDL_TouchID; + TSDL_TouchID = type cint64; + + PSDL_FingerID = ^TSDL_FingerID; + TSDL_FingerID = type cint64; + + PSDL_GestureID = ^TSDL_GestureID; + TSDL_GestureID = type cint64; + + PSDL_EventType = ^TSDL_EventType; + TSDL_EventType = type cuint32; + + PPSDL_GameController = ^PSDL_GameController; + PSDL_GameController = ^TSDL_GameController; + TSDL_GameController = record end; + + PPSDL_GameControllerType = ^PSDL_GameControllerType; + PSDL_GameControllerType = ^TSDL_GameControllerType; + TSDL_GameControllerType = type cint; + +const + SDL_CONTROLLER_TYPE_UNKNOWN = TSDL_GameControllerType(0); + SDL_CONTROLLER_TYPE_XBOX360 = TSDL_GameControllerType(1); + SDL_CONTROLLER_TYPE_XBOXONE = TSDL_GameControllerType(2); + SDL_CONTROLLER_TYPE_PS3 = TSDL_GameControllerType(3); + SDL_CONTROLLER_TYPE_PS4 = TSDL_GameControllerType(4); + SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO = TSDL_GameControllerType(5); + SDL_CONTROLLER_TYPE_VIRTUAL = TSDL_GameControllerType(6); + SDL_CONTROLLER_TYPE_PS5 = TSDL_GameControllerType(7); + SDL_CONTROLLER_TYPE_AMAZON_LUNA = TSDL_GameControllerType(8); + SDL_CONTROLLER_TYPE_GOOGLE_STADIA = TSDL_GameControllerType(9); + SDL_CONTROLLER_TYPE_NVIDIA_SHIELD = TSDL_GameControllerType(10); + SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT = TSDL_GameControllerType(11); + SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT = TSDL_GameControllerType(12); + SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR = TSDL_GameControllerType(13); + +type + PPSDL_GameControllerBindType = ^PSDL_GameControllerBindType; + PSDL_GameControllerBindType = ^TSDL_GameControllerBindType; + TSDL_GameControllerBindType = type cint; + +const + SDL_CONTROLLER_BINDTYPE_NONE = TSDL_GameControllerBindType(0); + SDL_CONTROLLER_BINDTYPE_BUTTON = TSDL_GameControllerBindType(1); + SDL_CONTROLLER_BINDTYPE_AXIS = TSDL_GameControllerBindType(2); + SDL_CONTROLLER_BINDTYPE_HAT = TSDL_GameControllerBindType(3); + +type + THat = record + hat: cint; + hat_mask: cint; + end; + + PPSDL_GameControllerButtonBind = ^PSDL_GameControllerButtonBind; + PSDL_GameControllerButtonBind = ^TSDL_GameControllerButtonBind; + TSDL_GameControllerButtonBind = record + bindType: TSDL_GameControllerBindType; + case cint of + 0: ( button: cint; ); + 1: ( axis: cint; ); + 2: ( hat: THat; ); + end; + +type + PPSDL_Joystick = ^PSDL_Joystick; + PSDL_Joystick = ^TSDL_Joystick; + TSDL_Joystick = record end; + + PPSDL_JoystickGUID = ^PSDL_JoystickGUID; + PSDL_JoystickGUID = ^TSDL_JoystickGUID; + TSDL_JoystickGUID = type TGUID; + + PPSDL_JoystickID = ^PSDL_JoystickID; + PSDL_JoystickID = ^TSDL_JoystickID; + TSDL_JoystickID = type cint32; + +type + PPSDL_JoystickType = ^PSDL_JoystickType; + PSDL_JoystickType = ^TSDL_JoystickType; + TSDL_JoystickType = type Integer; + +const + SDL_JOYSTICK_TYPE_UNKNOWN = TSDL_JoystickType(0); + SDL_JOYSTICK_TYPE_GAMECONTROLLER = TSDL_JoystickType(1); + SDL_JOYSTICK_TYPE_WHEEL = TSDL_JoystickType(2); + SDL_JOYSTICK_TYPE_ARCADE_STICK = TSDL_JoystickType(3); + SDL_JOYSTICK_TYPE_FLIGHT_STICK = TSDL_JoystickType(4); + SDL_JOYSTICK_TYPE_DANCE_PAD = TSDL_JoystickType(5); + SDL_JOYSTICK_TYPE_GUITAR = TSDL_JoystickType(6); + SDL_JOYSTICK_TYPE_DRUM_KIT = TSDL_JoystickType(7); + SDL_JOYSTICK_TYPE_ARCADE_PAD = TSDL_JoystickType(8); + SDL_JOYSTICK_TYPE_THROTTLE = TSDL_JoystickType(9); + +type + PPSDL_JoystickPowerLevel = ^PSDL_JoystickPowerLevel; + PSDL_JoystickPowerLevel = ^TSDL_JoystickPowerLevel; + TSDL_JoystickPowerLevel = type Integer; + +const + SDL_JOYSTICK_POWER_UNKNOWN = TSDL_JoystickPowerLevel(-1); + SDL_JOYSTICK_POWER_EMPTY = TSDL_JoystickPowerLevel(0); {* <= 5% *} + SDL_JOYSTICK_POWER_LOW = TSDL_JoystickPowerLevel(1); {* <= 20% *} + SDL_JOYSTICK_POWER_MEDIUM = TSDL_JoystickPowerLevel(2); {* <= 70% *} + SDL_JOYSTICK_POWER_FULL = TSDL_JoystickPowerLevel(3); {* <= 100% *} + SDL_JOYSTICK_POWER_WIRED = TSDL_JoystickPowerLevel(4); + SDL_JOYSTICK_POWER_MAX = TSDL_JoystickPowerLevel(5); + +const + SDL_IPHONE_MAX_GFORCE = 5.0; + +type + PPSDL_GameControllerButton = ^PSDL_GameControllerButton; + PSDL_GameControllerButton = ^TSDL_GameControllerButton; + TSDL_GameControllerButton = type cint; + +const + SDL_CONTROLLER_BUTTON_INVALID = TSDL_GameControllerButton(-1); + SDL_CONTROLLER_BUTTON_A = TSDL_GameControllerButton(0); + SDL_CONTROLLER_BUTTON_B = TSDL_GameControllerButton(1); + SDL_CONTROLLER_BUTTON_X = TSDL_GameControllerButton(2); + SDL_CONTROLLER_BUTTON_Y = TSDL_GameControllerButton(3); + SDL_CONTROLLER_BUTTON_BACK = TSDL_GameControllerButton(4); + SDL_CONTROLLER_BUTTON_GUIDE = TSDL_GameControllerButton(5); + SDL_CONTROLLER_BUTTON_START = TSDL_GameControllerButton(6); + SDL_CONTROLLER_BUTTON_LEFTSTICK = TSDL_GameControllerButton(7); + SDL_CONTROLLER_BUTTON_RIGHTSTICK = TSDL_GameControllerButton(8); + SDL_CONTROLLER_BUTTON_LEFTSHOULDER = TSDL_GameControllerButton(9); + SDL_CONTROLLER_BUTTON_RIGHTSHOULDER = TSDL_GameControllerButton(10); + SDL_CONTROLLER_BUTTON_DPAD_UP = TSDL_GameControllerButton(11); + SDL_CONTROLLER_BUTTON_DPAD_DOWN = TSDL_GameControllerButton(12); + SDL_CONTROLLER_BUTTON_DPAD_LEFT = TSDL_GameControllerButton(13); + SDL_CONTROLLER_BUTTON_DPAD_RIGHT = TSDL_GameControllerButton(14); + SDL_CONTROLLER_BUTTON_MISC1 = TSDL_GameControllerButton(15); {**< Xbox Series X share button, PS5 microphone button, Nintendo Switch Pro capture button, Amazon Luna microphone button *} + SDL_CONTROLLER_BUTTON_PADDLE1 = TSDL_GameControllerButton(16); {**< Xbox Elite paddle P1 *} + SDL_CONTROLLER_BUTTON_PADDLE2 = TSDL_GameControllerButton(17); {**< Xbox Elite paddle P3 *} + SDL_CONTROLLER_BUTTON_PADDLE3 = TSDL_GameControllerButton(18); {**< Xbox Elite paddle P2 *} + SDL_CONTROLLER_BUTTON_PADDLE4 = TSDL_GameControllerButton(19); {**< Xbox Elite paddle P4 *} + SDL_CONTROLLER_BUTTON_TOUCHPAD = TSDL_GameControllerButton(20); {**< PS4/PS5 touchpad button *} + SDL_CONTROLLER_BUTTON_MAX = TSDL_GameControllerButton(21); + +type + PPSDL_GameControllerAxis = ^PSDL_GameControllerAxis; + PSDL_GameControllerAxis = ^TSDL_GameControllerAxis; + TSDL_GameControllerAxis = type cint; + +const + SDL_CONTROLLER_AXIS_INVALID = TSDL_GameControllerAxis(-1); + SDL_CONTROLLER_AXIS_LEFTX = TSDL_GameControllerAxis(0); + SDL_CONTROLLER_AXIS_LEFTY = TSDL_GameControllerAxis(1); + SDL_CONTROLLER_AXIS_RIGHTX = TSDL_GameControllerAxis(2); + SDL_CONTROLLER_AXIS_RIGHTY = TSDL_GameControllerAxis(3); + SDL_CONTROLLER_AXIS_TRIGGERLEFT = TSDL_GameControllerAxis(4); + SDL_CONTROLLER_AXIS_TRIGGERRIGHT = TSDL_GameControllerAxis(5); + SDL_CONTROLLER_AXIS_MAX = TSDL_GameControllerAxis(6); + +/// + +const + + { General keyboard/mouse state definitions } + SDL_RELEASED = 0; + SDL_PRESSED = 1; + + SDL_FIRSTEVENT = TSDL_EventType(0); // Unused (do not remove) (needed in pascal?) + + SDL_COMMONEVENT = TSDL_EventType(1); //added for pascal-compatibility + + { Application events } + SDL_QUITEV = TSDL_EventType($100); // User-requested quit (originally SDL_QUIT, but changed, cause theres a method called SDL_QUIT) + + + { These application events have special meaning on iOS, see README.iOS for details *} + + {* The application is being terminated by the OS. * + * Called on iOS in applicationWillTerminate() * + * Called on Android in onDestroy() *} + SDL_APP_TERMINATING = TSDL_EventType($101); + + {* The application is low on memory, free memory if possible. * + * Called on iOS in applicationDidReceiveMemoryWarning() * + * Called on Android in onLowMemory() *} + SDL_APP_LOWMEMORY = TSDL_EventType($102); + + {* The application is about to enter the background. * + * Called on iOS in applicationWillResignActive() * + * Called on Android in onPause() *} + SDL_APP_WILLENTERBACKGROUND = TSDL_EventType($103); + + {* The application did enter the background and may not get CPU for some time. * + * Called on iOS in applicationDidEnterBackground() * + * Called on Android in onPause() *} + SDL_APP_DIDENTERBACKGROUND = TSDL_EventType($104); + + {* The application is about to enter the foreground. * + * Called on iOS in applicationWillEnterForeground() * + * Called on Android in onResume() *} + SDL_APP_WILLENTERFOREGROUND = TSDL_EventType($105); + + {* The application is now interactive. * + * Called on iOS in applicationDidBecomeActive() * + * Called on Android in onResume() *} + SDL_APP_DIDENTERFOREGROUND = TSDL_EventType($106); + + {* The user's locale preferences have changed. *} + SDL_LOCALECHANGED = TSDL_EventType($107); + + { Display events } + SDL_DISPLAYEVENT = TSDL_EventType($150); // Display state change + + { Window events } + SDL_WINDOWEVENT = TSDL_EventType($200); // Window state change + SDL_SYSWMEVENT = TSDL_EventType($201); // System specific event + + { Keyboard events } + SDL_KEYDOWN = TSDL_EventType($300); // Key pressed + SDL_KEYUP = TSDL_EventType($301); // Key released + SDL_TEXTEDITING = TSDL_EventType($302); // Keyboard text editing (composition) + SDL_TEXTINPUT = TSDL_EventType($303); // Keyboard text input + SDL_KEYMAPCHANGED = TSDL_EventType($304); // Keymap changed due to a system event such as an input language or keyboard layout change. + SDL_TEXTEDITING_EXT = TSDL_EventType($305); // Extended keyboard text editing (composition) + + { Mouse events } + SDL_MOUSEMOTION = TSDL_EventType($400); // Mouse moved + SDL_MOUSEBUTTONDOWN = TSDL_EventType($401); // Mouse button pressed + SDL_MOUSEBUTTONUP = TSDL_EventType($402); // Mouse button released + SDL_MOUSEWHEEL = TSDL_EventType($403); // Mouse wheel motion + + { Joystick events } + SDL_JOYAXISMOTION = TSDL_EventType($600); // Joystick axis motion + SDL_JOYBALLMOTION = TSDL_EventType($601); // Joystick trackball motion + SDL_JOYHATMOTION = TSDL_EventType($602); // Joystick hat position change + SDL_JOYBUTTONDOWN = TSDL_EventType($603); // Joystick button pressed + SDL_JOYBUTTONUP = TSDL_EventType($604); // Joystick button released + SDL_JOYDEVICEADDED = TSDL_EventType($605); // A new joystick has been inserted into the system + SDL_JOYDEVICEREMOVED = TSDL_EventType($606); // An opened joystick has been removed + SDL_JOYBATTERYUPDATED = TSDL_EventType($607); // Joystick battery level change + + { Game controller events } + SDL_CONTROLLERAXISMOTION = TSDL_EventType($650); // Game controller axis motion + SDL_CONTROLLERBUTTONDOWN = TSDL_EventType($651); // Game controller button pressed + SDL_CONTROLLERBUTTONUP = TSDL_EventType($652); // Game controller button released + SDL_CONTROLLERDEVICEADDED = TSDL_EventType($653); // A new Game controller has been inserted into the system + SDL_CONTROLLERDEVICEREMOVED = TSDL_EventType($654); // An opened Game controller has been removed + SDL_CONTROLLERDEVICEREMAPPED = TSDL_EventType($655); // The controller mapping was updated + SDL_CONTROLLERTOUCHPADDOWN = TSDL_EventType($666); // Game controller touchpad was touched + SDL_CONTROLLERTOUCHPADMOTION = TSDL_EventType($667); // Game controller touchpad finger was moved + SDL_CONTROLLERTOUCHPADUP = TSDL_EventType($668); // Game controller touchpad finger was lifted + SDL_CONTROLLERSENSORUPDATE = TSDL_EventType($669); // Game controller sensor was updated + + { Touch events } + SDL_FINGERDOWN = TSDL_EventType($700); + SDL_FINGERUP = TSDL_EventType($701); + SDL_FINGERMOTION = TSDL_EventType($702); + + { Gesture events } + SDL_DOLLARGESTURE = TSDL_EventType($800); + SDL_DOLLARRECORD = TSDL_EventType($801); + SDL_MULTIGESTURE = TSDL_EventType($802); + + { Clipboard events } + SDL_CLIPBOARDUPDATE = TSDL_EventType($900); // The clipboard changed + + { Drag and drop events } + SDL_DROPFILE = TSDL_EventType($1000); // The system requests a file open + SDL_DROPTEXT = TSDL_EventType($1001); // text/plain drag-and-drop event + SDL_DROPBEGIN = TSDL_EventType($1002); // A new set of drops is beginning (NULL filename) + SDL_DROPCOMPLETE = TSDL_EventType($1003); // Current set of drops is now complete (NULL filename) + + { Audio hotplug events } + SDL_AUDIODEVICEADDED = TSDL_EventType($1100); // A new audio device is available + SDL_AUDIODEVICEREMOVED = TSDL_EventType($1101); // An audio device has been removed. + + { Sensor events } + SDL_SENSORUPDATED = TSDL_EventType($1200); // A sensor was updated + + { Render events } + SDL_RENDER_TARGETS_RESET = TSDL_EventType($2000); // The render targets have been reset + SDL_RENDER_DEVICE_RESET = TSDL_EventType($2001); // The device has been reset and all textures need to be recreated + + { Internal events } + SDL_POLLSENTINEL = TSDL_EventType($7F00); // Signals the end of an event poll cycle + + {** Events SDL_USEREVENT through SDL_LASTEVENT are for your use, + * and should be allocated with SDL_RegisterEvents() + *} + SDL_USEREVENT = TSDL_EventType($8000); + + {** + * This last event is only for bounding internal arrays (needed in pascal ??) + *} + SDL_LASTEVENT = TSDL_EventType($FFFF); + +type + {** + * Fields shared by every event + *} + PPSDL_CommonEvent = ^PSDL_CommonEvent; + PSDL_CommonEvent = ^TSDL_CommonEvent; + TSDL_CommonEvent = record + type_: cuint32; + timestamp: cuint32; + end; + + {** + * Display state change event data (event.display.*) + *} + PPSDL_DisplayEvent = ^PSDL_DisplayEvent; + PSDL_DisplayEvent = ^TSDL_DisplayEvent; + TSDL_DisplayEvent = record + type_: cuint32; // SDL_DISPLAYEVENT + timestamp: cuint32; // In milliseconds, populated using SDL_GetTicks() + display: cuint32; // The associated display index + event: cuint8; // SDL_DisplayEventID + padding1: cuint8; + padding2: cuint8; + padding3: cuint8; + data1: cint32; // event dependent data + end; + + {** + * Window state change event data (event.window.*) + *} + PPSDL_WindowEvent = ^PSDL_WindowEvent; + PSDL_WindowEvent = ^TSDL_WindowEvent; + TSDL_WindowEvent = record + type_: cuint32; // SDL_WINDOWEVENT + timestamp: cuint32; + windowID: cuint32; // The associated window + event: cuint8; // SDL_WindowEventID + padding1: cuint8; + padding2: cuint8; + padding3: cuint8; + data1: cint32; // event dependent data + data2: cint32; // event dependent data + end; + + {** + * Keyboard button event structure (event.key.*) + *} + PPSDL_KeyboardEvent = ^PSDL_KeyboardEvent; + PSDL_KeyboardEvent = ^TSDL_KeyboardEvent; + TSDL_KeyboardEvent = record + type_: cuint32; // SDL_KEYDOWN or SDL_KEYUP + timestamp: cuint32; + windowID: cuint32; // The window with keyboard focus, if any + state: cuint8; // SDL_PRESSED or SDL_RELEASED + repeat_: cuint8; // Non-zero if this is a key repeat + padding2: cuint8; + padding3: cuint8; + keysym: TSDL_KeySym; // The key that was pressed or released + end; + +const + SDL_TEXTEDITINGEVENT_TEXT_SIZE = 32; + +type + {** + * Keyboard text editing event structure (event.edit.*) + *} + PPSDL_TextEditingEvent = ^PSDL_TextEditingEvent; + PSDL_TextEditingEvent = ^TSDL_TextEditingEvent; + TSDL_TextEditingEvent = record + type_: cuint32; // SDL_TEXTEDITING + timestamp: cuint32; + windowID: cuint32; // The window with keyboard focus, if any + text: array[0..SDL_TEXTEDITINGEVENT_TEXT_SIZE] of Char; // The editing text + start: cint32; // The start cursor of selected editing text + length: cint32; // The length of selected editing text + end; + + {** + * Extended keyboard text editing event structure (event.editExt.*) when text would be + * truncated if stored in the text buffer SDL_TextEditingEvent + *} + PPSDL_TextEditingExtEvent = ^PSDL_TextEditingExtEvent; + PSDL_TextEditingExtEvent = ^TSDL_TextEditingExtEvent; + TSDL_TextEditingExtEvent = record + type_: cuint32; // SDL_TEXTEDITING_EXT + timestamp: cuint32; // In milliseconds, populated using SDL_GetTicks() + windowID: cuint32; // The window with keyboard focus, if any + text: PAnsiChar; // The editing text, which should be freed with SDL_free(), and will not be NIL + start: cint32; // The start cursor of selected editing text + length: cint32; // The length of selected editing text + end; + +const + SDL_TEXTINPUTEVENT_TEXT_SIZE = 32; + +type + + {** + * Keyboard text input event structure (event.text.*) + *} + PPSDL_TextInputEvent = ^PSDL_TextInputEvent; + PSDL_TextInputEvent = ^TSDL_TextInputEvent; + TSDL_TextInputEvent = record + type_: cuint32; // SDL_TEXTINPUT + timestamp: cuint32; + windowID: cuint32; // The window with keyboard focus, if any + text: array[0..SDL_TEXTINPUTEVENT_TEXT_SIZE] of Char; // The input text + end; + + {** + * Mouse motion event structure (event.motion.*) + *} + PPSDL_MouseMotionEvent = ^PSDL_MouseMotionEvent; + PSDL_MouseMotionEvent = ^TSDL_MouseMotionEvent; + TSDL_MouseMotionEvent = record + type_: cuint32; // SDL_MOUSEMOTION + timestamp: cuint32; // In milliseconds, populated using SDL_GetTicks() + windowID: cuint32; // The window with mouse focus, if any + which: cuint32; // The mouse instance id, or SDL_TOUCH_MOUSEID + state: cuint32; // The current button state + x: cint32; // X coordinate, relative to window + y: cint32; // Y coordinate, relative to window + xrel: cint32; // The relative motion in the X direction + yrel: cint32; // The relative motion in the Y direction + end; + + {** + * Mouse button event structure (event.button.*) + *} + PPSDL_MouseButtonEvent = ^PSDL_MouseButtonEvent; + PSDL_MouseButtonEvent = ^TSDL_MouseButtonEvent; + TSDL_MouseButtonEvent = record + type_: cuint32; // SDL_MOUSEBUTTONDOWN or SDL_MOUSEBUTTONUP + timestamp: cuint32; + windowID: cuint32; // The window with mouse focus, if any + which: cuint32; // The mouse instance id, or SDL_TOUCH_MOUSEID + button: cuint8; // The mouse button index + state: cuint8; // SDL_PRESSED or SDL_RELEASED + clicks: cuint8; // 1 for single-click, 2 for double-click, etc. + padding1: cuint8; + x: cint32; // X coordinate, relative to window + y: cint32; // Y coordinate, relative to window + end; + + {** + * Mouse wheel event structure (event.wheel.*) + *} + PPSDL_MouseWheelEvent = ^PSDL_MouseWheelEvent; + PSDL_MouseWheelEvent = ^TSDL_MouseWheelEvent; + TSDL_MouseWheelEvent = record + type_: cuint32; // SDL_MOUSEWHEEL + timestamp: cuint32; + windowID: cuint32; // The window with mouse focus, if any + which: cuint32; // The mouse instance id, or SDL_TOUCH_MOUSEID + x: cint32; // The amount scrolled horizontally + y: cint32; // The amount scrolled vertically + direction: cuint32; // Set to one of the SDL_MOUSEWHEEL_* defines. When FLIPPED the values in X and Y will be opposite. Multiply by -1 to change them back + preciseX: cfloat; // The amount scrolled horizontally, positive to the right and negative to the left, with float precision (added in 2.0.18) + preciseY: cfloat; // The amount scrolled vertically, positive away from the user and negative toward the user, with float precision (added in 2.0.18) + mouseX: cint32; // X coordinate, relative to window (added in 2.26.0) + mouseY: cint32; // Y coordinate, relative to window (added in 2.26.0) + end; + + {** + * Joystick axis motion event structure (event.jaxis.*) + *} + PPSDL_JoyAxisEvent = ^PSDL_JoyAxisEvent; + PSDL_JoyAxisEvent = ^TSDL_JoyAxisEvent; + TSDL_JoyAxisEvent = record + type_: cuint32; // SDL_JOYAXISMOTION + timestamp: cuint32; + which: TSDL_JoystickID; // The joystick instance id + axis: cuint8; // The joystick axis index + padding1: cuint8; + padding2: cuint8; + padding3: cuint8; + value: cint16; // The axis value (range: -32768 to 32767) + padding4: cuint16; + end; + + {** + * Joystick trackball motion event structure (event.jball.*) + *} + PPSDL_JoyBallEvent = ^PSDL_JoyBallEvent; + PSDL_JoyBallEvent = ^TSDL_JoyBallEvent; + TSDL_JoyBallEvent = record + type_: cuint32; // SDL_JOYBALLMOTION + timestamp: cuint32; + which: TSDL_JoystickID; // The joystick instance id + ball: cuint8; // The joystick trackball index + padding1: cuint8; + padding2: cuint8; + padding3: cuint8; + xrel: cint16; // The relative motion in the X direction + yrel: cint16; // The relative motion in the Y direction + end; + + {** + * Joystick hat position change event structure (event.jhat.*) + *} + PPSDL_JoyHatEvent = ^PSDL_JoyHatEvent; + PSDL_JoyHatEvent = ^TSDL_JoyHatEvent; + TSDL_JoyHatEvent = record + type_: cuint32; // SDL_JOYHATMOTION + timestamp: cuint32; + which: TSDL_JoystickID; // The joystick instance id + hat: cuint8; // The joystick hat index + value: cuint8; {* The hat position value. + * SDL_HAT_LEFTUP SDL_HAT_UP SDL_HAT_RIGHTUP + * SDL_HAT_LEFT SDL_HAT_CENTERED SDL_HAT_RIGHT + * SDL_HAT_LEFTDOWN SDL_HAT_DOWN SDL_HAT_RIGHTDOWN + * + * Note that zero means the POV is centered. + *} + padding1: cuint8; + padding2: cuint8; + end; + + {** + * Joystick button event structure (event.jbutton.*) + *} + PPSDL_JoyButtonEvent = ^PSDL_JoyButtonEvent; + PSDL_JoyButtonEvent = ^TSDL_JoyButtonEvent; + TSDL_JoyButtonEvent = record + type_: cuint32; // SDL_JOYBUTTONDOWN or SDL_JOYBUTTONUP + timestamp: cuint32; + which: TSDL_JoystickID; // The joystick instance id + button: cuint8; // The joystick button index + state: cuint8; // SDL_PRESSED or SDL_RELEASED + padding1: cuint8; + padding2: cuint8; + end; + + {** + * Joystick device event structure (event.jdevice.*) + *} + PPSDL_JoyDeviceEvent = ^PSDL_JoyDeviceEvent; + PSDL_JoyDeviceEvent = ^TSDL_JoyDeviceEvent; + TSDL_JoyDeviceEvent = record + type_: cuint32; // SDL_JOYDEVICEADDED or SDL_JOYDEVICEREMOVED + timestamp: cuint32; + which: cint32; // The joystick device index for the ADDED event, instance id for the REMOVED event + end; + + {** + * Joysick battery level change event structure (event.jbattery.*) + *} + PPSDL_JoyBatteryEvent = ^PSDL_JoyBatteryEvent; + PSDL_JoyBatteryEvent = ^TSDL_JoyBatteryEvent; + TSDL_JoyBatteryEvent = record + type_: cuint32; // SDL_JOYBATTERYUPDATED + timestamp: cuint32; // In milliseconds, populated using SDL_GetTicks() + which: TSDL_JoystickID; // The joystick instance id + level: TSDL_JoystickPowerLevel; // The joystick battery level + end; + + {** + * Game controller axis motion event structure (event.caxis.*) + *} + PPSDL_ControllerAxisEvent = ^PSDL_ControllerAxisEvent; + PSDL_ControllerAxisEvent = ^TSDL_ControllerAxisEvent; + TSDL_ControllerAxisEvent = record + type_: cuint32; // SDL_CONTROLLERAXISMOTION + timestamp: cuint32; + which: TSDL_JoystickID; // The joystick instance id + axis: cuint8; // The controller axis (SDL_GameControllerAxis) + padding1: cuint8; + padding2: cuint8; + padding3: cuint8; + value: cint16; // The axis value (range: -32768 to 32767) + padding4: cuint16; + end; + + {** + * Game controller button event structure (event.cbutton.*) + *} + PPSDL_ControllerButtonEvent = ^PSDL_ControllerButtonEvent; + PSDL_ControllerButtonEvent = ^TSDL_ControllerButtonEvent; + TSDL_ControllerButtonEvent = record + type_: cuint32; // SDL_CONTROLLERBUTTONDOWN or SDL_CONTROLLERBUTTONUP + timestamp: cuint32; + which: TSDL_JoystickID; // The joystick instance id + button: cuint8; // The controller button (SDL_GameControllerButton) + state: cuint8; // SDL_PRESSED or SDL_RELEASED + padding1: cuint8; + padding2: cuint8; + end; + + + {** + * Controller device event structure (event.cdevice.*) + *} + PPSDL_ControllerDeviceEvent = ^PSDL_ControllerDeviceEvent; + PSDL_ControllerDeviceEvent = ^TSDL_ControllerDeviceEvent; + TSDL_ControllerDeviceEvent = record + type_: cuint32; // SDL_CONTROLLERDEVICEADDED, SDL_CONTROLLERDEVICEREMOVED, or SDL_CONTROLLERDEVICEREMAPPED + timestamp: cuint32; + which: cint32; // The joystick device index for the ADDED event, instance id for the REMOVED or REMAPPED event + end; + + {** + * Game controller touchpad event structure (event.ctouchpad.*) + *} + PPSDL_ControllerTouchpadEvent = ^PSDL_ControllerTouchpadEvent; + PSDL_ControllerTouchpadEvent = ^TSDL_ControllerTouchpadEvent; + TSDL_ControllerTouchpadEvent = record + type_: cuint32; // SDL_CONTROLLERTOUCHPADDOWN or SDL_CONTROLLERTOUCHPADMOTION or SDL_CONTROLLERTOUCHPADUP + timestamp: cuint32; // In milliseconds, populated using SDL_GetTicks() + which: TSDL_JoystickID; // The joystick instance id + touchpad: cint32; // The index of the touchpad + finger: cint32; // The index of the finger on the touchpad + x: cfloat; // Normalized in the range 0...1 with 0 being on the left + y: cfloat; // Normalized in the range 0...1 with 0 being at the top + pressure: cfloat; // Normalized in the range 0...1 + end; + + {** + * Game controller sensor event structure (event.csensor.*) + *} + PPSDL_ControllerSensorEvent = ^PSDL_ControllerSensorEvent; + PSDL_ControllerSensorEvent = ^TSDL_ControllerSensorEvent; + TSDL_ControllerSensorEvent = record + type_: cuint32; // SDL_CONTROLLERSENSORUPDATE + timestamp: cuint32; // In milliseconds, populated using SDL_GetTicks() + which: TSDL_JoystickID; // The joystick instance id + sensor: cint32; // The type of the sensor, one of the values of SDL_SensorType + data: array[0..2] of cfloat; // Up to 3 values from the sensor, as defined in SDL_sensor.h + end; + + {** + * Audio device event structure (event.adevice.*) + *} + PPSDL_AudioDeviceEvent = ^PSDL_AudioDeviceEvent; + PSDL_AudioDeviceEvent = ^TSDL_AudioDeviceEvent; + TSDL_AudioDeviceEvent = record + type_: cuint32; // ::SDL_AUDIODEVICEADDED, or ::SDL_AUDIODEVICEREMOVED + timestamp: cuint32; + which: cuint32; // The audio device index for the ADDED event (valid until next SDL_GetNumAudioDevices() call), SDL_AudioDeviceID for the REMOVED event + iscapture: cuint8; // zero if an output device, non-zero if a capture device. + padding1: cuint8; + padding2: cuint8; + padding3: cuint8; + end; + + + {** + * Touch finger event structure (event.tfinger.*) + *} + PPSDL_TouchFingerEvent = ^PSDL_TouchFingerEvent; + PSDL_TouchFingerEvent = ^TSDL_TouchFingerEvent; + TSDL_TouchFingerEvent = record + type_: cuint32; // SDL_FINGERMOTION or SDL_FINGERDOWN or SDL_FINGERUP + timestamp: cuint32; + touchId: TSDL_TouchID; // The touch device id + fingerId: TSDL_FingerID; + x: cfloat; // Normalized in the range 0...1 + y: cfloat; // Normalized in the range 0...1 + dx: cfloat; // Normalized in the range 0...1 + dy: cfloat; // Normalized in the range 0...1 + pressure: cfloat; // Normalized in the range 0...1 + window: cuint32; // The window underneath the finger, if any + end; + + {** + * Multiple Finger Gesture Event (event.mgesture.*) + *} + PPSDL_MultiGestureEvent = ^PSDL_MultiGestureEvent; + PSDL_MultiGestureEvent = ^TSDL_MultiGestureEvent; + TSDL_MultiGestureEvent = record + type_: cuint32; // SDL_MULTIGESTURE + timestamp: cuint32; + touchId: TSDL_TouchID; // The touch device index + dTheta: cfloat; + dDist: cfloat; + x: cfloat; + y: cfloat; + numFingers: cuint16; + padding: cuint16; + end; + + + {* (event.dgesture.*) *} + PPSDL_DollarGestureEvent = ^PSDL_DollarGestureEvent; + PSDL_DollarGestureEvent = ^TSDL_DollarGestureEvent; + TSDL_DollarGestureEvent = record + type_: cuint32; // SDL_DOLLARGESTURE + timestamp: cuint32; + touchId: TSDL_TouchID; // The touch device id + gestureId: TSDL_GestureID; + numFingers: cuint32; + error: cfloat; + x: cfloat; // Normalized center of gesture + y: cfloat; // Normalized center of gesture + end; + + + {** + * An event used to request a file open by the system (event.drop.*) + * This event is disabled by default, you can enable it with SDL_EventState() + * If you enable this event, you must free the filename in the event. + *} + PPSDL_DropEvent = ^PSDL_DropEvent; + PSDL_DropEvent = ^TSDL_DropEvent; + TSDL_DropEvent = record + type_: cuint32; // SDL_DROPBEGIN or SDL_DROPFILE or SDL_DROPTEXT or SDL_DROPCOMPLETE + timestamp: cuint32; + file_: PAnsiChar; // The file name, which should be freed with SDL_free(), is NIL on begin/complete + windowID: cuint32; // The window that was dropped on, if any + end; + + {** + * Sensor event structure (event.sensor.*) + *} + PPSDL_SensorEvent = ^PSDL_SensorEvent; + PSDL_SensorEvent = ^TSDL_SensorEvent; + TSDL_SensorEvent = record + type_: cuint32; // SDL_SENSORUPDATED + timestamp: cuint32; // In milliseconds, populated using SDL_GetTicks() + which: cint32; // The instance ID of the sensor + data: array[0..5] of cfloat; // Up to 6 values from the sensor - additional values can be queried using SDL_SensorGetData() + end; + + {** + * The "quit requested" event + *} + PPSDL_QuitEvent = ^PSDL_QuitEvent; + PSDL_QuitEvent = ^TSDL_QuitEvent; + TSDL_QuitEvent = record + type_: cuint32; // SDL_QUIT + timestamp: cuint32; + end; + + {** + * A user-defined event type (event.user.*) + *} + PPSDL_UserEvent = ^PSDL_UserEvent; + PSDL_UserEvent = ^TSDL_UserEvent; + TSDL_UserEvent = record + type_: cuint32; // SDL_USEREVENT through SDL_NUMEVENTS-1 + timestamp: cuint32; + windowID: cuint32; // The associated window if any + code: cint32; // User defined event code + data1: Pointer; // User defined data pointer + data2: Pointer; // User defined data pointer + end; + + PSDL_SysWMmsg=Pointer; + + {** + * A video driver dependent system event (event.syswm.*) + * This event is disabled by default, you can enable it with SDL_EventState() + * + * If you want to use this event, you should include SDL_syswm.h. + *} + PPSDL_SysWMEvent = ^PSDL_SysWMEvent; + PSDL_SysWMEvent = ^TSDL_SysWMEvent; + TSDL_SysWMEvent = record + type_: cuint32; // SDL_SYSWMEVENT + timestamp: cuint32; + msg: PSDL_SysWMmsg; // driver dependent data (defined in SDL_syswm.h) + end; + + {** + * General event structure + *} + PPSDL_Event = ^PSDL_Event; + PSDL_Event = ^TSDL_Event; + TSDL_Event = record + case cint of + 0: (type_: cuint32); + + SDL_COMMONEVENT: (common: TSDL_CommonEvent); + SDL_DISPLAYEVENT: (display: TSDL_DisplayEvent); + SDL_WINDOWEVENT: (window: TSDL_WindowEvent); + + SDL_KEYUP, + SDL_KEYDOWN: (key: TSDL_KeyboardEvent); + SDL_TEXTEDITING: (edit: TSDL_TextEditingEvent); + SDL_TEXTEDITING_EXT: (exitExt: TSDL_TextEditingExtEvent); + SDL_TEXTINPUT: (text: TSDL_TextInputEvent); + + SDL_MOUSEMOTION: (motion: TSDL_MouseMotionEvent); + SDL_MOUSEBUTTONUP, + SDL_MOUSEBUTTONDOWN: (button: TSDL_MouseButtonEvent); + SDL_MOUSEWHEEL: (wheel: TSDL_MouseWheelEvent); + + SDL_JOYAXISMOTION: (jaxis: TSDL_JoyAxisEvent); + SDL_JOYBALLMOTION: (jball: TSDL_JoyBallEvent); + SDL_JOYHATMOTION: (jhat: TSDL_JoyHatEvent); + SDL_JOYBUTTONDOWN, + SDL_JOYBUTTONUP: (jbutton: TSDL_JoyButtonEvent); + SDL_JOYDEVICEADDED, + SDL_JOYDEVICEREMOVED: (jdevice: TSDL_JoyDeviceEvent); + SDL_JOYBATTERYUPDATED: (jbattery: TSDL_JoyBatteryEvent); + + SDL_CONTROLLERAXISMOTION: (caxis: TSDL_ControllerAxisEvent); + SDL_CONTROLLERBUTTONUP, + SDL_CONTROLLERBUTTONDOWN: (cbutton: TSDL_ControllerButtonEvent); + SDL_CONTROLLERDEVICEADDED, + SDL_CONTROLLERDEVICEREMOVED, + SDL_CONTROLLERDEVICEREMAPPED: (cdevice: TSDL_ControllerDeviceEvent); + SDL_CONTROLLERTOUCHPADDOWN, + SDL_CONTROLLERTOUCHPADMOTION, + SDL_CONTROLLERTOUCHPADUP: (ctouchpad: TSDL_ControllerTouchpadEvent); + SDL_CONTROLLERSENSORUPDATE: (csensor: TSDL_ControllerSensorEvent); + + SDL_AUDIODEVICEADDED, + SDL_AUDIODEVICEREMOVED: (adevice: TSDL_AudioDeviceEvent); + + SDL_SENSORUPDATED: (sensor: TSDL_SensorEvent); + + SDL_QUITEV: (quit: TSDL_QuitEvent); + + SDL_USEREVENT: (user: TSDL_UserEvent); + SDL_SYSWMEVENT: (syswm: TSDL_SysWMEvent); + + SDL_FINGERDOWN, + SDL_FINGERUP, + SDL_FINGERMOTION: (tfinger: TSDL_TouchFingerEvent); + SDL_MULTIGESTURE: (mgesture: TSDL_MultiGestureEvent); + SDL_DOLLARGESTURE,SDL_DOLLARRECORD: (dgesture: TSDL_DollarGestureEvent); + + SDL_DROPFILE: (drop: TSDL_DropEvent); + end; + +/// + +function SDL_InitSubSystem(flags: TSDL_Init): cint; cdecl; +procedure SDL_QuitSubSystem(flags: TSDL_Init); cdecl; + +var + SDL_PollEvent:function(event: PSDL_Event): cint32 cdecl; + + SDL_LockJoysticks :procedure(); cdecl; + SDL_UnlockJoysticks:procedure(); cdecl; + SDL_NumJoysticks :function(): cint; cdecl; + + SDL_JoystickGetDeviceGUID:function(device_index: cint): TSDL_JoystickGUID; cdecl; + SDL_JoystickGetGUID :function(joystick: PSDL_Joystick): TSDL_JoystickGUID; cdecl; + +//// + SDL_IsGameController :function(joystick_index: cint): Boolean cdecl; + SDL_GameControllerOpen :function(joystick_index: cint): PSDL_GameController cdecl; + SDL_GameControllerClose:procedure(gamecontroller: PSDL_GameController) cdecl; + + SDL_GameControllerGetJoystick:function(gamecontroller: PSDL_GameController): PSDL_Joystick cdecl; + SDL_GameControllerGetAttached:function(gamecontroller: PSDL_GameController): Boolean cdecl; +// + SDL_GameControllerNumMappings:function():cint; cdecl; + SDL_GameControllerName:function(gamecontroller: PSDL_GameController): PAnsiChar cdecl; + SDL_GameControllerGetVendor:function(gamecontroller: PSDL_GameController): cuint16; cdecl; + SDL_GameControllerGetProduct:function(gamecontroller: PSDL_GameController): cuint16; cdecl; + SDL_GameControllerGetProductVersion:function(gamecontroller: PSDL_GameController): cuint16; cdecl; + SDL_GameControllerGetFirmwareVersion:function(gamecontroller: PSDL_GameController): cuint16; cdecl; + SDL_GameControllerGetSerial:function(gamecontroller: PSDL_GameController): PAnsiChar; cdecl; + SDL_GameControllerPath:function(gamecontroller: PSDL_GameController): PAnsiChar; cdecl; + SDL_GameControllerHasRumble:function(gamecontroller: PSDL_GameController): Boolean; cdecl; + SDL_GameControllerGetButton:function(gamecontroller: PSDL_GameController; button: TSDL_GameControllerButton): cuint8 cdecl; + SDL_GameControllerGetNumTouchpads:function(gamecontroller: PSDL_GameController): cint; cdecl; + SDL_GameControllerGetNumTouchpadFingers:function(gamecontroller: PSDL_GameController; touchpad: cint): cint; cdecl; + + SDL_GameControllerGetTouchpadFinger:function( + gamecontroller: PSDL_GameController; + touchpad, finger: cint; + state: pcuint8; + x, y, pressure: pcfloat + ): cint; cdecl; + + SDL_GameControllerGetAxis:function(gamecontroller: PSDL_GameController; axis: TSDL_GameControllerAxis): cint16 cdecl; + + SDL_GameControllerRumble:function( + gamecontroller: PSDL_GameController; + low_frequency_rumble, high_frequency_rumble: cuint16; + duration_ms: cuint32 + ): cint; cdecl; + + SDL_GameControllerHasRumbleTriggers:function(gamecontroller: PSDL_GameController): Boolean; cdecl; + + SDL_GameControllerRumbleTriggers:function( + gamecontroller: PSDL_GameController; + left_rumble, right_rumble: cuint16; + duration_ms: cuint32 + ): cint; cdecl; + + SDL_GameControllerHasLED:function(gamecontroller: PSDL_GameController): Boolean; cdecl; + SDL_GameControllerSetLED:function(gamecontroller: PSDL_GameController; red, green, blue: cuint8): cint; cdecl; + +implementation + +var + init_flags:TSDL_Init=0; + lib_handle:TLibHandle=NilHandle; + + /// + _SDL_InitSubSystem:function (flags: TSDL_Init): cint; cdecl; + _SDL_QuitSubSystem:procedure(flags: TSDL_Init); cdecl; + /// + +function SDL_InitSubSystem(flags: TSDL_Init): cint; cdecl; +begin + if (lib_handle=NilHandle) then + begin + lib_handle:=SafeLoadLibrary(SDL_LibName); + if (lib_handle=NilHandle) then Exit(-1); + end; + + Pointer(_SDL_InitSubSystem):=GetProcedureAddress(lib_handle,'SDL_InitSubSystem'); + Pointer(_SDL_QuitSubSystem):=GetProcedureAddress(lib_handle,'SDL_QuitSubSystem'); + Pointer(SDL_PollEvent) :=GetProcedureAddress(lib_handle,'SDL_PollEvent'); + + if (_SDL_InitSubSystem=nil) then + begin + UnloadLibrary(lib_handle); + lib_handle:=NilHandle; + init_flags:=0; + Exit(-1); + end; + + Result:=_SDL_InitSubSystem(flags); + if (Result<>0) then Exit; + + init_flags:=init_flags or flags; + + if ((flags and SDL_INIT_JOYSTICK)<>0) then + begin + Pointer(SDL_LockJoysticks ):=GetProcedureAddress(lib_handle,'SDL_LockJoysticks'); + Pointer(SDL_UnlockJoysticks ):=GetProcedureAddress(lib_handle,'SDL_UnlockJoysticks'); + Pointer(SDL_NumJoysticks ):=GetProcedureAddress(lib_handle,'SDL_NumJoysticks'); + Pointer(SDL_JoystickGetDeviceGUID):=GetProcedureAddress(lib_handle,'SDL_JoystickGetDeviceGUID'); + Pointer(SDL_JoystickGetGUID ):=GetProcedureAddress(lib_handle,'SDL_JoystickGetGUID'); + end; + + if ((flags and SDL_INIT_GAMECONTROLLER)<>0) then + begin + Pointer(SDL_IsGameController ):=GetProcedureAddress(lib_handle,'SDL_IsGameController'); + Pointer(SDL_GameControllerOpen ):=GetProcedureAddress(lib_handle,'SDL_GameControllerOpen'); + Pointer(SDL_GameControllerClose ):=GetProcedureAddress(lib_handle,'SDL_GameControllerClose'); + Pointer(SDL_GameControllerGetJoystick ):=GetProcedureAddress(lib_handle,'SDL_GameControllerGetJoystick'); + Pointer(SDL_GameControllerGetAttached ):=GetProcedureAddress(lib_handle,'SDL_GameControllerGetAttached'); + Pointer(SDL_GameControllerNumMappings ):=GetProcedureAddress(lib_handle,'SDL_GameControllerNumMappings'); + Pointer(SDL_GameControllerName ):=GetProcedureAddress(lib_handle,'SDL_GameControllerName'); + Pointer(SDL_GameControllerGetVendor ):=GetProcedureAddress(lib_handle,'SDL_GameControllerGetVendor'); + Pointer(SDL_GameControllerGetProduct ):=GetProcedureAddress(lib_handle,'SDL_GameControllerGetProduct'); + Pointer(SDL_GameControllerGetProductVersion ):=GetProcedureAddress(lib_handle,'SDL_GameControllerGetProductVersion'); + Pointer(SDL_GameControllerGetFirmwareVersion ):=GetProcedureAddress(lib_handle,'SDL_GameControllerGetFirmwareVersion'); + Pointer(SDL_GameControllerGetSerial ):=GetProcedureAddress(lib_handle,'SDL_GameControllerGetSerial'); + Pointer(SDL_GameControllerPath ):=GetProcedureAddress(lib_handle,'SDL_GameControllerPath'); + Pointer(SDL_GameControllerHasRumble ):=GetProcedureAddress(lib_handle,'SDL_GameControllerHasRumble'); + Pointer(SDL_GameControllerGetButton ):=GetProcedureAddress(lib_handle,'SDL_GameControllerGetButton'); + Pointer(SDL_GameControllerGetNumTouchpads ):=GetProcedureAddress(lib_handle,'SDL_GameControllerGetNumTouchpads'); + Pointer(SDL_GameControllerGetNumTouchpadFingers):=GetProcedureAddress(lib_handle,'SDL_GameControllerGetNumTouchpadFingers'); + Pointer(SDL_GameControllerGetTouchpadFinger ):=GetProcedureAddress(lib_handle,'SDL_GameControllerGetTouchpadFinger'); + Pointer(SDL_GameControllerGetAxis ):=GetProcedureAddress(lib_handle,'SDL_GameControllerGetAxis'); + Pointer(SDL_GameControllerRumble ):=GetProcedureAddress(lib_handle,'SDL_GameControllerRumble'); + Pointer(SDL_GameControllerHasRumbleTriggers ):=GetProcedureAddress(lib_handle,'SDL_GameControllerHasRumbleTriggers'); + Pointer(SDL_GameControllerRumbleTriggers ):=GetProcedureAddress(lib_handle,'SDL_GameControllerRumbleTriggers'); + Pointer(SDL_GameControllerHasLED ):=GetProcedureAddress(lib_handle,'SDL_GameControllerHasLED'); + Pointer(SDL_GameControllerSetLED ):=GetProcedureAddress(lib_handle,'SDL_GameControllerSetLED'); + end; +end; + +procedure SDL_QuitSubSystem(flags: TSDL_Init); cdecl; +begin + if (lib_handle=NilHandle) then Exit; + + init_flags:=init_flags and (not flags); + + if (_SDL_QuitSubSystem<>nil) then + begin + _SDL_QuitSubSystem(flags); + end; + + if (init_flags=0) then + begin + UnloadLibrary(lib_handle); + lib_handle:=NilHandle; + + Pointer(_SDL_InitSubSystem):=nil; + Pointer(_SDL_QuitSubSystem):=nil; + Pointer(SDL_PollEvent) :=nil; + end; + + if ((flags and SDL_INIT_JOYSTICK)<>0) then + begin + Pointer(SDL_LockJoysticks ):=nil; + Pointer(SDL_UnlockJoysticks ):=nil; + Pointer(SDL_NumJoysticks ):=nil; + Pointer(SDL_JoystickGetDeviceGUID):=nil; + Pointer(SDL_JoystickGetGUID ):=nil; + end; + + if ((flags and SDL_INIT_GAMECONTROLLER)<>0) then + begin + Pointer(SDL_IsGameController ):=nil; + Pointer(SDL_GameControllerOpen ):=nil; + Pointer(SDL_GameControllerClose ):=nil; + Pointer(SDL_GameControllerGetJoystick ):=nil; + Pointer(SDL_GameControllerGetAttached ):=nil; + Pointer(SDL_GameControllerNumMappings ):=nil; + Pointer(SDL_GameControllerName ):=nil; + Pointer(SDL_GameControllerGetVendor ):=nil; + Pointer(SDL_GameControllerGetProduct ):=nil; + Pointer(SDL_GameControllerGetProductVersion ):=nil; + Pointer(SDL_GameControllerGetFirmwareVersion ):=nil; + Pointer(SDL_GameControllerGetSerial ):=nil; + Pointer(SDL_GameControllerPath ):=nil; + Pointer(SDL_GameControllerHasRumble ):=nil; + Pointer(SDL_GameControllerGetButton ):=nil; + Pointer(SDL_GameControllerGetNumTouchpads ):=nil; + Pointer(SDL_GameControllerGetNumTouchpadFingers):=nil; + Pointer(SDL_GameControllerGetTouchpadFinger ):=nil; + Pointer(SDL_GameControllerGetAxis ):=nil; + Pointer(SDL_GameControllerRumble ):=nil; + Pointer(SDL_GameControllerHasRumbleTriggers ):=nil; + Pointer(SDL_GameControllerRumbleTriggers ):=nil; + Pointer(SDL_GameControllerHasLED ):=nil; + Pointer(SDL_GameControllerSetLED ):=nil; + end; +end; + + + +end. + diff --git a/src/inputs/sdl2_pad_interface.pas b/src/inputs/sdl2_pad_interface.pas new file mode 100644 index 00000000..183a2190 --- /dev/null +++ b/src/inputs/sdl2_pad_interface.pas @@ -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. + diff --git a/src/inputs/umappableinputs.pas b/src/inputs/umappableinputs.pas index 2b244701..d4513239 100644 --- a/src/inputs/umappableinputs.pas +++ b/src/inputs/umappableinputs.pas @@ -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. + + diff --git a/src/inputs/xinput.pas b/src/inputs/xinput.pas index 799742cf..27d8de68 100644 --- a/src/inputs/xinput.pas +++ b/src/inputs/xinput.pas @@ -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. + + diff --git a/src/inputs/xinput_pad_interface.pas b/src/inputs/xinput_pad_interface.pas new file mode 100644 index 00000000..293ba023 --- /dev/null +++ b/src/inputs/xinput_pad_interface.pas @@ -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. + + + diff --git a/src/ps4_libscepad.pas b/src/ps4_libscepad.pas index 6f90aa62..c0bdc763 100644 --- a/src/ps4_libscepad.pas +++ b/src/ps4_libscepad.pas @@ -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);