events_input - ryzom/ryzomcore GitHub Wiki


title: Events and Managing Inputs description: published: true date: 2023-03-01T05:17:03.139Z tags: editor: markdown dateCreated: 2022-03-07T10:53:39.022Z

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:

  • Events
  • Event Listeners
  • Event Emitters
  • The Event Server

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

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

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

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:

  • addToServer - this method takes an event server as an argument and adds itself to this server.
  • removeFromServer - this method takes an event server as an argument and removes itself from this server.
  • isKeyDown - this method checks to see if the specified key (see the TKey enum) is currently down.
  • isKeyPushed - this method checks to see if the specified key has been pushed, the default (which can be overriden) is to only check for keys that have been pushed and released.
  • reset - this method resets the "down" states stored within this listener.

IEventListener

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

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.

IEventEmitter

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

The Event Server is at the heart of the event system within NeL.

CEventServer

A server is made of:

  • a multimap of (CClassId, IEventListener*)
  • a list of IEventEmitter*
  • a list of CEvent*

When a call to the method pump is done, the server pumps its emitters for events. Events are stacked up in the list.

event_pump.svg

Then, for each event, according to their id, the server applies the right callbacks stored in the multimap.

event_listener.svg

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.

event_callback.svg

Example

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. {.is-info}

// 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)
}

Working With Events

ddd

Keyboard Input

todo

Mouse Input

todo

Game Device Input

todo

Customizing the Event System

ddd

Source

⚠️ **GitHub.com Fallback** ⚠️