File: joystick.c

/*
  joystick.c

  A basic test of reading various attributes of a 
  joystick and displaying them using primitives and 
  fonts. Also tests setting video modes, and menus.
*/

#define CPWDLL_EXTERN
#include <cpw.h>

/* for testing : switch between a joystick callback and a timer / polling */
/* #define USE_JOY_CALLBACK 1 */

/* font face */

static CpwFontFace font;

/* joystick info */

static CpwJoystickList list;
static CpwJoystickInfo info;

/* available video modes info */

static CpwVideoList vlist;

/* handy window characteristics holder */

static CpwWindowInfo windowInfo = { 1,100,100,250,250 };

/* performance marks */

static uint_32 m1, m2, m3;

/****************************************************/
/*  2D Matrix Setup                                 */
/****************************************************/

void set2DMatrix( void ) 
{
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    gluOrtho2D( 0, windowInfo.width, 0, windowInfo.height );
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
}

/****************************************************/
/*  3D Matrix Setup                                 */
/****************************************************/

void set3DMatrix( void ) 
{
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    gluPerspective( 60, windowInfo.width / windowInfo.height, 1, 100 );
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
}

/****************************************************/
/*  Draw Window One                                 */
/****************************************************/

void drawWindowOne( pCpw cpw ) 
{
    uint_32 i;
    uint_32 fscale;
    char buf[10];

#ifndef USE_JOY_CALLBACK

    cpwEnterMark( cpw, m2 );
    cpwJoystickPoll( cpw, &info );
    cpwExitMark( cpw, m2 );

#endif

    cpwEnterMark( cpw, m1 );

    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    set2DMatrix();

    cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_SCALE,  1.0 );

    glScaled( windowInfo.width / 250.0, windowInfo.height / 300.0, 1.0 ); 
    fscale = (int)( 5.0 * ( ( windowInfo.width / 250.0 ) + ( windowInfo.height / 300.0 ) ) );

    /* x-axis */

    glColor3d( 0.0, 1.0, 0.2 );

    glPushMatrix();
      glTranslated( 20.0, 200.0, 0.0 );
      cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_WIDTH,  20.0 );
      cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_HEIGHT, 60.0 );
      cpwDrawPrimitive( cpw, CPW_PRIM_2D_RECTANGLE );    
    glPopMatrix();

    glColor3d( 0.4, 0.4, 1.0 );

    glPushMatrix();
      glTranslated( 21.0, 201.0, 0.0 );
      cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_WIDTH,  18.0 );
      cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_HEIGHT, (float)58.0 * 
           ( (float) MCpwJoystickXAxis( info ) / (float) MCpwJoystickAxisRangeMax( list ) ) );
      cpwDrawPrimitive( cpw, CPW_PRIM_2D_FILLEDRECTANGLE );    
    glPopMatrix();

    glPushMatrix();
      glRasterPos2d( 15.0, 185.0 );
      cpwFontMode( cpw, CPW_FONTOPT_SIZE, fscale );
      cpwDrawFont( cpw, font, "X-Axis", 1 );
    glPopMatrix();

    /* y-axis */

    glColor3d( 0.0, 1.0, 0.2 );

    glPushMatrix();
      glTranslated( 60.0, 200.0, 0.0 );
      cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_WIDTH,  20.0 );
      cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_HEIGHT, 60.0 );
      cpwDrawPrimitive( cpw, CPW_PRIM_2D_RECTANGLE );    
    glPopMatrix();

    glColor3d( 0.4, 0.4, 1.0 );

    glPushMatrix();
      glTranslated( 61.0, 201.0, 0.0 );
      cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_WIDTH,  18.0 );
      cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_HEIGHT, (float) 58.0 * 
           ( (float) MCpwJoystickYAxis( info ) / (float) MCpwJoystickAxisRangeMax( list ) ) );
      cpwDrawPrimitive( cpw, CPW_PRIM_2D_FILLEDRECTANGLE );    
    glPopMatrix();

    glPushMatrix();
      glRasterPos2d( 55.0, 185.0 );
      cpwFontMode( cpw, CPW_FONTOPT_SIZE, fscale );
      cpwDrawFont( cpw, font, "Y-Axis", 1 );
    glPopMatrix();

    /* z-axis */

    glColor3d( 0.0, 1.0, 0.2 );

    glPushMatrix();
      glTranslated( 100.0, 200.0, 0.0 );
      cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_WIDTH,  20.0 );
      cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_HEIGHT, 60.0 );
      cpwDrawPrimitive( cpw, CPW_PRIM_2D_RECTANGLE );    
    glPopMatrix();

    glColor3d( 0.4, 0.4, 1.0 );

    glPushMatrix();
      glTranslated( 101.0, 201.0, 0.0 );
      cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_WIDTH,  18.0 );
      cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_HEIGHT, (float)58.0 * 
           ( (float) MCpwJoystickZAxis( info ) / (float) MCpwJoystickAxisRangeMax( list ) ) );
      cpwDrawPrimitive( cpw, CPW_PRIM_2D_FILLEDRECTANGLE );    
    glPopMatrix();

    glPushMatrix();
      glRasterPos2d( 95.0, 185.0 );
      cpwFontMode( cpw, CPW_FONTOPT_SIZE, fscale );
      cpwDrawFont( cpw, font, "Z-Axis", 1 );
    glPopMatrix();

    /* buttons */

    for ( i = 1; i <= MCpwJoystick1ButtonCount( list ); i++ ) {

        glColor3d( 0.0, 1.0, 0.2 );
    
        glPushMatrix();
          glTranslated( 10 + ( i * 20.0 ), 150.0, 0.0 );
          cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_INNERRADIUS,  0.0 );
          cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_OUTERRADIUS,  8.0 );
          cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_SLICES, 10.0 );
          cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_STACKS, 1.0 );
          cpwDrawPrimitive( cpw, CPW_PRIM_3D_SOLIDDISK );    
        glPopMatrix();

        glPushMatrix();
          glRasterPos2d( 2 + ( i * 20.0 ), 130.0 );
          cpwFontMode( cpw, CPW_FONTOPT_SIZE, fscale );
          sprintf( buf, "%2d", i );
          cpwDrawFont( cpw, font, buf, 1 );
        glPopMatrix();
        
        if ( MCpwJoystick1Button( info, i ) )
            glColor3d( 0.8, 0.0, 0.0 );
        else
            glColor3d( 0.0, 1.0, 0.2 );

        glPushMatrix();
          glTranslated( 10 + ( i * 20.0 ), 150.0, 0.0 );
          //glTranslated( 11 + ( i * 20.0 ), 151.0, 0.0 );
          cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_OUTERRADIUS,  6.0 );
          cpwDrawPrimitive( cpw, CPW_PRIM_3D_SOLIDDISK );    
        glPopMatrix();

    }

    /* z-rotation */

    glColor3d( 0.0, 1.0, 0.2 );

    glPushMatrix();
      glTranslated( 140.0, 200.0, 0.0 );
      cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_WIDTH,  20.0 );
      cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_HEIGHT, 60.0 );
      cpwDrawPrimitive( cpw, CPW_PRIM_2D_RECTANGLE );    
    glPopMatrix();

    glColor3d( 0.4, 0.4, 1.0 );

    glPushMatrix();
      glTranslated( 141.0, 201.0, 0.0 );
      cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_WIDTH,  18.0 );
      cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_HEIGHT, (float)58.0 * 
           ( (float) MCpwJoystickZRot( info ) / (float) MCpwJoystickRotRangeMax( list ) ) );
      cpwDrawPrimitive( cpw, CPW_PRIM_2D_FILLEDRECTANGLE );    
    glPopMatrix();
 
    glPushMatrix();
      glRasterPos2d( 137.0, 185.0 );
      cpwFontMode( cpw, CPW_FONTOPT_SIZE, fscale );
      cpwDrawFont( cpw, font, "Z-Rot", 1 );
    glPopMatrix();

    /* first pov hat position */

    glPushMatrix();
      glColor3d( 0.4, 0.4, 1.0 );
      glTranslated( 200.0, 250.0, 0.0 );
      cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_INNERRADIUS,  0.0 );
      cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_OUTERRADIUS,  15.0 );
      cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_SLICES, 10.0 );
      cpwSetPrimitiveOpt( cpw, CPW_PRIMOPT_STACKS, 1.0 );
      cpwDrawPrimitive( cpw, CPW_PRIM_3D_WIREDISK );    
    glPopMatrix();

    glPushMatrix();

      glTranslated( 200.0, 250.0, 0.0 );

      if ( MCpwJoystickPov( list, 1 ) ) {

          switch( MCpwJoystickPov1( info ) ) {
              case CPW_POV_CENTERED:
                  glRotatef( 90, 1, 0, 0 );
              break;
              case CPW_POV_UP:
              break;
              case CPW_POV_UPRIGHT:
                  glRotatef( -45, 0, 0, 1 );
              break;
              case CPW_POV_RIGHT:
                  glRotatef( -90, 0, 0, 1 );
              break;
              case CPW_POV_DOWNRIGHT:
                  glRotatef( -135, 0, 0, 1 );
              break;
              case CPW_POV_DOWN:
                  glRotatef( 180, 0, 0, 1 );
              break;
              case CPW_POV_DOWNLEFT:
                  glRotatef( 135, 0, 0, 1 );
              break;
              case CPW_POV_LEFT:
                  glRotatef( 90, 0, 0, 1 );
              break;
              case CPW_POV_UPLEFT:
                  glRotatef( 45, 0, 0, 1 );
              break;
          }
      }

      glColor3d( 0.0, 1.0, 0.0 );

      glBegin( GL_POLYGON );
    
      glVertex3f( -4, 0.0,  0.0 );
      glVertex3f( 0, 15.0, 0.0 );
      glVertex3f( 4, 0.0,  0.0 );

      glEnd();

    glPopMatrix();

    glPushMatrix();

      glRasterPos2d( 187.0, 220.0 );
      cpwFontMode( cpw, CPW_FONTOPT_SIZE, fscale );
      cpwDrawFont( cpw, font, "Pov-1", 1 );

    glPopMatrix();
  
    cpwExitMark( cpw, m1 );

}

