/* * main.c -- * * This file contains the main program for "wish", a windowing * shell based on Tk and Tcl, modified to be used with C++ libraries. * It also provides a template that * can be used as the basis for main programs for other Tk * applications. * * Copyright (c) 1990-1993 The Regents of the University of California. * All rights reserved. * * Permission is hereby granted, without written agreement and without * license or royalty fees, to use, copy, modify, and distribute this * software and its documentation for any purpose, provided that the * above copyright notice and the following two paragraphs appear in * all copies of this software. * * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. */ /* [mbl] changed include files to use only accessible ones */ #include #include /* * Global variables used by the main program: */ static Tk_Window w; /* The main window for the application. If * NULL then the application no longer * exists. */ static Tcl_Interp *interp; /* Interpreter for this application. */ static Tcl_DString command; /* Used to assemble lines of terminal input * into Tcl commands. */ static int tty; /* Non-zero means standard input is a * terminal-like device. Zero means it's * a file. */ /* * Command-line options: */ int synchronize = 0; char *fileName = NULL; char *name = NULL; char *display = NULL; char *geometry = NULL; Tk_ArgvInfo argTable[] = { {"-file", TK_ARGV_STRING, (char *) NULL, (char *) &fileName, "File from which to read commands"}, {"-geometry", TK_ARGV_STRING, (char *) NULL, (char *) &geometry, "Initial geometry for window"}, {"-display", TK_ARGV_STRING, (char *) NULL, (char *) &display, "Display to use"}, {"-name", TK_ARGV_STRING, (char *) NULL, (char *) &name, "Name to use for application"}, {"-sync", TK_ARGV_CONSTANT, (char *) 1, (char *) &synchronize, "Use synchronous mode for display server"}, {(char *) NULL, TK_ARGV_END, (char *) NULL, (char *) NULL, (char *) NULL} }; /* * Declaration for Tcl command procedure to create demo widget. This * procedure is only invoked if SQUARE_DEMO is defined. */ extern int SquareCmd _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])); /* * Forward declarations for procedures defined later in this file: */ static void DelayedMap _ANSI_ARGS_((ClientData clientData)); static void StdinProc _ANSI_ARGS_((ClientData clientData, int mask)); static void StructureProc _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr)); /* *---------------------------------------------------------------------- * * main -- * * Main program for Wish. * * Results: * None. This procedure never returns (it exits the process when * it's done * * Side effects: * This procedure initializes the wish world and then starts * interpreting commands; almost anything could happen, depending * on the script being interpreted. * *---------------------------------------------------------------------- */ /* [mbl] renamed to tkmain to have main in the C++ file */ int tkmain(argc, argv) int argc; /* Number of arguments. */ char **argv; /* Array of argument strings. */ { char *args, *p, *msg; char buf[20]; int result; Tk_3DBorder border; interp = Tcl_CreateInterp(); #ifdef TCL_MEM_DEBUG Tcl_InitMemory(interp); #endif /* * Parse command-line arguments. */ if (Tk_ParseArgv(interp, (Tk_Window) NULL, &argc, argv, argTable, 0) != TCL_OK) { fprintf(stderr, "%s\n", interp->result); exit(1); } if (name == NULL) { if (fileName != NULL) { p = fileName; } else { p = argv[0]; } name = strrchr(p, '/'); if (name != NULL) { name++; } else { name = p; } } /* * If a display was specified, put it into the DISPLAY * environment variable so that it will be available for * any sub-processes created by us. */ if (display != NULL) { Tcl_SetVar2(interp, "env", "DISPLAY", display, TCL_GLOBAL_ONLY); } /* * Initialize the Tk application and arrange to map the main window * after the startup script has been executed, if any. This way * the script can withdraw the window so it isn't ever mapped * at all. */ w = Tk_CreateMainWindow(interp, display, name); if (w == NULL) { fprintf(stderr, "%s\n", interp->result); exit(1); } Tk_SetClass(w, "Tk"); Tk_CreateEventHandler(w, StructureNotifyMask, StructureProc, (ClientData) NULL); Tk_DoWhenIdle(DelayedMap, (ClientData) NULL); if (synchronize) { XSynchronize(Tk_Display(w), True); } Tk_GeometryRequest(w, 200, 200); border = Tk_Get3DBorder(interp, w, None, "#ffe4c4"); if (border == NULL) { Tcl_SetResult(interp, (char *) NULL, TCL_STATIC); Tk_SetWindowBackground(w, WhitePixelOfScreen(Tk_Screen(w))); } else { Tk_SetBackgroundFromBorder(w, border); } XSetForeground(Tk_Display(w), DefaultGCOfScreen(Tk_Screen(w)), BlackPixelOfScreen(Tk_Screen(w))); /* * Make command-line arguments available in the Tcl variables "argc" * and "argv". Also set the "geometry" variable from the geometry * specified on the command line. */ args = Tcl_Merge(argc-1, argv+1); Tcl_SetVar(interp, "argv", args, TCL_GLOBAL_ONLY); ckfree(args); sprintf(buf, "%d", argc-1); Tcl_SetVar(interp, "argc", buf, TCL_GLOBAL_ONLY); Tcl_SetVar(interp, "argv0", (fileName != NULL) ? fileName : argv[0], TCL_GLOBAL_ONLY); if (geometry != NULL) { Tcl_SetVar(interp, "geometry", geometry, TCL_GLOBAL_ONLY); } /* * Add a few application-specific commands to the application's * interpreter. */ #ifdef SQUARE_DEMO Tcl_CreateCommand(interp, "square", SquareCmd, (ClientData) w, (void (*)()) NULL); #endif /* * Invoke application-specific initialization. */ if (Tcl_AppInit(interp) != TCL_OK) { fprintf(stderr, "%s\n", interp->result); exit(1); } /* * Invoke the script specified on the command line, if any. */ tty = isatty(0); if (fileName != NULL) { result = Tcl_VarEval(interp, "source ", fileName, (char *) NULL); if (result != TCL_OK) { goto error; } tty = 0; } else { /* * Commands will come from standard input. Set up a handler * to receive those characters and print a prompt if the input * device is a terminal. */ Tk_CreateFileHandler(0, TK_READABLE, StdinProc, (ClientData) 0); if (tty) { printf("wish: "); } } fflush(stdout); Tcl_DStringInit(&command); (void) Tcl_Eval(interp, "update"); /* * Loop infinitely, waiting for commands to execute. When there * are no windows left, Tk_MainLoop returns and we clean up and * exit. */ Tk_MainLoop(); Tcl_DeleteInterp(interp); Tcl_DStringFree(&command); exit(0); error: msg = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY); if (msg == NULL) { msg = interp->result; } fprintf(stderr, "%s\n", msg); Tcl_Eval(interp, "destroy ."); exit(1); return 0; /* Needed only to prevent compiler warnings. */ } /* *---------------------------------------------------------------------- * * StdinProc -- * * This procedure is invoked by the event dispatcher whenever * standard input becomes readable. It grabs the next line of * input characters, adds them to a command being assembled, and * executes the command if it's complete. * * Results: * None. * * Side effects: * Could be almost arbitrary, depending on the command that's * typed. * *---------------------------------------------------------------------- */ /* ARGSUSED */ static void StdinProc(clientData, mask) ClientData clientData; /* Not used. */ int mask; /* Not used. */ { #define BUFFER_SIZE 4000 char input[BUFFER_SIZE+1]; static int gotPartial = 0; char *cmd; int result, count; count = read(fileno(stdin), input, BUFFER_SIZE); if (count <= 0) { if (!gotPartial) { if (tty) { Tcl_Eval(interp, "exit"); exit(0); } else { Tk_DeleteFileHandler(0); } return; } else { count = 0; } } cmd = Tcl_DStringAppend(&command, input, count); if (count != 0) { if ((input[count-1] != '\n') && (input[count-1] != ';')) { gotPartial = 1; return; } if (!Tcl_CommandComplete(cmd)) { gotPartial = 1; return; } } gotPartial = 0; /* * Disable the stdin file handler; otherwise if the command * re-enters the event loop we might process commands from * stdin before the current command is finished. Among other * things, this will trash the text of the command being evaluated. */ Tk_CreateFileHandler(0, 0, StdinProc, (ClientData) 0); result = Tcl_RecordAndEval(interp, cmd, 0); Tk_CreateFileHandler(0, TK_READABLE, StdinProc, (ClientData) 0); Tcl_DStringFree(&command); if (*interp->result != 0) { if ((result != TCL_OK) || (tty)) { printf("%s\n", interp->result); } } if (tty) { printf("wish: "); fflush(stdout); } } /* *---------------------------------------------------------------------- * * StructureProc -- * * This procedure is invoked whenever a structure-related event * occurs on the main window. If the window is deleted, the * procedure modifies "w" to record that fact. * * Results: * None. * * Side effects: * Variable "w" may get set to NULL. * *---------------------------------------------------------------------- */ /* ARGSUSED */ static void StructureProc(clientData, eventPtr) ClientData clientData; /* Information about window. */ XEvent *eventPtr; /* Information about event. */ { if (eventPtr->type == DestroyNotify) { w = NULL; } } /* *---------------------------------------------------------------------- * * DelayedMap -- * * This procedure is invoked by the event dispatcher once the * startup script has been processed. It waits for all other * pending idle handlers to be processed (so that all the * geometry information will be correct), then maps the * application's main window. * * Results: * None. * * Side effects: * The main window gets mapped. * *---------------------------------------------------------------------- */ /* ARGSUSED */ static void DelayedMap(clientData) ClientData clientData; /* Not used. */ { while (Tk_DoOneEvent(TK_IDLE_EVENTS) != 0) { /* Empty loop body. */ } if (w == NULL) { return; } Tk_MapWindow(w); }