Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/core/command/TclBind.cc @ 8079

Last change on this file since 8079 was 8079, checked in by landauf, 13 years ago

merged usability branch back to trunk

incomplete summary of the changes in this branch:

  • enhanced keyboard navigation in GUIs
  • implemented new graphics menu and changeable window size at runtime
  • added developer mode
  • HUD shows if game is paused, game pauses if ingame menu is opened
  • removed a few obsolete commands and hid some that are more for internal use
  • numpad works in console and gui
  • faster loading of level info
  • enhanced usage of compositors (Shader class)
  • improved camera handling, configurable FOV and aspect ratio
  • Property svn:eol-style set to native
File size: 8.9 KB
Line 
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
29#include "TclBind.h"
30
31#include <exception>
32#include <string>
33#include <cpptcl/cpptcl.h>
34
35#include "SpecialConfig.h"
36#include "util/Debug.h"
37#include "util/Exception.h"
38#include "util/StringUtils.h"
39#include "core/PathConfig.h"
40#include "CommandExecutor.h"
41#include "ConsoleCommand.h"
42#include "TclThreadManager.h"
43
44namespace orxonox
45{
46    SetConsoleCommand("tcl", &TclBind::tcl);
47    SetConsoleCommand("bgerror", &TclBind::bgerror).hide();
48
49    TclBind* TclBind::singletonPtr_s = 0;
50
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    */
55    TclBind::TclBind(const std::string& datapath)
56    {
57        this->interpreter_ = 0;
58        this->bSetTclDataPath_ = false;
59        this->setDataPath(datapath);
60    }
61
62    /**
63        @brief Destructor: Deletes the Tcl-interpreter.
64    */
65    TclBind::~TclBind()
66    {
67        if (this->interpreter_)
68            delete this->interpreter_;
69    }
70
71    /**
72        @brief Defines the path to the directory that contains the Orxonox-specific Tcl-files and initializes the Tcl-interpreter accordingly.
73    */
74    void TclBind::setDataPath(const std::string& datapath)
75    {
76        // String has POSIX slashes
77        this->tclDataPath_ = datapath + "tcl" + '/';
78        this->bSetTclDataPath_ = true;
79
80        this->initializeTclInterpreter();
81    }
82
83    /**
84        @brief Creates and initializes the Tcl-interpreter by registering all callbacks and defining some useful functions.
85    */
86    void TclBind::initializeTclInterpreter()
87    {
88        if (this->bSetTclDataPath_ && !this->interpreter_)
89        {
90            this->interpreter_ = this->createTclInterpreter();
91
92            this->interpreter_->def("::orxonox::query", TclBind::tcl_query, Tcl::variadic());
93            this->interpreter_->def("::orxonox::execute", TclBind::tcl_execute, Tcl::variadic());
94            this->interpreter_->def("::orxonox::crossquery", TclThreadManager::tcl_crossquery, Tcl::variadic());
95            this->interpreter_->def("::orxonox::crossexecute", TclThreadManager::tcl_crossexecute, Tcl::variadic());
96
97            try
98            {
99                this->interpreter_->def("query", TclBind::tcl_query, Tcl::variadic());
100                this->interpreter_->def("execute", TclBind::tcl_execute, Tcl::variadic());
101                this->interpreter_->eval("proc crossquery   {id args} { ::orxonox::crossquery 0 $id $args }");
102                this->interpreter_->eval("proc crossexecute {id args} { ::orxonox::crossexecute 0 $id $args }");
103                this->interpreter_->eval("proc running      {}        { return 1 }");
104                this->interpreter_->eval("set id 0");
105                this->interpreter_->eval("rename exit ::tcl::exit; proc exit {} { execute exit }");
106            }
107            catch (Tcl::tcl_error const &e)
108            {   COUT(1) << "Tcl error while creating Tcl-interpreter: " << e.what() << std::endl;   }
109        }
110    }
111
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    */
116    Tcl::interpreter* TclBind::createTclInterpreter()
117    {
118        Tcl::interpreter* interpreter = new Tcl::interpreter();
119        const std::string& libpath = TclBind::getTclLibraryPath();
120
121        try
122        {
123            if (!libpath.empty())
124                interpreter->eval("set tcl_library \"" + libpath + '"');
125
126            Tcl_Init(interpreter->get());
127
128            interpreter->eval("source \"" + TclBind::getInstance().tclDataPath_ + "/init.tcl\"");
129        }
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;   }
132
133        return interpreter;
134    }
135
136    /**
137        @brief Returns the path to the Tcl-library (not the Orxonox-specific Tcl-files).
138    */
139    std::string TclBind::getTclLibraryPath()
140    {
141#ifdef DEPENDENCY_PACKAGE_ENABLE
142        if (PathConfig::isDevelopmentRun())
143            return (std::string(specialConfig::dependencyLibraryDirectory) + "/tcl");
144        else
145            return (PathConfig::getRootPathString() + "lib/tcl");
146#else
147        return "";
148#endif
149    }
150
151    /**
152        @brief Callback: Used to send an Orxonox-command from Tcl to the CommandExecutor and to send its result back to Tcl.
153    */
154    std::string TclBind::tcl_query(Tcl::object const &args)
155    {
156        COUT(4) << "Tcl_query: " << args.get() << std::endl;
157        return TclBind::tcl_helper(args, true);
158    }
159
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    {
174        const std::string& command = stripEnclosingBraces(args.get());
175
176        int error;
177        std::string result;
178
179        CommandEvaluation evaluation = CommandExecutor::evaluate(command);
180
181        if (bQuery)
182            result = evaluation.query(&error).getString();
183        else
184            error = evaluation.execute();
185
186        switch (error)
187        {
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;
192        }
193
194        if (error == CommandExecutor::Error)
195            COUT(3) << "Did you mean \"" << evaluation.getCommandSuggestion() << "\"?" << std::endl;
196
197        return result;
198    }
199
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    */
204    std::string TclBind::tcl(const std::string& tclcode)
205    {
206        if (TclBind::getInstance().interpreter_)
207        {
208            try
209            {
210                return TclBind::getInstance().interpreter_->eval("uplevel #0 " + tclcode);
211            }
212            catch (Tcl::tcl_error const &e)
213            {   COUT(1) << "Tcl error: " << e.what() << std::endl;   }
214        }
215
216        return "";
217    }
218
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    */
223    void TclBind::bgerror(const std::string& error)
224    {
225        COUT(1) << "Tcl background error: " << stripEnclosingBraces(error) << std::endl;
226    }
227
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    */
234    std::string TclBind::eval(const std::string& tclcode, int* error)
235    {
236        if (error)
237            *error = CommandExecutor::Success;
238
239        try
240        {
241            // execute the code
242            return TclBind::getInstance().interpreter_->eval(tclcode);
243        }
244        catch (Tcl::tcl_error const &e)
245        {   COUT(1) << "Tcl error: " << e.what() << std::endl;   }
246
247        if (error)
248            *error = CommandExecutor::Error;
249        return "";
250    }
251}
Note: See TracBrowser for help on using the repository browser.