IGUIElement.remove() causes segmentation fault

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
WLGfx
Posts: 9
Joined: Fri Nov 23, 2012 12:56 am

IGUIElement.remove() causes segmentation fault

Post by WLGfx »

I'm almost finished with implementing my own GUI handler. Using the GUI editor to generate xml files, my code loads them up when needed and during the event receiver function will call a list of gui handler callbacks. However, when I come to remove a loaded GUI (I've given it a dummy parent element to make it easier to locate), it will crash.

It happens in the main.cpp file when it comes to remove the element from the GUI environment.

If anyone knows how to help it would greatly be appreciated as I've done as much searching to get this far so I don't have to ask any questions.

ilib.h

Code: Select all

#ifndef ILIB_H
#define ILIB_H
 
// I'm linking statically so I don't have to load the .so or .dll file at run
 
#define _IRR_STATIC_LIB_
#include "irrlicht.h"
 
using namespace irr;
 
// typedef for the function pointer which handles individual GUI loaded events
// ie void my_gui(SEvent& event);
 
typedef void(*gui_func)(const SEvent& event);
 
struct irr_gui_loader {
    //gui_func func;
    void (*func)(const SEvent &event);
    gui::IGUIElement    *parent;
    core::stringc       filename;
    int                 id;
 
    irr_gui_loader(gui_func f, gui::IGUIElement *p, char *file, int ID) {
        func = f;
        parent = p;
        filename = file;
        id = ID; }
};
 
class ievents;
 
class ilib
{
    public:
        ilib();
        virtual ~ilib();
 
        IrrlichtDevice          *device;
        video::IVideoDriver     *driver;
        scene::ISceneManager    *smgr;
        gui::IGUIEnvironment    *gui;
        ievents                 *events;
 
        bool    start(int width,int height,wchar_t *title=L"");
        void    add_gui(char *file, gui_func func, int ID);
        void    remove_gui(int ID);
        void    drawAll(int col=0x000000);
 
        video::IVideoDriver *getDriver() { return driver; }
        scene::ISceneManager *getScene() { return smgr; }
        gui::IGUIEnvironment *getgui() { return gui; }
 
    protected:
    private:
};
 
class ievents : public IEventReceiver
{
public:
 
    bool keys[KEY_KEY_CODES_COUNT];
    short   mouse_buttons;
    int     mouse_x, mouse_y;
    int     mouse_move_x, mouse_move_y;
    int     mouse_last_x, mouse_last_y;
    int     mouse_wheel;
 
    int     getMouseButtons() { return mouse_buttons; }
    int     getMouseX() { return mouse_x; }
    int     getMouseY() { return mouse_y; }
    int     getMouseMoveX() { int mx=mouse_move_x; mouse_move_x=0; return mx; }
    int     getMouseMoveY() { int my=mouse_move_y; mouse_move_y=0; return my; }
    int     getMouseWheel() { int mw=mouse_wheel; mouse_wheel=0; return mw; }
 
    // This array holds the list of functions that our GUI handler calls
    // Each function corresponds to a particular GUI that is loaded
    core::array<irr_gui_loader> gui_handlers;
 
    // This is the one method that we have to implement
    virtual bool OnEvent(const SEvent& event);
 
    ievents();
 
private:
};
 
#endif // ILIB_H
 
ilib.cpp

Code: Select all

#include "ilib.h"
 
using namespace irr;
 
// Event handler
// -------------
 
ievents::ievents()
{
    for (int c=0;c<KEY_KEY_CODES_COUNT;c++) keys[c]=false;
}
 
bool ievents::OnEvent(const SEvent& event)
{
    switch ( (int)event.EventType )
    {
        case EET_KEY_INPUT_EVENT:
 
            // KEYBOARD
            // --------
 
            keys[event.KeyInput.Key] = event.KeyInput.PressedDown;
 
            return true;
 
            break;
 
        case EET_MOUSE_INPUT_EVENT:
 
            // MOUSE
            // -----
 
            switch ( event.MouseInput.Event )
            {
                case EMIE_LMOUSE_PRESSED_DOWN:
                    mouse_buttons |= 1;
                    break;
                case EMIE_LMOUSE_LEFT_UP:
                    mouse_buttons &= 1;
                    break;
                case EMIE_RMOUSE_PRESSED_DOWN:
                    mouse_buttons |= 2;
                    break;
                case EMIE_RMOUSE_LEFT_UP:
                    mouse_buttons &= 2;
                    break;
                case EMIE_MMOUSE_PRESSED_DOWN:
                    mouse_buttons |= 4;
                    break;
                case EMIE_MMOUSE_LEFT_UP:
                    mouse_buttons &= 4;
                    break;
                case EMIE_MOUSE_MOVED:
                    mouse_x = event.MouseInput.X;
                    mouse_y = event.MouseInput.Y;
                    mouse_move_x += mouse_x - mouse_last_x;
                    mouse_move_y += mouse_y - mouse_last_y;
                    mouse_last_x = mouse_x;
                    mouse_last_y = mouse_y;
                    break;
                case EMIE_MOUSE_WHEEL:
                    mouse_wheel += event.MouseInput.Wheel;
                    break;
            }
 
            return false;   // pass on to GUI
 
            break;
 
        case EET_GUI_EVENT:
 
            // GUI
            // ---
 
            {
                int pos = gui_handlers.size();
 
                while ( pos-- )
                {
                    void (*guifunc)(const SEvent&) = gui_handlers[pos].func;
                    guifunc(event);
                }
            }
 
            return true;
 
            break;
    }
 
    return false;
}
 
