Event Channel Design Documentation
Event channel distributes different kinds of events to other engine modules. Scripts can also subscribe most of the events defined by the event channel. The diagram below illustrates the most important classes and their relationships.
Event manager is the hub of the event channel. It takes care of managing the client subscriptions and related event dispatching. EventManager polls SDL for events and converts them to appropriate event classes. Based on subscription types, events are delivered to event channel clients. E.g. in case client B is interested receiving key events, it:
- inherits itself from KeyListener
- Register itself to KeyController
- receives KeyEvents via KeyListener interface
Event channel uses interfaces to hide implementation from clients; e.g. using plain KeyController interface it is guaranteed that mouse or widget related details are not visible to then client (thus improving encapsulation). In case client would use EventManager interface directly it would also gain visibility to event types that are not needed in that module. The same principle works also to other direction; event manager does not know anything about its client, since all communication happens through the ...Listener and EventSource interfaces.
Most important interface types are:
- Listeners (e.g. KeyListener, MouseListener)
- Manager pushes event to clients using these interfaces
- Controllers (e.g. KeyController)
- Clients can subscribe themselves to corresponding events using these interfaces
- Dispatchers (e.g. CommandDispatcher)
- Clients can send events to other clients using this interface
- In the picture client A is able to send commands to client B, if:
- client B inherits CommandListener
- client B subscribes to listen commands via CommandController
- Client A inherits from EventSource
- Client A sends a command via CommandDispatcher (and sets itself as the eventsource of sent command)
Dispatching raw SDL_Events
In some cases other modules may require raw SDL events. This is the case e.g. with gui (which passes events to guichan). In those cases, clients should subscribe themselves as SDLEventListeners. After subscription, message dispatching happens in the following way:
EventManager: get event from SDL EventManager: convert event to fife event EventManager: dispatch fife event to appropriate listeners EventManager: dispatch raw event to SDLEventListeners
Note that other modules might react to received SDL_events in such way, that they send new events to other modules via event channel
After receiving SDL event and converting it to corresponding engine event, the following happens:
EventManager: for each KeyEventListener: EventManager: dispatch event to listener KeyEventListener: examine the received event (e.g. capslock, shift, key value) KeyEventListener: in case criteria is met, react to keyevent (e.g. move character)
Events can be consumed by the listeners, so that dispatching of consumed event is stopped. The problem with consuming is the concept of focus; in some cases it makes sense to consume the event (e.g. when typing text to text area, arrow keys should move only the caret, not the player agent), while in others not (e.g. ESC key should quit the game in every case). By default events can be consumed by clients. This behavior can be adjusted for each key from event manager.
Widget events are converted from gui events that engine happens to be using (atm that means guichan). The key difference here is that gui does the actual dispatching in the following way:
EventManager: for SDL_Event that is a key event EventManager: for each KeyListener: EventManager: dispatch KeyEvent: Gui: if there is active focus handler (*) Gui: consume the key event EventManager: for each SDLEventListener: EventManager: dispatch event to listener Gui: receive SDLEvent Gui: push event to guichan Guichan: create appropriate Guichan event Guichan: send event to listeners Gui: receive event from guichan, convert to WidgetEvent Gui: dispatch widget event to KeyEventController EventManager: receive widget event, for each WidgetEventListener WidgetEventListener: receive widget event, react to that
Mouse events are converted to widget events in similar manner, the key difference however is in the Gui when MouseEvent is received ((*)-marking). In that case Gui consumes mouse event in case there is visible widget underneath the click point.
Clients can dispatch commands to other clients via CommandDispatcher interface:
client x: create Command client x: dispatch command via CommandDispatcher EventManager: dispatch command to CommandListeners client y: receive command