SDL2 joystick interrogator (third version)

25Sep15
Interrogating a Sony PlayStation 2 DualShock 2 controller; I've yet to figure out why SDL2 reports that the DualShock 2 has five axes (although my PlayStation-to-USB adapter might be to blame), I've yet to figure out a solution – which doesn't involve a ⅓ axis dead zone – to the problem of the thumbsticks on the DualShock 2 not always returning all the way to the origin when they're released, and I last touched a DualShock 2 a long, long time ago so I'd forgotten that the D-pad is disabled in analogue mode :/

Interrogating a Sony PlayStation 2 DualShock 2 controller; I’ve yet to figure out why SDL2 reports that the DualShock 2 has five axes (although my PlayStation-to-USB adapter might be to blame), I’ve yet to figure out a solution – which doesn’t involve a ⅓ axis dead zone – to the problem of the thumbsticks on the DualShock 2 not always returning all the way to the origin when they’re released, and I last touched a DualShock 2 a long, long time ago so I’d forgotten that the D-pad is disabled in analogue mode :/

I recently rebooted development of a project I tried to develop for the Ludum Dare 20 contest held in April 2011, the theme of which was “It’s dangerous to go alone! Take this!”

As the project will require a controller with an analogue stick to play I’ve begun to develop a utility to interrogate the capabilities of various controllers – and as I feel that the SDL2 documentation is lacking with regards to the joystick support offered by that library I’m posting here the source code to the third version of that utility, for the benefit both of myself and of others.

SDL2 joystick interrogator.mm:

#include <SDL2/SDL.h>
#include "seal_intToString.h"
#include "seal_stringConcatenation.h"
#include "seal_stringQueue.h"
#include "seal_tree.h"
#include "seal_tileBackground.h"

void p_StringToTileBackground( const char * const p_string, seal_TileBackground *p_tileBackground )
{
  const size_t transparent_sub_texture = 10 /* '0'..'9' */ + 26 /* 'A'..'Z' */ + 5 /* '+', '-', '.', '(', ')' */;
  p_tileBackground->p_SetAllSubTextures( transparent_sub_texture );
  size_t i = 0;
  int x = 0, y = p_tileBackground->f_Height() - 1;
  for( ; i < strlen( p_string ); i ++, x ++ )
  {
    const char character = p_string[ i ];
    size_t sub_texture = transparent_sub_texture;
    if(( character >= '0' ) && ( character <= '9' ))
      sub_texture = character - '0';
    else
      if(( character >= 'a' ) && ( character <= 'z' ))
        sub_texture = character - 'a' + 10;
      else
        if(( character >= 'A' ) && ( character <= 'Z' ))
          sub_texture = character - 'A' + 10;
        else
          switch( character )
          {
            case '+': sub_texture = 36; break;
            case '-': sub_texture = 37; break;
            case '.': sub_texture = 38; break;
            case '(': sub_texture = 39; break;
            case ')': sub_texture = 40;
          }
    if( character == '\n' )
    {
      x = -1;
      y --;
    }
    else
      p_tileBackground->p_SetSubTexture( x, y, sub_texture );
  }
}

static seal_Texture *g_font;
static seal_TileBackground *g_number_of_joysticks_background;

void p_UpdateNumberOfJoysticksBackground( void )
{
  const int number_of_joysticks = SDL_NumJoysticks();
  char *number_of_joysticks_string = f_seal_IntToString( number_of_joysticks, false );
  char *message = f_seal_ConcatenateStrings(( number_of_joysticks == 0 )?( char* )"No":number_of_joysticks_string, " joystick", ( number_of_joysticks == 1 )?"":"s", " detected.", NULL );
  free( number_of_joysticks_string );
  p_StringToTileBackground( message, g_number_of_joysticks_background );
  free( message );
}

typedef struct t_JoystickTreeNode_struct
{
  SDL_JoystickID m_instanceId;
  bool m_open;
  SDL_Joystick *m_joystick;

  seal_TileBackground *m_nameBackground;

  int m_numberOfButtons;
  bool *m_buttonPressed;
  seal_TileBackground *m_buttonsPressedBackground;

  int m_numberOfAxes;
  Sint16 *m_axis;
  seal_TileBackground *m_axesBackground;
}
t_JoystickTreeNode;

class JoystickTree : public seal_Tree<t_JoystickTreeNode*, SDL_JoystickID>
{
  private:
    t_JoystickTreeNode *m_mostRecentlyAdded;

  public:
    JoystickTree( void )
    {
      m_mostRecentlyAdded = NULL;
    }

