SDL2 joystick interrogator (second version)

13Aug15
Interrogating a Sony PlayStation 2 DualShock 2 controller.

Interrogating a Sony PlayStation 2 DualShock 2 controller.

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 second 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' */ + 3 /* '.', '(', ')' */;
  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
          if( character == '.' )
            sub_texture = 36;
          else
            if( character == '(' )
              sub_texture = 37;
            else
              if( character == ')' )
                sub_texture = 38;
    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;
  int m_numberOfButtons;
  bool *m_buttonPressed;
  seal_TileBackground *m_buttonsPressedBackground;
}
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;
      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 );
      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_Render( void )
    {
      if(( m_mostRecentlyAdded != NULL ) && ( m_mostRecentlyAdded->m_open ))
        m_mostRecentlyAdded->m_buttonsPressedBackground->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 );
        free( p->m_buttonPressed );
        delete p->m_buttonsPressedBackground;
      }
    }

    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 );
    }
};

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

  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 );
      }
    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.png

Advertisements


No Responses Yet to “SDL2 joystick interrogator (second 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: