00001 /*************************************************************************** 00002 *cr 00003 *cr (C) Copyright 1995-2019 The Board of Trustees of the 00004 *cr University of Illinois 00005 *cr All Rights Reserved 00006 *cr 00007 ***************************************************************************/ 00008 00009 /*************************************************************************** 00010 * RCS INFORMATION: 00011 * 00012 * $RCSfile: UIText.C,v $ 00013 * $Author: johns $ $Locker: $ $State: Exp $ 00014 * $Revision: 1.198 $ $Date: 2019年01月17日 21:21:02 $ 00015 * 00016 *************************************************************************** 00017 * DESCRIPTION: 00018 * 00019 * This is the User Interface for text commands. It reads characters from 00020 * the console, and executes the commands. 00021 * 00022 * This will use the Tcl library for general script interpretation, which 00023 * allows for general script capabilities such as variable substitution, 00024 * loops, etc. If Tcl cannot be used, text commands will still be available 00025 * in the program, but the general script capabilities will be lost. 00026 ***************************************************************************/ 00027 00028 #ifdef VMDPYTHON 00029 #include "PythonTextInterp.h" 00030 #endif 00031 00032 #include <stdio.h> 00033 #include <stdlib.h> 00034 #include <ctype.h> 00035 00036 #include "UIText.h" 00037 #include "TextEvent.h" 00038 #include "CommandQueue.h" 00039 #include "Inform.h" 00040 #include "config.h" 00041 #include "utilities.h" 00042 #include "PlainTextInterp.h" 00043 #include "VMDApp.h" 00044 #include "TclTextInterp.h" 00045 #include "Molecule.h" 00046 #include "MoleculeList.h" 00047 #include "SymbolTable.h" 00048 00049 #if defined(VMDTKCON) 00050 #include "vmdconsole.h" 00051 #endif 00052 00054 00055 // constructor 00056 UIText::UIText(VMDApp *vmdapp, int guienabled, int mpienabled) 00057 #ifdef VMDVRJUGGLER 00058 : UIObject(vmdapp), _isInitialized(false) { 00059 #else 00060 : UIObject(vmdapp) { 00061 #endif 00062 00063 // UIText logs all commands 00064 tclinterp = NULL; 00065 pythoninterp = NULL; 00066 00067 #if defined(VMDTKCON) 00068 cmdbufstr = new Inform("", VMDCON_ALWAYS); 00069 #else 00070 cmdbufstr = new Inform(""); 00071 #endif 00072 00073 // UIText logs all commands 00074 for (int i=0; i<Command::TOTAL; i++) command_wanted(i); 00075 00076 // Initialize the Tcl interpreter if enabled 00077 #ifdef VMDTCL 00078 tclinterp = new TclTextInterp(app, guienabled, mpienabled); 00079 #ifdef VMDTKCON 00080 // hook the default tcl interpreter into vmdcon as default. 00081 vmdcon_set_status(vmdcon_get_status(), ((TclTextInterp *)tclinterp)->get_interp()); 00082 #endif 00083 #endif 00084 00085 // The Python interpreter is initialized on first use. 00086 // However, if there's no Tcl interpreter, initialize the Python interpreter 00087 // right away. 00088 #ifdef VMDPYTHON 00089 if (!tclinterp) 00090 pythoninterp = new PythonTextInterp(app); 00091 #endif 00092 00093 // start with the Tcl interpreter by default, if it exists. If there is 00094 // no Tcl interpreter, go with Python. Last resort: PlainTextInterpreter 00095 // (which has nothing to do with crypto, by the way). 00096 if (tclinterp) 00097 interp = tclinterp; 00098 else if (pythoninterp) 00099 interp = pythoninterp; 00100 else 00101 interp = new PlainTextInterp; 00102 00103 reset(); 00104 } 00105 00106 // This is called in VMDApp::VMDinit just before reading the .vmdrc file. 00107 // It has to be done after the previous initialization because the 00108 // event look was not yet available. 00109 void UIText::read_init(void) { 00110 interp->doInit(); 00111 #ifdef VMDVRJUGGLER 00112 _isInitialized = true; 00113 #endif 00114 } 00115 00116 // destructor 00117 UIText::~UIText(void) { 00118 if (tclinterp) tclinterp->logfile_cb("exit"); 00119 #ifdef VMDTCL 00120 delete tclinterp; 00121 #endif 00122 00123 #ifdef VMDPYTHON 00124 delete pythoninterp; 00125 #endif 00126 delete cmdbufstr; 00127 } 00128 00129 #ifdef VMDVRJUGGLER 00130 bool UIText::isInitialized(){ 00131 // msgInfo << "isInitialized()" << sendmsg; 00132 return _isInitialized; 00133 } 00134 #endif 00135 00136 int UIText::save_state(const char *fname) { 00137 if (tclinterp) { 00138 JString cmd("save_state "); 00139 cmd += "{"; 00140 cmd += fname; 00141 cmd += "}"; 00142 return tclinterp->evalString((const char *)cmd); 00143 } 00144 return FALSE; 00145 } 00146 00147 // specify new file to read commands from 00148 void UIText::read_from_file(const char *fname) { 00149 #ifdef VMDVRJUGGLER 00150 if (isInitialized()) { 00151 // msgInfo << "is Initialized" << sendmsg; 00152 } else { 00153 msgInfo << "is not Initialized" << sendmsg; 00154 } 00155 00156 if (_isInitialized) { 00157 // && interp){ // prevent segfault when this is called during init 00158 // msgInfo << "interp not null;" << sendmsg; 00159 interp->evalFile(fname); 00160 } else { 00161 msgInfo << "not ready to read " << fname << sendmsg; 00162 } 00163 #else 00164 interp->evalFile(fname); 00165 #endif 00166 } 00167 00168 // check for an event; return TRUE if we found an event; FALSE otherwise 00169 int UIText::check_event(void) { 00170 00171 // no tk event handling when building as a shared object 00172 // for embedding in python. 00173 #if defined(VMD_SHARED) 00174 return FALSE; 00175 #endif 00176 00177 // let the text interpreter have control for a while 00178 // If a Python interpreter has been initialized, let it do the Tk updates. 00179 // Python takes care of updating Tk; if we try to update Tk from within 00180 // Tcl when Tkinter widgets have been created, we crash and burn as Tk 00181 // is not thread safe. 00182 // If Python was not able to update Tk, possibly because Tkinter was not 00183 // found, then use the Tcl interpreter. 00184 if (!pythoninterp || (pythoninterp && !pythoninterp->doTkUpdate())) { 00185 if (tclinterp) { 00186 tclinterp->doTkUpdate(); 00187 } else { 00188 // last resort - use whatever interpreter we've got. 00189 interp->doTkUpdate(); 00190 } 00191 } 00192 interp->doEvent(); 00193 return TRUE; 00194 } 00195 00196 00197 int UIText::act_on_command(int cmdtype, Command *cmd) { 00198 int action=1; 00199 00200 #if defined(VMDNVTX) 00201 // log command in profiler, if possible 00202 int profile_pushed=0; 00203 if (cmd->has_text(cmdbufstr)) { 00204 profile_pushed=1; 00205 PROFILE_PUSH_RANGE(cmdbufstr->text(), 1); 00206 } 00207 #endif 00208 00209 if (tclinterp) { 00210 // log command, if possible 00211 if (cmd->has_text(cmdbufstr)) { 00212 tclinterp->logfile_cb(cmdbufstr->text()); 00213 #ifdef VMDVRJUGGLER 00214 if (app->jugglerMode == VRJ_MASTER) { 00215 app->logfile_juggler(cmdbufstr->text()); 00216 } 00217 #endif 00218 cmdbufstr->reset(); 00219 } 00220 } 00221 00222 if (cmdtype == Command::INTERP_EVENT) { 00223 // downcast to InterpEvent 00224 InterpEvent *event = (InterpEvent *)cmd; 00225 if (tclinterp) 00226 event->do_callback(tclinterp); 00227 // ACK! This used be "else if (pythoninterp)", which means python 00228 // callbacks never get called if you build with Tcl. 00229 if (pythoninterp) 00230 event->do_callback(pythoninterp); 00231 } else { 00232 action=0; // no action taken 00233 } 00234 00235 #if defined(VMDNVTX) 00236 if (profile_pushed) { 00237 PROFILE_POP_RANGE(); // ensure we perform matching pop operation 00238 } 00239 #endif 00240 00241 return action; // return whether we used the command or not 00242 } 00243 00244 00245 int UIText::change_interp(const char *interpname) { 00246 if (!interpname) return FALSE; 00247 if (!strupcmp(interpname, "tcl")) { 00248 if (tclinterp) { 00249 msgInfo << "Text interpreter now Tcl" << sendmsg; 00250 interp = tclinterp; 00251 return TRUE; 00252 } else { 00253 msgErr << "Sorry, no Tcl text interpreter available" << sendmsg; 00254 // try for Python 00255 interpname = "python"; 00256 } 00257 } 00258 // fall through to Python if no Tcl interpreter is available 00259 if (!strupcmp(interpname, "python")) { 00260 if (pythoninterp) { 00261 msgInfo << "Text interpreter now Python" << sendmsg; 00262 interp = pythoninterp; 00263 // On MACOSX, when we change to the Python interpreter _after_ the 00264 // first time it's created (i.e. gopython, EOF, gopython), we get 00265 // kicked out right away because for some reason stdin has the EOF 00266 // flag set. So, we clear the EOF flag here. 00267 clearerr(stdin); 00268 return TRUE; 00269 } else { 00270 #if defined(VMDPYTHON) 00271 pythoninterp = new PythonTextInterp(app); 00272 if (pythoninterp) { 00273 msgInfo << "Text interpreter now Python" << sendmsg; 00274 interp = pythoninterp; 00275 return TRUE; 00276 } else { 00277 msgErr << "Sorry, unable to start Python text interpreter" << sendmsg; 00278 } 00279 #else 00280 msgErr << "Sorry, this version of VMD was compiled with Python support disabled" << sendmsg; 00281 #endif 00282 } 00283 } else { 00284 msgErr << "Unsupported text interpreter requested" << sendmsg; 00285 } 00286 return FALSE; 00287 } 00288