    void p_Open( const Sint32 p )
    {
      SDL_Joystick *joystick = SDL_JoystickOpen( p );
      const SDL_JoystickID instanceId = SDL_JoystickInstanceID( joystick );
      t_JoystickTreeNode *n = f_Get( instanceId );
      if( n == NULL )
      {
        n = ( t_JoystickTreeNode* )malloc( sizeof( t_JoystickTreeNode ));
        p_Set( n );
      }
      n->m_instanceId = instanceId;
      n->m_open = true;
      n->m_joystick = joystick;

      const char * const name = ( SDL_JoystickName( joystick ) != NULL )?SDL_JoystickName( joystick ):"NULL";
      n->m_nameBackground = new seal_TileBackground( g_font, strlen( name ), 1 );
      p_StringToTileBackground( name, n->m_nameBackground );

      n->m_numberOfButtons = SDL_JoystickNumButtons( joystick );
      n->m_buttonPressed = ( bool* )malloc( sizeof( bool ) * n->m_numberOfButtons );
      int i = 0;
      for( ; i < n->m_numberOfButtons; i ++ )
        n->m_buttonPressed[ i ] = SDL_JoystickGetButton( joystick, i );
      n->m_buttonsPressedBackground = new seal_TileBackground( g_font, 13, n->m_numberOfButtons + 1 );
      p_UpdateButtonsPressedBackground( n );

      n->m_numberOfAxes = SDL_JoystickNumAxes( joystick );
      n->m_axis = ( Sint16* )malloc( sizeof( Sint16 ) * n->m_numberOfAxes );
      for( i = 0; i < n->m_numberOfAxes; i ++ )
        n->m_axis[ i ] = SDL_JoystickGetAxis( joystick, i );
      n->m_axesBackground = new seal_TileBackground( g_font, 13, n->m_numberOfAxes + 1 );
      n->m_axesBackground->p_SetBottomLeftX( n->m_buttonsPressedBackground->f_Right());
      p_UpdateAxesBackground( n );

      n->m_nameBackground->p_SetBottomLeftY( n->m_buttonsPressedBackground->f_Top());

      m_mostRecentlyAdded = n;
    }

    void p_Close( const Sint32 p )
    {
      t_JoystickTreeNode *n = f_Get( p );
      if(( n != NULL ) && ( n->m_open ))
        p_Delete( n );
    }

    void p_PressButton( const SDL_JoystickID p_instanceId, const Uint8 p_button )
    {
      t_JoystickTreeNode *n = f_Get( p_instanceId );
      if(( n != NULL ) && ( n->m_open ))
      {
        n->m_buttonPressed[ p_button ] = true;
        p_UpdateButtonsPressedBackground( n );
      }
    }

    void p_ReleaseButton( const SDL_JoystickID p_instanceId, const Uint8 p_button )
    {
      t_JoystickTreeNode *n = f_Get( p_instanceId );
      if(( n != NULL ) && ( n->m_open ))
      {
        n->m_buttonPressed[ p_button ] = false;
        p_UpdateButtonsPressedBackground( n );
      }
    }

    void p_MoveAxis( const SDL_JoystickID p_instanceID, const Uint8 p_axis, const Sint16 p_value )
    {
      t_JoystickTreeNode *n = f_Get( p_instanceID );
      if(( n != NULL ) && ( n->m_open ))
      {
        n->m_axis[ p_axis ] = p_value;
        p_UpdateAxesBackground( n );
      }
    }

    void p_Render( void )
    {
      if(( m_mostRecentlyAdded != NULL ) && ( m_mostRecentlyAdded->m_open ))
      {
        m_mostRecentlyAdded->m_nameBackground->p_Render();
        m_mostRecentlyAdded->m_buttonsPressedBackground->p_Render();
        m_mostRecentlyAdded->m_axesBackground->p_Render();
      }
    }

  private:
    t_seal_TREE_BRANCH_DIRECTION f_Compare_TT( t_JoystickTreeNode *p_old, t_JoystickTreeNode *p_new )
    {
      if( p_new->m_instanceId < p_old->m_instanceId )
        return k_seal_TREE_BRANCH_DIRECTION__LEFT;
      if( p_new->m_instanceId > p_old->m_instanceId )
        return k_seal_TREE_BRANCH_DIRECTION__RIGHT;
      return k_seal_TREE_BRANCH_DIRECTION__STRAIGHT;
    }

    t_seal_TREE_BRANCH_DIRECTION f_Compare_TU( t_JoystickTreeNode *p_content, SDL_JoystickID p_identifier )
    {
      if( p_identifier < p_content->m_instanceId )
        return k_seal_TREE_BRANCH_DIRECTION__LEFT;
      if( p_identifier > p_content->m_instanceId )
        return k_seal_TREE_BRANCH_DIRECTION__RIGHT;
      return k_seal_TREE_BRANCH_DIRECTION__STRAIGHT;
    }

    t_JoystickTreeNode *f_IsNotInTree( SDL_JoystickID p )
    {
      return NULL;
    }

    void p_Delete( t_JoystickTreeNode *p )
    {
      if( p->m_open )
      {
        p->m_open = false;
        SDL_JoystickClose( p->m_joystick );
        delete p->m_nameBackground;
        free( p->m_buttonPressed );
        delete p->m_buttonsPressedBackground;
        free( p->m_axis );
        delete p->m_axesBackground;
      }
    }

