Home - Forums - Documentation - Gallery - Bugs

Step Three: Event handling in CS

When you have some experience in programming, then maybe you see, there is a great weakness in the code. We use a special “use_console” flag to check, our console is alive or not. The code is not 'clean', to many “if (use_console)” branch complicates the event handling. We fix this in this tutorial.

A short introduction to event handling. Crystal Space is a fully event driven engine. So CS broadcasts internally many system event to render the new frame, or when you resize the canvas, or start/close the system etc. The event handling system watching the user’s keyboard, mouse or joystick, and broadcasts this events to the listeners. Our application’s main class (cimGame) derived from csBaseEventHandler. This built-in helper class listens the frame, keyboard, joy and mouse events, it does nothing with this events, just calls the OnKeyboard(), ProcessFrame() etc. member function. We overrode the most important member functions in cimGame. This mechanism hides the depths of the event handling, and gives us an easy to use Delphi-style method to dealing with events. This technique is enough for simple applications, but not enough for such great games, like Cally In The Maze :-)

In the reality CS generates or catches the events in the background, puts them in a queue, and the plugins and your applications listening this events. An event handler says to CS, I’m interesting for this and this and this events. When you have such events, inform me. And CS obeys.

In Cim we have two class now, both deal with events. Our main class currently delegates for the console the keyboard events, so we need ‘use-consol flag’ –style half solution to decide, we have to send to the console the keyboard and frame events or not. We write in this tutorial a separated event handler for the console, and simplify our cimGame class.

First we extend our console class declaration wit this code in console.h:

    struct EventHandler : public scfImplementation1<EventHandler,     iEventHandler>
    {
        cimConsole* parent;

        EventHandler (cimConsole* parent) : scfImplementationType (this),
            parent (parent) {}
        virtual ~EventHandler() {}
        virtual bool HandleEvent (iEvent& e) { return parent->HandleEvent(e); }

        CS_EVENTHANDLER_NAMES("cim.console")
        CS_EVENTHANDLER_NIL_CONSTRAINTS
    };
    csRef<EventHandler> eventHandler;

The iEventhandler abstract has a HandleEvent(iEvent&) member function. Here can we handle our events. We just call the parent’s (console class) HandleEvent() member function.

We need to listen the keyboard events to toggle the console or write into it, and the post process event to draw the console, when it is visible. So the console class will have a new member:

csEventID FinalProcess;

This code comes in the Initialize() function:

    eventHandler.AttachNew (new EventHandler (this));
    csRef<iEventQueue> queue = CS_QUERY_REGISTRY(object_reg, iEventQueue);
    if (queue.IsValid())
    {
        csEventID events[]=
        {
            csevKeyboardEvent(object_reg),
            csevPostProcess(object_reg),
            CS_EVENTLIST_END
        };
	  
        queue->RegisterListener(eventHandler,events);  
    }
   
    FinalProcess = csevPostProcess (object_reg);

We create an instance from our event handler class, then we query the event queue. Then we create an event list from the important events:

    csEventID events[]=
    {
        csevKeyboardEvent(object_reg),
        csevPostProcess(object_reg),
        CS_EVENTLIST_END
    };

The list MUST be closed with CS_EVENTLIST_END macro. Next we register the listener:

queue->RegisterListener(eventHandler,events);  

Now we store in our FinalProcess member the ID of the csevPostProcess:

  FinalProcess = csevPostProcess (object_reg);

The event handler function will be look like this:

bool cimConsole::HandleEvent(iEvent &ev)
{
    if (CS_IS_KEYBOARD_EVENT(object_reg, ev))
    {
        csKeyEventType eventtype = csKeyEventHelper::GetEventType(&ev);
        if (eventtype == csKeyEventTypeDown)
        {
            // The user pressed a key (as opposed to releasing it).
            utf32_char code = csKeyEventHelper::GetCookedCode(&ev);
            if (code==CSKEY_TAB)
            {
                Toggle();
                return true;
            }
        }
    }
	
    if (ev.Name == FinalProcess)
    {
        csRef<iGraphics3D> g3d=CS_QUERY_REGISTRY(object_reg,iGraphics3D);
        csRef<iGraphics2D> g2d=CS_QUERY_REGISTRY(object_reg,iGraphics2D);

        if (conout->GetVisible ())
        {
            g3d->BeginDraw (CSDRAW_2DGRAPHICS);
            conout->Draw2D (0);
            g3d->BeginDraw (CSDRAW_3DGRAPHICS);
            conout->Draw3D (0);			
        }
        return false;	
    }

Here you can see, all console based event handling code comes here. That means, we don’t need this code into the cimGame class. You can always handle seperately the different events with some method, like this.

Ok, we don't create any console class, when the EnableConsole config option is false. We make a little change in the Setup() function:

if (use_console)
	{
		console.AttachNew(new cimConsole);
		if ( !console->Initialize(GetObjectRegistry() ))
		return ReportError("Failed to initialize console!");
		console->Show();
	}

That's it. Happy event handling!

Get the sources from here

Tutorial Home

Next: Step4 - Using CEGUI

Retrieved from "/main/Cimstep3"
| Article | Discussion | View source | History |