From f0b9df0a4c92b2125d0b7b5600aaeee90e9ed486 Mon Sep 17 00:00:00 2001 From: dgp Date: Thu, 16 Mar 2006 17:32:27 +0000 Subject: * generic/tkConsole.c: Substantial rewrite of [console] support. * generic/tkInt.h: Included Obj-ification of the [console] and [consoleinterp] commands, and reworking of all the supporting data structures for cleaner sharing and lifetime management especially in multi-threaded configurations. --- ChangeLog | 10 +- generic/tkConsole.c | 655 ++++++++++++++++++++++++++++------------------------ generic/tkInt.h | 4 +- 3 files changed, 366 insertions(+), 303 deletions(-) diff --git a/ChangeLog b/ChangeLog index 888e0ba..a5ed2c6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2006-03-16 Don Porter + + * generic/tkConsole.c: Substantial rewrite of [console] support. + * generic/tkInt.h: Included Obj-ification of the [console] and + [consoleinterp] commands, and reworking of all the supporting data + structures for cleaner sharing and lifetime management especially + in multi-threaded configurations. + 2006-03-16 Donal K. Fellows * library/msgs/pt.msg: Messages for Portuguese (strictly just for @@ -18,7 +26,7 @@ single pixels. Masses of thanks to George Staplin for helping to trace this down to the COMPLEX_ALPHA flag handling code. [Bug 1409140] -006-03-13 Don Porter +2006-03-13 Don Porter * tests/scrollbar.test: Corrected several broken calls to [testmetrics] that were crashing the test suite. diff --git a/generic/tkConsole.c b/generic/tkConsole.c index 7f64485..306c6da 100644 --- a/generic/tkConsole.c +++ b/generic/tkConsole.c @@ -10,67 +10,56 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkConsole.c,v 1.24 2005/11/11 23:51:27 dkf Exp $ + * RCS: @(#) $Id: tkConsole.c,v 1.25 2006/03/16 17:32:27 dgp Exp $ */ #include "tk.h" -#include - -#include "tkInt.h" /* - * A data structure of the following type holds information for each console - * which a handler (i.e. a Tcl command) has been defined for a particular - * top-level window. + * Each console is associated with an instance of the ConsoleInfo struct. + * It keeps track of what interp holds the Tk application that displays + * the console, and what interp is controlled by the interactions in that + * console. A refCount permits the struct to be shared as instance data + * by commands and by channels. */ typedef struct ConsoleInfo { - Tcl_Interp *consoleInterp; /* Interpreter for the console. */ - Tcl_Interp *interp; /* Interpreter to send console commands. */ + Tcl_Interp *consoleInterp; /* Interpreter displaying the console. */ + Tcl_Interp *interp; /* Interpreter controlled by console. */ + int refCount; } ConsoleInfo; /* - * Each interpreter with a console attached stores a reference to the - * interpreter's ConsoleInfo in the interpreter's AssocData store. The - * alternative is to look the values up by examining the "console" command and - * that is fragile. [Bug 1016385] - */ - -#define TK_CONSOLE_INFO_KEY "tk::ConsoleInfo" - -typedef struct ThreadSpecificData { - Tcl_Interp *gStdoutInterp; -} ThreadSpecificData; -static Tcl_ThreadDataKey dataKey; -static int consoleInitialized = 0; - -/* - * The Mutex below is used to lock access to the consoleIntialized flag + * Each console channel holds an instance of the ChannelData struct as + * its instance data. It contains ConsoleInfo, so the channel can work + * with the appropriate console window, and a type value to distinguish + * the stdout channel from the stderr channel. */ -TCL_DECLARE_MUTEX(consoleMutex) +typedef struct ChannelData { + ConsoleInfo *info; + int type; /* TCL_STDOUT or TCL_STDERR */ +} ChannelData; /* - * Forward declarations for functions defined later in this file: - * - * The first three will be used in the tk app shells... + * Prototypes for local procedures defined in this file: */ -static int ConsoleCmd(ClientData clientData, - Tcl_Interp *interp, int argc, CONST char **argv); +static int ConsoleClose(ClientData instanceData, Tcl_Interp *interp); static void ConsoleDeleteProc(ClientData clientData); static void ConsoleEventProc(ClientData clientData, XEvent *eventPtr); -static int InterpreterCmd(ClientData clientData, - Tcl_Interp *interp, int argc, CONST char **argv); - +static int ConsoleHandle(ClientData instanceData, + int direction, ClientData *handlePtr); static int ConsoleInput(ClientData instanceData, char *buf, int toRead, int *errorCode); +static int ConsoleObjCmd(ClientData clientData, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[]); static int ConsoleOutput(ClientData instanceData, CONST char *buf, int toWrite, int *errorCode); -static int ConsoleClose(ClientData instanceData, Tcl_Interp *interp); static void ConsoleWatch(ClientData instanceData, int mask); -static int ConsoleHandle(ClientData instanceData, - int direction, ClientData *handlePtr); +static void InterpDeleteProc(ClientData clientData, Tcl_Interp *interp); +static int InterpreterObjCmd(ClientData clientData, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[]); /* * This structure describes the channel type structure for file based IO: @@ -227,6 +216,9 @@ void Tk_InitConsoleChannels( Tcl_Interp *interp) { + static Tcl_ThreadDataKey consoleInitKey; + int *consoleInitPtr, doIn, doOut, doErr; + ConsoleInfo *info; Tcl_Channel consoleChannel; /* @@ -238,75 +230,88 @@ Tk_InitConsoleChannels( return; } - Tcl_MutexLock(&consoleMutex); - if (!consoleInitialized) { + consoleInitPtr = Tcl_GetThreadData(&consoleInitKey, (int)sizeof(int)); + if (*consoleInitPtr) { + /* We've already initialized console channels in this thread. */ + return; + } + *consoleInitPtr = 1; - consoleInitialized = 1; + doIn = ShouldUseConsoleChannel(TCL_STDIN); + doOut = ShouldUseConsoleChannel(TCL_STDOUT); + doErr = ShouldUseConsoleChannel(TCL_STDERR); + if (!(doIn || doOut || doErr)) { /* - * check for STDIN, otherwise create it - * - * Don't do this check on the Mac, because it is hard to prevent - * callbacks from the SIOUX layer from opening stdout & stdin, but we - * don't want to use the SIOUX console. Since the console is not - * actually created till something is written to the channel, it is - * okay to just ignore it here. - * - * This is still a bit of a hack, however, and should be cleaned up - * when we have a better abstraction for the console. + * No std channels should be tied to the console; + * Thus, no need to create the console */ + return; + } - if (ShouldUseConsoleChannel(TCL_STDIN)) { - consoleChannel = Tcl_CreateChannel(&consoleChannelType, "console0", - (ClientData) TCL_STDIN, TCL_READABLE); - if (consoleChannel != NULL) { - Tcl_SetChannelOption(NULL, consoleChannel, - "-translation", "lf"); - Tcl_SetChannelOption(NULL, consoleChannel, - "-buffering", "none"); - Tcl_SetChannelOption(NULL, consoleChannel, - "-encoding", "utf-8"); - } - Tcl_SetStdChannel(consoleChannel, TCL_STDIN); - } - - /* - * check for STDOUT, otherwise create it - */ + /* + * At least one std channel wants to be tied to the console, + * so create the interp for it to live in. + */ - if (ShouldUseConsoleChannel(TCL_STDOUT)) { - consoleChannel = Tcl_CreateChannel(&consoleChannelType, "console1", - (ClientData) TCL_STDOUT, TCL_WRITABLE); - if (consoleChannel != NULL) { - Tcl_SetChannelOption(NULL, consoleChannel, - "-translation", "lf"); - Tcl_SetChannelOption(NULL, consoleChannel, - "-buffering", "none"); - Tcl_SetChannelOption(NULL, consoleChannel, - "-encoding", "utf-8"); - } - Tcl_SetStdChannel(consoleChannel, TCL_STDOUT); + info = (ConsoleInfo *) ckalloc(sizeof(ConsoleInfo)); + info->consoleInterp = NULL; + info->interp = NULL; + info->refCount = 0; + + if (doIn) { + ChannelData *data = (ChannelData *) ckalloc(sizeof(ChannelData)); + data->info = info; + data->info->refCount++; + data->type = TCL_STDIN; + consoleChannel = Tcl_CreateChannel(&consoleChannelType, "console0", + (ClientData) data, TCL_READABLE); + if (consoleChannel != NULL) { + Tcl_SetChannelOption(NULL, consoleChannel, + "-translation", "lf"); + Tcl_SetChannelOption(NULL, consoleChannel, + "-buffering", "none"); + Tcl_SetChannelOption(NULL, consoleChannel, + "-encoding", "utf-8"); } + Tcl_SetStdChannel(consoleChannel, TCL_STDIN); + } - /* - * check for STDERR, otherwise create it - */ + if (doOut) { + ChannelData *data = (ChannelData *) ckalloc(sizeof(ChannelData)); + data->info = info; + data->info->refCount++; + data->type = TCL_STDOUT; + consoleChannel = Tcl_CreateChannel(&consoleChannelType, "console1", + (ClientData) data, TCL_WRITABLE); + if (consoleChannel != NULL) { + Tcl_SetChannelOption(NULL, consoleChannel, + "-translation", "lf"); + Tcl_SetChannelOption(NULL, consoleChannel, + "-buffering", "none"); + Tcl_SetChannelOption(NULL, consoleChannel, + "-encoding", "utf-8"); + } + Tcl_SetStdChannel(consoleChannel, TCL_STDOUT); + } - if (ShouldUseConsoleChannel(TCL_STDERR)) { - consoleChannel = Tcl_CreateChannel(&consoleChannelType, "console2", - (ClientData) TCL_STDERR, TCL_WRITABLE); - if (consoleChannel != NULL) { - Tcl_SetChannelOption(NULL, consoleChannel, - "-translation", "lf"); - Tcl_SetChannelOption(NULL, consoleChannel, - "-buffering", "none"); - Tcl_SetChannelOption(NULL, consoleChannel, - "-encoding", "utf-8"); - } - Tcl_SetStdChannel(consoleChannel, TCL_STDERR); + if (doErr) { + ChannelData *data = (ChannelData *) ckalloc(sizeof(ChannelData)); + data->info = info; + data->info->refCount++; + data->type = TCL_STDERR; + consoleChannel = Tcl_CreateChannel(&consoleChannelType, "console1", + (ClientData) data, TCL_WRITABLE); + if (consoleChannel != NULL) { + Tcl_SetChannelOption(NULL, consoleChannel, + "-translation", "lf"); + Tcl_SetChannelOption(NULL, consoleChannel, + "-buffering", "none"); + Tcl_SetChannelOption(NULL, consoleChannel, + "-encoding", "utf-8"); } + Tcl_SetStdChannel(consoleChannel, TCL_STDERR); } - Tcl_MutexUnlock(&consoleMutex); } /* @@ -331,59 +336,110 @@ int Tk_CreateConsoleWindow( Tcl_Interp *interp) /* Interpreter to use for prompting. */ { - Tcl_Interp *consoleInterp; + Tcl_Channel chan; + ChannelData *data; ConsoleInfo *info; - Tk_Window mainWindow = Tk_MainWindow(interp); - ThreadSpecificData *tsdPtr = (ThreadSpecificData *) - Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); - static const char *initCmd = "source $tk_library/console.tcl"; + Tcl_Interp *consoleInterp = NULL; + Tk_Window mainWindow; + Tcl_Command token; + int result = TCL_OK; - consoleInterp = Tcl_CreateInterp(); - if (consoleInterp == NULL) { + /* + * Fetch the instance data from whatever std channel is a + * console channel. If none is, we're here by mistake! + */ + + if (Tcl_GetChannelType(chan = Tcl_GetStdChannel(TCL_STDIN)) + == &consoleChannelType) { + } else if (Tcl_GetChannelType(chan = Tcl_GetStdChannel(TCL_STDOUT)) + == &consoleChannelType) { + } else if (Tcl_GetChannelType(chan = Tcl_GetStdChannel(TCL_STDERR)) + == &consoleChannelType) { + } else { + /* TODO: consider creating window anyway */ + Tcl_SetObjResult(interp, Tcl_NewStringObj("no console channels", -1)); goto error; } + data = (ChannelData *)Tcl_GetChannelInstanceData(chan); + info = data->info; + /* * Initialized Tcl and Tk. */ + consoleInterp = Tcl_CreateInterp(); if (Tcl_Init(consoleInterp) != TCL_OK) { goto error; } if (Tk_Init(consoleInterp) != TCL_OK) { goto error; } - tsdPtr->gStdoutInterp = interp; + + if (info->consoleInterp) { + /* The console channels are already connected to a console window. + * Move channels to this new one, but leave the old one functioning. */ + + + } + + info->consoleInterp = consoleInterp; + info->interp = interp; + + /* TODO: establish exit handler for cleanup */ + + Tcl_CallWhenDeleted(consoleInterp, InterpDeleteProc, (ClientData) info); + info->refCount++; /* * Add console commands to the interp */ - info = (ConsoleInfo *) ckalloc(sizeof(ConsoleInfo)); - info->interp = interp; - info->consoleInterp = consoleInterp; - Tcl_CreateCommand(interp, "console", ConsoleCmd, (ClientData) info, - (Tcl_CmdDeleteProc *) ConsoleDeleteProc); - Tcl_CreateCommand(consoleInterp, "consoleinterp", InterpreterCmd, + token = Tcl_CreateObjCommand(interp, "console", ConsoleObjCmd, + (ClientData) info, ConsoleDeleteProc); + info->refCount++; + + /* + * We don't have to count the ref held by the [consoleinterp] command + * in the consoleInterp. The ref held by the consoleInterp delete + * handler takes care of us. + */ + Tcl_CreateObjCommand(consoleInterp, "consoleinterp", InterpreterObjCmd, (ClientData) info, NULL); - Tcl_SetAssocData(interp, TK_CONSOLE_INFO_KEY, NULL, (ClientData) info); - Tk_CreateEventHandler(mainWindow, StructureNotifyMask, ConsoleEventProc, - (ClientData) info); + mainWindow = Tk_MainWindow(interp); + if (mainWindow) { + Tk_CreateEventHandler(mainWindow, StructureNotifyMask, + ConsoleEventProc, (ClientData) info); + info->refCount++; + } Tcl_Preserve((ClientData) consoleInterp); - if (Tcl_Eval(consoleInterp, initCmd) == TCL_ERROR) { - /* - * goto error; -- no problem for now... - */ - printf("Eval error: %s", consoleInterp->result); + result = Tcl_GlobalEval(consoleInterp, "source $tk_library/console.tcl"); + if (result == TCL_ERROR) { + Tcl_SetReturnOptions(interp, + Tcl_GetReturnOptions(consoleInterp, result)); + Tcl_SetObjResult(interp, Tcl_GetObjResult(consoleInterp)); } Tcl_Release((ClientData) consoleInterp); + if (result == TCL_ERROR) { + Tcl_DeleteCommandFromToken(interp, token); + mainWindow = Tk_MainWindow(interp); + if (mainWindow) { + Tk_DeleteEventHandler(mainWindow, StructureNotifyMask, + ConsoleEventProc, (ClientData) info); + if (--info->refCount <= 0) { + ckfree((char *) info); + } + } + goto error; + } return TCL_OK; - error: - if (consoleInterp != NULL) { - Tcl_DeleteInterp(consoleInterp); + error: + Tcl_AddErrorInfo(interp, "\n (creating console window)"); + if (consoleInterp && !Tcl_InterpDeleted(consoleInterp)) { + Tcl_DeleteInterp(consoleInterp); } return TCL_ERROR; } @@ -413,17 +469,30 @@ ConsoleOutput( int toWrite, /* How many bytes to write? */ int *errorCode) /* Where to store error code. */ { - ThreadSpecificData *tsdPtr = (ThreadSpecificData *) - Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + ChannelData *data = (ChannelData *)instanceData; + ConsoleInfo *info = data->info; *errorCode = 0; Tcl_SetErrno(0); - if (tsdPtr->gStdoutInterp != NULL) { - TkConsolePrint(tsdPtr->gStdoutInterp, (int) instanceData, buf, - toWrite); + if (info) { + Tcl_Interp *consoleInterp = info->consoleInterp; + + if (consoleInterp && !Tcl_InterpDeleted(consoleInterp)) { + Tcl_Obj *cmd = Tcl_NewStringObj("tk::ConsoleOutput", -1); + if (data->type == TCL_STDERR) { + Tcl_ListObjAppendElement(NULL, cmd, + Tcl_NewStringObj("stderr", -1)); + } else { + Tcl_ListObjAppendElement(NULL, cmd, + Tcl_NewStringObj("stdout", -1)); + } + Tcl_ListObjAppendElement(NULL, cmd, Tcl_NewStringObj(buf, toWrite)); + Tcl_IncrRefCount(cmd); + Tcl_GlobalEvalObj(consoleInterp, cmd); + Tcl_DecrRefCount(cmd); + } } - return toWrite; } @@ -477,9 +546,16 @@ ConsoleClose( ClientData instanceData, /* Unused. */ Tcl_Interp *interp) /* Unused. */ { - ThreadSpecificData *tsdPtr = (ThreadSpecificData *) - Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); - tsdPtr->gStdoutInterp = NULL; + ChannelData *data = (ChannelData *)instanceData; + ConsoleInfo *info = data->info; + + if (info) { + if (--info->refCount <= 0) { + /* Assuming the Tcl_Interp * fields must already be NULL */ + ckfree((char *) info); + } + } + ckfree((char *) data); return 0; } @@ -543,146 +619,171 @@ ConsoleHandle( /* *---------------------------------------------------------------------- * - * ConsoleCmd -- + * ConsoleObjCmd -- * * The console command implements a Tcl interface to the various console * options. * * Results: - * None. + * A standard Tcl result. * * Side effects: - * None. + * See the user documentation. * *---------------------------------------------------------------------- */ static int -ConsoleCmd( - ClientData clientData, /* Not used. */ - Tcl_Interp *interp, /* Current interpreter. */ - int argc, /* Number of arguments. */ - CONST char **argv) /* Argument strings. */ +ConsoleObjCmd( + ClientData clientData, /* Access to the console interp */ + Tcl_Interp *interp, /* Current interpreter */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[]) /* Argument objects */ { + int index, result; + static CONST char *options[] = {"eval", "hide", "show", "title", NULL}; + enum option {CON_EVAL, CON_HIDE, CON_SHOW, CON_TITLE}; + Tcl_Obj *cmd = NULL; ConsoleInfo *info = (ConsoleInfo *) clientData; - char c; - size_t length; - int result; - Tcl_Interp *consoleInterp; - - if (argc < 2) { - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " option ?arg arg ...?\"", NULL); + Tcl_Interp *consoleInterp = info->consoleInterp; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "option ?arg?"); + return TCL_ERROR; + } + if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0, &index) + != TCL_OK) { return TCL_ERROR; } - c = argv[1][0]; - length = strlen(argv[1]); - result = TCL_OK; - consoleInterp = info->consoleInterp; - Tcl_Preserve((ClientData) consoleInterp); - - if ((c == 't') && (strncmp(argv[1], "title", length)) == 0) { - Tcl_DString dString; - - Tcl_DStringInit(&dString); - Tcl_DStringAppend(&dString, "wm title . ", -1); - if (argc == 3) { - Tcl_DStringAppendElement(&dString, argv[2]); + switch ((enum option) index) { + case CON_EVAL: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "script"); + return TCL_ERROR; + } + cmd = objv[2]; + break; + case CON_HIDE: + if (objc != 2) { + Tcl_WrongNumArgs(interp, 2, objv, NULL); + return TCL_ERROR; } - Tcl_Eval(consoleInterp, Tcl_DStringValue(&dString)); - Tcl_DStringFree(&dString); - } else if ((c == 'h') && (strncmp(argv[1], "hide", length)) == 0) { - Tcl_Eval(consoleInterp, "wm withdraw ."); - } else if ((c == 's') && (strncmp(argv[1], "show", length)) == 0) { - Tcl_Eval(consoleInterp, "wm deiconify ."); - } else if ((c == 'e') && (strncmp(argv[1], "eval", length)) == 0) { - if (argc == 3) { - result = Tcl_Eval(consoleInterp, argv[2]); - Tcl_AppendResult(interp, Tcl_GetStringResult(consoleInterp), - NULL); - } else { - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " eval command\"", NULL); - result = TCL_ERROR; + cmd = Tcl_NewStringObj("wm withdraw .", -1); + break; + case CON_SHOW: + if (objc != 2) { + Tcl_WrongNumArgs(interp, 2, objv, NULL); + return TCL_ERROR; + } + cmd = Tcl_NewStringObj("wm deiconify .", -1); + break; + case CON_TITLE: + if (objc > 3) { + Tcl_WrongNumArgs(interp, 2, objv, "?title?"); + return TCL_ERROR; } + cmd = Tcl_NewStringObj("wm title .", -1); + if (objc == 3) { + Tcl_ListObjAppendElement(NULL, cmd, objv[2]); + } + break; + } + + Tcl_IncrRefCount(cmd); + if (consoleInterp && !Tcl_InterpDeleted(consoleInterp)) { + Tcl_Preserve((ClientData) consoleInterp); + result = Tcl_GlobalEvalObj(consoleInterp, cmd); + Tcl_SetReturnOptions(interp, + Tcl_GetReturnOptions(consoleInterp, result)); + Tcl_SetObjResult(interp, Tcl_GetObjResult(consoleInterp)); + Tcl_Release((ClientData) consoleInterp); } else { - Tcl_AppendResult(interp, "bad option \"", argv[1], - "\": should be hide, show, or title", NULL); - result = TCL_ERROR; + Tcl_AppendResult(interp, "no active console interp", NULL); + result = TCL_ERROR; } - Tcl_Release((ClientData) consoleInterp); + Tcl_DecrRefCount(cmd); return result; } /* *---------------------------------------------------------------------- * - * InterpreterCmd -- + * InterpreterObjCmd -- * * This command allows the console interp to communicate with the main * interpreter. * * Results: - * None. - * - * Side effects: - * None. + * A standard Tcl result. * *---------------------------------------------------------------------- */ static int -InterpreterCmd( - ClientData clientData, /* Not used. */ - Tcl_Interp *interp, /* Current interpreter. */ - int argc, /* Number of arguments. */ - CONST char **argv) /* Argument strings. */ +InterpreterObjCmd( + ClientData clientData, /* */ + Tcl_Interp *interp, /* Current interpreter */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[]) /* Argument objects */ { + int index, result = TCL_OK; + static CONST char *options[] = {"eval", "record", NULL}; + enum option {OTHER_EVAL, OTHER_RECORD}; ConsoleInfo *info = (ConsoleInfo *) clientData; - char c; - size_t length; - int result; - Tcl_Interp *consoleInterp; - Tcl_Interp *otherInterp; - - if (argc < 2) { - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " option ?arg arg ...?\"", NULL); + Tcl_Interp *otherInterp = info->interp; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "option arg"); + return TCL_ERROR; + } + if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0, &index) + != TCL_OK) { + return TCL_ERROR; + } + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "script"); + return TCL_ERROR; + } + + if ((otherInterp == NULL) || Tcl_InterpDeleted(otherInterp)) { + Tcl_AppendResult(interp, "no active master interp", NULL); return TCL_ERROR; } - c = argv[1][0]; - length = strlen(argv[1]); - consoleInterp = info->consoleInterp; - Tcl_Preserve((ClientData) consoleInterp); - otherInterp = info->interp; Tcl_Preserve((ClientData) otherInterp); - if ((c == 'e') && (strncmp(argv[1], "eval", length)) == 0) { - result = Tcl_GlobalEval(otherInterp, argv[2]); - Tcl_AppendResult(interp, otherInterp->result, NULL); - } else if ((c == 'r') && (strncmp(argv[1], "record", length)) == 0) { - Tcl_RecordAndEval(otherInterp, argv[2], TCL_EVAL_GLOBAL); - result = TCL_OK; - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, otherInterp->result, NULL); - } else { - Tcl_AppendResult(interp, "bad option \"", argv[1], - "\": should be eval or record", NULL); - result = TCL_ERROR; + switch ((enum option) index) { + case OTHER_EVAL: + result = Tcl_GlobalEvalObj(otherInterp, objv[2]); + /* + * TODO: Should exceptions be filtered here? + */ + Tcl_SetReturnOptions(interp, + Tcl_GetReturnOptions(otherInterp, result)); + Tcl_SetObjResult(interp, Tcl_GetObjResult(otherInterp)); + break; + case OTHER_RECORD: + Tcl_RecordAndEvalObj(otherInterp, objv[2], TCL_EVAL_GLOBAL); + /* + * By not setting result, we discard any exceptions or errors here + * and always return TCL_OK. All the caller wants is the + * interp result to display, whether that's result or error message. + */ + Tcl_SetObjResult(interp, Tcl_GetObjResult(otherInterp)); + break; } Tcl_Release((ClientData) otherInterp); - Tcl_Release((ClientData) consoleInterp); return result; } /* *---------------------------------------------------------------------- * - * ConsoleDeleteProc -- + * InterpDeleteProc -- * - * If the console command is deleted we destroy the console window and - * all associated data structures. + * React when the interp in which the console is displayed is deleted + * for any reason. * * Results: * None. @@ -694,131 +795,87 @@ InterpreterCmd( */ static void -ConsoleDeleteProc( - ClientData clientData) +InterpDeleteProc( + ClientData clientData, + Tcl_Interp *interp) { ConsoleInfo *info = (ConsoleInfo *) clientData; - ThreadSpecificData *tsdPtr = (ThreadSpecificData *) - Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); - - /* - * Also need to null this out to prevent any further use. - * - * Fix [Bug #756840] - */ - if (tsdPtr != NULL) { - tsdPtr->gStdoutInterp = NULL; + if (info->consoleInterp == interp) { + info->consoleInterp = NULL; + } + if (--info->refCount <= 0) { + ckfree((char *) info); } - - Tcl_DeleteInterp(info->consoleInterp); - info->consoleInterp = NULL; } /* *---------------------------------------------------------------------- * - * ConsoleEventProc -- + * ConsoleDeleteProc -- * - * This event function is registered on the main window of the slave - * interpreter. If the user or a running script causes the main window to - * be destroyed, then we need to inform the console interpreter by - * invoking "::tk::ConsoleExit". + * If the console command is deleted we destroy the console window and + * all associated data structures. * * Results: * None. * * Side effects: - * Invokes the "::tk::ConsoleExit" command in the console interp. + * A new console it created. * *---------------------------------------------------------------------- */ static void -ConsoleEventProc( - ClientData clientData, - XEvent *eventPtr) +ConsoleDeleteProc( + ClientData clientData) { ConsoleInfo *info = (ConsoleInfo *) clientData; - Tcl_Interp *consoleInterp; - - if (eventPtr->type == DestroyNotify) { - consoleInterp = info->consoleInterp; - - /* - * It is possible that the console interpreter itself has already been - * deleted. In that case the consoleInterp field will be set to - * NULL. If the interpreter is already gone, we do not have to do any - * work here. - */ - - if (consoleInterp == NULL) { - return; - } - Tcl_Preserve((ClientData) consoleInterp); - Tcl_Eval(consoleInterp, "::tk::ConsoleExit"); - Tcl_Release((ClientData) consoleInterp); + if (info->consoleInterp) { + Tcl_DeleteInterp(info->consoleInterp); + } + if (--info->refCount <= 0) { + ckfree((char *) info); } } /* *---------------------------------------------------------------------- * - * TkConsolePrint -- + * ConsoleEventProc -- * - * Prints to the give text to the console. Given the main interp this - * functions find the appropiate console interp and forwards the text to - * be added to that console. + * This event function is registered on the main window of the slave + * interpreter. If the user or a running script causes the main window to + * be destroyed, then we need to inform the console interpreter by + * invoking "::tk::ConsoleExit". * * Results: * None. * * Side effects: - * None. + * Invokes the "::tk::ConsoleExit" command in the console interp. * *---------------------------------------------------------------------- */ -void -TkConsolePrint( - Tcl_Interp *interp, /* Main interpreter. */ - int devId, /* TCL_STDOUT for stdout, TCL_STDERR for - * stderr. */ - CONST char *buffer, /* Text buffer. */ - long size) /* Size of text buffer. */ +static void +ConsoleEventProc( + ClientData clientData, + XEvent *eventPtr) { - Tcl_DString command, output; - ConsoleInfo *info; - Tcl_Interp *consoleInterp; - - if (interp == NULL) { - return; - } + if (eventPtr->type == DestroyNotify) { + ConsoleInfo *info = (ConsoleInfo *) clientData; + Tcl_Interp *consoleInterp = info->consoleInterp; - info = (ConsoleInfo *) Tcl_GetAssocData(interp, TK_CONSOLE_INFO_KEY, NULL); - if (info == NULL || info->consoleInterp == NULL) { - return; - } + if (consoleInterp && !Tcl_InterpDeleted(consoleInterp)) { + Tcl_GlobalEval(consoleInterp, "tk::ConsoleExit"); + } - Tcl_DStringInit(&command); - if (devId == TCL_STDERR) { - Tcl_DStringAppend(&command, "::tk::ConsoleOutput stderr ", -1); - } else { - Tcl_DStringAppend(&command, "::tk::ConsoleOutput stdout ", -1); + if (--info->refCount <= 0) { + ckfree((char *) info); + } } - - Tcl_DStringInit(&output); - Tcl_DStringAppend(&output, buffer, size); - Tcl_DStringAppendElement(&command, Tcl_DStringValue(&output)); - Tcl_DStringFree(&output); - - consoleInterp = info->consoleInterp; - Tcl_Preserve((ClientData) consoleInterp); - Tcl_Eval(consoleInterp, Tcl_DStringValue(&command)); - Tcl_Release((ClientData) consoleInterp); - - Tcl_DStringFree(&command); } /* diff --git a/generic/tkInt.h b/generic/tkInt.h index a468c4b..0b4ca96 100644 --- a/generic/tkInt.h +++ b/generic/tkInt.h @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: $Id: tkInt.h,v 1.69 2005/11/27 02:36:14 das Exp $ + * RCS: $Id: tkInt.h,v 1.70 2006/03/16 17:32:28 dgp Exp $ */ #ifndef _TKINT @@ -1085,8 +1085,6 @@ MODULE_SCOPE int Tk_WinfoObjCmd(ClientData clientData, MODULE_SCOPE int Tk_WmObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); -MODULE_SCOPE void TkConsolePrint(Tcl_Interp *interp, - int devId, CONST char *buffer, long size); MODULE_SCOPE void TkEventInit(void); MODULE_SCOPE void TkRegisterObjTypes(void); MODULE_SCOPE int TkCreateMenuCmd(Tcl_Interp *interp); -- cgit v0.12