/****************************************************/
/*  Window Draw Event callback                      */
/****************************************************/

void draw( pCpw cpw, uint_32 winid )
{
    switch( winid ) {
        case 1:
        drawWindowOne( cpw );
        break;
    }
    cpwSwapWindowBuffers( cpw, winid );
}

/****************************************************/
/*  Window Create / Destroy Event callback          */
/****************************************************/

/* the window's context is set current prior to this call,
   do any init we need per window */

void window( pCpw cpw, uint_32 winid, bool flag )
{
    /* creation event */

    if ( flag == true ) {

        //glShadeModel( GL_SMOOTH );
        glDepthFunc( GL_LEQUAL );
        glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
        glEnable( GL_LINE_SMOOTH );
        //glEnable( GL_POLYGON_SMOOTH );
        glEnable( GL_BLEND );
        glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
        glClearColor( 0.0, 0.0, 0.0, 1.0 );

        switch( winid ) {
            case 1:
            break;
        }
        return;
    }

    /* window close event */

    if ( flag == false ) {
        cpwDestroyWindow( cpw, winid );
        return;
    }
}

/****************************************************/
/*  Window Resize Event callback                    */
/****************************************************/

void reshape( pCpw cpw, uint_32 winid, uint_32 width, uint_32 height )
{
    if ( height == 0 ) { height=1; }

    windowInfo.width = width;
    windowInfo.height = height;

    set2DMatrix();

    glViewport( 0, 0, width, height );
}

