Skip to main content
GameDev.net gamedev.net
🔒 Locked

Simple Event Handling

Started by Myopic Rhino Sep 16, 2004 at 6:19 PM 11 replies 2.1k views
swiftcoder
swiftcoder
Hmm, nice article.

But I would like to suggest modifying HandleEvent() to return bool, true if the event was handled, and false if it was not. Then keep the EventReceiver list in order of most recently added, and stop iterating the list and sending events when a HandleEvent() returns true;

This allows EventReceivers to "eat" events, if they do not wish previously created EventReceivers to receive events (useful in many circumstances, especially modal dialogs, tracking mouse drags, etc.).
Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]
CloudNine
CloudNine
Why not, in the event handler constructor, push back 'this' into an EventHandler linked list kept by EventDispatcher? It would remove the need for a RegisterHandler function in EventDispatcher.
caesar4
caesar4
ive done a similar handler, except i dont use a middleman
heres my code, hope u find it usefull
	/*! \class CEvent	 * \brief The Basic EventNotifier class, all other events must be derived from it	 * \date April-2004	 * \version 0.1Beta	 * \code	 * //example use	 * class CPlayer : public ReCore::CEvent{	 * 	public:	 * 		INT iFile;	 * 		CPlayer(){iFile = 0;};	 * 		bool CanRemoveEvent(){return true;};	 * 		INT NotifyEvent(INT iReason,void *data,INT iDataLen);	 * };	 * INT CPlayer::NotifyEvent(INT,void*,INT){	 * 	............	 * 	............	 * 	return 1;	 * }	 * \endcode	 */	class CEvent{		public:			/*! \brief Notifier function			 * \param iReason why is the event signaled			 * \param pData a pointer for any data being passed to the event			 * \param iDataLen the lenght of the data being passed			 * \return a positive int on success			 */			virtual int NotifyEvent(int iReason, void *pData, int iDataLen)=0;			//! \brief This is for event lists/quiees, usually returns true			virtual bool CanRemoveEvent()=0;			//! \brief Function operator, allows an easier execution of the event			INT operator () () {				return this->NotifyEvent(EventTriggered,NULL,0);			};			/*! \brief An enum of the most basic event reasons			 * \note			 * Any custom reasons must be greater than the			 * highest value in this list or smaller than			 * the smallest value			 */			enum EReason{				EventTriggered		= 1,				EventDestroy		= 2,				EventImpossible		= 3,				EventOnError		= 4,				EventOnWarning		= 5,				EventOnStop			= 6,				EventOnStart		= 7,				EventOnFail			= 8,				EventOnPlay			= 9,				EventOnPause		=10,				EventOnResume		=11,				EventOnOpen			=12,				EventOnClose		=13,				EventOnDestroy		=14,				EventOnDone			=15,				EventOnMouseMove	=16,				EventOnLClick		=17,				EventOnRClick		=18,				EventOnMClick		=19,				EventOnMouseWheel	=20,				EventOnKeyDown		=21,				EventOnKeyUp		=22,				EventOnNetConnect	=23,				EventOnNetDisconnect=24,				EventOnNetRecieve	=25,				EventOnNetError		=26,				EventOnNetPacketLost=27,			};	};	const int CEventFirst= CEvent::EReason::EventTriggered;	const int CEventLast = CEvent::EReason::EventOnNetPacketLost;


[Edited by - caesar4 on September 19, 2004 2:35:40 PM]
Cartman's definition of sexual harrasement:"When you are trying to have intercourse with a lady friend, and some other guy comes up and tickles your balls from behind"(watch South Park, it rocks)
grekster
grekster
Quote:
There are certain performance issues to consider when using this system. Spending too much time in the EventHandler methods basically slows down the overall event dispatching.


I was just wondering how bad these performance issues would be? Would this system be good enough for a 2d side scroller or maybe isometric view game?

Anyway very good article was very interesting to read.
Quote: Original post by BosskIn Soviet Russia, you STFU WITH THOSE LAME JOKES!
amag
amag
I agree with CloudNine, put the linked list stuff outside of IEventHandler. As the name say, IEventHandler should be an interface thus it should not contain any implementation. Besides it's much nicer to use std::list. However if you want to roll your own do it in an inside class of EventDispatcher.
class EventDispatcher{...stuff  struct node  {    IEventHandler *handler;    node *next;  } *head;...more stuff};

Otherwise it's a nice job and article. Good luck!
Thunder_Hawk
Thunder_Hawk
Umm...What happens if an EventHandler should die before the end of the program and an event gets sent? From what I can see, this could be rather dangerous. As the others have said, it makes little sense from the user's point of view to have EventHandler behave as a node in a linked list. Furthermore (to correct the problem I mentioned), you need to implement some kind of Unregister function in EventDispatcher.

Otherwise, nice job. Thanks for contributing! [cool]
______________________________________________________________________________________The Phoenix shall arise from the ashes... ThunderHawk -- ¦þ"So. Any n00bs need some pointers? I have a std::vector<n00b*> right here..." - ZahlmanMySite | Forum FAQ | File Forma
mrluisp
mrluisp
Thanks for the feedback guys. Sorry for the late resposne. The email I submitted the article with (a few months ago) was my uni email and is no longer active, so I didn't even realize the article was up there until yesterday.

It makes a lot more sense to move the list into the eventDispatcher. And it definitely needs an unregister method. I didn't even think about that.

I don't think the performance would be that bad as long as the number of messages and listeners isn't too high.

Again, thanks for all the feedback. Hopefully, I'll incorporate some of your suggestions into the article sometime in the future.
lxxxk
lxxxk
Hi man,I read ur article,it's very useful!
I glance over ur article and catch its theory,but I find ur code made my brain messed :-),so I write my class based on ur theory.It's build all in one class,can delete node automatically after object destroyed and perform effectively :-)
Please let me know if you have any commnet of my code.

XEventHandler.h
#pragma once//Purpose://if any of ur class wanna respond to ur global custom event,make it derived from this class//and overload the virtual function ProcessEvent() to process ur event//call XEventHandler::Dispatch() whenever u wanna dispatch an event//Extention://u can add event type in enum EVENT_TYPE of this class and define it's relative arg,context in ur docs//Remarks://I name the class which derived from this class "device",it means device can respond to eventsclass XEventHandler{	static XEventHandler* m_pDeviceList;	XEventHandler* m_pNext;public:	struct EVENT	{		DWORD dwEventType;		DWORD dwArg;		LPVOID pContext;		EVENT();		EVENT(DWORD eventType, DWORD arg, LPVOID context)		{			dwEventType=eventType;			dwArg=arg;			pContext=context;		}	};	enum EVENT_TYPE	{	};public:	XEventHandler(void);	~XEventHandler(void);	static void Dispatch(DWORD eventType, DWORD arg=0, LPVOID context=NULL);protected:	static void AddDevice(XEventHandler* pDevice);	static void RemoveDevice(XEventHandler* pDevice);	virtual void ProcessEvent(EVENT event)=0;};

XEventHandler.cpp
#include "StdAfx.h"#include ".\xeventhandler.h"XEventHandler* XEventHandler::m_pDeviceList;XEventHandler::XEventHandler(void){	m_pNext=NULL;	AddDevice(this);}XEventHandler::~XEventHandler(void){	RemoveDevice(this);}void XEventHandler::Dispatch(DWORD eventType, DWORD arg, LPVOID context){	EVENT event(eventType,arg,context);	for(XEventHandler* p=m_pDeviceList; p; p=p->m_pNext)		p->ProcessEvent(event);}void XEventHandler::AddDevice(XEventHandler* pDevice){	if(m_pDeviceList)	{		XEventHandler* pre=m_pDeviceList;		for(XEventHandler* p=m_pDeviceList; p; p=p->m_pNext)			pre=p;		pre->m_pNext=pDevice;	}	else	{		m_pDeviceList=pDevice;	}}void XEventHandler::RemoveDevice(XEventHandler* pDevice){	XEventHandler* pre=NULL;	for(XEventHandler* p=m_pDeviceList; p;)	{		if(p == pDevice)		{			if(p == m_pDeviceList)			{				m_pDeviceList=m_pDeviceList->m_pNext;				p=m_pDeviceList;				pre=p;			}			else			{				pre->m_pNext=p->m_pNext;				p=p->m_pNext;			}		}		else		{			pre=p;			p=p->m_pNext;		}	}}
luskan
luskan
Hi, great article. In my event handling system I have Post and Send functions for processing an event. Post() immediately sends event to all registrated objects and Send() simply puts it to event list just like it is done with windows messages. This way you can add time when some event should be fired in the future. Anyway this is just my few cents.
Qw3r7yU10p!
Qw3r7yU10p!
I know it's not exactly the same:

