{
Copyright (C) 2022 furious programming
This joystick tester is a translation to the Free Pascal language.
Original code is written in C language by Sam Lantinga:
<https://fossies.org/linux/SDL2/test/testjoystick.c>
This tester program uses slightly modified Pascal headers ("SDL-for-Pascal")
developed by "PascalGameDevelopment" and others:
<https://github.com/PascalGameDevelopment/SDL2-for-Pascal>
Tested on Windows 10 64-bit, using "SDL2.dll' library version 2.0.20.0 and
with the "8bitdo SN30 Pro" wired controller. Everything works perfectly.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely.
}
{$MODE OBJFPC}{$LONGSTRINGS ON}
{ Simple program to test the SDL joystick routines }
uses
SDL2;
const
SCREEN_WIDTH = 640;
SCREEN_HEIGHT = 480;
var
window: PSDL_Window = nil;
screen: PSDL_Renderer = nil;
joystick: PSDL_Joystick = nil;
done: TSDL_Bool = SDL_FALSE;
function IfThen(Condition: Boolean; IfTrue, IfFalse: PAnsiChar): PAnsiChar;
begin
if Condition then
Result := IfTrue
else
Result := IfFalse;
end;
procedure PrintJoystick(joy: PSDL_Joystick);
var
type_: PAnsiChar;
guid: array [0 .. 63] of AnsiChar;
begin
Assert(SDL_JoystickFromInstanceID(SDL_JoystickInstanceID(joy)) = joy);
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joy), guid, SizeOf(guid));
case SDL_JoystickGetType(joy) of
SDL_JOYSTICK_TYPE_GAMECONTROLLER: type_ := 'Game Controller';
SDL_JOYSTICK_TYPE_WHEEL: type_ := 'Wheel';
SDL_JOYSTICK_TYPE_ARCADE_STICK: type_ := 'Arcade Stick';
SDL_JOYSTICK_TYPE_FLIGHT_STICK: type_ := 'Flight Stick';
SDL_JOYSTICK_TYPE_DANCE_PAD: type_ := 'Dance Pad';
SDL_JOYSTICK_TYPE_GUITAR: type_ := 'Guitar';
SDL_JOYSTICK_TYPE_DRUM_KIT: type_ := 'Drum Kit';
SDL_JOYSTICK_TYPE_ARCADE_PAD: type_ := 'Arcade Pad';
SDL_JOYSTICK_TYPE_THROTTLE: type_ := 'Throttle';
otherwise
type_ := 'Unknown';
end;
SDL_Log('Joystick', []);
SDL_Log(' name: %s', [SDL_JoystickName(joy)]);
SDL_Log(' type: %s', [type_]);
SDL_Log(' LED: %s', [IfThen(SDL_JoystickHasLED(joy) = SDL_TRUE, 'yes', 'no')]);
SDL_Log(' rumble: %s', [IfThen(SDL_JoystickHasRumble(joy) = SDL_TRUE, 'yes', 'no')]);
SDL_Log('trigger rumble: %s', [IfThen(SDL_JoystickHasRumbleTriggers(joy) = SDL_TRUE, 'yes', 'no')]);
SDL_Log(' axes: %d', [SDL_JoystickNumAxes(joy)]);
SDL_Log(' balls: %d', [SDL_JoystickNumBalls(joy)]);
SDL_Log(' hats: %d', [SDL_JoystickNumHats(joy)]);
SDL_Log(' buttons: %d', [SDL_JoystickNumButtons(joy)]);
SDL_Log(' instance id: %d', [SDL_JoystickInstanceID(joy)]);
SDL_Log(' guid: %s', [guid]);
SDL_Log(' VID/PID: 0x%.4x/0x%.4x', [SDL_JoystickGetVendor(joy), SDL_JoystickGetProduct(joy)]);
end;
procedure DrawRect(r: PSDL_Renderer; x, y, w, h: Integer);
var
area: TSDL_Rect;
begin
area.x := x;
area.y := y;
area.w := w;
area.h := h;
SDL_RenderFillRect(r, @area);
end;
procedure Loop();
var
event: TSDL_Event;
i, x, y: Integer;
hat_pos: UInt8;
begin
{ blank screen, set up for drawing this frame. }
SDL_SetRenderDrawColor(screen, $00, $00, $00, SDL_ALPHA_OPAQUE);
SDL_RenderClear(screen);
while SDL_PollEvent(@event) = 1 do
case event.type_ of
SDL_JOYDEVICEADDED:
begin
SDL_Log('Joystick device %d added.', [event.jdevice.which]);
if (joystick = nil) then
begin
joystick := SDL_JoystickOpen(event.jdevice.which);
if (joystick <> nil) then
PrintJoystick(joystick)
else
SDL_Log('Couldn''t open joystick: %s', [SDL_GetError()]);
end;
end;
SDL_JOYDEVICEREMOVED:
begin
SDL_Log('Joystick device %d removed.', [event.jdevice.which]);
if (event.jdevice.which = SDL_JoystickInstanceID(joystick)) then
begin
SDL_JoystickClose(joystick);
joystick := SDL_JoystickOpen(0);
end;
end;
SDL_JOYAXISMOTION:
SDL_Log(
'Joystick %d axis %d value: %d', [
event.jaxis.which,
event.jaxis.axis,
event.jaxis.value
]
);
SDL_JOYHATMOTION:
begin
SDL_Log('Joystick %d hat %d value:', [event.jhat.which, event.jhat.hat]);
if (event.jhat.value = SDL_HAT_CENTERED) then SDL_Log(' centered', []);
if (event.jhat.value and SDL_HAT_UP <> 0) then SDL_Log(' up', []);
if (event.jhat.value and SDL_HAT_RIGHT <> 0) then SDL_Log(' right', []);
if (event.jhat.value and SDL_HAT_DOWN <> 0) then SDL_Log(' down', []);
if (event.jhat.value and SDL_HAT_LEFT <> 0) then SDL_Log(' left', []);
SDL_Log('', []);
end;
SDL_JOYBALLMOTION:
SDL_Log(
'Joystick %d ball %d delta: (%d,%d)', [
event.jball.which,
event.jball.ball,
event.jball.xrel,
event.jball.yrel
]
);
SDL_JOYBUTTONDOWN:
begin
SDL_Log('Joystick %d button %d down', [event.jbutton.which, event.jbutton.button]);
{ First button triggers a 0.5 second full strength rumble }
if (event.jbutton.button = 0) then
SDL_JoystickRumble(joystick, $FFFF, $FFFF, 500);
end;
SDL_JOYBUTTONUP:
SDL_Log('Joystick %d button %d up', [event.jbutton.which, event.jbutton.button]);
SDL_KEYDOWN:
begin
{
Press the L key to lag for 3 seconds, to see what happens
when SDL doesn't service the event loop quickly.
}
if (event.key.keysym.sym = SDLK_l) then
begin
SDL_Log('Lagging for 3 seconds...', []);
SDL_Delay(3000);
end
else
if ((event.key.keysym.sym = SDLK_ESCAPE) or (event.key.keysym.sym = SDLK_AC_BACK)) then
done := SDL_TRUE;
end;
end;
if joystick <> nil then
begin
{ Update visual joystick state }
SDL_SetRenderDrawColor(screen, $00, $FF, $00, SDL_ALPHA_OPAQUE);
for i := 0 to SDL_JoystickNumButtons(joystick) - 1 do
if (SDL_JoystickGetButton(joystick, i) = SDL_PRESSED) then
DrawRect(screen, (i mod 20) * 34, SCREEN_HEIGHT - 68 + (i div 20) * 34, 32, 32);
SDL_SetRenderDrawColor(screen, $FF, $00, $00, SDL_ALPHA_OPAQUE);
i := 0;
while i < SDL_JoystickNumAxes(joystick) do
begin
{ Draw the X/Y axis }
x := SDL_JoystickGetAxis(joystick, i) + 32768;
x *= SCREEN_WIDTH;
x := x div 65535;
if x < 0 then
x := 0
else
if x > SCREEN_WIDTH - 16 then
x := SCREEN_WIDTH - 16;
i += 1;
if i < SDL_JoystickNumAxes(joystick) then
y := SDL_JoystickGetAxis(joystick, i) + 32768
else
y := 32768;
y *= SCREEN_HEIGHT;
y := y div 65535;
if y < 0 then
y := 0
else
if y > SCREEN_HEIGHT - 16 then
y := SCREEN_HEIGHT - 16;
DrawRect(screen, x, y, 16, 16);
i += 1;
end;
SDL_SetRenderDrawColor(screen, $00, $00, $FF, SDL_ALPHA_OPAQUE);
for i := 0 to SDL_JoystickNumHats(joystick) - 1 do
begin
{ Derive the new position }
x := SCREEN_WIDTH div 2;
y := SCREEN_HEIGHT div 2;
hat_pos := SDL_JoystickGetHat(joystick, i);
if hat_pos and SDL_HAT_UP <> 0 then
y := 0
else
if hat_pos and SDL_HAT_DOWN <> 0 then
y := SCREEN_HEIGHT - 8;
if hat_pos and SDL_HAT_LEFT <> 0 then
x := 0
else
if hat_pos and SDL_HAT_RIGHT <> 0 then
x := SCREEN_WIDTH - 8;
DrawRect(screen, x, y, 8, 8);
end;
end;
SDL_RenderPresent(screen);
end;
begin
SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, '0');
{ Enable standard application logging }
SDL_LogSetPriority(Ord(SDL_LOG_CATEGORY_APPLICATION), SDL_LOG_PRIORITY_INFO);
{ Initialize SDL (Note: video is required to start event loop) }
if SDL_Init(SDL_INIT_VIDEO or SDL_INIT_JOYSTICK) < 0 then
begin
SDL_LogError(Ord(SDL_LOG_CATEGORY_APPLICATION), 'Couldn''t initialize SDL: %s', [SDL_GetError()]);
Halt(1);
end;
{ Create a window to display joystick axis position }
window := SDL_CreateWindow('Joystick Test', SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH,
SCREEN_HEIGHT, 0);
if window = nil then
begin
SDL_LogError(Ord(SDL_LOG_CATEGORY_APPLICATION), 'Couldn''t create window: %s', [SDL_GetError()]);
Halt(0);
end;
screen := SDL_CreateRenderer(window, -1, 0);
if screen = nil then
begin
SDL_LogError(Ord(SDL_LOG_CATEGORY_APPLICATION), 'Couldn''t create renderer: %s', [SDL_GetError()]);
SDL_DestroyWindow(window);
Halt(0);
end;
SDL_SetRenderDrawColor(screen, $00, $00, $00, SDL_ALPHA_OPAQUE);
SDL_RenderClear(screen);
SDL_RenderPresent(screen);
{ Loop, getting joystick events! }
while done = SDL_FALSE do
Loop();
SDL_DestroyRenderer(screen);
SDL_DestroyWindow(window);
SDL_QuitSubSystem(SDL_INIT_VIDEO or SDL_INIT_JOYSTICK);
end.