/****************************************************/
/*  Joystick callback                               */
/****************************************************/

void joystick( pCpw cpw, CpwJoystickInfo * iinfo )
{
    memcpy( &info, iinfo, sizeof( info ) );
    cpwPostWindowRedisplay( cpw, 1 );
}

/****************************************************/
/*  Mainloop callback                               */
/****************************************************/

void mainloop( pCpw cpw, bool eventspending )
{
}

/****************************************************/
/*  Timer callback                                  */
/****************************************************/

void timer( pCpw cpw, uint_32 id )
{
    cpwPostWindowRedisplay( cpw, 1 );
}

/****************************************************/
/*  Video Modes                                     */
/****************************************************/

static uint_32 lastChecked;
static uint_32 defMode;

bool changeVideoMode( pCpw cpw, uint_32 videoMode ) 
{
    CpwVideoHints hint;

    hint.width  = vlist.list[videoMode]->width;
    hint.height = vlist.list[videoMode]->height;
    hint.depth  = vlist.list[videoMode]->depth;

    /* change the video mode */

    if ( !cpwChangeVideoMode( cpw, hint ) )
        return false;

    return true;
}

void setupVideoMenu( pCpw cpw, uint_32 submenu )
{
    char buf[100];
    uint_32 count = 0;

    if ( !cpwListVideoModes( cpw, &vlist ) ) return;

    cpwAddMenuEntry( cpw, submenu, "Reset", 20, false );

    while ( count < vlist.size ) {

        if ( vlist.list[count]->active == true ) {

            sprintf( buf, "%d-bit %dx%d", vlist.list[count]->depth, 
                                          vlist.list[count]->width, 
                                          vlist.list[count]->height );

            cpwAddMenuEntry( cpw, submenu, buf, 21+count, true );
            lastChecked = 21+count;
            defMode = 21+count;
        
        } else {

            sprintf( buf, "%d-bit %dx%d", vlist.list[count]->depth, 
                                          vlist.list[count]->width, 
                                          vlist.list[count]->height );

            cpwAddMenuEntry( cpw, submenu, buf, 21+count, false );
    
        }

        count++;
    }

    return;
}

