Home » Community » Forums » » Simple Event Handling
  Intel sponsors gamedev.net search:   
[Control Panel] [Register] [Bookmarks] [Who's Online] [Active Topics] [Stats] [FAQ] [Search]

Add Forum to Favorites |  Send Topic To a Friend | View Forum FAQ | Track this topic


 Last Thread Next Thread 
 Simple Event Handling
Post Reply 
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 - swiftcoding

 User Rating: 1743   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

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.

 User Rating: 1083   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

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]

 User Rating: 445   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

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.

 User Rating: 1353   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

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!

 User Rating: 1035   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

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!

______________________________________________________________________________________
The Phoenix shall arise from the ashes... ThunderHawk -- ¦þ

"So. Any n00bs need some pointers? I have a std::vector<n00b*> right here..." - Zahlman

MySite | Forum FAQ | File Formats
______________________________________________________________________________________


 User Rating: 1142   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Why not use a pre-built list for the events? Such as or . Easier than making your own.

Also, probably a good idea to have events register themselves when they are created. That way the programming using this system might not forget to register the event (or un-register).

- Jason

 User Rating: 1015    Report this Post to a Moderator | Link

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.

 User Rating: 1015   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

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 events

class 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;
		}
	}
}



 User Rating: 1017   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

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.

 User Rating: 1015   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

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;
//etc

struct 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;
}



 User Rating: 1478   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

<quote>

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;
}
}

<unquote>

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

 User Rating: 1015   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

All times are ET (US)

Post Reply
 Last Thread Next Thread 
Forum Rules:
You may not post new threads
You may post replies
You may not edit your posts
You may not use HTML in your posts
Jump To:
Administrative Options: