(C++) CTimeKeyEventReceiver

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
Halifax
Posts: 1424
Joined: Sun Apr 29, 2007 10:40 pm
Location: $9D95

(C++) CTimeKeyEventReceiver

Post by Halifax »

Alright, for lack of a better name, here is CTimeKeyEventReceiver. But anyways, what it does is basically just time how long a key or mouse button has been held. You can also query whether a key has just been pressed, which means that it won't repeat, unlike just querying for the key, which will repeat.

Anyways I have included a demo to give you an idea as well, everything compiles out of the box. The main reason I made this is for a camera I am making in which I needed timed keys, other than that I am just releasing it in case someone else would like something of the sort as well.

Now for the code. It's all Irrlicht "coding standards" compliant so it should be easy to learn, and it's nothing much but a really small layer. Note that gui message handling, etc., isn't put into here because I didn't need it. If someone else wants to do that, then fell free.

Oh yes, and also pay attention to the method updateAll(). It must be called after key handling for pressed keys to register correctly. If you don't do it, then pressed keys won't register at all.

Also a note on why I made the method updateAll(), it's because of how Irrlicht handles key handling. There is a delay between the first moment the key was pressed, and when Irrlicht repeats the press. So the delay created some discrepancies in the timing, so that's another reason. It was also to fix the getKeyPressed() and getMouseButtonPressed() because people usually check keys in a frame-dependent process which means that the delay, from Irrlicht, would cause pressed keys to be repeated.

Overall it is hard to explain, but believe I tried it already, so just make sure you use updateAll() like in the demo. Anyways, here is the code.

EKeyState.h

Code: Select all

#ifndef __E_KEY_STATE_H_INCLUDED__
#define __E_KEY_STATE_H_INCLUDED__

enum E_KEY_STATE
{
	EKS_PRESSED,
	EKS_DOWN,
	EKS_UP,
	EKS_RELEASED
};

#endif // __E_KEY_STATE_H_INCLUDED__
EMouseButton.h

Code: Select all

#ifndef __E_MOUSE_BUTTON_H_INCLUDED__
#define __E_MOUSE_BUTTON_H_INCLUDED__

enum E_MOUSE_BUTTON
{
	EMB_LEFT,
	EMB_MIDDLE,
	EMB_RIGHT
};

#endif // __E_MOUSE_BUTTON_H_INCLUDED__
CTimeKeyEventReceiver.h

Code: Select all

#ifndef __C_TIME_KEY_EVENT_RECEIVER_H_INCLUDED__
#define __C_TIME_KEY_EVENT_RECEIVER_H_INCLUDED__

#include <Irrlicht.h>
#include "EKeyState.h"
#include "EMouseButton.h"

struct SKeyInformation
{
	E_KEY_STATE key;
	irr::u32 time;
};

class CTimeKeyEventReceiver : public irr::IEventReceiver
{
	public:
	
		//! constructor
		CTimeKeyEventReceiver(irr::ITimer* timer);
		
		//! destructor
		virtual ~CTimeKeyEventReceiver();
		
		//! OnEvent callback
		virtual bool OnEvent(const irr::SEvent& event);
		
		//! Tells whether the key is pressed or not
		bool getKey(irr::EKEY_CODE key) const;
		
		//! Tells whether the key is pressed once or not
		bool getKeyPressed(irr::EKEY_CODE key) const;
		
		//! Tells whether the mouse button is pressed or not
		bool getMouseButton(E_MOUSE_BUTTON button) const;
		
		//! Tells whether the mouse button is pressed once or not
		bool getMouseButtonPressed(E_MOUSE_BUTTON button) const;
		
		//! Tells how long the key has been pressed
		irr::u32 getKeyTime(irr::EKEY_CODE key) const;
		
		//! Tells how long the mouse button has been pressed
		irr::u32 getMouseButtonTime(E_MOUSE_BUTTON button) const;
		
		//! Updates all the key states
		void updateAll();
	
	private:
	
		inline void pressKeyboardButton(irr::u32 index);
		
		inline void releaseKeyboardButton(irr::u32 index);
		
		inline void pressMouseButton(irr::u32 index);
		
		inline void releaseMouseButton(irr::u32 index);
	
	protected:
	
		irr::ITimer* Timer;
		irr::u32 LastTime;
		
		SKeyInformation Keyboard[irr::KEY_KEY_CODES_COUNT];
		SKeyInformation Mouse[2];
};

#endif // __C_TIME_KEY_EVENT_RECEIVER_H_INCLUDED_
CTimeKeyEventReceiver.cpp

Code: Select all

#include "CTimeKeyEventReceiver.h"

using namespace irr;
using namespace irr::core;
using namespace irr::io;
using namespace irr::video;
using namespace irr::scene;
using namespace irr::gui;

