Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/gamestates2/src/libraries/core/GUIManager.cc @ 6736

Last change on this file since 6736 was 6736, checked in by rgrieder, 14 years ago

All CEGUI calls that can execute Lua code should be protected against ScriptException.
Although this is bad design (can leave the game in an undefined state), the game doesn't terminate just because of simple script problem.
When the GUI code is more mature, we should probably remove this again.

  • Property svn:eol-style set to native
File size: 14.3 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 *      Reto Grieder
24 *      Benjamin Knecht
25 *   Co-authors:
26 *      ...
27 *
28 */
29
30#include "GUIManager.h"
31
32#include <boost/bind.hpp>
33#include <memory>
34extern "C" {
35#include <lua.h>
36}
37#include <CEGUIDefaultLogger.h>
38#include <CEGUIExceptions.h>
39#include <CEGUIInputEvent.h>
40#include <CEGUIMouseCursor.h>
41#include <CEGUIResourceProvider.h>
42#include <CEGUISystem.h>
43#include <CEGUIWindow.h>
44#include <ogreceguirenderer/OgreCEGUIRenderer.h>
45
46#include "SpecialConfig.h" // Configures the macro below
47#ifdef CEGUILUA_USE_INTERNAL_LIBRARY
48#   include <ceguilua/CEGUILua.h>
49#else
50#   include <CEGUILua.h>
51#endif
52
53#include "util/Clock.h"
54#include "util/Convert.h"
55#include "util/Debug.h"
56#include "util/Exception.h"
57#include "util/OrxAssert.h"
58#include "ConsoleCommand.h"
59#include "Core.h"
60#include "GraphicsManager.h"
61#include "LuaState.h"
62#include "PathConfig.h"
63#include "Resource.h"
64#include "input/InputManager.h"
65#include "input/InputState.h"
66#include "input/KeyBinderManager.h"
67
68namespace orxonox
69{
70    static void key_esc()
71        { GUIManager::getInstance().keyESC(); }
72    SetConsoleCommandShortcutExternAlias(key_esc, "keyESC");
73
74    class CEGUILogger : public CEGUI::DefaultLogger
75    {
76    public:
77        void logEvent(const CEGUI::String& message, CEGUI::LoggingLevel level = CEGUI::Standard)
78        {
79            int orxonoxLevel = CEGUI::Standard;
80            switch (level)
81            {
82                case CEGUI::Errors:      orxonoxLevel = 1; break;
83                case CEGUI::Warnings:    orxonoxLevel = 2; break;
84                case CEGUI::Standard:    orxonoxLevel = 4; break;
85                case CEGUI::Informative: orxonoxLevel = 5; break;
86                case CEGUI::Insane:      orxonoxLevel = 6; break;
87                default: OrxAssert(false, "CEGUI log level out of range, inpect immediately!");
88            }
89            OutputHandler::getOutStream(orxonoxLevel)
90                << "CEGUI: " << message << std::endl;
91
92            CEGUI::DefaultLogger::logEvent(message, level);
93        }
94    };
95
96    static CEGUI::MouseButton convertButton(MouseButtonCode::ByEnum button);
97
98    GUIManager* GUIManager::singletonPtr_s = 0;
99
100    SetConsoleCommandShortcut(GUIManager, showGUI).accessLevel(AccessLevel::User).defaultValue(1, false).defaultValue(2, true);
101    SetConsoleCommandShortcut(GUIManager, hideGUI).accessLevel(AccessLevel::User);
102
103    /**
104    @brief
105        Constructs the GUIManager by starting up CEGUI
106
107        Creates the interface to Ogre, sets up the CEGUI renderer and the Lua script module together with the Lua engine.
108        The log is set up and connected to the CEGUILogger.
109        After Lua setup tolua++-elements are linked to Lua-state to give Lua access to C++-code.
110        Finally initial Lua code is executed (maybe we can do this with the CEGUI startup script automatically).
111    @param renderWindow
112        Ogre's render window. Without this, the GUI cannot be displayed.
113    @return true if success, otherwise false
114    */
115    GUIManager::GUIManager(const std::pair<int, int>& mousePosition)
116        : resourceProvider_(NULL)
117        , camera_(NULL)
118    {
119        using namespace CEGUI;
120
121        COUT(3) << "Initialising CEGUI." << std::endl;
122
123        // Note: No SceneManager specified yet
124        guiRenderer_.reset(new OgreCEGUIRenderer(GraphicsManager::getInstance().getRenderWindow(), Ogre::RENDER_QUEUE_OVERLAY, false, 3000));
125        resourceProvider_ = guiRenderer_->createResourceProvider();
126        resourceProvider_->setDefaultResourceGroup("GUI");
127
128        // setup scripting
129        luaState_.reset(new LuaState());
130        rootFileInfo_ = Resource::getInfo("InitialiseGUI.lua");
131        // This is necessary to ensure that input events also use the right resource info when triggering lua functions
132        luaState_->setDefaultResourceInfo(this->rootFileInfo_);
133        scriptModule_.reset(new LuaScriptModule(luaState_->getInternalLuaState()));
134
135        // Create our own logger to specify the filepath
136        std::auto_ptr<CEGUILogger> ceguiLogger(new CEGUILogger());
137        ceguiLogger->setLogFilename(PathConfig::getLogPathString() + "cegui.log");
138        // set the log level according to ours (translate by subtracting 1)
139        ceguiLogger->setLoggingLevel(
140            static_cast<LoggingLevel>(OutputHandler::getInstance().getSoftDebugLevel("logFile") - 1));
141        this->ceguiLogger_ = ceguiLogger.release();
142
143        // create the CEGUI system singleton
144        guiSystem_.reset(new System(guiRenderer_.get(), resourceProvider_, 0, scriptModule_.get()));
145
146        // Align CEGUI mouse with OIS mouse
147        guiSystem_->injectMousePosition((float)mousePosition.first, (float)mousePosition.second);
148
149        // Hide the mouse cursor unless playing in full screen mode
150        if (!GraphicsManager::getInstance().isFullScreen())
151            CEGUI::MouseCursor::getSingleton().hide();
152
153        // Initialise the basic Lua code
154        this->luaState_->doFile("InitialiseGUI.lua");
155    }
156
157    /**
158    @brief
159        Basically shuts down CEGUI (member smart pointers) but first unloads our Tolua modules.
160    */
161    GUIManager::~GUIManager()
162    {
163    }
164
165    /**
166    @brief
167        used to tick the GUI
168    @param time
169        clock which provides time value for the GUI System
170
171        Ticking the GUI means updating it with a certain regularity.
172        The elapsed time since the last call is given in the time value provided by the clock.
173        This time value is then used to provide a fluent animation of the GUI.
174    */
175    void GUIManager::preUpdate(const Clock& time)
176    {
177        assert(guiSystem_);
178        this->protectedCall(boost::bind(&CEGUI::System::injectTimePulse, _1, time.getDeltaTime()));
179    }
180
181    /**
182    @brief
183        Tells the GUIManager which SceneManager to use
184    @param camera
185        The current camera on which the GUI should be displayed on.
186
187        In fact the GUIManager needs the SceneManager and not the Camera to display the GUI.
188        This means the GUI is not bound to a camera but rather to the SceneManager.
189        Hiding the GUI when needed can therefore not be resolved by just NOT setting the current camera.
190    */
191    void GUIManager::setCamera(Ogre::Camera* camera)
192    {
193        this->camera_ = camera;
194        if (camera == NULL)
195            this->guiRenderer_->setTargetSceneManager(0);
196        else
197            this->guiRenderer_->setTargetSceneManager(camera->getSceneManager());
198    }
199
200    /**
201    @brief
202        Executes Lua code
203    @param str
204        reference to string object holding the Lua code which is to be executed
205
206        This function gives total access to the GUI. You can execute ANY Lua code here.
207    */
208    void GUIManager::executeCode(const std::string& str)
209    {
210        this->luaState_->doString(str, rootFileInfo_);
211    }
212
213    /** Loads a GUI sheet by Lua script
214    @param name
215        The name of the GUI (like the script name, but without the extension)
216    */
217    void GUIManager::loadGUI(const std::string& name)
218    {
219        this->executeCode("loadGUI(\"" + name + "\")");
220    }
221
222    /**
223    @brief
224        Displays specified GUI on screen
225    @param name
226        The name of the GUI
227
228        The function executes the Lua function with the same name in case the GUIManager is ready.
229    */
230    /*static*/ void GUIManager::showGUI(const std::string& name, bool bHidePrevious)
231    {
232        GUIManager::getInstance().executeCode("showMenuSheet(\"" + name + "\", " + multi_cast<std::string>(bHidePrevious) + ")");
233    }
234
235    /**
236    @brief
237        Hack-ish. Needed for GUIOverlay.
238    */
239    void GUIManager::showGUIExtra(const std::string& name, const std::string& ptr, bool bHidePrevious)
240    {
241        this->executeCode("showMenuSheet(\"" + name + "\", " + multi_cast<std::string>(bHidePrevious) + ", " + ptr + ")");
242    }
243
244    /**
245    @brief
246        Hides specified GUI.
247    @param name
248        The name of the GUI.
249    */
250    /*static*/ void GUIManager::hideGUI(const std::string& name)
251    {
252        GUIManager::getInstance().executeCode("hideMenuSheet(\"" + name + "\")");
253    }
254
255    const std::string& GUIManager::createInputState(const std::string& name, TriBool::Value showCursor, TriBool::Value useKeyboard, bool bBlockJoyStick)
256    {
257        InputState* state = InputManager::getInstance().createInputState(name);
258
259        /* Table that maps isFullScreen() and showCursor to mouseExclusive
260        isFullscreen / showCursor | True  | False | Dontcare
261        ----------------------------------------------------
262        true                      | True  | True  | Dontcare
263        ----------------------------------------------------
264        false                     | False | True  | Dontcare
265        */
266        if (showCursor == TriBool::Dontcare)
267            state->setMouseExclusive(TriBool::Dontcare);
268        else if (GraphicsManager::getInstance().isFullScreen() || showCursor == TriBool::False)
269            state->setMouseExclusive(TriBool::True);
270        else
271            state->setMouseExclusive(TriBool::False);
272
273        if (showCursor == TriBool::True)
274            state->setMouseHandler(this);
275        else if (showCursor == TriBool::False)
276            state->setMouseHandler(&InputHandler::EMPTY);
277
278        if (useKeyboard == TriBool::True)
279            state->setKeyHandler(this);
280        else if (useKeyboard == TriBool::False)
281            state->setKeyHandler(&InputHandler::EMPTY);
282
283        if (bBlockJoyStick)
284            state->setJoyStickHandler(&InputHandler::EMPTY);
285
286        return state->getName();
287    }
288
289    void GUIManager::keyESC()
290    {
291        this->executeCode("keyESC()");
292    }
293
294    void GUIManager::setBackground(const std::string& name)
295    {
296        this->executeCode("setBackground(\"" + name + "\")");
297    }
298
299    void GUIManager::keyPressed(const KeyEvent& evt)
300    {
301        this->protectedCall(boost::bind(&CEGUI::System::injectKeyDown, _1, evt.getKeyCode()));
302        this->protectedCall(boost::bind(&CEGUI::System::injectChar, _1, evt.getText()));
303    }
304
305    void GUIManager::keyReleased(const KeyEvent& evt)
306    {
307        this->protectedCall(boost::bind(&CEGUI::System::injectKeyUp, _1, evt.getKeyCode()));
308    }
309
310    /**
311    @brief
312        Function receiving a mouse button pressed event.
313    @param id
314        ID of the mouse button which got pressed
315
316        This function is inherited by MouseHandler and injects the event into CEGUI.
317        It is for CEGUI to process the event.
318    */
319    void GUIManager::buttonPressed(MouseButtonCode::ByEnum id)
320    {
321        this->protectedCall(boost::bind(&CEGUI::System::injectMouseButtonDown, _1, convertButton(id)));
322    }
323
324    /**
325    @brief
326        Function receiving a mouse button released event.
327    @param id
328        ID of the mouse button which got released
329
330        This function is inherited by MouseHandler and injects the event into CEGUI.
331        It is for CEGUI to process the event.
332    */
333    void GUIManager::buttonReleased(MouseButtonCode::ByEnum id)
334    {
335        this->protectedCall(boost::bind(&CEGUI::System::injectMouseButtonUp, _1, convertButton(id)));
336    }
337
338    void GUIManager::mouseMoved(IntVector2 abs, IntVector2 rel, IntVector2 clippingSize)
339    {
340        this->protectedCall(boost::bind(&CEGUI::System::injectMousePosition, _1, (float)abs.x, (float)abs.y));
341    }
342
343    void GUIManager::mouseScrolled(int abs, int rel)
344    {
345        this->protectedCall(boost::bind(&CEGUI::System::injectMouseWheelChange, _1, (float)rel));
346    }
347
348    /**
349    @brief
350        converts mouse event code to CEGUI event code
351    @param button
352        code of the mouse button as we use it in Orxonox
353    @return
354        code of the mouse button as it is used by CEGUI
355
356        Simple conversion from mouse event code in Orxonox to the one used in CEGUI.
357     */
358    static inline CEGUI::MouseButton convertButton(MouseButtonCode::ByEnum button)
359    {
360        switch (button)
361        {
362        case MouseButtonCode::Left:
363            return CEGUI::LeftButton;
364
365        case MouseButtonCode::Right:
366            return CEGUI::RightButton;
367
368        case MouseButtonCode::Middle:
369            return CEGUI::MiddleButton;
370
371        case MouseButtonCode::Button3:
372            return CEGUI::X1Button;
373
374        case MouseButtonCode::Button4:
375            return CEGUI::X2Button;
376
377        default:
378            return CEGUI::NoButton;
379        }
380    }
381
382    /** Executes a CEGUI function normally, but catches CEGUI::ScriptException.
383        When a ScriptException occurs, the error message will be displayed and
384        the program carries on.
385    @remarks
386        The exception behaviour may pose problems if the code is not written
387        exception-safe (and you can forget about that in Lua). The program might
388        be left in an undefined state. But otherwise one script error would
389        terminate the whole program...
390    @note
391        Your life gets easier if you use boost::bind to create the object/function.
392    @param function
393        Any callable object/function that takes this->guiSystem_ as its only parameter.
394    @return
395        True if input was handled, false otherwise. A caught exception yields true.
396    */
397    template <typename FunctionType>
398    bool GUIManager::protectedCall(FunctionType function)
399    {
400        try
401        {
402            return function(this->guiSystem_);
403        }
404        catch (CEGUI::ScriptException& ex)
405        {
406            // Display the error and proceed. See @remarks why this can be dangerous.
407            COUT(1) << ex.getMessage() << std::endl;
408            return true;
409        }
410    }
411
412    void GUIManager::subscribeEventHelper(CEGUI::Window* window, const std::string& event, const std::string& function)
413    {
414        window->subscribeScriptedEvent(event, function);
415    }
416}
Note: See TracBrowser for help on using the repository browser.