/****************************************************/
/*  Menu Select callback                            */
/****************************************************/

void menuselect( pCpw cpw, uint_32 winid, uint_32 menuid, uint_32 entryid )
{
    bool res = false;
    
    if ( entryid == 4 ) cpwBreakMainLoop( cpw );

    if ( entryid >= 20 && entryid <= vlist.size + 21 ) {
        
        /* change video mode */

        if ( entryid == 20 ) 
            res = cpwResetVideoMode( cpw );
        else
            res = changeVideoMode( cpw, entryid-21 );

    }

    if ( res ) {

        if ( entryid == 20 ) {
            cpwCheckMenuEntry( cpw, menuid, defMode );
            cpwUncheckMenuEntry( cpw, menuid, lastChecked );
            lastChecked = defMode;
            return;
        }

        cpwCheckMenuEntry( cpw, menuid, entryid );
        cpwUncheckMenuEntry( cpw, menuid, lastChecked );
        lastChecked = entryid;
    }
}

/****************************************************/
/*  Main                                            */
/****************************************************/

int main(int argc, char* argv[])
{
    pCpw cpw;
    uint_32 win, topmenu, submenu1, submenu2;

    cpw = null;

    /****************************************************/
    /*  Init                                            */
    /****************************************************/

    cpwInitContext( &cpw ); /* create the context and init it */
    cpwInitWindowProperty( cpw, CPW_WINDOWPROP_EXPECTMENU, 0, 0 );
    cpwInitWindowProperty( cpw, CPW_WINDOWPROP_POSITION, 100, 100 );
    cpwInitWindowProperty( cpw, CPW_WINDOWPROP_SIZE, 300, 250 );

    /****************************************************/
    /*  Creaing Windows                                 */
    /****************************************************/

    win = cpwCreateWindowEx( cpw, "Joystick Test", windowInfo.x, windowInfo.y, 
                             windowInfo.width, windowInfo.height );

    /****************************************************/
    /*  Joysticks                                       */
    /****************************************************/

    memset( &list, 0, sizeof( CpwJoystickList ) );
    memset( &info, 0, sizeof( CpwJoystickInfo ) );
    
    /* ask for one joystick and setup some custom ranges */
    
    MCpwJoysticksRequested( list, 1 );

    /* set some custom axis and rotation ranges */
    /* (using helper macros in cpw_macros.h) */

    MCpwJoystickAxisRange( list, 0, 100 );
    MCpwJoystickRotRange( list, 0, 359 );
    MCpwJoystickVelRange( list, 0, 100 );
    MCpwJoystickAccelRange( list, 0, 100 );

    /* init a joystick if one is present */

    if ( !cpwInitJoysticks( cpw, &list ) ) {
        printf( "No joystick support found.\n" );
    }

    /* save the id of the joystick we found in info structure */

    info.id = MCpwJoystick1Id( list );

#ifdef USE_JOY_CALLBACK

    /* init a joystick callback for every 50 milliseconds */
        
    cpwJoystickCallback( cpw, joystick, MCpwJoystick1Id( list ), 50 );

#endif
    
    /****************************************************/
    /*  Window Menus                                    */
    /****************************************************/

    topmenu = cpwCreateMenu( cpw, menuselect );

    submenu1 = cpwCreateSubMenu( cpw );
    submenu2 = cpwCreateSubMenu( cpw );

    cpwAddMenuEntry( cpw, submenu1, "Open", 1, false );
    cpwAddMenuEntry( cpw, submenu1, "Close", 2, false );
    cpwAddMenuSeperator( cpw, submenu1, 3 );
    cpwAddMenuEntry( cpw, submenu1, "Print", 3, false );
    cpwAddMenuSeperator( cpw, submenu1, 5 );
    cpwAddMenuEntry( cpw, submenu1, "Exit", 4, false );

    setupVideoMenu( cpw, submenu2 );

    cpwAddSubMenu( cpw, topmenu, "Control", submenu1, 100 );
    cpwAddSubMenu( cpw, topmenu, "Screen", submenu2, 102 );

    cpwAssignMenuToWindow( cpw, topmenu, win );

    /****************************************************/
    /*  Event Callbacks                                 */
    /****************************************************/

    cpwCreateCallback( cpw, window );
    cpwDisplayCallback( cpw, draw );
    cpwReshapeCallback( cpw, reshape );
    cpwMainLoopCallback( cpw, mainloop, 1000 );

    /****************************************************/
    /*  Timers                                          */
    /****************************************************/

#ifndef USE_JOY_CALLBACK

    cpwTimerCallback( cpw, 10, 1, true, timer );

#endif

    /****************************************************/
    /*  Fonts                                           */
    /****************************************************/

    cpwFontMode( cpw, CPW_FONTOPT_PIXELMAP, 0 ); /* we only support this type right now */
    cpwFontMode( cpw, CPW_FONTOPT_ALIGNLEFT, 0 );
    cpwFontMode( cpw, CPW_FONTOPT_PIXELMAP_GLFORMAT, GL_LUMINANCE );

    font = cpwLoadFont( cpw, "arial.ttf", CPW_FONTLOC_HOST, "", "" );
    if ( font == 0 )
        printf("Could not load TrueType font file Arial.\n");

    /****************************************************/
    /*  Performance Timing                              */
    /****************************************************/

    m1 = cpwAddMark( cpw, "draw window", true );
    m2 = cpwAddMark( cpw, "joystick poll", true );
    m3 = cpwAddMark( cpw, "total time   ", true );

    /****************************************************/
    /*  FPS                                             */
    /****************************************************/
    
    cpwFpsInit( cpw );
    
    /****************************************************/
    /*  MainLoop                                        */
    /****************************************************/

    cpwEnterMark( cpw, m3 );

    cpwMainLoop( cpw );

    cpwExitMark( cpw, m3 );

    /****************************************************/
    /*  Exit and Free                                   */
    /****************************************************/

    cpwFreeVideoList( cpw, &vlist );

    cpwFreeContext( &cpw );

    return 0;
}