Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/usability/src/libraries/core/command/TclBind.cc @ 8030

Last change on this file since 8030 was 8030, checked in by landauf, 13 years ago
  • TclBind: consistent definitions of query and execute binds
  • TclBind: fixed wrong binding of crossexecute
  • removed redundant console output of the "tcl" command
  • removed tclquery and tclexecute console commands (they were just shortcuts for TclThreadManager query/execute)
  • the following tcl helper commands are now hidden in the console: error, warning, info, debug, bgerror
  • removed old console commands which shadow functions of Tcl or are unnecessary with Tcl: puts, source, read, write, append
  • Property svn:eol-style set to native
File size: 8.9 KB
RevLine 
[1502]1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Fabian 'x3n' Landau
24 *   Co-authors:
25 *      ...
26 *
27 */
28
[1792]29#include "TclBind.h"
30
[3196]31#include <exception>
[1502]32#include <string>
[3196]33#include <cpptcl/cpptcl.h>
34
[3370]35#include "SpecialConfig.h"
[3196]36#include "util/Debug.h"
[5747]37#include "util/Exception.h"
[3280]38#include "util/StringUtils.h"
[7203]39#include "core/PathConfig.h"
[3196]40#include "CommandExecutor.h"
[1502]41#include "ConsoleCommand.h"
42#include "TclThreadManager.h"
43
44namespace orxonox
45{
[7236]46    SetConsoleCommand("tcl", &TclBind::tcl);
[8030]47    SetConsoleCommand("bgerror", &TclBind::bgerror).hide();
[1502]48
[3370]49    TclBind* TclBind::singletonPtr_s = 0;
[1792]50
[7401]51    /**
52        @brief Constructor: Initializes the Tcl-interpreter with a given data path.
53        @param datapath Path to the directory that contains the Orxonox-specific Tcl-files
54    */
[1792]55    TclBind::TclBind(const std::string& datapath)
[1502]56    {
57        this->interpreter_ = 0;
[3370]58        this->bSetTclDataPath_ = false;
[1792]59        this->setDataPath(datapath);
[1502]60    }
61
[7401]62    /**
63        @brief Destructor: Deletes the Tcl-interpreter.
64    */
[1502]65    TclBind::~TclBind()
66    {
67        if (this->interpreter_)
68            delete this->interpreter_;
69    }
70
[7401]71    /**
72        @brief Defines the path to the directory that contains the Orxonox-specific Tcl-files and initializes the Tcl-interpreter accordingly.
73    */
[1502]74    void TclBind::setDataPath(const std::string& datapath)
75    {
[2710]76        // String has POSIX slashes
[3370]77        this->tclDataPath_ = datapath + "tcl" + '/';
78        this->bSetTclDataPath_ = true;
[1502]79
[3370]80        this->initializeTclInterpreter();
[1502]81    }
82
[7401]83    /**
84        @brief Creates and initializes the Tcl-interpreter by registering all callbacks and defining some useful functions.
85    */
[3370]86    void TclBind::initializeTclInterpreter()
[1502]87    {
[3370]88        if (this->bSetTclDataPath_ && !this->interpreter_)
[1502]89        {
[3370]90            this->interpreter_ = this->createTclInterpreter();
91
92            this->interpreter_->def("::orxonox::query", TclBind::tcl_query, Tcl::variadic());
[8030]93            this->interpreter_->def("::orxonox::execute", TclBind::tcl_execute, Tcl::variadic());
[3370]94            this->interpreter_->def("::orxonox::crossquery", TclThreadManager::tcl_crossquery, Tcl::variadic());
95            this->interpreter_->def("::orxonox::crossexecute", TclThreadManager::tcl_crossexecute, Tcl::variadic());
[1502]96
97            try
98            {
[8030]99                this->interpreter_->def("query", TclBind::tcl_query, Tcl::variadic());
100                this->interpreter_->def("execute", TclBind::tcl_execute, Tcl::variadic());
[3370]101                this->interpreter_->eval("proc crossquery   {id args} { ::orxonox::crossquery 0 $id $args }");
[8030]102                this->interpreter_->eval("proc crossexecute {id args} { ::orxonox::crossexecute 0 $id $args }");
[3370]103                this->interpreter_->eval("proc running      {}        { return 1 }");
[1502]104                this->interpreter_->eval("set id 0");
[3370]105                this->interpreter_->eval("rename exit ::tcl::exit; proc exit {} { execute exit }");
[1502]106            }
107            catch (Tcl::tcl_error const &e)
108            {   COUT(1) << "Tcl error while creating Tcl-interpreter: " << e.what() << std::endl;   }
109        }
110    }
111
[7401]112    /**
113        @brief Creates and initializes a new Tcl-interpreter and calls the Orxonox-specific
114        init.tcl script that defines some special functions which are required by Orxonox.
115    */
[3370]116    Tcl::interpreter* TclBind::createTclInterpreter()
[1502]117    {
[3370]118        Tcl::interpreter* interpreter = new Tcl::interpreter();
[6417]119        const std::string& libpath = TclBind::getTclLibraryPath();
[3370]120
121        try
[1502]122        {
[6417]123            if (!libpath.empty())
124                interpreter->eval("set tcl_library \"" + libpath + '"');
[3370]125
126            Tcl_Init(interpreter->get());
127
128            interpreter->eval("source \"" + TclBind::getInstance().tclDataPath_ + "/init.tcl\"");
[1502]129        }
[3370]130        catch (Tcl::tcl_error const &e)
131        {   COUT(1) << "Tcl error while creating Tcl-interpreter: " << e.what() << std::endl; COUT(1) << "Error: Tcl isn't properly initialized. Orxonox might possibly not work like that." << std::endl;   }
[1502]132
[3370]133        return interpreter;
[1502]134    }
135
[7401]136    /**
137        @brief Returns the path to the Tcl-library (not the Orxonox-specific Tcl-files).
138    */
[3370]139    std::string TclBind::getTclLibraryPath()
140    {
141#ifdef DEPENDENCY_PACKAGE_ENABLE
[5929]142        if (PathConfig::isDevelopmentRun())
[5695]143            return (std::string(specialConfig::dependencyLibraryDirectory) + "/tcl");
[3370]144        else
[5929]145            return (PathConfig::getRootPathString() + "lib/tcl");
[3370]146#else
147        return "";
148#endif
149    }
150
[7401]151    /**
152        @brief Callback: Used to send an Orxonox-command from Tcl to the CommandExecutor and to send its result back to Tcl.
153    */
[1502]154    std::string TclBind::tcl_query(Tcl::object const &args)
155    {
156        COUT(4) << "Tcl_query: " << args.get() << std::endl;
[8030]157        return TclBind::tcl_helper(args, true);
158    }
[1502]159
[8030]160    /**
161        @brief Callback: Used to send an Orxonox-command from Tcl to the CommandExecutor.
162    */
163    void TclBind::tcl_execute(Tcl::object const &args)
164    {
165        COUT(4) << "Tcl_execute: " << args.get() << std::endl;
166        TclBind::tcl_helper(args, false);
167    }
168
169    /**
170        @brief Helper function, used by tcl_query() and tcl_execute().
171    */
172    std::string TclBind::tcl_helper(Tcl::object const &args, bool bQuery)
173    {
[6417]174        const std::string& command = stripEnclosingBraces(args.get());
[1502]175
[7228]176        int error;
[8030]177        std::string result;
178
[7238]179        CommandEvaluation evaluation = CommandExecutor::evaluate(command);
[8030]180
181        if (bQuery)
182            result = evaluation.query(&error).getString();
183        else
184            error = evaluation.execute();
185
[7228]186        switch (error)
[1502]187        {
[7228]188            case CommandExecutor::Error:       COUT(1) << "Error: Can't execute command \"" << command << "\", command doesn't exist. (B)" << std::endl; break;
189            case CommandExecutor::Incomplete:  COUT(1) << "Error: Can't execute command \"" << command << "\", not enough arguments given. (B)" << std::endl; break;
190            case CommandExecutor::Deactivated: COUT(1) << "Error: Can't execute command \"" << command << "\", command is not active. (B)" << std::endl; break;
191            case CommandExecutor::Denied:      COUT(1) << "Error: Can't execute command \"" << command << "\", access denied. (B)" << std::endl; break;
[1502]192        }
193
[7238]194        if (error == CommandExecutor::Error)
195            COUT(3) << "Did you mean \"" << evaluation.getCommandSuggestion() << "\"?" << std::endl;
196
[7189]197        return result;
[1502]198    }
199
[7401]200    /**
201        @brief Console command, executes Tcl code. Can be used to bind Tcl-commands to a key, because native
202        Tcl-commands can not be evaluated and are thus not supported by the key-binder.
203    */
[1502]204    std::string TclBind::tcl(const std::string& tclcode)
205    {
206        if (TclBind::getInstance().interpreter_)
207        {
208            try
209            {
[8030]210                return TclBind::getInstance().interpreter_->eval("uplevel #0 " + tclcode);
[1502]211            }
212            catch (Tcl::tcl_error const &e)
[8030]213            {   COUT(1) << "Tcl error: " << e.what() << std::endl;   }
[1502]214        }
215
216        return "";
217    }
218
[7401]219    /**
220        @brief Console command and implementation of the Tcl-feature "bgerror" which is called if an error
221        occurred in the background of a Tcl-script.
222    */
[6417]223    void TclBind::bgerror(const std::string& error)
[1502]224    {
225        COUT(1) << "Tcl background error: " << stripEnclosingBraces(error) << std::endl;
226    }
227
[7401]228    /**
229        @brief Executes Tcl-code and returns the return-value.
230        @param tclcode A string that contains Tcl-code
231        @param error A pointer to an integer (or NULL) that is used to write an error-code (see @ref CommandExecutorErrorCodes "CommandExecutor error codes")
232        @return Returns the return-value of the executed code (or an empty string if there's no return-value)
233    */
[7228]234    std::string TclBind::eval(const std::string& tclcode, int* error)
[1502]235    {
[7228]236        if (error)
237            *error = CommandExecutor::Success;
[7189]238
[1502]239        try
240        {
[7401]241            // execute the code
[7189]242            return TclBind::getInstance().interpreter_->eval(tclcode);
[1502]243        }
244        catch (Tcl::tcl_error const &e)
245        {   COUT(1) << "Tcl error: " << e.what() << std::endl;   }
246
[7228]247        if (error)
248            *error = CommandExecutor::Error;
[7189]249        return "";
[1502]250    }
251}
Note: See TracBrowser for help on using the repository browser.