4 * This file implements a Tcl console for systems that may not
5 * otherwise have access to a console. It uses the Text widget
6 * and provides special access via a console command.
8 * Copyright (c) 1995-1996 Sun Microsystems, Inc.
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13 * SCCS: @(#) tkConsole.c 1.51 97/04/25 16:52:39
19 * A data structure of the following type holds information for each console
20 * which a handler (i.e. a Tcl command) has been defined for a particular
24 typedef struct ConsoleInfo {
25 Tcl_Interp *consoleInterp; /* Interpreter for the console. */
26 Tcl_Interp *interp; /* Interpreter to send console commands. */
29 static Tcl_Interp *gStdoutInterp = NULL;
32 * Forward declarations for procedures defined later in this file:
35 static int ConsoleCmd _ANSI_ARGS_((ClientData clientData,
36 Tcl_Interp *interp, int argc, char **argv));
37 static void ConsoleDeleteProc _ANSI_ARGS_((ClientData clientData));
38 static void ConsoleEventProc _ANSI_ARGS_((ClientData clientData,
40 static int InterpreterCmd _ANSI_ARGS_((ClientData clientData,
41 Tcl_Interp *interp, int argc, char **argv));
43 static int ConsoleInput _ANSI_ARGS_((ClientData instanceData,
44 char *buf, int toRead, int *errorCode));
45 static int ConsoleOutput _ANSI_ARGS_((ClientData instanceData,
46 char *buf, int toWrite, int *errorCode));
47 static int ConsoleClose _ANSI_ARGS_((ClientData instanceData,
49 static void ConsoleWatch _ANSI_ARGS_((ClientData instanceData,
51 static int ConsoleHandle _ANSI_ARGS_((ClientData instanceData,
52 int direction, ClientData *handlePtr));
55 void TkConsolePrint (Tcl_Interp *interp, int devId, char *buffer, long size); /* Size of text buffer. */
58 * This structure describes the channel type structure for file based IO:
61 static Tcl_ChannelType consoleChannelType = {
62 "console", /* Type name. */
63 NULL, /* Always non-blocking.*/
64 ConsoleClose, /* Close proc. */
65 ConsoleInput, /* Input proc. */
66 ConsoleOutput, /* Output proc. */
67 NULL, /* Seek proc. */
68 NULL, /* Set option proc. */
69 NULL, /* Get option proc. */
70 ConsoleWatch, /* Watch for events on console. */
71 ConsoleHandle, /* Get a handle from the device. */
75 *----------------------------------------------------------------------
79 * Create the console channels and install them as the standard
80 * channels. All I/O will be discarded until TkConsoleInit is
81 * called to attach the console to a text widget.
87 * Creates the console channel and installs it as the standard
90 *----------------------------------------------------------------------
96 Tcl_Channel consoleChannel;
98 consoleChannel = Tcl_CreateChannel(&consoleChannelType, "console0",
99 (ClientData) TCL_STDIN, TCL_READABLE);
100 if (consoleChannel != NULL) {
101 Tcl_SetChannelOption(NULL, consoleChannel, "-translation", "lf");
102 Tcl_SetChannelOption(NULL, consoleChannel, "-buffering", "none");
104 Tcl_SetStdChannel(consoleChannel, TCL_STDIN);
105 consoleChannel = Tcl_CreateChannel(&consoleChannelType, "console1",
106 (ClientData) TCL_STDOUT, TCL_WRITABLE);
107 if (consoleChannel != NULL) {
108 Tcl_SetChannelOption(NULL, consoleChannel, "-translation", "lf");
109 Tcl_SetChannelOption(NULL, consoleChannel, "-buffering", "none");
111 Tcl_SetStdChannel(consoleChannel, TCL_STDOUT);
112 consoleChannel = Tcl_CreateChannel(&consoleChannelType, "console2",
113 (ClientData) TCL_STDERR, TCL_WRITABLE);
114 if (consoleChannel != NULL) {
115 Tcl_SetChannelOption(NULL, consoleChannel, "-translation", "lf");
116 Tcl_SetChannelOption(NULL, consoleChannel, "-buffering", "none");
118 Tcl_SetStdChannel(consoleChannel, TCL_STDERR);
122 *----------------------------------------------------------------------
126 * Initialize the console. This code actually creates a new
127 * application and associated interpreter. This effectivly hides
128 * the implementation from the main application.
134 * A new console it created.
136 *----------------------------------------------------------------------
140 TkConsoleInit(interp)
141 Tcl_Interp *interp; /* Interpreter to use for prompting. */
143 Tcl_Interp *consoleInterp;
145 Tk_Window mainWindow = Tk_MainWindow(interp);
147 static char initCmd[] = "source -rsrc {Console}";
149 static char initCmd[] = "source $tk_library/console.tcl";
152 consoleInterp = Tcl_CreateInterp();
153 if (consoleInterp == NULL) {
158 * Initialized Tcl and Tk.
161 if (Tcl_Init(consoleInterp) != TCL_OK) {
164 if (Tk_Init(consoleInterp) != TCL_OK) {
167 gStdoutInterp = interp;
170 * Add console commands to the interp
172 info = (ConsoleInfo *) ckalloc(sizeof(ConsoleInfo));
173 info->interp = interp;
174 info->consoleInterp = consoleInterp;
175 Tcl_CreateCommand(interp, "console", ConsoleCmd, (ClientData) info,
176 (Tcl_CmdDeleteProc *) ConsoleDeleteProc);
177 Tcl_CreateCommand(consoleInterp, "consoleinterp", InterpreterCmd,
178 (ClientData) info, (Tcl_CmdDeleteProc *) NULL);
180 Tk_CreateEventHandler(mainWindow, StructureNotifyMask, ConsoleEventProc,
183 Tcl_Preserve((ClientData) consoleInterp);
184 if (Tcl_Eval(consoleInterp, initCmd) == TCL_ERROR) {
185 /* goto error; -- no problem for now... */
186 printf("Eval error: %s", consoleInterp->result);
188 Tcl_Release((ClientData) consoleInterp);
192 if (consoleInterp != NULL) {
193 Tcl_DeleteInterp(consoleInterp);
199 *----------------------------------------------------------------------
203 * Writes the given output on the IO channel. Returns count of how
204 * many characters were actually written, and an error indication.
207 * A count of how many characters were written is returned and an
208 * error indication is returned in an output argument.
211 * Writes output on the actual channel.
213 *----------------------------------------------------------------------
217 ConsoleOutput(instanceData, buf, toWrite, errorCode)
218 ClientData instanceData; /* Indicates which device to use. */
219 char *buf; /* The data buffer. */
220 int toWrite; /* How many bytes to write? */
221 int *errorCode; /* Where to store error code. */
226 if (gStdoutInterp != NULL) {
227 TkConsolePrint(gStdoutInterp, (int) instanceData, buf, toWrite);
234 *----------------------------------------------------------------------
238 * Read input from the console. Not currently implemented.
241 * Always returns EOF.
246 *----------------------------------------------------------------------
251 ConsoleInput(instanceData, buf, bufSize, errorCode)
252 ClientData instanceData; /* Unused. */
253 char *buf; /* Where to store data read. */
254 int bufSize; /* How much space is available
256 int *errorCode; /* Where to store error code. */
258 return 0; /* Always return EOF. */
262 *----------------------------------------------------------------------
266 * Closes the IO channel.
269 * Always returns 0 (success).
272 * Frees the dummy file associated with the channel.
274 *----------------------------------------------------------------------
279 ConsoleClose(instanceData, interp)
280 ClientData instanceData; /* Unused. */
281 Tcl_Interp *interp; /* Unused. */
287 *----------------------------------------------------------------------
291 * Called by the notifier to set up the console device so that
292 * events will be noticed. Since there are no events on the
293 * console, this routine just returns without doing anything.
301 *----------------------------------------------------------------------
306 ConsoleWatch(instanceData, mask)
307 ClientData instanceData; /* Device ID for the channel. */
308 int mask; /* OR-ed combination of
309 * TCL_READABLE, TCL_WRITABLE and
310 * TCL_EXCEPTION, for the events
311 * we are interested in. */
316 *----------------------------------------------------------------------
320 * Invoked by the generic IO layer to get a handle from a channel.
321 * Because console channels are not devices, this function always
325 * Always returns TCL_ERROR.
330 *----------------------------------------------------------------------
335 ConsoleHandle(instanceData, direction, handlePtr)
336 ClientData instanceData; /* Device ID for the channel. */
337 int direction; /* TCL_READABLE or TCL_WRITABLE to indicate
338 * which direction of the channel is being
340 ClientData *handlePtr; /* Where to store handle */
346 *----------------------------------------------------------------------
350 * The console command implements a Tcl interface to the various console
359 *----------------------------------------------------------------------
363 ConsoleCmd(clientData, interp, argc, argv)
364 ClientData clientData; /* Not used. */
365 Tcl_Interp *interp; /* Current interpreter. */
366 int argc; /* Number of arguments. */
367 char **argv; /* Argument strings. */
369 ConsoleInfo *info = (ConsoleInfo *) clientData;
373 Tcl_Interp *consoleInterp;
376 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
377 " option ?arg arg ...?\"", (char *) NULL);
382 length = strlen(argv[1]);
384 consoleInterp = info->consoleInterp;
385 Tcl_Preserve((ClientData) consoleInterp);
386 if ((c == 't') && (strncmp(argv[1], "title", length)) == 0) {
389 Tcl_DStringInit(&dString);
390 Tcl_DStringAppend(&dString, "wm title . ", -1);
392 Tcl_DStringAppendElement(&dString, argv[2]);
394 Tcl_Eval(consoleInterp, Tcl_DStringValue(&dString));
395 Tcl_DStringFree(&dString);
396 } else if ((c == 'h') && (strncmp(argv[1], "hide", length)) == 0) {
397 Tcl_Eval(info->consoleInterp, "wm withdraw .");
398 } else if ((c == 's') && (strncmp(argv[1], "show", length)) == 0) {
399 Tcl_Eval(info->consoleInterp, "wm deiconify .");
400 } else if ((c == 'e') && (strncmp(argv[1], "eval", length)) == 0) {
402 Tcl_Eval(info->consoleInterp, argv[2]);
404 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
405 " eval command\"", (char *) NULL);
409 Tcl_AppendResult(interp, "bad option \"", argv[1],
410 "\": should be hide, show, or title",
414 Tcl_Release((ClientData) consoleInterp);
419 *----------------------------------------------------------------------
423 * This command allows the console interp to communicate with the
432 *----------------------------------------------------------------------
436 InterpreterCmd(clientData, interp, argc, argv)
437 ClientData clientData; /* Not used. */
438 Tcl_Interp *interp; /* Current interpreter. */
439 int argc; /* Number of arguments. */
440 char **argv; /* Argument strings. */
442 ConsoleInfo *info = (ConsoleInfo *) clientData;
446 Tcl_Interp *otherInterp;
449 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
450 " option ?arg arg ...?\"", (char *) NULL);
455 length = strlen(argv[1]);
456 otherInterp = info->interp;
457 Tcl_Preserve((ClientData) otherInterp);
458 if ((c == 'e') && (strncmp(argv[1], "eval", length)) == 0) {
459 result = Tcl_GlobalEval(otherInterp, argv[2]);
460 Tcl_AppendResult(interp, otherInterp->result, (char *) NULL);
461 } else if ((c == 'r') && (strncmp(argv[1], "record", length)) == 0) {
462 Tcl_RecordAndEval(otherInterp, argv[2], TCL_EVAL_GLOBAL);
464 Tcl_AppendResult(interp, otherInterp->result, (char *) NULL);
466 Tcl_AppendResult(interp, "bad option \"", argv[1],
467 "\": should be eval or record",
471 Tcl_Release((ClientData) otherInterp);
476 *----------------------------------------------------------------------
478 * ConsoleDeleteProc --
480 * If the console command is deleted we destroy the console window
481 * and all associated data structures.
487 * A new console it created.
489 *----------------------------------------------------------------------
493 ConsoleDeleteProc(clientData)
494 ClientData clientData;
496 ConsoleInfo *info = (ConsoleInfo *) clientData;
498 Tcl_DeleteInterp(info->consoleInterp);
499 info->consoleInterp = NULL;
503 *----------------------------------------------------------------------
505 * ConsoleEventProc --
507 * This event procedure is registered on the main window of the
508 * slave interpreter. If the user or a running script causes the
509 * main window to be destroyed, then we need to inform the console
510 * interpreter by invoking "tkConsoleExit".
516 * Invokes the "tkConsoleExit" procedure in the console interp.
518 *----------------------------------------------------------------------
522 ConsoleEventProc(clientData, eventPtr)
523 ClientData clientData;
526 ConsoleInfo *info = (ConsoleInfo *) clientData;
527 Tcl_Interp *consoleInterp;
529 if (eventPtr->type == DestroyNotify) {
530 consoleInterp = info->consoleInterp;
533 * It is possible that the console interpreter itself has
534 * already been deleted. In that case the consoleInterp
535 * field will be set to NULL. If the interpreter is already
536 * gone, we do not have to do any work here.
539 if (consoleInterp == (Tcl_Interp *) NULL) {
542 Tcl_Preserve((ClientData) consoleInterp);
543 Tcl_Eval(consoleInterp, "tkConsoleExit");
544 Tcl_Release((ClientData) consoleInterp);
549 *----------------------------------------------------------------------
553 * Prints to the give text to the console. Given the main interp
554 * this functions find the appropiate console interp and forwards
555 * the text to be added to that console.
563 *----------------------------------------------------------------------
567 TkConsolePrint(interp, devId, buffer, size)
568 Tcl_Interp *interp; /* Main interpreter. */
569 int devId; /* TCL_STDOUT for stdout, TCL_STDERR for
571 char *buffer; /* Text buffer. */
572 long size; /* Size of text buffer. */
574 Tcl_DString command, output;
578 Tcl_Interp *consoleInterp;
581 if (interp == NULL) {
585 if (devId == TCL_STDERR) {
586 cmd = "tkConsoleOutput stderr ";
588 cmd = "tkConsoleOutput stdout ";
591 result = Tcl_GetCommandInfo(interp, "console", &cmdInfo);
595 info = (ConsoleInfo *) cmdInfo.clientData;
597 Tcl_DStringInit(&output);
598 Tcl_DStringAppend(&output, buffer, size);
600 Tcl_DStringInit(&command);
601 Tcl_DStringAppend(&command, cmd, strlen(cmd));
602 Tcl_DStringAppendElement(&command, output.string);
604 consoleInterp = info->consoleInterp;
605 Tcl_Preserve((ClientData) consoleInterp);
606 Tcl_Eval(consoleInterp, command.string);
607 Tcl_Release((ClientData) consoleInterp);
609 Tcl_DStringFree(&command);
610 Tcl_DStringFree(&output);