To add a new event, derive it from Event and add it to EventHandler and EventHandlerImpl.

To handle events derived from EventHandlerImpl and override OnHandle for the types of events that you can handle.

I would have liked to de-couple the events from the handlers. Can anyone think of how?
//event.cpp#include <string>#include <iostream>struct Event;struct EventMouseLeftButtonPress;struct EventMouseLeftButtonRelease;struct EventNewGameEasy;//etcstruct EventHandlerImpl {	virtual void OnHandle(Event&) {		std::cout << "EventHandlerImpl::Event: do nothing" << std::endl;	}	virtual void OnHandle(EventMouseLeftButtonPress&) {		std::cout << "EventHandlerImpl::EventMouseLeftButtonPress: do nothing" << std::endl;	}	virtual void OnHandle(EventMouseLeftButtonRelease&) {		std::cout << "EventHandlerImpl::EventMouseLeftButtonRelease: do nothing" << std::endl;	}	virtual void OnHandle(EventNewGameEasy&) {		std::cout << "EventHandlerImpl::EventNewGameEasy: do nothing" << std::endl;	}	//etc.};struct EventHandler {	EventHandler(EventHandlerImpl* pImpl) : m_pImpl(pImpl) {	}	void Handle(Event& event) {		m_pImpl->OnHandle(event);	}	void Handle(EventMouseLeftButtonPress& event) {		m_pImpl->OnHandle(event);	}	void Handle(EventMouseLeftButtonRelease& event) {		m_pImpl->OnHandle(event);	}	void Handle(EventNewGameEasy& event) {		m_pImpl->OnHandle(event);	}	//etc.private:	EventHandlerImpl* m_pImpl;	EventHandler(const EventHandler&);	EventHandler& operator=(const EventHandler&);};struct Event {	virtual void Accept(EventHandler& handler) {		handler.Handle(*this);	}};struct EventMouseLeftButtonPress : Event {	EventMouseLeftButtonPress(int x, int y) : m_x(x), m_y(y) {	}	void Accept(EventHandler& handler) {		handler.Handle(*this);	}	int m_x, m_y;};struct EventMouseLeftButtonRelease : Event {	EventMouseLeftButtonRelease(int x, int y) : m_x(x), m_y(y) {	}	void Accept(EventHandler& handler) {		handler.Handle(*this);	}	int m_x, m_y;};struct EventNewGameEasy : Event {	EventNewGameEasy(const std::string& name) : m_name(name) {	}	void Accept(EventHandler& handler) {		handler.Handle(*this);	}	std::string m_name;};struct EventHandlerA : EventHandlerImpl {	virtual void OnHandle(EventMouseLeftButtonPress& event) {		std::cout << "mouse left button down at " << event.m_x << " " << event.m_y << std::endl;	}	virtual void OnHandle(EventMouseLeftButtonRelease& event) {		std::cout << "mouse left button up at " << event.m_x << " " << event.m_y << std::endl;	}};struct EventHandlerB : EventHandlerImpl {	virtual void OnHandle(EventNewGameEasy& event) {		std::cout << "new game easy setting, player name: " << event.m_name << std::endl;	}};int main() {	EventHandler a(new EventHandlerA);	EventHandler b(new EventHandlerB);	EventMouseLeftButtonPress e1(30, 17);	EventNewGameEasy e2("quorn");	EventMouseLeftButtonRelease e3(50, 29);	a.Handle(e1);	b.Handle(e1);	a.Handle(e2);	b.Handle(e2);	a.Handle(e3);	b.Handle(e3);	return 0;}
Nick the Engineer
Nick the Engineer


Every class that wants to listen to events needs to inherent from IEventHandler, and needs to implement the EventHandler virtual function. A typical implementation of this method will have a switch statement (switching on the Event.Type variable), and will have a case statement for any events the class wants to handle. Here's a short example:

void Foo::EventHandler(const Event &e) {
switch (e.Type) {
case E_NEWGAMEEASY:
// handle creating a new easy game.
break;
case E_MOUSELEFTBUTTONPRESS:
// handle mouse button being pressed
break;
default:
break;
}
}



Note that function pointers will do a better job here than a switch-case block.

See the following articles for a quick rundown on function pointers:

http://www.newty.de/fpt/intro.html#what
http://www.gamedev.net/reference/articles/article2116.asp

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.