NeL provides a sophisticated yet easy to use event system to provide your application with a variety of events. The event system is comprised of four primary components:
To manage events, a class has to get or create a server. The server stores emitters, listeners, and events. When the server is required to pump events, it checks every emitters to get last events. All the listeners are stored in the server as a pair (type of event, listener). Thus a particular event can be handled by several listeners. It's the class which adds emitters and listeners it needs to the server.
Events are all based upon the CEvent class and have a unique ID using the CClassId interface. The header nel/misc/events.h has a full listing of available events and the event hierarchy and their corresponding unique IDs.
CEvent inherits CClassId. A predefined event is built by inheriting CEvent and using a CClassId, built whith a unique id.
Existing events can be found in events.h.
Event listeners are classes that implement the IEventListener class. The interface defines the basic callback mechanism necessary for the event server to distribute events appropriately. Their implementation is fairly straight-forward and dependent upon your needs. Here's a brief example:
class CMyEventListener : public IEventListener
{
virtual void operator ()(const CEvent& event)
{
CEventChar ec = (CEventChar &) event; // here we know that we receive a CEventChar event
printf("%c", ec.Char);
}
}
You can see that the event listener interface requires the operator overload for callbacks. In this example we have assumed that the event is a CEventChar event.
In addition to the simple interface the NeL framework also provides an asynchronus event listener class called CEventAsyncListener. This handy utility class allows you to receive events through it and then make checks within your code on-demand. Rather than writing an event listener based on the interface that gets called at the emitting of an event you can check with the asynchronus listener at various points in your code.
int main(void)
{
bool running = true;
// perform some setup
// ...
// Create a new async listener.
NLMISC::CEventListenerAsync asyncEventListener;
// Tell it to register itself with the event server.
asyncEventListener.addToServer(eventServer);
// main game loop.
while(running)
{
// some logic
//...
// Watch for ESC to be pushed and
// signal the end of the game
if(asyncEventListener.isKeyPushed(KeyESCAPE))
running=false;
}
// shutting down the listener.
asyncEventListener.removeFromServer(eventServer);
// shut down the rest of the game.
// ...
}
This listener implementation only has five methods of interest to users since it focuses on keyboard input:
The interface provides a callback. A listener must implements this interface. Existing listeners can be found in event_listener.h. In the following example, the listener is supposed to have been added to an event server along with the event type EventCharId:
CCallback cb; server.addListener (EventCharId, &cb);
When this callback is called with such event it prints the char:
class CCallback : public IEventListener
{
virtual void operator ()(const CEvent& event)
{
CEventChar ec = (CEventChar &) event; // here we know that we receive a CEventChar event
printf("%c", ec.Char);
}
};
Event emitters are the systems that perform the low level work of getting input and information from the OS or hardware and then providing it to the event server. Event emitters are based upon the IEventEmitter interface. Typically developers using NeL will not need to author event emitters as the framework already has implementations of the most commonly used input methods.
It's the interface which gets low-level events and posts them to the server as NEL events. An emitter must implements this interface. Existing emitters can be found in emitters.h.
The Event Server is at the heart of the event system within NeL.
A server is made of:
When a call to the method pump is done, the server pumps its emitters for events. Events are stacked up in the list.
Then, for each event, according to their id, the server applies the right callbacks stored in the multimap.
As for emitters, both server and class know the IListener. The listener callback is the operator() which takes an event in parameter. Thus, the user defines the listener/callback he needs and adds it to the server.
Here is an example of the use of the special listener CEventListenerAsync (defined in event_listener.cpp).
This listener stores key states: if a key is pressed its value is on, otherwise it is off.
// declaring a listener
CEventListenerAsync asyncListener;
// declaring the server
CEventServer server;
// adding an emitter to the server
...
// here, a driver is initialized
server.addEmitter(driver->getEventEmitter()); //in this ex the driver provides the emitter
// adding the listener to the server
asyncListener.addToServer(server);
// events loop
do
{
//pump for events
server.pump();
// user main function
mainproc();
}
while (!asyncListener.isKeyPush(KeyESCAPE));
// removing listener from server
asyncListener.removeFromServer(Server);
The listener method CEventListenerAsync::addToServer adds two types of events to a server: EventKeyUpId and EventKeyDownId.
void CEventListenerAsync::addToServer (CEventServer& server)
{
server.addListener(EventKeyUpId, this);
server.addListener(EventKeyDownId, this);
}
The remove method is similar to add method, we must precess both event IDs for the listener to be removed.
void CEventListenerAsync::removeFromServer (CEventServer& server)
{
server.removeListener (EventKeyUpId, this);
server.removeListener (EventKeyDownId, this)
}
ddd
todo
todo
todo
ddd