Recent

Author Topic: SDL2 — joystick tester program rewritten from C to Free Pascal  (Read 300 times)

furious programming

  • Hero Member
  • *****
  • Posts: 582
  • Working on my own "loz alttp"-like game (FP+SDL2)
I was looking for information on joystick handling in SDL2 and came across a simple joystick testing program written in C by Sam Lantinga:

https://fossies.org/linux/SDL2/test/testjoystick.c

I rewrote it to Free Pascal and checked my gamepad, i.e. 8bitdo SN30 Pro. Everything works fine, buttons and axes are correctly recognized and operated, rumble (vibrations) works fine as well. If you need some code to learn joysticks handling in SDL2, the following code may be useful (there is a project for Lazarus in the attachment).

To run the program, the SDL2.dll file should be copied to the program directory. Just go to the GitHub reposity, download the archive and copy the dll file.

Code: Pascal  [Select][+][-]
  1. {
  2.   Copyright (C) 2022 furious programming
  3.  
  4.   This joystick tester is a translation to the Free Pascal language.
  5.   Original code is written in C language by Sam Lantinga:
  6.  
  7.   <https://fossies.org/linux/SDL2/test/testjoystick.c>
  8.  
  9.  
  10.   This tester program uses slightly modified Pascal headers ("SDL-for-Pascal")
  11.   developed by "PascalGameDevelopment" and others:
  12.  
  13.   <https://github.com/PascalGameDevelopment/SDL2-for-Pascal>
  14.  
  15.   Tested on Windows 10 64-bit, using "SDL2.dll' library version 2.0.20.0 and
  16.   with the "8bitdo SN30 Pro" wired controller. Everything works perfectly.
  17.  
  18.  
  19.   This software is provided 'as-is', without any express or implied
  20.   warranty.  In no event will the authors be held liable for any damages
  21.   arising from the use of this software.
  22.  
  23.   Permission is granted to anyone to use this software for any purpose,
  24.   including commercial applications, and to alter it and redistribute it
  25.   freely.
  26. }
  27.  
  28. {$MODE OBJFPC}{$LONGSTRINGS ON}
  29.  
  30. { Simple program to test the SDL joystick routines }
  31.  
  32. uses
  33.   SDL2;
  34.  
  35. const
  36.   SCREEN_WIDTH  = 640;
  37.   SCREEN_HEIGHT = 480;
  38.  
  39. var
  40.   window: PSDL_Window = nil;
  41.   screen: PSDL_Renderer = nil;
  42.   joystick: PSDL_Joystick = nil;
  43.   done: TSDL_Bool = SDL_FALSE;
  44.  
  45.   function IfThen(Condition: Boolean; IfTrue, IfFalse: PAnsiChar): PAnsiChar;
  46.   begin
  47.     if Condition then
  48.       Result := IfTrue
  49.     else
  50.       Result := IfFalse;
  51.   end;
  52.  
  53.   procedure PrintJoystick(joy: PSDL_Joystick);
  54.   var
  55.     type_: PAnsiChar;
  56.     guid: array [0 .. 63] of AnsiChar;
  57.   begin
  58.     Assert(SDL_JoystickFromInstanceID(SDL_JoystickInstanceID(joy)) = joy);
  59.     SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joy), guid, SizeOf(guid));
  60.  
  61.     case SDL_JoystickGetType(joy) of
  62.       SDL_JOYSTICK_TYPE_GAMECONTROLLER: type_ := 'Game Controller';
  63.       SDL_JOYSTICK_TYPE_WHEEL:          type_ := 'Wheel';
  64.       SDL_JOYSTICK_TYPE_ARCADE_STICK:   type_ := 'Arcade Stick';
  65.       SDL_JOYSTICK_TYPE_FLIGHT_STICK:   type_ := 'Flight Stick';
  66.       SDL_JOYSTICK_TYPE_DANCE_PAD:      type_ := 'Dance Pad';
  67.       SDL_JOYSTICK_TYPE_GUITAR:         type_ := 'Guitar';
  68.       SDL_JOYSTICK_TYPE_DRUM_KIT:       type_ := 'Drum Kit';
  69.       SDL_JOYSTICK_TYPE_ARCADE_PAD:     type_ := 'Arcade Pad';
  70.       SDL_JOYSTICK_TYPE_THROTTLE:       type_ := 'Throttle';
  71.     otherwise
  72.       type_ := 'Unknown';
  73.     end;
  74.  
  75.     SDL_Log('Joystick', []);
  76.     SDL_Log('          name: %s', [SDL_JoystickName(joy)]);
  77.     SDL_Log('          type: %s', [type_]);
  78.     SDL_Log('           LED: %s', [IfThen(SDL_JoystickHasLED(joy) = SDL_TRUE, 'yes', 'no')]);
  79.     SDL_Log('        rumble: %s', [IfThen(SDL_JoystickHasRumble(joy) = SDL_TRUE, 'yes', 'no')]);
  80.     SDL_Log('trigger rumble: %s', [IfThen(SDL_JoystickHasRumbleTriggers(joy) = SDL_TRUE, 'yes', 'no')]);
  81.     SDL_Log('          axes: %d', [SDL_JoystickNumAxes(joy)]);
  82.     SDL_Log('         balls: %d', [SDL_JoystickNumBalls(joy)]);
  83.     SDL_Log('          hats: %d', [SDL_JoystickNumHats(joy)]);
  84.     SDL_Log('       buttons: %d', [SDL_JoystickNumButtons(joy)]);
  85.     SDL_Log('   instance id: %d', [SDL_JoystickInstanceID(joy)]);
  86.     SDL_Log('          guid: %s', [guid]);
  87.     SDL_Log('       VID/PID: 0x%.4x/0x%.4x', [SDL_JoystickGetVendor(joy), SDL_JoystickGetProduct(joy)]);
  88.   end;
  89.  
  90.   procedure DrawRect(r: PSDL_Renderer; x, y, w, h: Integer);
  91.   var
  92.     area: TSDL_Rect;
  93.   begin
  94.     area.x := x;
  95.     area.y := y;
  96.     area.w := w;
  97.     area.h := h;
  98.  
  99.     SDL_RenderFillRect(r, @area);
  100.   end;
  101.  
  102.   procedure Loop();
  103.   var
  104.     event: TSDL_Event;
  105.     i, x, y: Integer;
  106.     hat_pos: UInt8;
  107.   begin
  108.     { blank screen, set up for drawing this frame. }
  109.     SDL_SetRenderDrawColor(screen, $00, $00, $00, SDL_ALPHA_OPAQUE);
  110.     SDL_RenderClear(screen);
  111.  
  112.     while SDL_PollEvent(@event) = 1 do
  113.     case event.type_ of
  114.  
  115.       SDL_JOYDEVICEADDED:
  116.       begin
  117.         SDL_Log('Joystick device %d added.', [event.jdevice.which]);
  118.  
  119.         if (joystick = nil) then
  120.         begin
  121.           joystick := SDL_JoystickOpen(event.jdevice.which);
  122.  
  123.           if (joystick <> nil) then
  124.             PrintJoystick(joystick)
  125.           else
  126.             SDL_Log('Couldn''t open joystick: %s', [SDL_GetError()]);
  127.         end;
  128.       end;
  129.  
  130.       SDL_JOYDEVICEREMOVED:
  131.       begin
  132.         SDL_Log('Joystick device %d removed.', [event.jdevice.which]);
  133.  
  134.         if (event.jdevice.which = SDL_JoystickInstanceID(joystick)) then
  135.         begin
  136.           SDL_JoystickClose(joystick);
  137.           joystick := SDL_JoystickOpen(0);
  138.         end;
  139.       end;
  140.  
  141.       SDL_JOYAXISMOTION:
  142.         SDL_Log(
  143.           'Joystick %d axis %d value: %d', [
  144.             event.jaxis.which,
  145.             event.jaxis.axis,
  146.             event.jaxis.value
  147.           ]
  148.         );
  149.  
  150.       SDL_JOYHATMOTION:
  151.       begin
  152.         SDL_Log('Joystick %d hat %d value:', [event.jhat.which, event.jhat.hat]);
  153.  
  154.         if (event.jhat.value = SDL_HAT_CENTERED)     then SDL_Log(' centered', []);
  155.         if (event.jhat.value and SDL_HAT_UP    <> 0) then SDL_Log(' up', []);
  156.         if (event.jhat.value and SDL_HAT_RIGHT <> 0) then SDL_Log(' right', []);
  157.         if (event.jhat.value and SDL_HAT_DOWN  <> 0) then SDL_Log(' down', []);
  158.         if (event.jhat.value and SDL_HAT_LEFT  <> 0) then SDL_Log(' left', []);
  159.  
  160.         SDL_Log('', []);
  161.       end;
  162.  
  163.       SDL_JOYBALLMOTION:
  164.         SDL_Log(
  165.           'Joystick %d ball %d delta: (%d,%d)', [
  166.             event.jball.which,
  167.             event.jball.ball,
  168.             event.jball.xrel,
  169.             event.jball.yrel
  170.           ]
  171.         );
  172.  
  173.       SDL_JOYBUTTONDOWN:
  174.       begin
  175.         SDL_Log('Joystick %d button %d down', [event.jbutton.which, event.jbutton.button]);
  176.  
  177.         { First button triggers a 0.5 second full strength rumble }
  178.         if (event.jbutton.button = 0) then
  179.             SDL_JoystickRumble(joystick, $FFFF, $FFFF, 500);
  180.       end;
  181.  
  182.       SDL_JOYBUTTONUP:
  183.         SDL_Log('Joystick %d button %d up', [event.jbutton.which, event.jbutton.button]);
  184.  
  185.       SDL_KEYDOWN:
  186.       begin
  187.         {
  188.           Press the L key to lag for 3 seconds, to see what happens
  189.           when SDL doesn't service the event loop quickly.
  190.         }
  191.         if (event.key.keysym.sym = SDLK_l) then
  192.         begin
  193.           SDL_Log('Lagging for 3 seconds...', []);
  194.           SDL_Delay(3000);
  195.         end
  196.         else
  197.         if ((event.key.keysym.sym = SDLK_ESCAPE) or (event.key.keysym.sym = SDLK_AC_BACK)) then
  198.           done := SDL_TRUE;
  199.       end;
  200.     end;
  201.  
  202.     if joystick <> nil then
  203.     begin
  204.       { Update visual joystick state }
  205.       SDL_SetRenderDrawColor(screen, $00, $FF, $00, SDL_ALPHA_OPAQUE);
  206.  
  207.       for i := 0 to SDL_JoystickNumButtons(joystick) - 1 do
  208.         if (SDL_JoystickGetButton(joystick, i) = SDL_PRESSED) then
  209.           DrawRect(screen, (i mod 20) * 34, SCREEN_HEIGHT - 68 + (i div 20) * 34, 32, 32);
  210.  
  211.       SDL_SetRenderDrawColor(screen, $FF, $00, $00, SDL_ALPHA_OPAQUE);
  212.       i := 0;
  213.  
  214.       while i < SDL_JoystickNumAxes(joystick) do
  215.       begin
  216.         { Draw the X/Y axis }
  217.         x := SDL_JoystickGetAxis(joystick, i) + 32768;
  218.         x *= SCREEN_WIDTH;
  219.         x := x div 65535;
  220.  
  221.         if x < 0 then
  222.             x := 0
  223.         else
  224.             if x > SCREEN_WIDTH - 16 then
  225.               x := SCREEN_WIDTH - 16;
  226.  
  227.         i += 1;
  228.  
  229.         if i < SDL_JoystickNumAxes(joystick) then
  230.           y := SDL_JoystickGetAxis(joystick, i) + 32768
  231.         else
  232.           y := 32768;
  233.  
  234.         y *= SCREEN_HEIGHT;
  235.         y := y div 65535;
  236.  
  237.         if y < 0 then
  238.           y := 0
  239.         else
  240.           if y > SCREEN_HEIGHT - 16 then
  241.             y := SCREEN_HEIGHT - 16;
  242.  
  243.         DrawRect(screen, x, y, 16, 16);
  244.         i += 1;
  245.       end;
  246.  
  247.       SDL_SetRenderDrawColor(screen, $00, $00, $FF, SDL_ALPHA_OPAQUE);
  248.  
  249.       for i := 0 to SDL_JoystickNumHats(joystick) - 1 do
  250.       begin
  251.         { Derive the new position }
  252.         x := SCREEN_WIDTH  div 2;
  253.         y := SCREEN_HEIGHT div 2;
  254.         hat_pos := SDL_JoystickGetHat(joystick, i);
  255.  
  256.         if hat_pos and SDL_HAT_UP <> 0 then
  257.           y := 0
  258.         else
  259.           if hat_pos and SDL_HAT_DOWN <> 0 then
  260.             y := SCREEN_HEIGHT - 8;
  261.  
  262.         if hat_pos and SDL_HAT_LEFT <> 0 then
  263.           x := 0
  264.         else
  265.           if hat_pos and SDL_HAT_RIGHT <> 0 then
  266.             x := SCREEN_WIDTH - 8;
  267.  
  268.         DrawRect(screen, x, y, 8, 8);
  269.       end;
  270.     end;
  271.  
  272.     SDL_RenderPresent(screen);
  273.   end;
  274.  
  275. begin
  276.   SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, '0');
  277.  
  278.   { Enable standard application logging }
  279.   SDL_LogSetPriority(Ord(SDL_LOG_CATEGORY_APPLICATION), SDL_LOG_PRIORITY_INFO);
  280.  
  281.   { Initialize SDL (Note: video is required to start event loop) }
  282.   if SDL_Init(SDL_INIT_VIDEO or SDL_INIT_JOYSTICK) < 0 then
  283.   begin
  284.     SDL_LogError(Ord(SDL_LOG_CATEGORY_APPLICATION), 'Couldn''t initialize SDL: %s', [SDL_GetError()]);
  285.     Halt(1);
  286.   end;
  287.  
  288.   { Create a window to display joystick axis position }
  289.   window := SDL_CreateWindow('Joystick Test', SDL_WINDOWPOS_CENTERED,
  290.                              SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH,
  291.                              SCREEN_HEIGHT, 0);
  292.  
  293.   if window = nil then
  294.   begin
  295.     SDL_LogError(Ord(SDL_LOG_CATEGORY_APPLICATION), 'Couldn''t create window: %s', [SDL_GetError()]);
  296.     Halt(0);
  297.   end;
  298.  
  299.   screen := SDL_CreateRenderer(window, -1, 0);
  300.  
  301.   if screen = nil then
  302.   begin
  303.     SDL_LogError(Ord(SDL_LOG_CATEGORY_APPLICATION), 'Couldn''t create renderer: %s', [SDL_GetError()]);
  304.     SDL_DestroyWindow(window);
  305.     Halt(0);
  306.   end;
  307.  
  308.   SDL_SetRenderDrawColor(screen, $00, $00, $00, SDL_ALPHA_OPAQUE);
  309.   SDL_RenderClear(screen);
  310.   SDL_RenderPresent(screen);
  311.  
  312.   { Loop, getting joystick events! }
  313.   while done = SDL_FALSE do
  314.     Loop();
  315.  
  316.   SDL_DestroyRenderer(screen);
  317.   SDL_DestroyWindow(window);
  318.  
  319.   SDL_QuitSubSystem(SDL_INIT_VIDEO or SDL_INIT_JOYSTICK);
  320. end.

Have fun.
Lazarus 2.2.0 with FPC 3.2.2 (2022-01-02), Windows 10 — all 64-bit

 

TinyPortal © 2005-2018