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.43 96/08/26 19:42:51
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 ConsoleReady _ANSI_ARGS_((ClientData instanceData,
53 static Tcl_File ConsoleFile _ANSI_ARGS_((ClientData instanceData,
57 * This structure describes the channel type structure for file based IO:
60 static Tcl_ChannelType consoleChannelType = {
61 "console", /* Type name. */
62 NULL, /* Always non-blocking.*/
63 ConsoleClose, /* Close proc. */
64 ConsoleInput, /* Input proc. */
65 ConsoleOutput, /* Output proc. */
66 NULL, /* Seek proc. */
67 NULL, /* Set option proc. */
68 NULL, /* Get option proc. */
69 ConsoleWatch, /* Watch for events on console. */
70 ConsoleReady, /* Are events present? */
71 ConsoleFile, /* Get a Tcl_File 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, "interp", 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 notifier to notice whether any events are present
321 * on the console. Since there are no events on the console, this
322 * routine always returns zero.
330 *----------------------------------------------------------------------
335 ConsoleReady(instanceData, mask)
336 ClientData instanceData; /* Device ID for the channel. */
337 int mask; /* OR-ed combination of
338 * TCL_READABLE, TCL_WRITABLE and
339 * TCL_EXCEPTION, for the events
340 * we are interested in. */
346 *----------------------------------------------------------------------
350 * Invoked by the generic IO layer to get a Tcl_File from a channel.
351 * Because console channels do not use Tcl_Files, this function always
360 *----------------------------------------------------------------------
365 ConsoleFile(instanceData, direction)
366 ClientData instanceData; /* Device ID for the channel. */
367 int direction; /* TCL_READABLE or TCL_WRITABLE
368 * to indicate which direction of
369 * the channel is being requested. */
371 return (Tcl_File) NULL;
375 *----------------------------------------------------------------------
379 * The console command implements a Tcl interface to the various console
388 *----------------------------------------------------------------------
392 ConsoleCmd(clientData, interp, argc, argv)
393 ClientData clientData; /* Not used. */
394 Tcl_Interp *interp; /* Current interpreter. */
395 int argc; /* Number of arguments. */
396 char **argv; /* Argument strings. */
398 ConsoleInfo *info = (ConsoleInfo *) clientData;
402 Tcl_Interp *consoleInterp;
405 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
406 " option ?arg arg ...?\"", (char *) NULL);
411 length = strlen(argv[1]);
413 consoleInterp = info->consoleInterp;
414 Tcl_Preserve((ClientData) consoleInterp);
415 if ((c == 't') && (strncmp(argv[1], "title", length)) == 0) {
417 char *wmCmd = "wm title . {";
419 Tcl_DStringInit(&dString);
420 Tcl_DStringAppend(&dString, wmCmd, strlen(wmCmd));
421 Tcl_DStringAppend(&dString, argv[2], strlen(argv[2]));
422 Tcl_DStringAppend(&dString, "}", strlen("}"));
423 Tcl_Eval(consoleInterp, dString.string);
424 Tcl_DStringFree(&dString);
425 } else if ((c == 'h') && (strncmp(argv[1], "hide", length)) == 0) {
426 Tcl_Eval(info->consoleInterp, "wm withdraw .");
427 } else if ((c == 's') && (strncmp(argv[1], "show", length)) == 0) {
428 Tcl_Eval(info->consoleInterp, "wm deiconify .");
429 } else if ((c == 'e') && (strncmp(argv[1], "eval", length)) == 0) {
430 Tcl_Eval(info->consoleInterp, argv[2]);
432 Tcl_AppendResult(interp, "bad option \"", argv[1],
433 "\": should be hide, show, or title",
437 Tcl_Release((ClientData) consoleInterp);
442 *----------------------------------------------------------------------
446 * This command allows the console interp to communicate with the
455 *----------------------------------------------------------------------
459 InterpreterCmd(clientData, interp, argc, argv)
460 ClientData clientData; /* Not used. */
461 Tcl_Interp *interp; /* Current interpreter. */
462 int argc; /* Number of arguments. */
463 char **argv; /* Argument strings. */
465 ConsoleInfo *info = (ConsoleInfo *) clientData;
469 Tcl_Interp *otherInterp;
472 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
473 " option ?arg arg ...?\"", (char *) NULL);
478 length = strlen(argv[1]);
480 otherInterp = info->interp;
481 Tcl_Preserve((ClientData) otherInterp);
482 if ((c == 'e') && (strncmp(argv[1], "eval", length)) == 0) {
483 result = Tcl_GlobalEval(otherInterp, argv[2]);
484 Tcl_AppendResult(interp, otherInterp->result, (char *) NULL);
485 } else if ((c == 'r') && (strncmp(argv[1], "record", length)) == 0) {
486 Tcl_RecordAndEval(otherInterp, argv[2], TCL_EVAL_GLOBAL);
488 Tcl_AppendResult(interp, otherInterp->result, (char *) NULL);
490 Tcl_AppendResult(interp, "bad option \"", argv[1],
491 "\": should be eval or record",
495 Tcl_Release((ClientData) otherInterp);
500 *----------------------------------------------------------------------
502 * ConsoleDeleteProc --
504 * If the console command is deleted we destroy the console window
505 * and all associated data structures.
511 * A new console it created.
513 *----------------------------------------------------------------------
517 ConsoleDeleteProc(clientData)
518 ClientData clientData;
520 ConsoleInfo *info = (ConsoleInfo *) clientData;
522 Tcl_DeleteInterp(info->consoleInterp);
523 info->consoleInterp = NULL;
527 *----------------------------------------------------------------------
529 * ConsoleEventProc --
531 * This event procedure is registered on the main window of the
532 * slave interpreter. If the user or a running script causes the
533 * main window to be destroyed, then we need to inform the console
534 * interpreter by invoking "tkConsoleExit".
540 * Invokes the "tkConsoleExit" procedure in the console interp.
542 *----------------------------------------------------------------------
546 ConsoleEventProc(clientData, eventPtr)
547 ClientData clientData;
550 ConsoleInfo *info = (ConsoleInfo *) clientData;
551 Tcl_Interp *consoleInterp;
553 if (eventPtr->type == DestroyNotify) {
554 consoleInterp = info->consoleInterp;
555 Tcl_Preserve((ClientData) consoleInterp);
556 Tcl_Eval(consoleInterp, "tkConsoleExit");
557 Tcl_Release((ClientData) consoleInterp);
562 *----------------------------------------------------------------------
566 * Prints to the give text to the console. Given the main interp
567 * this functions find the appropiate console interp and forwards
568 * the text to be added to that console.
576 *----------------------------------------------------------------------
580 TkConsolePrint(interp, devId, buffer, size)
581 Tcl_Interp *interp; /* Main interpreter. */
582 int devId; /* TCL_STDOUT for stdout, TCL_STDERR for
584 char *buffer; /* Text buffer. */
585 long size; /* Size of text buffer. */
587 Tcl_DString command, output;
591 Tcl_Interp *consoleInterp;
594 if (interp == NULL) {
598 if (devId == TCL_STDERR) {
599 cmd = "tkConsoleOutput stderr ";
601 cmd = "tkConsoleOutput stdout ";
604 result = Tcl_GetCommandInfo(interp, "console", &cmdInfo);
608 info = (ConsoleInfo *) cmdInfo.clientData;
610 Tcl_DStringInit(&output);
611 Tcl_DStringAppend(&output, buffer, size);
613 Tcl_DStringInit(&command);
614 Tcl_DStringAppend(&command, cmd, strlen(cmd));
615 Tcl_DStringAppendElement(&command, output.string);
617 consoleInterp = info->consoleInterp;
618 Tcl_Preserve((ClientData) consoleInterp);
619 Tcl_Eval(consoleInterp, command.string);
620 Tcl_Release((ClientData) consoleInterp);
622 Tcl_DStringFree(&command);
623 Tcl_DStringFree(&output);