// library handler
// ---------------
 
ilib::ilib()
{
    device = 0;
}
 
ilib::~ilib()
{
    if (device) device->drop();
    if (events) delete events;
}
 
// start irrlicht with the basics, width, height and default events
 
bool ilib::start(int width,int height,wchar_t *title)
{
    device = createDevice(video::EDT_OPENGL,
                core::dimension2du(800,600),
                32, false, false, true, 0);
 
    if (!device) false;
 
    device->setWindowCaption(title);
    driver  = device->getVideoDriver();
    smgr    = device->getSceneManager();
    gui     = device->getGUIEnvironment();
    events  = new ievents;
 
    device->setEventReceiver(events);
 
    return true;
}
 
void ilib::add_gui(char *file, gui_func func, int ID)
{
    // add a blank parent node
 
    gui::IGUIElement *parent = gui->addGUIElement(file,0);
 
    if ( !gui->loadGUI(file) )
    {
        // failed to load the gui file then remove the dummy parent
 
        parent->remove();
    }
    else
    {
        // add it to the handlers list
 
        events->gui_handlers.push_back( irr_gui_loader(func, parent, file,ID) );
    }
}
 
void ilib::remove_gui(int ID)
{
    int pos = events->gui_handlers.size();
 
    while ( pos-- )
    {
        if ( events->gui_handlers[pos].id == ID )
        {
            events->gui_handlers[pos].parent->remove();
            events->gui_handlers.erase(pos);
        }
    }
}
 
void ilib::drawAll(int col)
{
    device->getTimer()->tick();
    driver->beginScene(true,false,col);
    smgr->drawAll();
    gui->drawAll();
    driver->endScene();
}
 
main.cpp (to test it out)

Code: Select all

#include "main.h"
 
using namespace std;
 
ilib il;
 
int bg_colour = 0x204000;
 
void winbuttons(const SEvent& event);
 
int main(int argc, char *argv[])
{
    il.start(640,480,L"Hello World");
    il.add_gui( "winbuttons2.xml", winbuttons, 1 );
 
    cout << "\nSo far, so good...\n";
 
    while ( il.device->run() )
    {
        // display something in the windows title
        core::stringw text = "Mouse ";
        text += core::stringw(il.events->getMouseX());
        text += " , ";
        text += core::stringw(il.events->getMouseY());
 
        text += " - GUI handlers = ";
        text += core::stringw( il.events->gui_handlers.size() );
 
        il.device->setWindowCaption(text.c_str());
        il.drawAll(bg_colour);
    }
 
    sleep(1);
 
    return 0;
}
 
void winbuttons(const SEvent& event)
{
    s32 id = event.GUIEvent.Caller->getID();
 
    if ( event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
    {
        switch ( id ) {
            // These ID's are what are edited within the GUI editor
            case 10:
                cout << "Button 1 clicked \n";
                bg_colour = 0x002040;
                break;
            case 11:
                cout << "Button 2 clicked \n";
                bg_colour = 0x200040;
                break;
            case 12:
                cout << "Quit button clicked\n";
                il.remove_gui(1);
                break;
        }
 
        // test the windows close button
 
        gui::IGUIWindow *win;
        win = (gui::IGUIWindow*)
                ( il.gui->getRootGUIElement()->getElementFromId(9) );
 
        if ( win->getCloseButton()->getID() == id ) // doesn't work yet
        {
            cout << "Window closed\n";
            il.remove_gui(1);
        }
    }
}
 
WLGfx
Posts: 9
Joined: Fri Nov 23, 2012 12:56 am

Re: IGUIElement.remove() causes segmentation fault

Post by WLGfx »

Figured it out after more testing.

I replaced:

Code: Select all

    gui::IGUIElement *parent = gui->addGUIElement(file,0);
 
    if ( !gui->loadGUI(file) )
    {
        // failed to load the gui file then remove the dummy parent
 
        parent->remove();
    }
    else
    {
        // add it to the handlers list
 
        events->gui_handlers.push_back( irr_gui_loader(func, parent, file,ID) );
    }
with:

Code: Select all

    gui::IGUIElement *parent = gui->addStaticText(L"",screen_rect);
 
    if ( !gui->loadGUI(file, parent) )
    {
        // failed to load the gui file then remove the dummy parent
 
        parent->remove();
    }
    else
    {
        // add it to the handlers list
 
        events->gui_handlers.push_back( irr_gui_loader(func, parent, file,ID) );
    }
which works great now.

But, does anybody know how to retrieve the event from a GUI windows close button?
CuteAlien
Admin
Posts: 9974
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: IGUIElement.remove() causes segmentation fault

Post by CuteAlien »

Sorry, too much code for me to read now. But EGET_ELEMENT_CLOSED is send when an gui-element is about to close.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
WLGfx
Posts: 9
Joined: Fri Nov 23, 2012 12:56 am

Re: IGUIElement.remove() causes segmentation fault

Post by WLGfx »

Yeah, it was late when I figured it out. Browsing through and came across it sometime yesterday.

The final piece to the code:

Code: Select all

    // catch the windows close event to safely remove the handler too
 
    if ( event.GUIEvent.EventType == gui::EGET_ELEMENT_CLOSED )
    {
        if ( id == 9 )
        {
            il.remove_gui(1);
        }
    }
which catches the event and check against the id and safely removes it from the handlers array.

There's only the one thing now to do and that's to handle the stopping of passing on events to other handlers by returning true or false back to the main event receiver which I forgot at first. Oh, and to tidy up the code...

Cheers... All working... :D
Post Reply