    void p_UpdateButtonsPressedBackground( t_JoystickTreeNode *p )
    {
      seal_StringQueue sQ;
      if( p->m_numberOfButtons == 0 )
        sQ.p_Push(( char* )"No buttons" );
      else
      {
        sQ.p_Push(( char* )"Buttons (" );
        char *number_of_buttons = f_seal_IntToString( p->m_numberOfButtons, false );
        sQ.p_Push( number_of_buttons );
        free( number_of_buttons );
        sQ.p_Push(( char* )")\n" );
      }
      int i = 0;
      for( ; i < p->m_numberOfButtons; i ++ )
      {
        if(( p->m_numberOfButtons >= 100 ) && ( i < 100 ))
          sQ.p_Push(( char* )" " );
        if(( p->m_numberOfButtons >= 10 ) && ( i < 10 ))
          sQ.p_Push(( char* )" " );
        char *i_string = f_seal_IntToString( i, false );
        sQ.p_Push( i_string );
        free( i_string );
        sQ.p_Push(( char* )" .. " );
        sQ.p_Push(( char* )(( !p->m_buttonPressed[ i ] )?"Up":"Down" ));
        if( i + 1 < p->m_numberOfButtons )
          sQ.p_Push(( char* )"\n" );
      }
      char *s = sQ.f_String();
      p_StringToTileBackground( s, p->m_buttonsPressedBackground );
      free( s );
    }

    void p_UpdateAxesBackground( t_JoystickTreeNode *p )
    {
      seal_StringQueue sQ;
      if( p->m_numberOfAxes == 0 )
        sQ.p_Push(( char* )"No axes" );
      else
      {
        sQ.p_Push(( char* )"Axes (" );
        char *number_of_axes = f_seal_IntToString( p->m_numberOfAxes, false );
        sQ.p_Push( number_of_axes );
        free( number_of_axes );
        sQ.p_Push(( char* )")\n" );
      }
      int i = 0;
      for( ; i < p->m_numberOfAxes; i ++ )
      {
        char *i_string = f_seal_IntToString( i, false );
        sQ.p_Push( i_string );
        free( i_string );
        sQ.p_Push(( char* )" .. " );
        char *axis = f_seal_IntToString( p->m_axis[ i ], true );
        sQ.p_Push( axis );
        free( axis );
        if( i + 1 < p->m_numberOfAxes )
          sQ.p_Push(( char* )"\n" );
      }
      char *s = sQ.f_String();
      p_StringToTileBackground( s, p->m_axesBackground );
      free( s );
    }
};

int main( const int argc, const char * const argv[] )
{
  const int W = 494, H = 304;

  SDL_Init( SDL_INIT_VIDEO | SDL_INIT_JOYSTICK );
  SDL_Window *window = SDL_CreateWindow( "SDL2 joystick interrogator", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W, H, SDL_WINDOW_OPENGL );
  SDL_GLContext context = SDL_GL_CreateContext( window );

  glMatrixMode( GL_PROJECTION );
  glLoadIdentity();
  glOrtho( 0, W, 0, H, -1, 1 );
  glMatrixMode( GL_MODELVIEW );
  glClearColor( 0.5, 0.5, 0.5, 1.0 );
  glEnable( GL_TEXTURE_2D );
  glEnableClientState( GL_VERTEX_ARRAY );
  glEnableClientState( GL_TEXTURE_COORD_ARRAY );

  g_font = new seal_Texture( "font.png", 19, 19 );
  g_number_of_joysticks_background = new seal_TileBackground( g_font, 23, 1 );
  g_number_of_joysticks_background->p_SetBottomLeftY( H - 19 );
  JoystickTree *joysticks = new JoystickTree();

  p_UpdateNumberOfJoysticksBackground();

  bool quit = false;
  do
  {
    SDL_Event event;
    while( SDL_PollEvent( &event ))
      switch( event.type )
      {
        case SDL_QUIT:
          quit = true;
          break;
        case SDL_JOYDEVICEADDED:
          p_UpdateNumberOfJoysticksBackground();
          joysticks->p_Open( event.jdevice.which );
          break;
        case SDL_JOYDEVICEREMOVED:
          p_UpdateNumberOfJoysticksBackground();
          joysticks->p_Close( event.jdevice.which );
          break;
        case SDL_JOYBUTTONDOWN:
          joysticks->p_PressButton( event.jbutton.which, event.jbutton.button );
          break;
        case SDL_JOYBUTTONUP:
          joysticks->p_ReleaseButton( event.jbutton.which, event.jbutton.button );
          break;
        case SDL_JOYAXISMOTION:
          joysticks->p_MoveAxis( event.jaxis.which, event.jaxis.axis, event.jaxis.value );
      }
    glClear( GL_COLOR_BUFFER_BIT );
    g_number_of_joysticks_background->p_Render();
    joysticks->p_Render();
    SDL_GL_SwapWindow( window );
  }
  while( !quit );

  delete joysticks;
  delete g_number_of_joysticks_background;
  delete g_font;

  SDL_GL_DeleteContext( context );
  SDL_DestroyWindow( window );
  SDL_Quit();
  return 0;
}

font.png:

font

Advertisements


No Responses Yet to “SDL2 joystick interrogator (third version)”

  1. Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: