tcltimer.c
来自「tcl是工具命令语言」· C语言 代码 · 共 1,143 行 · 第 1/3 页
C
1,143 行
* 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 * future. */ if (assocPtr == NULL) { assocPtr = (AfterAssocData *) ckalloc(sizeof(AfterAssocData)); assocPtr->interp = interp; assocPtr->firstAfterPtr = NULL; Tcl_SetAssocData(interp, "tclAfter", AfterCleanupProc, (ClientData) assocPtr); cmdInfo.proc = NULL; cmdInfo.clientData = (ClientData) NULL; cmdInfo.objProc = Tcl_AfterObjCmd; cmdInfo.objClientData = (ClientData) assocPtr; cmdInfo.deleteProc = NULL; cmdInfo.deleteData = (ClientData) assocPtr; Tcl_SetCommandInfo(interp, Tcl_GetStringFromObj(objv[0], &length), &cmdInfo); } /* * First lets see if the command was passed a number as the first argument. */ if (objv[1]->typePtr == &tclIntType) { ms = (int) objv[1]->internalRep.longValue; goto processInteger; } argString = Tcl_GetStringFromObj(objv[1], &length); if (isdigit(UCHAR(argString[0]))) { /* INTL: digit */ if (Tcl_GetIntFromObj(interp, objv[1], &ms) != TCL_OK) { return TCL_ERROR; }processInteger: if (ms < 0) { ms = 0; } if (objc == 2) { Tcl_Sleep(ms); return TCL_OK; } afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo))); afterPtr->assocPtr = assocPtr; if (objc == 3) { afterPtr->commandPtr = objv[2]; } else { afterPtr->commandPtr = Tcl_ConcatObj(objc-2, objv+2); } Tcl_IncrRefCount(afterPtr->commandPtr); /* * The variable below is used to generate unique identifiers for * after commands. This id can wrap around, which can potentially * cause problems. However, there are not likely to be problems * in practice, because after commands can only be requested 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 * any old ids will still be around when wrap-around occurs. */ afterPtr->id = tsdPtr->afterId; tsdPtr->afterId += 1; afterPtr->token = Tcl_CreateTimerHandler(ms, AfterProc, (ClientData) afterPtr); afterPtr->nextPtr = assocPtr->firstAfterPtr; assocPtr->firstAfterPtr = afterPtr; sprintf(buf, "after#%d", afterPtr->id); Tcl_AppendResult(interp, buf, (char *) NULL); return TCL_OK; } /* * If it's not a number it must be a subcommand. */ if (Tcl_GetIndexFromObj(NULL, objv[1], afterSubCmds, "argument", 0, &index) != TCL_OK) { Tcl_AppendResult(interp, "bad argument \"", argString, "\": must be cancel, idle, info, or a number", (char *) NULL); return TCL_ERROR; } switch ((enum afterSubCmds) index) { case AFTER_CANCEL: { Tcl_Obj *commandPtr; char *command, *tempCommand; int tempLength; if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "id|command"); return TCL_ERROR; } if (objc == 3) { commandPtr = objv[2]; } else { commandPtr = Tcl_ConcatObj(objc-2, objv+2);; } command = Tcl_GetStringFromObj(commandPtr, &length); for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL; afterPtr = afterPtr->nextPtr) { tempCommand = Tcl_GetStringFromObj(afterPtr->commandPtr, &tempLength); if ((length == tempLength) && (memcmp((void*) command, (void*) tempCommand, (unsigned) length) == 0)) { break; } } if (afterPtr == NULL) { afterPtr = GetAfterEvent(assocPtr, commandPtr); } if (objc != 3) { Tcl_DecrRefCount(commandPtr); } if (afterPtr != NULL) { if (afterPtr->token != NULL) { Tcl_DeleteTimerHandler(afterPtr->token); } else { Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr); } FreeAfterPtr(afterPtr); } break; } case AFTER_IDLE: if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "script script ..."); return TCL_ERROR; } afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo))); afterPtr->assocPtr = assocPtr; if (objc == 3) { afterPtr->commandPtr = objv[2]; } else { afterPtr->commandPtr = Tcl_ConcatObj(objc-2, objv+2); } Tcl_IncrRefCount(afterPtr->commandPtr); afterPtr->id = tsdPtr->afterId; tsdPtr->afterId += 1; afterPtr->token = NULL; afterPtr->nextPtr = assocPtr->firstAfterPtr; assocPtr->firstAfterPtr = afterPtr; Tcl_DoWhenIdle(AfterProc, (ClientData) afterPtr); sprintf(buf, "after#%d", afterPtr->id); Tcl_AppendResult(interp, buf, (char *) NULL); break; case AFTER_INFO: { Tcl_Obj *resultListPtr; if (objc == 2) { for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL; afterPtr = afterPtr->nextPtr) { if (assocPtr->interp == interp) { sprintf(buf, "after#%d", afterPtr->id); Tcl_AppendElement(interp, buf); } } return TCL_OK; } if (objc != 3) { Tcl_WrongNumArgs(interp, 2, objv, "?id?"); return TCL_ERROR; } afterPtr = GetAfterEvent(assocPtr, objv[2]); if (afterPtr == NULL) { Tcl_AppendResult(interp, "event \"", Tcl_GetString(objv[2]), "\" doesn't exist", (char *) NULL); return TCL_ERROR; } resultListPtr = Tcl_GetObjResult(interp); Tcl_ListObjAppendElement(interp, resultListPtr, afterPtr->commandPtr); Tcl_ListObjAppendElement(interp, resultListPtr, Tcl_NewStringObj( (afterPtr->token == NULL) ? "idle" : "timer", -1)); Tcl_SetObjResult(interp, resultListPtr); break; } default: { panic("Tcl_AfterObjCmd: bad subcommand index to afterSubCmds"); } } return TCL_OK;}/* *---------------------------------------------------------------------- * * GetAfterEvent -- * * This procedure parses an "after" id such as "after#4" and * returns a pointer to the AfterInfo structure. * * Results: * The return value is either a pointer to an AfterInfo structure, * if one is found that corresponds to "cmdString" and is for interp, * or NULL if no corresponding after event can be found. * * Side effects: * None. * *---------------------------------------------------------------------- */static AfterInfo *GetAfterEvent(assocPtr, commandPtr) AfterAssocData *assocPtr; /* Points to "after"-related information for * this interpreter. */ Tcl_Obj *commandPtr;{ char *cmdString; /* Textual identifier for after event, such * as "after#6". */ AfterInfo *afterPtr; int id; char *end; cmdString = Tcl_GetString(commandPtr); if (strncmp(cmdString, "after#", 6) != 0) { return NULL; } cmdString += 6; id = strtoul(cmdString, &end, 10); if ((end == cmdString) || (*end != 0)) { return NULL; } for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL; afterPtr = afterPtr->nextPtr) { if (afterPtr->id == id) { return afterPtr; } } return NULL;}/* *---------------------------------------------------------------------- * * AfterProc -- * * Timer callback to execute commands registered with the * "after" command. * * Results: * None. * * Side effects: * Executes whatever command was specified. If the command * returns an error, then the command "bgerror" is invoked * to process the error; if bgerror fails then information * about the error is output on stderr. * *---------------------------------------------------------------------- */static voidAfterProc(clientData) ClientData clientData; /* Describes command to execute. */{ AfterInfo *afterPtr = (AfterInfo *) clientData; AfterAssocData *assocPtr = afterPtr->assocPtr; AfterInfo *prevPtr; int result; Tcl_Interp *interp; char *script; int numBytes; /* * First remove the callback from our list of callbacks; otherwise * someone could delete the callback while it's being executed, which * could cause a core dump. */ if (assocPtr->firstAfterPtr == afterPtr) { assocPtr->firstAfterPtr = afterPtr->nextPtr; } else { for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr; prevPtr = prevPtr->nextPtr) { /* Empty loop body. */ } prevPtr->nextPtr = afterPtr->nextPtr; } /* * Execute the callback. */ interp = assocPtr->interp; Tcl_Preserve((ClientData) interp); script = Tcl_GetStringFromObj(afterPtr->commandPtr, &numBytes); result = Tcl_EvalEx(interp, script, numBytes, TCL_EVAL_GLOBAL); if (result != TCL_OK) { Tcl_AddErrorInfo(interp, "\n (\"after\" script)"); Tcl_BackgroundError(interp); } Tcl_Release((ClientData) interp); /* * Free the memory for the callback. */ Tcl_DecrRefCount(afterPtr->commandPtr); ckfree((char *) afterPtr);}/* *---------------------------------------------------------------------- * * FreeAfterPtr -- * * This procedure removes an "after" command from the list of * those that are pending and frees its resources. This procedure * does *not* cancel the timer handler; if that's needed, the * caller must do it. * * Results: * None. * * Side effects: * The memory associated with afterPtr is released. * *---------------------------------------------------------------------- */static voidFreeAfterPtr(afterPtr) AfterInfo *afterPtr; /* Command to be deleted. */{ AfterInfo *prevPtr; AfterAssocData *assocPtr = afterPtr->assocPtr; if (assocPtr->firstAfterPtr == afterPtr) { assocPtr->firstAfterPtr = afterPtr->nextPtr; } else { for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr; prevPtr = prevPtr->nextPtr) { /* Empty loop body. */ } prevPtr->nextPtr = afterPtr->nextPtr; } Tcl_DecrRefCount(afterPtr->commandPtr); ckfree((char *) afterPtr);}/* *---------------------------------------------------------------------- * * AfterCleanupProc -- * * This procedure is invoked whenever an interpreter is deleted * to cleanup the AssocData for "tclAfter". * * Results: * None. * * Side effects: * After commands are removed. * *---------------------------------------------------------------------- */ /* ARGSUSED */static voidAfterCleanupProc(clientData, interp) ClientData clientData; /* Points to AfterAssocData for the * interpreter. */ Tcl_Interp *interp; /* Interpreter that is being deleted. */{ AfterAssocData *assocPtr = (AfterAssocData *) clientData; AfterInfo *afterPtr; while (assocPtr->firstAfterPtr != NULL) { afterPtr = assocPtr->firstAfterPtr; assocPtr->firstAfterPtr = afterPtr->nextPtr; if (afterPtr->token != NULL) { Tcl_DeleteTimerHandler(afterPtr->token); } else { Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr); } Tcl_DecrRefCount(afterPtr->commandPtr); ckfree((char *) afterPtr); } ckfree((char *) assocPtr);}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?