CTimeKeyEventReceiver::CTimeKeyEventReceiver(ITimer* timer)
: Timer(timer)
{
	for (u32 i = 0; i < u32(KEY_KEY_CODES_COUNT); i ++)
	{
		Keyboard[i].key = EKS_UP;
		Keyboard[i].time = 0;
	}
	
	Mouse[EMB_LEFT].key = Mouse[EMB_MIDDLE].key = Mouse[EMB_RIGHT].key = EKS_UP;
	Mouse[EMB_LEFT].time = Mouse[EMB_MIDDLE].time = Mouse[EMB_RIGHT].time = 0;
	
	LastTime = Timer->getTime();
}

CTimeKeyEventReceiver::~CTimeKeyEventReceiver()
{
}

bool CTimeKeyEventReceiver::OnEvent(const SEvent& event)
{
	switch (event.EventType)
	{
		case EET_KEY_INPUT_EVENT:
		{
			EKEY_CODE key = event.KeyInput.Key;
						
			if (event.KeyInput.PressedDown)
			{
				pressKeyboardButton(key);
			}	
			else
			{
				releaseKeyboardButton(key);					
				Keyboard[key].time = 0;
			}
			
			return true;
			
		} break;
		
		case EET_MOUSE_INPUT_EVENT:
		{
		
			switch (event.MouseInput.Event)
			{
				case EMIE_LMOUSE_LEFT_UP:
					releaseMouseButton(EMB_LEFT);
					break;
				case EMIE_MMOUSE_LEFT_UP:
					releaseMouseButton(EMB_MIDDLE);
					break;
				case EMIE_RMOUSE_LEFT_UP:
					releaseMouseButton(EMB_RIGHT);
					break;
				
				case EMIE_LMOUSE_PRESSED_DOWN:
					pressMouseButton(EMB_LEFT);
					break;
				case EMIE_MMOUSE_PRESSED_DOWN:
					pressMouseButton(EMB_MIDDLE);
					break;
				case EMIE_RMOUSE_PRESSED_DOWN:
					pressMouseButton(EMB_RIGHT);
					break;
			}
			
			return true;
			
		} break;
	}
	
	return false;
}

void CTimeKeyEventReceiver::updateAll()
{
	u32 DiffTime = Timer->getTime() - LastTime;
	LastTime = Timer->getTime();
	
	for (u32 i = 0; i < u32(KEY_KEY_CODES_COUNT); ++ i)
	{
		if (Keyboard[i].key == EKS_PRESSED)
			Keyboard[i].key = EKS_DOWN;
		
		if (Keyboard[i].key == EKS_RELEASED)
			Keyboard[i].key = EKS_UP;
			
		if (Keyboard[i].key == EKS_DOWN)
			Keyboard[i].time += DiffTime;
	}
	
	for (u32 i = 0; i < 3; ++ i)
	{
		if (Mouse[i].key == EKS_PRESSED)
			Mouse[i].key = EKS_DOWN;
			
		if (Mouse[i].key == EKS_RELEASED)
			Mouse[i].key = EKS_UP;
			
		if (Mouse[i].key == EKS_DOWN)
			Mouse[i].time += DiffTime;
	}
}

bool CTimeKeyEventReceiver::getKey(EKEY_CODE key) const
{
	if (Keyboard[key].key == EKS_DOWN)
		return true;
	return false;
}

bool CTimeKeyEventReceiver::getKeyPressed(EKEY_CODE key) const
{
	if (Keyboard[key].key == EKS_PRESSED)
		return true;
	return false;
}

u32 CTimeKeyEventReceiver::getKeyTime(EKEY_CODE key) const
{
	return Keyboard[key].time;
}

bool CTimeKeyEventReceiver::getMouseButton(E_MOUSE_BUTTON button) const
{
	if (Mouse[button].key == EKS_DOWN)
		return true;
	return false;
}

bool CTimeKeyEventReceiver::getMouseButtonPressed(E_MOUSE_BUTTON button) const
{
	if (Mouse[button].key == EKS_PRESSED)
		return true;
	return false;
}

u32 CTimeKeyEventReceiver::getMouseButtonTime(E_MOUSE_BUTTON button)
{
	return Mouse[button].time;
}

void CTimeKeyEventReceiver::pressKeyboardButton(u32 index)
{
	if (Keyboard[index].key != EKS_DOWN)
		Keyboard[index].key = EKS_PRESSED;
}

void CTimeKeyEventReceiver::releaseKeyboardButton(u32 index)
{
	if (Keyboard[index].key != EKS_UP)
		Keyboard[index].key = EKS_RELEASED;
}

void CTimeKeyEventReceiver::pressMouseButton(u32 index)
{
	if (Mouse[index].key != EKS_DOWN)
		Mouse[index].key = EKS_PRESSED;
}

void CTimeKeyEventReceiver::releaseMouseButton(u32 index)
{
	if (Mouse[index].key != EKS_UP)
		Mouse[index].key = EKS_RELEASED;
}
Demo

Code: Select all

#include <Irrlicht.h>
#include <iostream>
#include "CTimeKeyEventReceiver.h"

using namespace irr;
using namespace irr::core;
using namespace irr::io;
using namespace irr::video;
using namespace irr::scene;
using namespace irr::gui;

