OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [tcl/] [generic/] [tclTimer.c] - Diff between revs 578 and 1765

Only display areas with differences | Details | Blame | View Log

Rev 578 Rev 1765
/*
/*
 * tclTimer.c --
 * tclTimer.c --
 *
 *
 *      This file provides timer event management facilities for Tcl,
 *      This file provides timer event management facilities for Tcl,
 *      including the "after" command.
 *      including the "after" command.
 *
 *
 * Copyright (c) 1997 by Sun Microsystems, Inc.
 * Copyright (c) 1997 by Sun Microsystems, Inc.
 *
 *
 * See the file "license.terms" for information on usage and redistribution
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 *
 * RCS: @(#) $Id: tclTimer.c,v 1.1.1.1 2002-01-16 10:25:29 markom Exp $
 * RCS: @(#) $Id: tclTimer.c,v 1.1.1.1 2002-01-16 10:25:29 markom Exp $
 */
 */
 
 
#include "tclInt.h"
#include "tclInt.h"
#include "tclPort.h"
#include "tclPort.h"
 
 
/*
/*
 * This flag indicates whether this module has been initialized.
 * This flag indicates whether this module has been initialized.
 */
 */
 
 
static int initialized = 0;
static int initialized = 0;
 
 
/*
/*
 * For each timer callback that's pending there is one record of the following
 * For each timer callback that's pending there is one record of the following
 * type.  The normal handlers (created by Tcl_CreateTimerHandler) are chained
 * type.  The normal handlers (created by Tcl_CreateTimerHandler) are chained
 * together in a list sorted by time (earliest event first).
 * together in a list sorted by time (earliest event first).
 */
 */
 
 
typedef struct TimerHandler {
typedef struct TimerHandler {
    Tcl_Time time;                      /* When timer is to fire. */
    Tcl_Time time;                      /* When timer is to fire. */
    Tcl_TimerProc *proc;                /* Procedure to call. */
    Tcl_TimerProc *proc;                /* Procedure to call. */
    ClientData clientData;              /* Argument to pass to proc. */
    ClientData clientData;              /* Argument to pass to proc. */
    Tcl_TimerToken token;               /* Identifies handler so it can be
    Tcl_TimerToken token;               /* Identifies handler so it can be
                                         * deleted. */
                                         * deleted. */
    struct TimerHandler *nextPtr;       /* Next event in queue, or NULL for
    struct TimerHandler *nextPtr;       /* Next event in queue, or NULL for
                                         * end of queue. */
                                         * end of queue. */
} TimerHandler;
} TimerHandler;
 
 
static TimerHandler *firstTimerHandlerPtr = NULL;
static TimerHandler *firstTimerHandlerPtr = NULL;
                                        /* First event in queue. */
                                        /* First event in queue. */
static int lastTimerId;                 /* Timer identifier of most recently
static int lastTimerId;                 /* Timer identifier of most recently
                                         * created timer. */
                                         * created timer. */
static int timerPending;                /* 1 if a timer event is in the queue. */
static int timerPending;                /* 1 if a timer event is in the queue. */
 
 
/*
/*
 * The data structure below is used by the "after" command to remember
 * The data structure below is used by the "after" command to remember
 * the command to be executed later.  All of the pending "after" commands
 * the command to be executed later.  All of the pending "after" commands
 * for an interpreter are linked together in a list.
 * for an interpreter are linked together in a list.
 */
 */
 
 
typedef struct AfterInfo {
typedef struct AfterInfo {
    struct AfterAssocData *assocPtr;
    struct AfterAssocData *assocPtr;
                                /* Pointer to the "tclAfter" assocData for
                                /* Pointer to the "tclAfter" assocData for
                                 * the interp in which command will be
                                 * the interp in which command will be
                                 * executed. */
                                 * executed. */
    char *command;              /* Command to execute.  Malloc'ed, so must
    char *command;              /* Command to execute.  Malloc'ed, so must
                                 * be freed when structure is deallocated. */
                                 * be freed when structure is deallocated. */
    int id;                     /* Integer identifier for command;  used to
    int id;                     /* Integer identifier for command;  used to
                                 * cancel it. */
                                 * cancel it. */
    Tcl_TimerToken token;       /* Used to cancel the "after" command.  NULL
    Tcl_TimerToken token;       /* Used to cancel the "after" command.  NULL
                                 * means that the command is run as an
                                 * means that the command is run as an
                                 * idle handler rather than as a timer
                                 * idle handler rather than as a timer
                                 * handler.  NULL means this is an "after
                                 * handler.  NULL means this is an "after
                                 * idle" handler rather than a
                                 * idle" handler rather than a
                                 * timer handler. */
                                 * timer handler. */
    struct AfterInfo *nextPtr;  /* Next in list of all "after" commands for
    struct AfterInfo *nextPtr;  /* Next in list of all "after" commands for
                                 * this interpreter. */
                                 * this interpreter. */
} AfterInfo;
} AfterInfo;
 
 
/*
/*
 * One of the following structures is associated with each interpreter
 * One of the following structures is associated with each interpreter
 * for which an "after" command has ever been invoked.  A pointer to
 * for which an "after" command has ever been invoked.  A pointer to
 * this structure is stored in the AssocData for the "tclAfter" key.
 * this structure is stored in the AssocData for the "tclAfter" key.
 */
 */
 
 
typedef struct AfterAssocData {
typedef struct AfterAssocData {
    Tcl_Interp *interp;         /* The interpreter for which this data is
    Tcl_Interp *interp;         /* The interpreter for which this data is
                                 * registered. */
                                 * registered. */
    AfterInfo *firstAfterPtr;   /* First in list of all "after" commands
    AfterInfo *firstAfterPtr;   /* First in list of all "after" commands
                                 * still pending for this interpreter, or
                                 * still pending for this interpreter, or
                                 * NULL if none. */
                                 * NULL if none. */
} AfterAssocData;
} AfterAssocData;
 
 
/*
/*
 * There is one of the following structures for each of the
 * There is one of the following structures for each of the
 * handlers declared in a call to Tcl_DoWhenIdle.  All of the
 * handlers declared in a call to Tcl_DoWhenIdle.  All of the
 * currently-active handlers are linked together into a list.
 * currently-active handlers are linked together into a list.
 */
 */
 
 
typedef struct IdleHandler {
typedef struct IdleHandler {
    Tcl_IdleProc (*proc);       /* Procedure to call. */
    Tcl_IdleProc (*proc);       /* Procedure to call. */
    ClientData clientData;      /* Value to pass to proc. */
    ClientData clientData;      /* Value to pass to proc. */
    int generation;             /* Used to distinguish older handlers from
    int generation;             /* Used to distinguish older handlers from
                                 * recently-created ones. */
                                 * recently-created ones. */
    struct IdleHandler *nextPtr;/* Next in list of active handlers. */
    struct IdleHandler *nextPtr;/* Next in list of active handlers. */
} IdleHandler;
} IdleHandler;
 
 
static IdleHandler *idleList;
static IdleHandler *idleList;
                                /* First in list of all idle handlers. */
                                /* First in list of all idle handlers. */
static IdleHandler *lastIdlePtr;
static IdleHandler *lastIdlePtr;
                                /* Last in list (or NULL for empty list). */
                                /* Last in list (or NULL for empty list). */
static int idleGeneration;      /* Used to fill in the "generation" fields
static int idleGeneration;      /* Used to fill in the "generation" fields
                                 * of IdleHandler structures.  Increments
                                 * of IdleHandler structures.  Increments
                                 * each time Tcl_DoOneEvent starts calling
                                 * each time Tcl_DoOneEvent starts calling
                                 * idle handlers, so that all old handlers
                                 * idle handlers, so that all old handlers
                                 * can be called without calling any of the
                                 * can be called without calling any of the
                                 * new ones created by old ones. */
                                 * new ones created by old ones. */
 
 
/*
/*
 * Prototypes for procedures referenced only in this file:
 * Prototypes for procedures referenced only in this file:
 */
 */
 
 
static void             AfterCleanupProc _ANSI_ARGS_((ClientData clientData,
static void             AfterCleanupProc _ANSI_ARGS_((ClientData clientData,
                            Tcl_Interp *interp));
                            Tcl_Interp *interp));
static void             AfterProc _ANSI_ARGS_((ClientData clientData));
static void             AfterProc _ANSI_ARGS_((ClientData clientData));
static void             FreeAfterPtr _ANSI_ARGS_((AfterInfo *afterPtr));
static void             FreeAfterPtr _ANSI_ARGS_((AfterInfo *afterPtr));
static AfterInfo *      GetAfterEvent _ANSI_ARGS_((AfterAssocData *assocPtr,
static AfterInfo *      GetAfterEvent _ANSI_ARGS_((AfterAssocData *assocPtr,
                            char *string));
                            char *string));
static void             InitTimer _ANSI_ARGS_((void));
static void             InitTimer _ANSI_ARGS_((void));
static void             TimerExitProc _ANSI_ARGS_((ClientData clientData));
static void             TimerExitProc _ANSI_ARGS_((ClientData clientData));
static int              TimerHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
static int              TimerHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
                            int flags));
                            int flags));
static void             TimerCheckProc _ANSI_ARGS_((ClientData clientData,
static void             TimerCheckProc _ANSI_ARGS_((ClientData clientData,
                            int flags));
                            int flags));
static void             TimerSetupProc _ANSI_ARGS_((ClientData clientData,
static void             TimerSetupProc _ANSI_ARGS_((ClientData clientData,
                            int flags));
                            int flags));


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * InitTimer --
 * InitTimer --
 *
 *
 *      This function initializes the timer module.
 *      This function initializes the timer module.
 *
 *
 * Results:
 * Results:
 *      None.
 *      None.
 *
 *
 * Side effects:
 * Side effects:
 *      Registers the idle and timer event sources.
 *      Registers the idle and timer event sources.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static void
