File: cpw_timers.c

/***************************************************************************/
/*                                                                         */
/*  cpw_timers.c                                                           */
/*                                                                         */
/*    The Cpw library Timer interface.                                     */
/*                                                                         */
/*  Copyright 2001-2002 by                                                 */
/*  Jim Mathies,                                                           */
/*                                                                         */
/*  This file is part of the Cpw project, and may only be used,            */
/*  modified, and distributed under the terms of the Cpw project           */
/*  license.  By continuing to use, modify, or distribute this file        */
/*  you indicate that you have read the license and understand and         */
/*  accept it fully.                                                       */
/*                                                                         */
/*  File Platform: cross                                                   */
/*                                                                         */
/***************************************************************************/

#include "cpw_timers.h"
#include "cpw_error.h"
#include "cpw_linkedlist.h"
#include "cpw_joystick.h"
#include CPW_LOCALHOST_H

/*************************************************************************/
/*                                                                       */
/*   init and exit.                                                      */
/*                                                                       */
/*************************************************************************/

bool cpw_timers_init( pCpw cpw )
{
    ll_initList( &cpw->list_timers );
    cpw->timers = false;
    return true;
}

void cpw_timers_exit( pCpw cpw )
{
    pCpwTimer timer;

    timer = cpw_timers_iteratestart( cpw );

    if ( timer == 0 ) return;
        
    while ( timer != 0 ) {
        cpwfree( timer );
        timer = cpw_timers_iteratenext( cpw );
    }

    ll_deleteList( &cpw->list_timers );
    return;
}

/*************************************************************************/
/*                                                                       */
/*   create & destroy for a timer api                                    */
/*                                                                       */
/*************************************************************************/

bool cpwTimerCallback( pCpw cpw, uint_32 milli, uint_32 id, bool redo, 
    CpwTimerCallback timerCallback )
{
    float_64 seconds;

    /* allocate a new timer struct */

    pCpwTimer timer = cpwmalloc( sizeof( CpwTimer ) );   

    if ( timer == 0 ) {
        cpw_error_set( cpw, cpw_error_outofmemory );
        return false;
    }
    memset( timer, 0, sizeof( CpwTimer ) );

    /* offset in seconds for this timer */

    seconds = (float_64)milli / (float_64)1000.0;

    /* add it to our linked list */

    timer->listid = ll_addVoid( &cpw->list_timers, (void *) timer );

    cpw->timers = true;

    /* setup */

    timer->internal      = false;
    timer->timerid       = id;
    timer->redo          = redo;
    timer->timerCallback = timerCallback;
    timer->hpdiff        = seconds * cpw_localhost_hpfreq();
    timer->hpcount       = cpw_localhost_hpcount(); /* running */
    
    return true;
}

bool  cpwKillTimer( pCpw cpw, uint_32 id )
{
    pCpwTimer timer;

    timer = cpw_timers_iteratestart( cpw );

    if ( timer == 0 ) return false;
        
    while ( timer != 0 ) {
        if ( timer->timerid == id )
            return ll_deleteElement( &cpw->list_timers, timer->listid );
        timer = cpw_timers_iteratenext( cpw );
    }
    return true;
}

/*************************************************************************/
/*                                                                       */
/*   managing timers                                                     */
/*                                                                       */
/*************************************************************************/

/* start an iteration of all timers */

pCpwTimer
cpw_timers_iteratestart( pCpw cpw )
{
    if ( !ll_initIterate( &cpw->list_timers ) ) return 0;
    return (pCpwTimer)le_getVoid( ll_getNext( &cpw->list_timers ) );
}

/* get next timer or 0 for no more */

pCpwTimer
cpw_timers_iteratenext( pCpw cpw )
{
    return (pCpwTimer)le_getVoid( ll_getNext( &cpw->list_timers ) );
}

/* check for a timeout and send the message */

bool
cpw_timers_check( pCpw cpw, pCpwTimer timer, bool * pending )
{
    /* returns false if a timer was deleted from the list */

    float_64 count = cpw_localhost_hpcount();

    if ( ( count - timer->hpcount ) >= timer->hpdiff ) {

        *pending = true;

        /* timeout */

        if ( !timer->internal ) {

            /* external application defined timer */

            timer->timerCallback( cpw, timer->timerid );

        } else {

            /* internal Cpw defined timer */

            cpw_timers_internaltimeout( cpw, timer );
        }

        if ( timer->redo == false ) {
            ll_deleteElement( &cpw->list_timers, timer->listid );
            cpwfree( timer );
            return false;
        }            
        
        timer->hpcount = count;
    } else {
        
        /* nothing to do */

        *pending = false;

    }

    return true;
}

/* check all timers for timeouts */

bool
cpw_timers_tick( pCpw cpw )
{
    pCpwTimer timer;
    bool pending = false;

    /* return false if there are no pending timers */

    if ( ( timer = cpw_timers_iteratestart( cpw ) ) == 0 ) {
        cpw->timers = false;
        return false;
    }

    while ( timer != 0 ) {
        if ( !cpw_timers_check( cpw, timer, &pending ) ) break;
        timer = cpw_timers_iteratenext( cpw );
    }

    return pending;
}

/*************************************************************************/
/*                                                                       */
/*   internal timers                                                     */
/*                                                                       */
/*************************************************************************/

bool cpw_timers_createinternal( pCpw cpw, uint_32 type, bool redo, uint_32 delay, uint_32 id )
{
    float_64 seconds;

    /* allocate a new timer struct */

    pCpwTimer timer = cpwmalloc( sizeof( CpwTimer ) );   

    if ( timer == 0 ) {
        cpw_error_set( cpw, cpw_error_outofmemory );
        return false;
    }
    memset( timer, 0, sizeof( CpwTimer ) );

    /* offset in seconds for this timer */

    seconds = (float_64)delay / (float_64)1000.0;

    /* add it to our linked list */

    timer->listid = ll_addVoid( &cpw->list_timers, (void *) timer );

    cpw->timers = true;

    /* setup */

    timer->internal      = true;
    timer->int_type      = type;
    timer->timerid       = id;
    timer->redo          = redo;
    timer->hpdiff        = seconds * cpw_localhost_hpfreq();
    timer->hpcount       = cpw_localhost_hpcount(); /* running */
    
    return true;
}

void cpw_timers_internaltimeout( pCpw cpw, pCpwTimer timer )
{
    switch( timer->int_type ) {

        case CPW_TIMER_INTERNAL_JOYSTICK:
            cpw_joystick_timout( cpw, timer->timerid );
        break;

    }
    return;
}