int main()
{
	IrrlichtDevice* irrDevice;
	E_DRIVER_TYPE driverType;
	s32 choice;
	
	std::cout << "Select your driver:\n" << "  (1) - OpenGL     \n" << 
	"  (2) - Direct3D9  \n" << std::endl;
	std::cin >> choice;
	
	if (choice == 2)
		driverType = EDT_DIRECT3D9;
	else
		driverType = EDT_OPENGL;
		
	irrDevice = createDevice(driverType, dimension2di(640, 480));
	
	IVideoDriver* irrDriver = irrDevice->getVideoDriver();
	ISceneManager* irrSceneMgr = irrDevice->getSceneManager();
	IGUIEnvironment* irrGUIEnv = irrDevice->getGUIEnvironment();
	
	CTimeKeyEventReceiver* irrEventRecv = new CTimeKeyEventReceiver(irrDevice->getTimer());
	irrDevice->setEventReceiver(irrEventRecv);
	
	irrDevice->setWindowCaption(L"CTimeKeyEventReceiver demo");
	
	s32 lastTime = -1, lastTimeM = -1;
	
	while (irrDevice->run())
	{
		if (irrDevice->isWindowActive())
		{
			
			irrDriver->beginScene(true, true, SColor(255,128,128,128));
				irrSceneMgr->drawAll();
				irrGUIEnv->drawAll();
			irrDriver->endScene();
			
			if (irrEventRecv->getKeyPressed(KEY_KEY_S))
			{
				std::cout << "S was pressed." << std::endl;
			}
			
			if (irrEventRecv->getKey(KEY_KEY_A))
			{
				s32 time = irrEventRecv->getKeyTime(KEY_KEY_A);
				if ((lastTime / 1000) != (time / 1000))
				{
					std::cout << "A key: " << time / 1000 << std::endl;
					lastTime = time;
				}
			}
			
			if (irrEventRecv->getMouseButtonPressed(EMB_RIGHT))
			{
				std::cout << "Right mouse button was pressed." << std::endl;
			}
			
			if (irrEventRecv->getMouseButton(EMB_LEFT))
			{
				s32 time = irrEventRecv->getMouseButtonTime(EMB_LEFT);
				if ((lastTimeM / 1000) != (time / 1000))
				{
					std::cout << "Left Mouse: " << time / 1000 << std::endl;
					lastTimeM = time;
				}
			}
			
			irrEventRecv->updateAll();
		}
	}
	
	irrDevice->drop();
	delete irrEventRecv;
	return 0;
}
Have fun.
TheQuestion = 2B || !2B
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

Just a quick comment, but it seems that your code would be more useful to people if it wasn't an event receiver, but if it was some other class that they could inherit from or composite their event receiver with...

Code: Select all

class CKeyTimer
{
  // keyboard stuff from your class, but no virtuals
};

class CMouseTimer
{
  // mouse stuff from your class, but no virtuals
};

class CEventReceiver : public IEventReceiver
{
  // the users event receiver
public:
  virtual bool OnEvent (const SEvent& event)
  {
    bool handled = false;

    // any app specific event handling

    switch (event.EventType) {
    case EET_KEY_INPUT_EVENT:
      _keyTimer.OnEvent(event); break;
    case EET_MOUSE_INPUT_EVENT:
      _keyTimer.OnEvent(event); break;
    // other cases
    }

    return handled;
  }

private:
  CKeyTimer _keyTimer;
  CMouseTimer _mouseTimer;
};
This makes your code a little more usable as most people have to write a bunch of code in their own event receiver to handle other events [like button clicks].

Travis
Halifax
Posts: 1424
Joined: Sun Apr 29, 2007 10:40 pm
Location: $9D95

Post by Halifax »

Ah, okay, thanks vitek. I will get that done for tommorow then.
TheQuestion = 2B || !2B
Trefall
Posts: 45
Joined: Tue Dec 05, 2006 8:49 pm

Post by Trefall »

Cool Halifax, this looks very usefull for timer based input mechanics. Thanks for the snippet!
MasterGod
Posts: 2061
Joined: Fri May 25, 2007 8:06 pm
Location: Israel
Contact:

Post by MasterGod »

Nice both of you.

P.S
vitek: little typo:

Code: Select all

case EET_MOUSE_INPUT_EVENT:
      _keyTimer.OnEvent(event); break;
Should be:

Code: Select all

case EET_MOUSE_INPUT_EVENT:
      _mouseTimer.OnEvent(event); break;
:wink:
Image
Dev State: Abandoned (For now..)
Requirements Analysis Doc: ~87%
UML: ~0.5%
Halifax
Posts: 1424
Joined: Sun Apr 29, 2007 10:40 pm
Location: $9D95

Post by Halifax »

Trefall wrote:Cool Halifax, this looks very usefull for timer based input mechanics. Thanks for the snippet!
Thanks. Yeah I basically need this until I get a 360 controller and start using XInput, in which there are analog sticks which can contain variable adjustment. So time basically simulates this instead, and is good enough to test my camera which I hope to finish for next week, or sometime around there.
TheQuestion = 2B || !2B
Post Reply