mirror of
https://github.com/minetest/irrlicht.git
synced 2024-11-16 15:30:25 +01:00
a3adfc196b
We had changed that once before in the other direction in svn r421 Reason back then was "Sleep(0) doesn't allow any lower priority threads to execute" But Microsoft changed the behaviour of Sleep(0) after Windows XP so that's no longer true. And the costs of it is pretty high - due to this using a timer with a 15ms resolutions it meant not just giving up the thread but it also always waited for 15ms on Windows. I also replaced a few sleep calls in examples for that reason with yield() calls. git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6459 dfc29bdd-3216-0410-991c-e03cc46cb475
513 lines
15 KiB
C++
513 lines
15 KiB
C++
/** Example 025 Xml Handling
|
|
|
|
Demonstrates loading and saving of configurations via XML
|
|
|
|
@author Y.M. Bosman \<yoran.bosman@gmail.com\>
|
|
|
|
This demo features a fully usable system for configuration handling. The code
|
|
can easily be integrated into own apps.
|
|
|
|
*/
|
|
|
|
#include <irrlicht.h>
|
|
#include "exampleHelper.h"
|
|
|
|
using namespace irr;
|
|
using namespace core;
|
|
using namespace scene;
|
|
using namespace video;
|
|
using namespace io;
|
|
using namespace gui;
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma comment(lib, "Irrlicht.lib")
|
|
#endif
|
|
|
|
|
|
/* SettingManager class.
|
|
|
|
This class loads and writes the settings and manages the options.
|
|
|
|
The class makes use of irrMap which is a an associative arrays using a
|
|
red-black tree it allows easy mapping of a key to a value, along the way there
|
|
is some information on how to use it.
|
|
*/
|
|
|
|
class SettingManager
|
|
{
|
|
public:
|
|
|
|
// Construct setting managers and set default settings
|
|
SettingManager(const stringw& settings_file): SettingsFile(settings_file), NullDevice(0)
|
|
{
|
|
// Irrlicht null device, we want to load settings before we actually created our device, therefore, nulldevice
|
|
NullDevice = irr::createDevice(irr::video::EDT_NULL);
|
|
|
|
//DriverOptions is an irrlicht map,
|
|
//we can insert values in the map in two ways by calling insert(key,value) or by using the [key] operator
|
|
//the [] operator overrides values if they already exist
|
|
DriverOptions.insert(L"Software", EDT_SOFTWARE);
|
|
DriverOptions.insert(L"OpenGL", EDT_OPENGL);
|
|
DriverOptions.insert(L"Direct3D9", EDT_DIRECT3D9);
|
|
|
|
//some resolution options
|
|
ResolutionOptions.insert(L"640x480", dimension2du(640,480));
|
|
ResolutionOptions.insert(L"800x600", dimension2du(800,600));
|
|
ResolutionOptions.insert(L"1024x768", dimension2du(1024,768));
|
|
|
|
//our preferred defaults
|
|
SettingMap.insert(L"driver", L"Direct3D9");
|
|
SettingMap.insert(L"resolution", L"640x480");
|
|
SettingMap.insert(L"fullscreen", L"0"); //0 is false
|
|
}
|
|
|
|
// Destructor, you could store settings automatically on exit of your
|
|
// application if you wanted to in our case we simply drop the
|
|
// nulldevice
|
|
~SettingManager()
|
|
{
|
|
if (NullDevice)
|
|
{
|
|
NullDevice->closeDevice();
|
|
NullDevice->drop();
|
|
}
|
|
};
|
|
|
|
/*
|
|
Load xml from disk, overwrite default settings
|
|
The xml we are trying to load has the following structure
|
|
settings nested in sections nested in the root node, like:
|
|
\verbatim
|
|
<pre>
|
|
<?xml version="1.0"?>
|
|
<mygame>
|
|
<video>
|
|
<setting name="driver" value="Direct3D9" />
|
|
<setting name="fullscreen" value="0" />
|
|
<setting name="resolution" value="1024x768" />
|
|
</video>
|
|
</mygame>
|
|
</pre>
|
|
\endverbatim
|
|
*/
|
|
bool load()
|
|
{
|
|
//if not able to create device don't attempt to load
|
|
if (!NullDevice)
|
|
return false;
|
|
|
|
irr::io::IXMLReader* xml = NullDevice->getFileSystem()->createXMLReader(SettingsFile); //create xml reader
|
|
if (!xml)
|
|
return false;
|
|
|
|
const stringw settingTag(L"setting"); //we'll be looking for this tag in the xml
|
|
stringw currentSection; //keep track of our current section
|
|
const stringw videoTag(L"video"); //constant for videotag
|
|
|
|
//while there is more to read
|
|
while (xml->read())
|
|
{
|
|
//check the node type
|
|
switch (xml->getNodeType())
|
|
{
|
|
//we found a new element
|
|
case irr::io::EXN_ELEMENT:
|
|
{
|
|
//we currently are in the empty or mygame section and find the video tag so we set our current section to video
|
|
if (currentSection.empty() && videoTag.equals_ignore_case(xml->getNodeName()))
|
|
{
|
|
currentSection = videoTag;
|
|
}
|
|
//we are in the video section and we find a setting to parse
|
|
else if (currentSection.equals_ignore_case(videoTag) && settingTag.equals_ignore_case(xml->getNodeName() ))
|
|
{
|
|
//read in the key
|
|
stringw key = xml->getAttributeValueSafe(L"name");
|
|
//if there actually is a key to set
|
|
if (!key.empty())
|
|
{
|
|
//set the setting in the map to the value,
|
|
//the [] operator overrides values if they already exist or inserts a new key value
|
|
//pair into the settings map if it was not defined yet
|
|
SettingMap[key] = xml->getAttributeValueSafe(L"value");
|
|
}
|
|
}
|
|
|
|
//..
|
|
// You can add your own sections and tags to read in here
|
|
//..
|
|
}
|
|
break;
|
|
|
|
//we found the end of an element
|
|
case irr::io::EXN_ELEMENT_END:
|
|
//we were at the end of the video section so we reset our tag
|
|
currentSection=L"";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// don't forget to delete the xml reader
|
|
xml->drop();
|
|
|
|
return true;
|
|
}
|
|
|
|
// Save the xml to disk. We use the nulldevice.
|
|
bool save()
|
|
{
|
|
|
|
//if not able to create device don't attempt to save
|
|
if (!NullDevice)
|
|
return false;
|
|
|
|
//create xml writer
|
|
irr::io::IXMLWriter* xwriter = NullDevice->getFileSystem()->createXMLWriter( SettingsFile );
|
|
if (!xwriter)
|
|
return false;
|
|
|
|
//write out the obligatory xml header. Each xml-file needs to have exactly one of those.
|
|
xwriter->writeXMLHeader();
|
|
|
|
//start element mygame, you replace the label "mygame" with anything you want
|
|
xwriter->writeElement(L"mygame");
|
|
xwriter->writeLineBreak(); //new line
|
|
|
|
//start section with video settings
|
|
xwriter->writeElement(L"video");
|
|
xwriter->writeLineBreak(); //new line
|
|
|
|
// getIterator gets us a pointer to the first node of the settings map
|
|
// every iteration we increase the iterator which gives us the next map node
|
|
// until we reach the end we write settings one by one by using the nodes key and value functions
|
|
map<stringw, stringw>::Iterator i = SettingMap.getIterator();
|
|
for(; !i.atEnd(); i++)
|
|
{
|
|
//write element as <setting name="key" value="x" />
|
|
//the second parameter indicates this is an empty element with no children, just attributes
|
|
xwriter->writeElement(L"setting",true, L"name", i->getKey().c_str(), L"value",i->getValue().c_str() );
|
|
xwriter->writeLineBreak();
|
|
}
|
|
xwriter->writeLineBreak();
|
|
|
|
//close video section
|
|
xwriter->writeClosingTag(L"video");
|
|
xwriter->writeLineBreak();
|
|
|
|
//..
|
|
// You can add writing sound settings, savegame information etc
|
|
//..
|
|
|
|
//close mygame section
|
|
xwriter->writeClosingTag(L"mygame");
|
|
|
|
//delete xml writer
|
|
xwriter->drop();
|
|
|
|
return true;
|
|
}
|
|
|
|
// Set setting in our manager
|
|
void setSetting(const stringw& name, const stringw& value)
|
|
{
|
|
SettingMap[name]=value;
|
|
}
|
|
|
|
// set setting overload to quickly assign integers to our setting map
|
|
void setSetting(const stringw& name, s32 value)
|
|
{
|
|
SettingMap[name]=stringw(value);
|
|
}
|
|
|
|
// Get setting as string
|
|
stringw getSetting(const stringw& key) const
|
|
{
|
|
//the find function of irr::map returns a pointer to a map::Node
|
|
//if the key can be found, otherwise it returns null
|
|
//the map node has the function getValue and getKey, as we already know the key, we return node->getValue()
|
|
map<stringw, stringw>::Node* n = SettingMap.find(key);
|
|
if (n)
|
|
return n->getValue();
|
|
else
|
|
return L"";
|
|
}
|
|
|
|
//
|
|
bool getSettingAsBoolean(const stringw& key ) const
|
|
{
|
|
stringw s = getSetting(key);
|
|
if (s.empty())
|
|
return false;
|
|
return s.equals_ignore_case(L"1");
|
|
}
|
|
|
|
//
|
|
s32 getSettingAsInteger(const stringw& key) const
|
|
{
|
|
//we implicitly cast to string instead of stringw because strtol10 does not accept wide strings
|
|
const stringc s = getSetting(key);
|
|
if (s.empty())
|
|
return 0;
|
|
|
|
return strtol10(s.c_str());
|
|
}
|
|
|
|
public:
|
|
map<stringw, s32> DriverOptions; //available options for driver config
|
|
map<stringw, dimension2du> ResolutionOptions; //available options for resolution config
|
|
private:
|
|
SettingManager(const SettingManager& other); // defined but not implemented
|
|
SettingManager& operator=(const SettingManager& other); // defined but not implemented
|
|
|
|
map<stringw, stringw> SettingMap; //current config
|
|
|
|
stringw SettingsFile; // filename of the xml
|
|
irr::IrrlichtDevice* NullDevice;
|
|
};
|
|
|
|
/*
|
|
Application context for global variables
|
|
*/
|
|
struct SAppContext
|
|
{
|
|
SAppContext()
|
|
: Device(0),Gui(0), Driver(0), Settings(0), ShouldQuit(false),
|
|
ButtonSave(0), ButtonExit(0), ListboxDriver(0),
|
|
ListboxResolution(0), CheckboxFullscreen(0)
|
|
{
|
|
}
|
|
|
|
~SAppContext()
|
|
{
|
|
if (Settings)
|
|
delete Settings;
|
|
|
|
if (Device)
|
|
{
|
|
Device->closeDevice();
|
|
Device->drop();
|
|
}
|
|
}
|
|
|
|
IrrlichtDevice* Device;
|
|
IGUIEnvironment* Gui;
|
|
IVideoDriver* Driver;
|
|
SettingManager* Settings;
|
|
bool ShouldQuit;
|
|
|
|
//settings dialog
|
|
IGUIButton* ButtonSave;
|
|
IGUIButton* ButtonExit;
|
|
IGUIListBox* ListboxDriver;
|
|
IGUIListBox* ListboxResolution;
|
|
IGUICheckBox* CheckboxFullscreen;
|
|
};
|
|
|
|
/*
|
|
A typical event receiver.
|
|
*/
|
|
class MyEventReceiver : public IEventReceiver
|
|
{
|
|
public:
|
|
MyEventReceiver(SAppContext & a) : App(a) { }
|
|
|
|
virtual bool OnEvent(const SEvent& event)
|
|
{
|
|
if (event.EventType == EET_GUI_EVENT )
|
|
{
|
|
switch ( event.GUIEvent.EventType )
|
|
{
|
|
//handle button click events
|
|
case EGET_BUTTON_CLICKED:
|
|
{
|
|
//Our save button was called so we obtain the settings from our dialog and save them
|
|
if ( event.GUIEvent.Caller == App.ButtonSave )
|
|
{
|
|
//if there is a selection write it
|
|
if ( App.ListboxDriver->getSelected() != -1)
|
|
App.Settings->setSetting(L"driver", App.ListboxDriver->getListItem(App.ListboxDriver->getSelected()));
|
|
|
|
//if there is a selection write it
|
|
if ( App.ListboxResolution->getSelected() != -1)
|
|
App.Settings->setSetting(L"resolution", App.ListboxResolution->getListItem(App.ListboxResolution->getSelected()));
|
|
|
|
App.Settings->setSetting(L"fullscreen", App.CheckboxFullscreen->isChecked());
|
|
|
|
|
|
if (App.Settings->save())
|
|
{
|
|
App.Gui->addMessageBox(L"Settings saved",L"Settings saved, please restart for settings to change effect","",true);
|
|
}
|
|
}
|
|
// cancel/exit button clicked, tell the application to exit
|
|
else if ( event.GUIEvent.Caller == App.ButtonExit)
|
|
{
|
|
App.ShouldQuit = true;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
SAppContext & App;
|
|
};
|
|
|
|
|
|
/*
|
|
Function to create a video settings dialog
|
|
This dialog shows the current settings from the configuration xml and allows them to be changed
|
|
*/
|
|
void createSettingsDialog(SAppContext& app)
|
|
{
|
|
// first get rid of alpha in gui
|
|
for (irr::s32 i=0; i<irr::gui::EGDC_COUNT ; ++i)
|
|
{
|
|
irr::video::SColor col = app.Gui->getSkin()->getColor((irr::gui::EGUI_DEFAULT_COLOR)i);
|
|
col.setAlpha(255);
|
|
app.Gui->getSkin()->setColor((irr::gui::EGUI_DEFAULT_COLOR)i, col);
|
|
}
|
|
|
|
//create video settings window
|
|
gui::IGUIWindow* windowSettings = app.Gui->addWindow(rect<s32>(10,10,400,400),true,L"Videosettings");
|
|
app.Gui->addStaticText (L"Select your desired video settings", rect< s32 >(10,20, 200, 40), false, true, windowSettings);
|
|
|
|
// add listbox for driver choice
|
|
app.Gui->addStaticText (L"Driver", rect< s32 >(10,50, 200, 60), false, true, windowSettings);
|
|
app.ListboxDriver = app.Gui->addListBox(rect<s32>(10,60,220,120), windowSettings, 1,true);
|
|
|
|
//add all available options to the driver choice listbox
|
|
map<stringw, s32>::Iterator i = app.Settings->DriverOptions.getIterator();
|
|
for(; !i.atEnd(); i++)
|
|
app.ListboxDriver->addItem(i->getKey().c_str());
|
|
|
|
//set currently selected driver
|
|
app.ListboxDriver->setSelected(app.Settings->getSetting("driver").c_str());
|
|
|
|
// add listbox for resolution choice
|
|
app.Gui->addStaticText (L"Resolution", rect< s32 >(10,130, 200, 140), false, true, windowSettings);
|
|
app.ListboxResolution = app.Gui->addListBox(rect<s32>(10,140,220,200), windowSettings, 1,true);
|
|
|
|
//add all available options to the resolution listbox
|
|
map<stringw, dimension2du>::Iterator ri = app.Settings->ResolutionOptions.getIterator();
|
|
for(; !ri.atEnd(); ri++)
|
|
app.ListboxResolution->addItem(ri->getKey().c_str());
|
|
|
|
//set currently selected resolution
|
|
app.ListboxResolution->setSelected(app.Settings->getSetting("resolution").c_str());
|
|
|
|
//add checkbox to toggle fullscreen, initially set to loaded setting
|
|
app.CheckboxFullscreen = app.Gui->addCheckBox(
|
|
app.Settings->getSettingAsBoolean("fullscreen"),
|
|
rect<s32>(10,220,220,240), windowSettings, -1,
|
|
L"Fullscreen");
|
|
|
|
//last but not least add save button
|
|
app.ButtonSave = app.Gui->addButton(
|
|
rect<s32>(80,250,150,270), windowSettings, 2,
|
|
L"Save video settings");
|
|
|
|
//exit/cancel button
|
|
app.ButtonExit = app.Gui->addButton(
|
|
rect<s32>(160,250,240,270), windowSettings, 2,
|
|
L"Cancel and exit");
|
|
}
|
|
|
|
/*
|
|
The main function. Creates all objects and does the XML handling.
|
|
*/
|
|
int main()
|
|
{
|
|
//create new application context
|
|
SAppContext app;
|
|
|
|
//create device creation parameters that can get overwritten by our settings file
|
|
SIrrlichtCreationParameters param;
|
|
param.DriverType = EDT_SOFTWARE;
|
|
param.WindowSize.set(640,480);
|
|
|
|
// Try to load config.
|
|
// I leave it as an exercise for the reader to store the configuration in the local application data folder,
|
|
// the only logical place to store config data for games. For all other operating systems I redirect to your manuals
|
|
app.Settings = new SettingManager(getExampleMediaPath() + "settings.xml");
|
|
if ( !app.Settings->load() )
|
|
{
|
|
// ...
|
|
// Here add your own exception handling, for now we continue because there are defaults set in SettingManager constructor
|
|
// ...
|
|
}
|
|
else
|
|
{
|
|
//settings xml loaded from disk,
|
|
|
|
//map driversetting to driver type and test if the setting is valid
|
|
//the DriverOptions map contains string representations mapped to to Irrlicht E_DRIVER_TYPE enum
|
|
//e.g "direct3d9" will become 4
|
|
//see DriverOptions in the settingmanager class for details
|
|
map<stringw, s32>::Node* driver = app.Settings->DriverOptions.find( app.Settings->getSetting("driver") );
|
|
|
|
if (driver)
|
|
{
|
|
if ( irr::IrrlichtDevice::isDriverSupported( static_cast<E_DRIVER_TYPE>( driver->getValue() )))
|
|
{
|
|
// selected driver is supported, so we use it.
|
|
param.DriverType = static_cast<E_DRIVER_TYPE>( driver->getValue());
|
|
}
|
|
}
|
|
|
|
//map resolution setting to dimension in a similar way as demonstrated above
|
|
map<stringw, dimension2du>::Node* res = app.Settings->ResolutionOptions.find( app.Settings->getSetting("resolution") );
|
|
if (res)
|
|
{
|
|
param.WindowSize = res->getValue();
|
|
}
|
|
|
|
//get fullscreen setting from config
|
|
param.Fullscreen = app.Settings->getSettingAsBoolean("fullscreen");
|
|
}
|
|
|
|
//create the irrlicht device using the settings
|
|
app.Device = createDeviceEx(param);
|
|
if (app.Device == 0)
|
|
{
|
|
// You can add your own exception handling on driver failure
|
|
exit(0);
|
|
}
|
|
|
|
app.Device->setWindowCaption(L"Xmlhandling - Irrlicht engine tutorial");
|
|
app.Driver = app.Device->getVideoDriver();
|
|
app.Gui = app.Device->getGUIEnvironment();
|
|
|
|
createSettingsDialog(app);
|
|
|
|
//set event receiver so we can respond to gui events
|
|
MyEventReceiver receiver(app);
|
|
app.Device->setEventReceiver(&receiver);
|
|
|
|
//enter main loop
|
|
while (!app.ShouldQuit && app.Device->run())
|
|
{
|
|
if (app.Device->isWindowActive())
|
|
{
|
|
app.Driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, SColor(0,200,200,200));
|
|
app.Gui->drawAll();
|
|
app.Driver->endScene();
|
|
}
|
|
app.Device->yield(); // be nice
|
|
}
|
|
|
|
//app destroys device in destructor
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
**/
|