Redirecting Lua Output
Written for CEGUI 0.7
Works with versions 0.7.x (obsolete)
What is the idea
I think you are here because you want to redirect Lua output to a window like a console or something similar. Then i will give you the answer. There is more than one way to do this. I will use the easy lua way here.
The setup
We will use a similar console window like in CEGUI_In_Practice_-_A_Game_Console. So if you don't know this Tutorial yet you can take a look but it is not a must. If you don't know how to use Lua with CEGUI take a look at Getting_Started_with_Lua_and_CEGUI.
This will be the Layout for our console. If your CEGUI version does not have Vanilla then you can use "TaharezLook". This layout is copied from "VanillaConsole.layout" which comes with CEGUI examples at the moment.
<?xml version="1.0" ?> <GUILayout> <Window Type="Vanilla/FrameWindow" Name="Vanilla/Console"> <Property Name="AlwaysOnTop" Value="True" /> <Property Name="UnifiedMinSize" Value="{{0.2,0},{0.2,0}}" /> <Property Name="UnifiedMaxSize" Value="{{0.8,0},{0.8,0}}" /> <Property Name="UnifiedPosition" Value="{{0.5,0},{0.5,0}}" /> <Property Name="UnifiedSize" Value="{{0.5,0},{0.45,0}}" /> <Property Name="Text" Value="Console" /> <Property Name="CloseButtonEnabled" Value="False" /> <Window Type="Vanilla/Button" Name="Vanilla/Console/Submit"> <Property Name="ID" Value="1" /> <Property Name="VerticalAlignment" Value="Bottom" /> <Property Name="HorizontalAlignment" Value="Right" /> <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" /> <Property Name="UnifiedPosition" Value="{{0,-7},{0,-7}}" /> <Property Name="UnifiedSize" Value="{{0.25,0},{0,30}}" /> <Property Name="Text" Value="Submit" /> </Window> <Window Type="Vanilla/Editbox" Name="Vanilla/Console/Editbox"> <Property Name="ID" Value="2" /> <Property Name="VerticalAlignment" Value="Bottom" /> <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" /> <Property Name="UnifiedPosition" Value="{{0,7},{0,-7}}" /> <Property Name="UnifiedSize" Value="{{0.75,-21},{0,30}}" /> <Property Name="Text" Value="" /> <Event Name="Clicked" Function="luabtn_clicked" /> </Window> <Window Type="Vanilla/MultiLineEditbox" Name="Vanilla/Console/History"> <Property Name="ID" Value="3" /> <Property Name="ReadOnly" Value="True" /> <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" /> <Property Name="UnifiedPosition" Value="{{0,7},{0,7}}" /> <Property Name="UnifiedSize" Value="{{1,-14},{1,-47}}" /> <Property Name="Text">Console ready for input:</Property> </Window> </Window> </GUILayout>
Please note that i have added the following line in the layout above to connect the event with the script i show later.
<Event Name="Clicked" Function="luabtn_clicked" />
After loading CEGUI we will do some basic setup. I use a Lua init script but you can also code this in C. My Lua init script looks like the following:
-- get CEGUI singletons local logger = CEGUI.Logger:getSingleton() logger:logEvent( ">>> Init script says hello" ) --logger:setLoggingLevel( CEGUI.Informative ) -- get a local reference to the singletons we use (not required) local system = CEGUI.System:getSingleton() local fontman = CEGUI.FontManager:getSingleton() local schememan = CEGUI.SchemeManager:getSingleton() -- load schemes schememan:create( "TaharezLook.scheme" ) schememan:create( "WindowsLook.scheme" ) schememan:create( "VanillaSkin.scheme" ) -- load a default font fontman:create( "DejaVuSans-10.font" ) -- set default mouse cursor system:setDefaultMouseCursor( "Vanilla-Images", "MouseArrow" ) -- set default tooltip type system:setDefaultTooltip("TaharezLook/Tooltip") logger:logEvent( "<<< Init script says goodbye" )
The Trick
Here comes now the interresting part (MyConsole.lua):
----------------------------------------- -- Start of handler functions ----------------------------------------- ----------------------------------------- -- Handler to add line which will be executed and execute it ----------------------------------------- function luabtn_clicked(args) local winMgr = CEGUI.WindowManager:getSingleton() local newText = winMgr:getWindow("Vanilla/Console/Editbox"):getText() local oldText = winMgr:getWindow("Vanilla/Console/History"):getText() print("executing: " .. newText) --execute code local f = loadstring(newText) if f then print(f()) else print("invalid syntax!") end end ----------------------------------------- -- Our print function which overrides default print behavior ----------------------------------------- function consolePrint(s) local winMgr = CEGUI.WindowManager:getSingleton() local oldText = winMgr:getWindow("Vanilla/Console/History"):getText() if s then CEGUI.toMultiLineEditbox(winMgr:getWindow("Vanilla/Console/History")):setText(oldText .. s) else CEGUI.toMultiLineEditbox(winMgr:getWindow("Vanilla/Console/History")):setText(oldText .. "") end end ----------------------------------------- -- Script Entry Point ----------------------------------------- local guiSystem = CEGUI.System:getSingleton() local winMgr = CEGUI.WindowManager:getSingleton() -- load our demo8 window layout local root = winMgr:loadWindowLayout("VanillaConsole.layout") -- set the layout as the root guiSystem:setGUISheet(root) --print/output redirect to cegui console window - overriding normal behavior print = consolePrint io.write = consolePrint out = consolePrint
As you can see we execute luabtn_clicked if someone clicks on the button. This function then trys to get a function out of the sting and trys to execute it if it is a function. If Lua is not able to build a function out of it the syntax is wrong and we will print that.
Because we have replaced the normal print function with our print version this function gets called every "print" instead. It just adds the string to print to our history window.
Don't forget to start the script
System::getSingletonPtr()->executeScriptFile("MyConsole.lua");
Error handling
Because we can make a typo in console input and do not want to crash our app we now need a error handling.
We can not use try/catch in Lua but we can do the error handling using the pcall function. I decided to do the error handling in C++ because i like try catch more and we get a good error message prepared from CEGUI ;)
We execute code in Lua if we click the button so we need to try/catch at the clickInjection. Because i don't know how i will use Lua in future i added the following try/catch everywhere i call a Lua script or inject something into CEGUI (also to the script execution call in the upper code block).
try { CEGUI::System::getSingleton().injectMouseButtonDown(convertOgreButtonToCegui(id)); } catch( CEGUI::Exception& e ) { showErrorMsgWindow((e.getMessage()+"\n\nFile: "+e.getFileName()+"\nLine: ").c_str(),__FILE__,__LINE__); } catch (...) { showErrorMsgWindow("Unknown CEGUI error!",__FILE__,__LINE__); }
inline void showErrorMsgWindow(const char* c_str, const char *file, const unsigned long line) { std::string str = c_str; str.append(file); str.append(CEGUI::PropertyHelper::intToString(line).c_str());//bad hack don't do this at home #ifdef _WIN32 MessageBox( NULL, str.c_str(), "Error",MB_OK | MB_ICONERROR | MB_TASKMODAL); #else std::cerr << str.c_str() << std::endl; #endif }
Don't forget the right includes ;)
Now we will get a message window in windows systems and a error message in other systems.
Conclusion
This shows by example how to redirect Lua output to a CEGUI window.
User:DEvil HUnter 22:20 - 28. August 2012 (MEZ)