static void
InitTimer()
InitTimer()
{
{
    initialized = 1;
    initialized = 1;
    lastTimerId = 0;
    lastTimerId = 0;
    timerPending = 0;
    timerPending = 0;
    idleGeneration = 0;
    idleGeneration = 0;
    firstTimerHandlerPtr = NULL;
    firstTimerHandlerPtr = NULL;
    lastIdlePtr = NULL;
    lastIdlePtr = NULL;
    idleList = NULL;
    idleList = NULL;
 
 
    Tcl_CreateEventSource(TimerSetupProc, TimerCheckProc, NULL);
    Tcl_CreateEventSource(TimerSetupProc, TimerCheckProc, NULL);
    Tcl_CreateExitHandler(TimerExitProc, NULL);
    Tcl_CreateExitHandler(TimerExitProc, NULL);
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * TimerExitProc --
 * TimerExitProc --
 *
 *
 *      This function is call at exit or unload time to remove the
 *      This function is call at exit or unload time to remove the
 *      timer and idle event sources.
 *      timer and idle event sources.
 *
 *
 * Results:
 * Results:
 *      None.
 *      None.
 *
 *
 * Side effects:
 * Side effects:
 *      Removes the timer and idle event sources.
 *      Removes the timer and idle event sources.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static void
static void
TimerExitProc(clientData)
TimerExitProc(clientData)
    ClientData clientData;      /* Not used. */
    ClientData clientData;      /* Not used. */
{
{
    Tcl_DeleteEventSource(TimerSetupProc, TimerCheckProc, NULL);
    Tcl_DeleteEventSource(TimerSetupProc, TimerCheckProc, NULL);
    initialized = 0;
    initialized = 0;
}
}


/*
/*
 *--------------------------------------------------------------
 *--------------------------------------------------------------
 *
 *
 * Tcl_CreateTimerHandler --
 * Tcl_CreateTimerHandler --
 *
 *
 *      Arrange for a given procedure to be invoked at a particular
 *      Arrange for a given procedure to be invoked at a particular
 *      time in the future.
 *      time in the future.
 *
 *
 * Results:
 * Results:
 *      The return value is a token for the timer event, which
 *      The return value is a token for the timer event, which
 *      may be used to delete the event before it fires.
 *      may be used to delete the event before it fires.
 *
 *
 * Side effects:
 * Side effects:
 *      When milliseconds have elapsed, proc will be invoked
 *      When milliseconds have elapsed, proc will be invoked
 *      exactly once.
 *      exactly once.
 *
 *
 *--------------------------------------------------------------
 *--------------------------------------------------------------
 */
 */
 
 
Tcl_TimerToken
Tcl_TimerToken
Tcl_CreateTimerHandler(milliseconds, proc, clientData)
Tcl_CreateTimerHandler(milliseconds, proc, clientData)
    int milliseconds;           /* How many milliseconds to wait
    int milliseconds;           /* How many milliseconds to wait
                                 * before invoking proc. */
                                 * before invoking proc. */
    Tcl_TimerProc *proc;        /* Procedure to invoke. */
    Tcl_TimerProc *proc;        /* Procedure to invoke. */
    ClientData clientData;      /* Arbitrary data to pass to proc. */
    ClientData clientData;      /* Arbitrary data to pass to proc. */
{
{
    register TimerHandler *timerHandlerPtr, *tPtr2, *prevPtr;
    register TimerHandler *timerHandlerPtr, *tPtr2, *prevPtr;
    Tcl_Time time;
    Tcl_Time time;
 
 
    if (!initialized) {
    if (!initialized) {
        InitTimer();
        InitTimer();
    }
    }
 
 
    timerHandlerPtr = (TimerHandler *) ckalloc(sizeof(TimerHandler));
    timerHandlerPtr = (TimerHandler *) ckalloc(sizeof(TimerHandler));
 
 
    /*
    /*
     * Compute when the event should fire.
     * Compute when the event should fire.
     */
     */
 
 
    TclpGetTime(&time);
    TclpGetTime(&time);
    timerHandlerPtr->time.sec = time.sec + milliseconds/1000;
    timerHandlerPtr->time.sec = time.sec + milliseconds/1000;
    timerHandlerPtr->time.usec = time.usec + (milliseconds%1000)*1000;
    timerHandlerPtr->time.usec = time.usec + (milliseconds%1000)*1000;
    if (timerHandlerPtr->time.usec >= 1000000) {
    if (timerHandlerPtr->time.usec >= 1000000) {
        timerHandlerPtr->time.usec -= 1000000;
        timerHandlerPtr->time.usec -= 1000000;
        timerHandlerPtr->time.sec += 1;
        timerHandlerPtr->time.sec += 1;
    }
    }
 
 
    /*
    /*
     * Fill in other fields for the event.
     * Fill in other fields for the event.
     */
     */
 
 
    timerHandlerPtr->proc = proc;
    timerHandlerPtr->proc = proc;
    timerHandlerPtr->clientData = clientData;
    timerHandlerPtr->clientData = clientData;
    lastTimerId++;
    lastTimerId++;
    timerHandlerPtr->token = (Tcl_TimerToken) lastTimerId;
    timerHandlerPtr->token = (Tcl_TimerToken) lastTimerId;
 
 
    /*
    /*
     * Add the event to the queue in the correct position
     * Add the event to the queue in the correct position
     * (ordered by event firing time).
     * (ordered by event firing time).
     */
     */
 
 
    for (tPtr2 = firstTimerHandlerPtr, prevPtr = NULL; tPtr2 != NULL;
    for (tPtr2 = firstTimerHandlerPtr, prevPtr = NULL; tPtr2 != NULL;
            prevPtr = tPtr2, tPtr2 = tPtr2->nextPtr) {
            prevPtr = tPtr2, tPtr2 = tPtr2->nextPtr) {
        if ((tPtr2->time.sec > timerHandlerPtr->time.sec)
        if ((tPtr2->time.sec > timerHandlerPtr->time.sec)
                || ((tPtr2->time.sec == timerHandlerPtr->time.sec)
                || ((tPtr2->time.sec == timerHandlerPtr->time.sec)
                && (tPtr2->time.usec > timerHandlerPtr->time.usec))) {
                && (tPtr2->time.usec > timerHandlerPtr->time.usec))) {
            break;
            break;
        }
        }
    }
    }
    timerHandlerPtr->nextPtr = tPtr2;
    timerHandlerPtr->nextPtr = tPtr2;
    if (prevPtr == NULL) {
    if (prevPtr == NULL) {
        firstTimerHandlerPtr = timerHandlerPtr;
        firstTimerHandlerPtr = timerHandlerPtr;
    } else {
    } else {
        prevPtr->nextPtr = timerHandlerPtr;
        prevPtr->nextPtr = timerHandlerPtr;
    }
    }
 
 
    TimerSetupProc(NULL, TCL_ALL_EVENTS);
    TimerSetupProc(NULL, TCL_ALL_EVENTS);
    return timerHandlerPtr->token;
    return timerHandlerPtr->token;
}
}


/*
/*
 *--------------------------------------------------------------
 *--------------------------------------------------------------
 *
 *
 * Tcl_DeleteTimerHandler --
 * Tcl_DeleteTimerHandler --
 *
 *
 *      Delete a previously-registered timer handler.
 *      Delete a previously-registered timer handler.
 *
 *
 * Results:
 * Results:
 *      None.
 *      None.
 *
 *
 * Side effects:
 * Side effects:
 *      Destroy the timer callback identified by TimerToken,
 *      Destroy the timer callback identified by TimerToken,
 *      so that its associated procedure will not be called.
 *      so that its associated procedure will not be called.
 *      If the callback has already fired, or if the given
 *      If the callback has already fired, or if the given
 *      token doesn't exist, then nothing happens.
 *      token doesn't exist, then nothing happens.
 *
 *
 *--------------------------------------------------------------
 *--------------------------------------------------------------
 */
 */
 
 
void
void
Tcl_DeleteTimerHandler(token)
Tcl_DeleteTimerHandler(token)
    Tcl_TimerToken token;       /* Result previously returned by
    Tcl_TimerToken token;       /* Result previously returned by
                                 * Tcl_DeleteTimerHandler. */
                                 * Tcl_DeleteTimerHandler. */
{
{
    register TimerHandler *timerHandlerPtr, *prevPtr;
    register TimerHandler *timerHandlerPtr, *prevPtr;
 
 
    for (timerHandlerPtr = firstTimerHandlerPtr, prevPtr = NULL;
    for (timerHandlerPtr = firstTimerHandlerPtr, prevPtr = NULL;
            timerHandlerPtr != NULL; prevPtr = timerHandlerPtr,
            timerHandlerPtr != NULL; prevPtr = timerHandlerPtr,
            timerHandlerPtr = timerHandlerPtr->nextPtr) {
            timerHandlerPtr = timerHandlerPtr->nextPtr) {
        if (timerHandlerPtr->token != token) {
        if (timerHandlerPtr->token != token) {
            continue;
            continue;
        }
        }
        if (prevPtr == NULL) {
        if (prevPtr == NULL) {
            firstTimerHandlerPtr = timerHandlerPtr->nextPtr;
            firstTimerHandlerPtr = timerHandlerPtr->nextPtr;
        } else {
        } else {
            prevPtr->nextPtr = timerHandlerPtr->nextPtr;
            prevPtr->nextPtr = timerHandlerPtr->nextPtr;
        }
        }
        ckfree((char *) timerHandlerPtr);
        ckfree((char *) timerHandlerPtr);
        return;
        return;
    }
    }
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * TimerSetupProc --
 * TimerSetupProc --
 *
 *
 *      This function is called by Tcl_DoOneEvent to setup the timer
 *      This function is called by Tcl_DoOneEvent to setup the timer
 *      event source for before blocking.  This routine checks both the
 *      event source for before blocking.  This routine checks both the
 *      idle and after timer lists.
 *      idle and after timer lists.
 *
 *
 * Results:
 * Results:
 *      None.
 *      None.
 *
 *
 * Side effects:
 * Side effects:
 *      May update the maximum notifier block time.
 *      May update the maximum notifier block time.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static void
static void
TimerSetupProc(data, flags)
TimerSetupProc(data, flags)
    ClientData data;            /* Not used. */
    ClientData data;            /* Not used. */
    int flags;                  /* Event flags as passed to Tcl_DoOneEvent. */
    int flags;                  /* Event flags as passed to Tcl_DoOneEvent. */
{
{
    Tcl_Time blockTime;
    Tcl_Time blockTime;
 
 
    if (((flags & TCL_IDLE_EVENTS) && idleList)
    if (((flags & TCL_IDLE_EVENTS) && idleList)
            || ((flags & TCL_TIMER_EVENTS) && timerPending)) {
            || ((flags & TCL_TIMER_EVENTS) && timerPending)) {
        /*
        /*
         * There is an idle handler or a pending timer event, so just poll.
         * There is an idle handler or a pending timer event, so just poll.
         */
         */
 
 
        blockTime.sec = 0;
        blockTime.sec = 0;
        blockTime.usec = 0;
        blockTime.usec = 0;
 
 
    } else if ((flags & TCL_TIMER_EVENTS) && firstTimerHandlerPtr) {
    } else if ((flags & TCL_TIMER_EVENTS) && firstTimerHandlerPtr) {
        /*
        /*
         * Compute the timeout for the next timer on the list.
         * Compute the timeout for the next timer on the list.
         */
         */
 
 
        TclpGetTime(&blockTime);
        TclpGetTime(&blockTime);
        blockTime.sec = firstTimerHandlerPtr->time.sec - blockTime.sec;
        blockTime.sec = firstTimerHandlerPtr->time.sec - blockTime.sec;
        blockTime.usec = firstTimerHandlerPtr->time.usec - blockTime.usec;
        blockTime.usec = firstTimerHandlerPtr->time.usec - blockTime.usec;
        if (blockTime.usec < 0) {
        if (blockTime.usec < 0) {
            blockTime.sec -= 1;
            blockTime.sec -= 1;
            blockTime.usec += 1000000;
            blockTime.usec += 1000000;
        }
        }
        if (blockTime.sec < 0) {
        if (blockTime.sec < 0) {
            blockTime.sec = 0;
            blockTime.sec = 0;
            blockTime.usec = 0;
            blockTime.usec = 0;
        }
        }
    } else {
    } else {
        return;
        return;
    }
    }
 
 
    Tcl_SetMaxBlockTime(&blockTime);
    Tcl_SetMaxBlockTime(&blockTime);
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * TimerCheckProc --
 * TimerCheckProc --
 *
 *
 *      This function is called by Tcl_DoOneEvent to check the timer
 *      This function is called by Tcl_DoOneEvent to check the timer
 *      event source for events.  This routine checks both the
 *      event source for events.  This routine checks both the
 *      idle and after timer lists.
 *      idle and after timer lists.
 *
 *
 * Results:
 * Results:
 *      None.
 *      None.
 *
 *
 * Side effects:
 * Side effects:
 *      May queue an event and update the maximum notifier block time.
 *      May queue an event and update the maximum notifier block time.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static void
static void
TimerCheckProc(data, flags)
TimerCheckProc(data, flags)
    ClientData data;            /* Not used. */
    ClientData data;            /* Not used. */
    int flags;                  /* Event flags as passed to Tcl_DoOneEvent. */
    int flags;                  /* Event flags as passed to Tcl_DoOneEvent. */
{
{
    Tcl_Event *timerEvPtr;
    Tcl_Event *timerEvPtr;
    Tcl_Time blockTime;
    Tcl_Time blockTime;
 
 
    if ((flags & TCL_TIMER_EVENTS) && firstTimerHandlerPtr) {
    if ((flags & TCL_TIMER_EVENTS) && firstTimerHandlerPtr) {
        /*
        /*
         * Compute the timeout for the next timer on the list.
         * Compute the timeout for the next timer on the list.
         */
         */
 
 
        TclpGetTime(&blockTime);
        TclpGetTime(&blockTime);
        blockTime.sec = firstTimerHandlerPtr->time.sec - blockTime.sec;
        blockTime.sec = firstTimerHandlerPtr->time.sec - blockTime.sec;
        blockTime.usec = firstTimerHandlerPtr->time.usec - blockTime.usec;
        blockTime.usec = firstTimerHandlerPtr->time.usec - blockTime.usec;
        if (blockTime.usec < 0) {
        if (blockTime.usec < 0) {
            blockTime.sec -= 1;
            blockTime.sec -= 1;
            blockTime.usec += 1000000;
            blockTime.usec += 1000000;
        }
        }
        if (blockTime.sec < 0) {
        if (blockTime.sec < 0) {
            blockTime.sec = 0;
            blockTime.sec = 0;
            blockTime.usec = 0;
            blockTime.usec = 0;
        }
        }
 
 
        /*
        /*
         * If the first timer has expired, stick an event on the queue.
         * If the first timer has expired, stick an event on the queue.
         */
         */
 
 
        if (blockTime.sec == 0 && blockTime.usec == 0 && !timerPending) {
        if (blockTime.sec == 0 && blockTime.usec == 0 && !timerPending) {
            timerPending = 1;
            timerPending = 1;
            timerEvPtr = (Tcl_Event *) ckalloc(sizeof(Tcl_Event));
            timerEvPtr = (Tcl_Event *) ckalloc(sizeof(Tcl_Event));
            timerEvPtr->proc = TimerHandlerEventProc;
            timerEvPtr->proc = TimerHandlerEventProc;
            Tcl_QueueEvent(timerEvPtr, TCL_QUEUE_TAIL);
            Tcl_QueueEvent(timerEvPtr, TCL_QUEUE_TAIL);
        }
        }
    }
    }
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * TimerHandlerEventProc --
 * TimerHandlerEventProc --
 *
 *
 *      This procedure is called by Tcl_ServiceEvent when a timer event
 *      This procedure is called by Tcl_ServiceEvent when a timer event
 *      reaches the front of the event queue.  This procedure handles
 *      reaches the front of the event queue.  This procedure handles
 *      the event by invoking the callbacks for all timers that are
 *      the event by invoking the callbacks for all timers that are
 *      ready.
 *      ready.
 *
 *
 * Results:
 * Results:
 *      Returns 1 if the event was handled, meaning it should be removed
 *      Returns 1 if the event was handled, meaning it should be removed
 *      from the queue.  Returns 0 if the event was not handled, meaning
 *      from the queue.  Returns 0 if the event was not handled, meaning
 *      it should stay on the queue.  The only time the event isn't
 *      it should stay on the queue.  The only time the event isn't
 *      handled is if the TCL_TIMER_EVENTS flag bit isn't set.
 *      handled is if the TCL_TIMER_EVENTS flag bit isn't set.
 *
 *
 * Side effects:
 * Side effects:
 *      Whatever the timer handler callback procedures do.
 *      Whatever the timer handler callback procedures do.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static int
static int
TimerHandlerEventProc(evPtr, flags)
TimerHandlerEventProc(evPtr, flags)
    Tcl_Event *evPtr;           /* Event to service. */
    Tcl_Event *evPtr;           /* Event to service. */
    int flags;                  /* Flags that indicate what events to
    int flags;                  /* Flags that indicate what events to
                                 * handle, such as TCL_FILE_EVENTS. */
                                 * handle, such as TCL_FILE_EVENTS. */
{
{
    TimerHandler *timerHandlerPtr, **nextPtrPtr;
    TimerHandler *timerHandlerPtr, **nextPtrPtr;
    Tcl_Time time;
    Tcl_Time time;
    int currentTimerId;
    int currentTimerId;
 
 
    /*
    /*
     * Do nothing if timers aren't enabled.  This leaves the event on the
     * Do nothing if timers aren't enabled.  This leaves the event on the
     * queue, so we will get to it as soon as ServiceEvents() is called
     * queue, so we will get to it as soon as ServiceEvents() is called
     * with timers enabled.
     * with timers enabled.
     */
     */
 
 
    if (!(flags & TCL_TIMER_EVENTS)) {
    if (!(flags & TCL_TIMER_EVENTS)) {
        return 0;
        return 0;
    }
    }
 
 
    /*
    /*
     * The code below is trickier than it may look, for the following
     * The code below is trickier than it may look, for the following
     * reasons:
     * reasons:
     *
     *
     * 1. New handlers can get added to the list while the current
     * 1. New handlers can get added to the list while the current
     *    one is being processed.  If new ones get added, we don't
     *    one is being processed.  If new ones get added, we don't
     *    want to process them during this pass through the list to avoid
     *    want to process them during this pass through the list to avoid
     *    starving other event sources.  This is implemented using the
     *    starving other event sources.  This is implemented using the
     *    token number in the handler:  new handlers will have a
     *    token number in the handler:  new handlers will have a
     *    newer token than any of the ones currently on the list.
     *    newer token than any of the ones currently on the list.
     * 2. The handler can call Tcl_DoOneEvent, so we have to remove
     * 2. The handler can call Tcl_DoOneEvent, so we have to remove
     *    the handler from the list before calling it. Otherwise an
     *    the handler from the list before calling it. Otherwise an
     *    infinite loop could result.
     *    infinite loop could result.
     * 3. Tcl_DeleteTimerHandler can be called to remove an element from
     * 3. Tcl_DeleteTimerHandler can be called to remove an element from
     *    the list while a handler is executing, so the list could
     *    the list while a handler is executing, so the list could
     *    change structure during the call.
     *    change structure during the call.
     * 4. Because we only fetch the current time before entering the loop,
     * 4. Because we only fetch the current time before entering the loop,
     *    the only way a new timer will even be considered runnable is if
     *    the only way a new timer will even be considered runnable is if
     *    its expiration time is within the same millisecond as the
     *    its expiration time is within the same millisecond as the
     *    current time.  This is fairly likely on Windows, since it has
     *    current time.  This is fairly likely on Windows, since it has
     *    a course granularity clock.  Since timers are placed
     *    a course granularity clock.  Since timers are placed
     *    on the queue in time order with the most recently created
     *    on the queue in time order with the most recently created
     *    handler appearing after earlier ones with the same expiration
     *    handler appearing after earlier ones with the same expiration
     *    time, we don't have to worry about newer generation timers
     *    time, we don't have to worry about newer generation timers
     *    appearing before later ones.
     *    appearing before later ones.
     */
     */
 
 
    timerPending = 0;
    timerPending = 0;
    currentTimerId = lastTimerId;
    currentTimerId = lastTimerId;
    TclpGetTime(&time);
    TclpGetTime(&time);
    while (1) {
    while (1) {
        nextPtrPtr = &firstTimerHandlerPtr;
        nextPtrPtr = &firstTimerHandlerPtr;
        timerHandlerPtr = firstTimerHandlerPtr;
        timerHandlerPtr = firstTimerHandlerPtr;
        if (timerHandlerPtr == NULL) {
        if (timerHandlerPtr == NULL) {
            break;
            break;
        }
        }
 
 
        if ((timerHandlerPtr->time.sec > time.sec)
        if ((timerHandlerPtr->time.sec > time.sec)
                || ((timerHandlerPtr->time.sec == time.sec)
                || ((timerHandlerPtr->time.sec == time.sec)
                        && (timerHandlerPtr->time.usec > time.usec))) {
                        && (timerHandlerPtr->time.usec > time.usec))) {
            break;
            break;
        }
        }
 
 
        /*
        /*
         * Bail out if the next timer is of a newer generation.
         * Bail out if the next timer is of a newer generation.
         */
         */
 
 
        if ((currentTimerId - (int)timerHandlerPtr->token) < 0) {
        if ((currentTimerId - (int)timerHandlerPtr->token) < 0) {
            break;
            break;
        }
        }
 
 
        /*
        /*
         * Remove the handler from the queue before invoking it,
         * Remove the handler from the queue before invoking it,
         * to avoid potential reentrancy problems.
         * to avoid potential reentrancy problems.
         */
         */
 
 
        (*nextPtrPtr) = timerHandlerPtr->nextPtr;
        (*nextPtrPtr) = timerHandlerPtr->nextPtr;
        (*timerHandlerPtr->proc)(timerHandlerPtr->clientData);
        (*timerHandlerPtr->proc)(timerHandlerPtr->clientData);
        ckfree((char *) timerHandlerPtr);
        ckfree((char *) timerHandlerPtr);
    }
    }
    TimerSetupProc(NULL, TCL_TIMER_EVENTS);
    TimerSetupProc(NULL, TCL_TIMER_EVENTS);
    return 1;
    return 1;
}
}


/*
/*
 *--------------------------------------------------------------
 *--------------------------------------------------------------
 *
 *
 * Tcl_DoWhenIdle --
 * Tcl_DoWhenIdle --
 *
 *
 *      Arrange for proc to be invoked the next time the system is
 *      Arrange for proc to be invoked the next time the system is
 *      idle (i.e., just before the next time that Tcl_DoOneEvent
 *      idle (i.e., just before the next time that Tcl_DoOneEvent
 *      would have to wait for something to happen).
 *      would have to wait for something to happen).
 *
 *
 * Results:
 * Results:
 *      None.
 *      None.
 *
 *
 * Side effects:
 * Side effects:
 *      Proc will eventually be called, with clientData as argument.
 *      Proc will eventually be called, with clientData as argument.
 *      See the manual entry for details.
 *      See the manual entry for details.
 *
 *
 *--------------------------------------------------------------
 *--------------------------------------------------------------
 */
 */
 
 
void
void
Tcl_DoWhenIdle(proc, clientData)
Tcl_DoWhenIdle(proc, clientData)
    Tcl_IdleProc *proc;         /* Procedure to invoke. */
    Tcl_IdleProc *proc;         /* Procedure to invoke. */
    ClientData clientData;      /* Arbitrary value to pass to proc. */
    ClientData clientData;      /* Arbitrary value to pass to proc. */
{
{
    register IdleHandler *idlePtr;
    register IdleHandler *idlePtr;
    Tcl_Time blockTime;
    Tcl_Time blockTime;
 
 
    if (!initialized) {
    if (!initialized) {
        InitTimer();
        InitTimer();
    }
    }
 
 
    idlePtr = (IdleHandler *) ckalloc(sizeof(IdleHandler));
    idlePtr = (IdleHandler *) ckalloc(sizeof(IdleHandler));
    idlePtr->proc = proc;
    idlePtr->proc = proc;
    idlePtr->clientData = clientData;
    idlePtr->clientData = clientData;
    idlePtr->generation = idleGeneration;
    idlePtr->generation = idleGeneration;
    idlePtr->nextPtr = NULL;
    idlePtr->nextPtr = NULL;
    if (lastIdlePtr == NULL) {
    if (lastIdlePtr == NULL) {
        idleList = idlePtr;
        idleList = idlePtr;
    } else {
    } else {
        lastIdlePtr->nextPtr = idlePtr;
        lastIdlePtr->nextPtr = idlePtr;
    }
    }
    lastIdlePtr = idlePtr;
    lastIdlePtr = idlePtr;
 
 
    blockTime.sec = 0;
    blockTime.sec = 0;
    blockTime.usec = 0;
    blockTime.usec = 0;
    Tcl_SetMaxBlockTime(&blockTime);
    Tcl_SetMaxBlockTime(&blockTime);
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * Tcl_CancelIdleCall --
 * Tcl_CancelIdleCall --
 *
 *
 *      If there are any when-idle calls requested to a given procedure
 *      If there are any when-idle calls requested to a given procedure
 *      with given clientData, cancel all of them.
 *      with given clientData, cancel all of them.
 *
 *
 * Results:
 * Results:
 *      None.
 *      None.
 *
 *
 * Side effects:
 * Side effects:
 *      If the proc/clientData combination were on the when-idle list,
 *      If the proc/clientData combination were on the when-idle list,
 *      they are removed so that they will never be called.
 *      they are removed so that they will never be called.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
void
void
Tcl_CancelIdleCall(proc, clientData)
Tcl_CancelIdleCall(proc, clientData)
    Tcl_IdleProc *proc;         /* Procedure that was previously registered. */
    Tcl_IdleProc *proc;         /* Procedure that was previously registered. */
    ClientData clientData;      /* Arbitrary value to pass to proc. */
    ClientData clientData;      /* Arbitrary value to pass to proc. */
{
{
    register IdleHandler *idlePtr, *prevPtr;
    register IdleHandler *idlePtr, *prevPtr;
    IdleHandler *nextPtr;
    IdleHandler *nextPtr;
 
 
    for (prevPtr = NULL, idlePtr = idleList; idlePtr != NULL;
    for (prevPtr = NULL, idlePtr = idleList; idlePtr != NULL;
            prevPtr = idlePtr, idlePtr = idlePtr->nextPtr) {
            prevPtr = idlePtr, idlePtr = idlePtr->nextPtr) {
        while ((idlePtr->proc == proc)
        while ((idlePtr->proc == proc)
                && (idlePtr->clientData == clientData)) {
                && (idlePtr->clientData == clientData)) {
            nextPtr = idlePtr->nextPtr;
            nextPtr = idlePtr->nextPtr;
            ckfree((char *) idlePtr);
            ckfree((char *) idlePtr);
            idlePtr = nextPtr;
            idlePtr = nextPtr;
            if (prevPtr == NULL) {
            if (prevPtr == NULL) {
                idleList = idlePtr;
                idleList = idlePtr;
            } else {
            } else {
                prevPtr->nextPtr = idlePtr;
                prevPtr->nextPtr = idlePtr;
            }
            }
            if (idlePtr == NULL) {
            if (idlePtr == NULL) {
                lastIdlePtr = prevPtr;
                lastIdlePtr = prevPtr;
                return;
                return;
            }
            }
        }
        }
    }
    }
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * TclServiceIdle --
 * TclServiceIdle --
 *
 *
 *      This procedure is invoked by the notifier when it becomes
 *      This procedure is invoked by the notifier when it becomes
 *      idle.  It will invoke all idle handlers that are present at
 *      idle.  It will invoke all idle handlers that are present at
 *      the time the call is invoked, but not those added during idle
 *      the time the call is invoked, but not those added during idle
 *      processing.
 *      processing.
 *
 *
 * Results:
 * Results:
 *      The return value is 1 if TclServiceIdle found something to
 *      The return value is 1 if TclServiceIdle found something to
 *      do, otherwise return value is 0.
 *      do, otherwise return value is 0.
 *
 *
 * Side effects:
 * Side effects:
 *      Invokes all pending idle handlers.
 *      Invokes all pending idle handlers.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
int
int
TclServiceIdle()
TclServiceIdle()
{
{
    IdleHandler *idlePtr;
    IdleHandler *idlePtr;
    int oldGeneration;
    int oldGeneration;
    Tcl_Time blockTime;
    Tcl_Time blockTime;
 
 
    if (idleList == NULL) {
    if (idleList == NULL) {
        return 0;
        return 0;
    }
    }
 
 
    oldGeneration = idleGeneration;
    oldGeneration = idleGeneration;
    idleGeneration++;
    idleGeneration++;
 
 
    /*
    /*
     * The code below is trickier than it may look, for the following
     * The code below is trickier than it may look, for the following
     * reasons:
     * reasons:
     *
     *
     * 1. New handlers can get added to the list while the current
     * 1. New handlers can get added to the list while the current
     *    one is being processed.  If new ones get added, we don't
     *    one is being processed.  If new ones get added, we don't
     *    want to process them during this pass through the list (want
     *    want to process them during this pass through the list (want
     *    to check for other work to do first).  This is implemented
     *    to check for other work to do first).  This is implemented
     *    using the generation number in the handler:  new handlers
     *    using the generation number in the handler:  new handlers
     *    will have a different generation than any of the ones currently
     *    will have a different generation than any of the ones currently
     *    on the list.
     *    on the list.
     * 2. The handler can call Tcl_DoOneEvent, so we have to remove
     * 2. The handler can call Tcl_DoOneEvent, so we have to remove
     *    the handler from the list before calling it. Otherwise an
     *    the handler from the list before calling it. Otherwise an
     *    infinite loop could result.
     *    infinite loop could result.
     * 3. Tcl_CancelIdleCall can be called to remove an element from
     * 3. Tcl_CancelIdleCall can be called to remove an element from
     *    the list while a handler is executing, so the list could
     *    the list while a handler is executing, so the list could
     *    change structure during the call.
     *    change structure during the call.
     */
     */
 
 
    for (idlePtr = idleList;
    for (idlePtr = idleList;
            ((idlePtr != NULL)
            ((idlePtr != NULL)
                    && ((oldGeneration - idlePtr->generation) >= 0));
                    && ((oldGeneration - idlePtr->generation) >= 0));
            idlePtr = idleList) {
            idlePtr = idleList) {
        idleList = idlePtr->nextPtr;
        idleList = idlePtr->nextPtr;
        if (idleList == NULL) {
        if (idleList == NULL) {
            lastIdlePtr = NULL;
            lastIdlePtr = NULL;
        }
        }
        (*idlePtr->proc)(idlePtr->clientData);
        (*idlePtr->proc)(idlePtr->clientData);
        ckfree((char *) idlePtr);
        ckfree((char *) idlePtr);
    }
    }
    if (idleList) {
    if (idleList) {
        blockTime.sec = 0;
        blockTime.sec = 0;
        blockTime.usec = 0;
        blockTime.usec = 0;
        Tcl_SetMaxBlockTime(&blockTime);
        Tcl_SetMaxBlockTime(&blockTime);
    }
    }
    return 1;
    return 1;
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * Tcl_AfterObjCmd --
 * Tcl_AfterObjCmd --
 *
 *
 *      This procedure is invoked to process the "after" Tcl command.
 *      This procedure is invoked to process the "after" Tcl command.
 *      See the user documentation for details on what it does.
 *      See the user documentation for details on what it does.
 *
 *
 * Results:
 * Results:
 *      A standard Tcl result.
 *      A standard Tcl result.
 *
 *
 * Side effects:
 * Side effects:
 *      See the user documentation.
 *      See the user documentation.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
        /* ARGSUSED */
        /* ARGSUSED */
int
int
Tcl_AfterObjCmd(clientData, interp, objc, objv)
Tcl_AfterObjCmd(clientData, interp, objc, objv)
    ClientData clientData;      /* Points to the "tclAfter" assocData for
    ClientData clientData;      /* Points to the "tclAfter" assocData for
                                 * this interpreter, or NULL if the assocData
                                 * this interpreter, or NULL if the assocData
                                 * hasn't been created yet.*/
                                 * hasn't been created yet.*/
    Tcl_Interp *interp;         /* Current interpreter. */
    Tcl_Interp *interp;         /* Current interpreter. */
    int objc;                   /* Number of arguments. */
    int objc;                   /* Number of arguments. */
    Tcl_Obj *CONST objv[];      /* Argument objects. */
    Tcl_Obj *CONST objv[];      /* Argument objects. */
{
{
    /*
    /*
     * The variable below is used to generate unique identifiers for
     * The variable below is used to generate unique identifiers for
     * after commands.  This id can wrap around, which can potentially
     * after commands.  This id can wrap around, which can potentially
     * cause problems.  However, there are not likely to be problems
     * cause problems.  However, there are not likely to be problems
     * in practice, because after commands can only be requested to
     * in practice, because after commands can only be requested to
     * about a month in the future, and wrap-around is unlikely to
     * about a month in the future, and wrap-around is unlikely to
     * occur in less than about 1-10 years.  Thus it's unlikely that
     * occur in less than about 1-10 years.  Thus it's unlikely that
     * any old ids will still be around when wrap-around occurs.
     * any old ids will still be around when wrap-around occurs.
     */
     */
 
 
    static int nextId = 1;
    static int nextId = 1;
    int ms;
    int ms;
    AfterInfo *afterPtr;
    AfterInfo *afterPtr;
    AfterAssocData *assocPtr = (AfterAssocData *) clientData;
    AfterAssocData *assocPtr = (AfterAssocData *) clientData;
    Tcl_CmdInfo cmdInfo;
    Tcl_CmdInfo cmdInfo;
    int length;
    int length;
    char *arg;
    char *arg;
    int index, result;
    int index, result;
    static char *subCmds[] = {
    static char *subCmds[] = {
        "cancel", "idle", "info",
        "cancel", "idle", "info",
        (char *) NULL};
        (char *) NULL};
 
 
    if (objc < 2) {
    if (objc < 2) {
        Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
        Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
        return TCL_ERROR;
        return TCL_ERROR;
    }
    }
 
 
    /*
    /*
     * Create the "after" information associated for this interpreter,
     * Create the "after" information associated for this interpreter,
     * if it doesn't already exist.  Associate it with the command too,
     * if it doesn't already exist.  Associate it with the command too,
     * so that it will be passed in as the ClientData argument in the
     * so that it will be passed in as the ClientData argument in the
     * future.
     * future.
     */
     */
 
 
    if (assocPtr == NULL) {
    if (assocPtr == NULL) {
        assocPtr = (AfterAssocData *) ckalloc(sizeof(AfterAssocData));
        assocPtr = (AfterAssocData *) ckalloc(sizeof(AfterAssocData));
        assocPtr->interp = interp;
        assocPtr->interp = interp;
        assocPtr->firstAfterPtr = NULL;
        assocPtr->firstAfterPtr = NULL;
        Tcl_SetAssocData(interp, "tclAfter", AfterCleanupProc,
        Tcl_SetAssocData(interp, "tclAfter", AfterCleanupProc,
                (ClientData) assocPtr);
                (ClientData) assocPtr);
        cmdInfo.proc = NULL;
        cmdInfo.proc = NULL;
        cmdInfo.clientData = (ClientData) NULL;
        cmdInfo.clientData = (ClientData) NULL;
        cmdInfo.objProc = Tcl_AfterObjCmd;
        cmdInfo.objProc = Tcl_AfterObjCmd;
        cmdInfo.objClientData = (ClientData) assocPtr;
        cmdInfo.objClientData = (ClientData) assocPtr;
        cmdInfo.deleteProc = NULL;
        cmdInfo.deleteProc = NULL;
        cmdInfo.deleteData = (ClientData) assocPtr;
        cmdInfo.deleteData = (ClientData) assocPtr;
        Tcl_SetCommandInfo(interp, Tcl_GetStringFromObj(objv[0], &length),
        Tcl_SetCommandInfo(interp, Tcl_GetStringFromObj(objv[0], &length),
                &cmdInfo);
                &cmdInfo);
    }
    }
 
 
    /*
    /*
     * First lets see if the command was passed a number as the first argument.
     * First lets see if the command was passed a number as the first argument.
     */
     */
 
 
    arg = Tcl_GetStringFromObj(objv[1], &length);
    arg = Tcl_GetStringFromObj(objv[1], &length);
    if (isdigit(UCHAR(arg[0]))) {
    if (isdigit(UCHAR(arg[0]))) {
        if (Tcl_GetIntFromObj(interp, objv[1], &ms) != TCL_OK) {
        if (Tcl_GetIntFromObj(interp, objv[1], &ms) != TCL_OK) {
            return TCL_ERROR;
            return TCL_ERROR;
        }
        }
        if (ms < 0) {
        if (ms < 0) {
            ms = 0;
            ms = 0;
        }
        }
        if (objc == 2) {
        if (objc == 2) {
            Tcl_Sleep(ms);
            Tcl_Sleep(ms);
            return TCL_OK;
            return TCL_OK;
        }
        }
        afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
        afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
        afterPtr->assocPtr = assocPtr;
        afterPtr->assocPtr = assocPtr;
        if (objc == 3) {
        if (objc == 3) {
            arg = Tcl_GetStringFromObj(objv[2], &length);
            arg = Tcl_GetStringFromObj(objv[2], &length);
            afterPtr->command = (char *) ckalloc((unsigned) (length + 1));
            afterPtr->command = (char *) ckalloc((unsigned) (length + 1));
            strcpy(afterPtr->command, arg);
            strcpy(afterPtr->command, arg);
        } else {
        } else {
            Tcl_Obj *objPtr = Tcl_ConcatObj(objc-2, objv+2);
            Tcl_Obj *objPtr = Tcl_ConcatObj(objc-2, objv+2);
            arg = Tcl_GetStringFromObj(objPtr, &length);
            arg = Tcl_GetStringFromObj(objPtr, &length);
            afterPtr->command = (char *) ckalloc((unsigned) (length + 1));
            afterPtr->command = (char *) ckalloc((unsigned) (length + 1));
            strcpy(afterPtr->command, arg);
            strcpy(afterPtr->command, arg);
            Tcl_DecrRefCount(objPtr);
            Tcl_DecrRefCount(objPtr);
        }
        }
        afterPtr->id = nextId;
        afterPtr->id = nextId;
        nextId += 1;
        nextId += 1;
        afterPtr->token = Tcl_CreateTimerHandler(ms, AfterProc,
        afterPtr->token = Tcl_CreateTimerHandler(ms, AfterProc,
                (ClientData) afterPtr);
                (ClientData) afterPtr);
        afterPtr->nextPtr = assocPtr->firstAfterPtr;
        afterPtr->nextPtr = assocPtr->firstAfterPtr;
        assocPtr->firstAfterPtr = afterPtr;
        assocPtr->firstAfterPtr = afterPtr;
        sprintf(interp->result, "after#%d", afterPtr->id);
        sprintf(interp->result, "after#%d", afterPtr->id);
        return TCL_OK;
        return TCL_OK;
    }
    }
 
 
    /*
    /*
     * If it's not a number it must be a subcommand.
     * If it's not a number it must be a subcommand.
     */
     */
    result = Tcl_GetIndexFromObj(NULL, objv[1], subCmds, "option",
    result = Tcl_GetIndexFromObj(NULL, objv[1], subCmds, "option",
            0, (int *) &index);
            0, (int *) &index);
    if (result != TCL_OK) {
    if (result != TCL_OK) {
        Tcl_AppendResult(interp, "bad argument \"", arg,
        Tcl_AppendResult(interp, "bad argument \"", arg,
                "\": must be cancel, idle, info, or a number",
                "\": must be cancel, idle, info, or a number",
                (char *) NULL);
                (char *) NULL);
        return TCL_ERROR;
        return TCL_ERROR;
    }
    }
 
 
    switch (index) {
    switch (index) {
        case 0:          /* cancel */
        case 0:          /* cancel */
            {
            {
                char *arg;
                char *arg;
                Tcl_Obj *objPtr = NULL;
                Tcl_Obj *objPtr = NULL;
 
 
                if (objc < 3) {
                if (objc < 3) {
                    Tcl_WrongNumArgs(interp, 2, objv, "id|command");
                    Tcl_WrongNumArgs(interp, 2, objv, "id|command");
                    return TCL_ERROR;
                    return TCL_ERROR;
                }
                }
                if (objc == 3) {
                if (objc == 3) {
                    arg = Tcl_GetStringFromObj(objv[2], &length);
                    arg = Tcl_GetStringFromObj(objv[2], &length);
                } else {
                } else {
                    objPtr = Tcl_ConcatObj(objc-2, objv+2);;
                    objPtr = Tcl_ConcatObj(objc-2, objv+2);;
                    arg = Tcl_GetStringFromObj(objPtr, &length);
                    arg = Tcl_GetStringFromObj(objPtr, &length);
                }
                }
                for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
                for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
                     afterPtr = afterPtr->nextPtr) {
                     afterPtr = afterPtr->nextPtr) {
                    if (strcmp(afterPtr->command, arg) == 0) {
                    if (strcmp(afterPtr->command, arg) == 0) {
                        break;
                        break;
                    }
                    }
                }
                }
                if (afterPtr == NULL) {
                if (afterPtr == NULL) {
                    afterPtr = GetAfterEvent(assocPtr, arg);
                    afterPtr = GetAfterEvent(assocPtr, arg);
                }
                }
                if (objPtr != NULL) {
                if (objPtr != NULL) {
                    Tcl_DecrRefCount(objPtr);
                    Tcl_DecrRefCount(objPtr);
                }
                }
                if (afterPtr != NULL) {
                if (afterPtr != NULL) {
                    if (afterPtr->token != NULL) {
                    if (afterPtr->token != NULL) {
                        Tcl_DeleteTimerHandler(afterPtr->token);
                        Tcl_DeleteTimerHandler(afterPtr->token);
                    } else {
                    } else {
                        Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);
                        Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);
                    }
                    }
                    FreeAfterPtr(afterPtr);
                    FreeAfterPtr(afterPtr);
                }
                }
                break;
                break;
            }
            }
        case 1:         /* idle */
        case 1:         /* idle */
            if (objc < 3) {
            if (objc < 3) {
                Tcl_WrongNumArgs(interp, 2, objv, "script script ...");
                Tcl_WrongNumArgs(interp, 2, objv, "script script ...");
                return TCL_ERROR;
                return TCL_ERROR;
            }
            }
            afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
            afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
            afterPtr->assocPtr = assocPtr;
            afterPtr->assocPtr = assocPtr;
            if (objc == 3) {
            if (objc == 3) {
                arg = Tcl_GetStringFromObj(objv[2], &length);
                arg = Tcl_GetStringFromObj(objv[2], &length);
                afterPtr->command = (char *) ckalloc((unsigned) length + 1);
                afterPtr->command = (char *) ckalloc((unsigned) length + 1);
                strcpy(afterPtr->command, arg);
                strcpy(afterPtr->command, arg);
            } else {
            } else {
                Tcl_Obj *objPtr = Tcl_ConcatObj(objc-2, objv+2);;
                Tcl_Obj *objPtr = Tcl_ConcatObj(objc-2, objv+2);;
                arg = Tcl_GetStringFromObj(objPtr, &length);
                arg = Tcl_GetStringFromObj(objPtr, &length);
                afterPtr->command = (char *) ckalloc((unsigned) (length + 1));
                afterPtr->command = (char *) ckalloc((unsigned) (length + 1));
                strcpy(afterPtr->command, arg);
                strcpy(afterPtr->command, arg);
                Tcl_DecrRefCount(objPtr);
                Tcl_DecrRefCount(objPtr);
            }
            }
            afterPtr->id = nextId;
            afterPtr->id = nextId;
            nextId += 1;
            nextId += 1;
            afterPtr->token = NULL;
            afterPtr->token = NULL;
            afterPtr->nextPtr = assocPtr->firstAfterPtr;
            afterPtr->nextPtr = assocPtr->firstAfterPtr;
            assocPtr->firstAfterPtr = afterPtr;
            assocPtr->firstAfterPtr = afterPtr;
            Tcl_DoWhenIdle(AfterProc, (ClientData) afterPtr);
            Tcl_DoWhenIdle(AfterProc, (ClientData) afterPtr);
            sprintf(interp->result, "after#%d", afterPtr->id);
            sprintf(interp->result, "after#%d", afterPtr->id);
            break;
            break;
        case 2:         /* info */
        case 2:         /* info */
            if (objc == 2) {
            if (objc == 2) {
                char buffer[30];
                char buffer[30];
 
 
                for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
                for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
                     afterPtr = afterPtr->nextPtr) {
                     afterPtr = afterPtr->nextPtr) {
                    if (assocPtr->interp == interp) {
                    if (assocPtr->interp == interp) {
                        sprintf(buffer, "after#%d", afterPtr->id);
                        sprintf(buffer, "after#%d", afterPtr->id);
                        Tcl_AppendElement(interp, buffer);
                        Tcl_AppendElement(interp, buffer);
                    }
                    }
                }
                }
                return TCL_OK;
                return TCL_OK;
            }
            }
            if (objc != 3) {
            if (objc != 3) {
                Tcl_WrongNumArgs(interp, 2, objv, "?id?");
                Tcl_WrongNumArgs(interp, 2, objv, "?id?");
                return TCL_ERROR;
                return TCL_ERROR;
            }
            }
            arg = Tcl_GetStringFromObj(objv[2], &length);
            arg = Tcl_GetStringFromObj(objv[2], &length);
            afterPtr = GetAfterEvent(assocPtr, arg);
            afterPtr = GetAfterEvent(assocPtr, arg);
            if (afterPtr == NULL) {
            if (afterPtr == NULL) {
                Tcl_AppendResult(interp, "event \"", arg,
                Tcl_AppendResult(interp, "event \"", arg,
                        "\" doesn't exist", (char *) NULL);
                        "\" doesn't exist", (char *) NULL);
                return TCL_ERROR;
                return TCL_ERROR;
            }
            }
            Tcl_AppendElement(interp, afterPtr->command);
            Tcl_AppendElement(interp, afterPtr->command);
            Tcl_AppendElement(interp,
            Tcl_AppendElement(interp,
                    (afterPtr->token == NULL) ? "idle" : "timer");
                    (afterPtr->token == NULL) ? "idle" : "timer");
            break;
            break;
    }
    }
    return TCL_OK;
    return TCL_OK;
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * GetAfterEvent --
 * GetAfterEvent --
 *
 *
 *      This procedure parses an "after" id such as "after#4" and
 *      This procedure parses an "after" id such as "after#4" and
 *      returns a pointer to the AfterInfo structure.
 *      returns a pointer to the AfterInfo structure.
 *
 *
 * Results:
 * Results:
 *      The return value is either a pointer to an AfterInfo structure,
 *      The return value is either a pointer to an AfterInfo structure,
 *      if one is found that corresponds to "string" and is for interp,
 *      if one is found that corresponds to "string" and is for interp,
 *      or NULL if no corresponding after event can be found.
 *      or NULL if no corresponding after event can be found.
 *
 *
 * Side effects:
 * Side effects:
 *      None.
 *      None.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static AfterInfo *
static AfterInfo *
GetAfterEvent(assocPtr, string)
GetAfterEvent(assocPtr, string)
    AfterAssocData *assocPtr;   /* Points to "after"-related information for
    AfterAssocData *assocPtr;   /* Points to "after"-related information for
                                 * this interpreter. */
                                 * this interpreter. */
    char *string;               /* Textual identifier for after event, such
    char *string;               /* Textual identifier for after event, such
                                 * as "after#6". */
                                 * as "after#6". */
{
{
    AfterInfo *afterPtr;
    AfterInfo *afterPtr;
    int id;
    int id;
    char *end;
    char *end;
 
 
    if (strncmp(string, "after#", 6) != 0) {
    if (strncmp(string, "after#", 6) != 0) {
        return NULL;
        return NULL;
    }
    }
    string += 6;
    string += 6;
    id = strtoul(string, &end, 10);
    id = strtoul(string, &end, 10);
    if ((end == string) || (*end != 0)) {
    if ((end == string) || (*end != 0)) {
        return NULL;
        return NULL;
    }
    }
    for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
    for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
            afterPtr = afterPtr->nextPtr) {
            afterPtr = afterPtr->nextPtr) {
        if (afterPtr->id == id) {
        if (afterPtr->id == id) {
            return afterPtr;
            return afterPtr;
        }
        }
    }
    }
    return NULL;
    return NULL;
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * AfterProc --
 * AfterProc --
 *
 *
 *      Timer callback to execute commands registered with the
 *      Timer callback to execute commands registered with the
 *      "after" command.
 *      "after" command.
 *
 *
 * Results:
 * Results:
 *      None.
 *      None.
 *
 *
 * Side effects:
 * Side effects:
 *      Executes whatever command was specified.  If the command
 *      Executes whatever command was specified.  If the command
 *      returns an error, then the command "bgerror" is invoked
 *      returns an error, then the command "bgerror" is invoked
 *      to process the error;  if bgerror fails then information
 *      to process the error;  if bgerror fails then information
 *      about the error is output on stderr.
 *      about the error is output on stderr.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static void
static void
AfterProc(clientData)
AfterProc(clientData)
    ClientData clientData;      /* Describes command to execute. */
    ClientData clientData;      /* Describes command to execute. */
{
{
    AfterInfo *afterPtr = (AfterInfo *) clientData;
    AfterInfo *afterPtr = (AfterInfo *) clientData;
    AfterAssocData *assocPtr = afterPtr->assocPtr;
    AfterAssocData *assocPtr = afterPtr->assocPtr;
    AfterInfo *prevPtr;
    AfterInfo *prevPtr;
    int result;
    int result;
    Tcl_Interp *interp;
    Tcl_Interp *interp;
 
 
    /*
    /*
     * First remove the callback from our list of callbacks;  otherwise
     * First remove the callback from our list of callbacks;  otherwise
     * someone could delete the callback while it's being executed, which
     * someone could delete the callback while it's being executed, which
     * could cause a core dump.
     * could cause a core dump.
     */
     */
 
 
    if (assocPtr->firstAfterPtr == afterPtr) {
    if (assocPtr->firstAfterPtr == afterPtr) {
        assocPtr->firstAfterPtr = afterPtr->nextPtr;
        assocPtr->firstAfterPtr = afterPtr->nextPtr;
    } else {
    } else {
        for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;
        for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;
                prevPtr = prevPtr->nextPtr) {
                prevPtr = prevPtr->nextPtr) {
            /* Empty loop body. */
            /* Empty loop body. */
        }
        }
        prevPtr->nextPtr = afterPtr->nextPtr;
        prevPtr->nextPtr = afterPtr->nextPtr;
    }
    }
 
 
    /*
    /*
     * Execute the callback.
     * Execute the callback.
     */
     */
 
 
    interp = assocPtr->interp;
    interp = assocPtr->interp;
    Tcl_Preserve((ClientData) interp);
    Tcl_Preserve((ClientData) interp);
    result = Tcl_GlobalEval(interp, afterPtr->command);
    result = Tcl_GlobalEval(interp, afterPtr->command);
    if (result != TCL_OK) {
    if (result != TCL_OK) {
        Tcl_AddErrorInfo(interp, "\n    (\"after\" script)");
        Tcl_AddErrorInfo(interp, "\n    (\"after\" script)");
        Tcl_BackgroundError(interp);
        Tcl_BackgroundError(interp);
    }
    }
    Tcl_Release((ClientData) interp);
    Tcl_Release((ClientData) interp);
 
 
    /*
    /*
     * Free the memory for the callback.
     * Free the memory for the callback.
     */
     */
 
 
    ckfree(afterPtr->command);
    ckfree(afterPtr->command);
    ckfree((char *) afterPtr);
    ckfree((char *) afterPtr);
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * FreeAfterPtr --
 * FreeAfterPtr --
 *
 *
 *      This procedure removes an "after" command from the list of
 *      This procedure removes an "after" command from the list of
 *      those that are pending and frees its resources.  This procedure
 *      those that are pending and frees its resources.  This procedure
 *      does *not* cancel the timer handler;  if that's needed, the
 *      does *not* cancel the timer handler;  if that's needed, the
 *      caller must do it.
 *      caller must do it.
 *
 *
 * Results:
 * Results:
 *      None.
 *      None.
 *
 *
 * Side effects:
 * Side effects:
 *      The memory associated with afterPtr is released.
 *      The memory associated with afterPtr is released.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static void
static void
FreeAfterPtr(afterPtr)
FreeAfterPtr(afterPtr)
    AfterInfo *afterPtr;                /* Command to be deleted. */
    AfterInfo *afterPtr;                /* Command to be deleted. */
{
{
    AfterInfo *prevPtr;
    AfterInfo *prevPtr;
    AfterAssocData *assocPtr = afterPtr->assocPtr;
    AfterAssocData *assocPtr = afterPtr->assocPtr;
 
 
    if (assocPtr->firstAfterPtr == afterPtr) {
    if (assocPtr->firstAfterPtr == afterPtr) {
        assocPtr->firstAfterPtr = afterPtr->nextPtr;
        assocPtr->firstAfterPtr = afterPtr->nextPtr;
    } else {
    } else {
        for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;
        for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;
                prevPtr = prevPtr->nextPtr) {
                prevPtr = prevPtr->nextPtr) {
            /* Empty loop body. */
            /* Empty loop body. */
        }
        }
        prevPtr->nextPtr = afterPtr->nextPtr;
        prevPtr->nextPtr = afterPtr->nextPtr;
    }
    }
    ckfree(afterPtr->command);
    ckfree(afterPtr->command);
    ckfree((char *) afterPtr);
    ckfree((char *) afterPtr);
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * AfterCleanupProc --
 * AfterCleanupProc --
 *
 *
 *      This procedure is invoked whenever an interpreter is deleted
 *      This procedure is invoked whenever an interpreter is deleted
 *      to cleanup the AssocData for "tclAfter".
 *      to cleanup the AssocData for "tclAfter".
 *
 *
 * Results:
 * Results:
 *      None.
 *      None.
 *
 *
 * Side effects:
 * Side effects:
 *      After commands are removed.
 *      After commands are removed.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
        /* ARGSUSED */
        /* ARGSUSED */
static void
static void
AfterCleanupProc(clientData, interp)
AfterCleanupProc(clientData, interp)
    ClientData clientData;      /* Points to AfterAssocData for the
    ClientData clientData;      /* Points to AfterAssocData for the
                                 * interpreter. */
                                 * interpreter. */
    Tcl_Interp *interp;         /* Interpreter that is being deleted. */
    Tcl_Interp *interp;         /* Interpreter that is being deleted. */
{
{
    AfterAssocData *assocPtr = (AfterAssocData *) clientData;
    AfterAssocData *assocPtr = (AfterAssocData *) clientData;
    AfterInfo *afterPtr;
    AfterInfo *afterPtr;
 
 
    while (assocPtr->firstAfterPtr != NULL) {
    while (assocPtr->firstAfterPtr != NULL) {
        afterPtr = assocPtr->firstAfterPtr;
        afterPtr = assocPtr->firstAfterPtr;
        assocPtr->firstAfterPtr = afterPtr->nextPtr;
        assocPtr->firstAfterPtr = afterPtr->nextPtr;
        if (afterPtr->token != NULL) {
        if (afterPtr->token != NULL) {
            Tcl_DeleteTimerHandler(afterPtr->token);
            Tcl_DeleteTimerHandler(afterPtr->token);
        } else {
        } else {
            Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);
            Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);
        }
        }
        ckfree(afterPtr->command);
        ckfree(afterPtr->command);
        ckfree((char *) afterPtr);
        ckfree((char *) afterPtr);
    }
    }
    ckfree((char *) assocPtr);
    ckfree((char *) assocPtr);
}
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.