Handling buffered input properly

Started by
1 comment, last by brekehan 21 years, 7 months ago
I wrote a class for to wrap DirectInput from what I can tell the input is buffered by DirectInput right away when a user hits a key, I then go through a buffer to get each keystroke in the order it was hit. I hope I am right. At any rate the buffer is processed inside the class in a loop, so to truely react to the input in the order it was entered I would have to process it inside this loop I presumed. This creates a problem where the class needs to call a function outside the class to be defined by the user. So what I did is just declare a function ProcessInput in the same module that takes a pointer to the class, presuming that anything needed by the user can be brought into scope with proper linking. And left it to the user to fill in the function. The function is then called from within the class every time the keyboard is read. This also raises the question if it is efficient to go through the entire processinput routine 10 times every time readkeyboard is called which is about 30 times a second....that makes 300 times a second this function is gone through ung!..that is if the buffer is full which I am not sure will happen or not I remembered something about friend functions, but I don''t think it was necesarry as my implementation *seems* to work ok. My question is did I handle the problem the best way? Should I just undo my class and leave it as c-style functions? Are there other alternatives? Please give me all the constructive critism possible I have been working on getting a good starting basis to handle input for my games for quite a while and am really getting frustrated as to figuring out the best way.
  

#ifndef INPUTSYSTEM_H
#define INPUTSYSTEM_H
//-----------------------------------------------------------------------

// InputSystem.h

// 

// InputSystem is a class wrapper for DirectInput 

// Currently only supports the keyboard.

//

// Christopher Pisz

// 09/11/02

//-----------------------------------------------------------------------


#define DIRECTINPUT_VERSION 0x0800      // DirectInput Version 8.0


#include <windows.h>                    // Windows header
#include <dinput.h>                     // Direct Input header


#define BUFFER_SIZE         10          // Size of the keyboard buffer


// Macros to check keystate

#define KeyDown(n)  ((input.keystate[n]) ? true : false)
#define KeyUp(n)    ((input.keystate[n]) ? false : true)

class InputSystem
{
public:
	UCHAR         keystate[256];        // Element = DIK codes, 0 if key up, 1 if key down


	InputSystem();
	~InputSystem();

	HRESULT InitKeyboard(HWND hWnd);    // Initialize Direct Input and the keyboard

	HRESULT AcquireKeyboard();          // Acquires the keyboard

	HRESULT UnacquireKeyboard();        // Unacquires the keyboard

	HRESULT ReadKeyboard();             // Read the keyboard

	void    Cleanup();                  // Clean up memory


private:
	LPDIRECTINPUT         pDI;          // Pointer to a device interface

	LPDIRECTINPUTDEVICE   pKeyboard;    // Pointer to the keyboard device object

};

void ProcessInput(InputSystem &input);  // User defined Input processing

                                        // Fill this function in!!!


#endif INPUTSYSTEM_H

//----------------------------------------------------------------------

// InputSystem.cpp

//    

// Christopher Pisz

// 09/11/02

//----------------------------------------------------------------------


#include "InputSystem.h"

//----------------------------------------------------------------------

// InputSystem is a class wrapper for DirectInput 

// Currently only supports the keyboard.

//

// Typical Usage:

// 1 Initialize DirectInput and the keyboard 

//   

//   HRESULT InputSystem::InitKeyboard(HWND hWnd)

//

// 2 Read the keyboard in a loop or on a timer every cycle to check

//   for user input and add a input processing routine

//

//   Note: Since we are processing input inside the ProcessInput()

//   function in this module be sure to link accordingly to get scope

//   to any objects needed inside the routine. 

//

//   HRESULT InputSystem::ReadKeyboard()

//   void ProcessInput(InputSystem &input)

//

// 3 Clean up memory used by InputSystem 

//

//   void InputSystem::Cleanup()

//

// 4 Note:

//   Be sure to unacquire the keyboard when the application is no

//   longer in use, for example if the app is minimized. re-acquire 

//   the keyboard when the application is in focus. 

//

//   HRESULT InputSystem::AcquireKeyboard()

//   HRESULT InputSystem::Unacquire()

//----------------------------------------------------------------------

InputSystem::InputSystem()
{
	ZeroMemory(keystate,sizeof(keystate));
	pDI       =NULL;
	pKeyboard =NULL;
}

InputSystem::~InputSystem()
{
	CoUninitialize();
}

//----------------------------------------------------------------------

// HRESULT InputSystem::InitKeyboard(HWND hWnd)

//

// This function initializes DirectInput and the keyboard device

// Be sure to also use void InputSystem::Cleanup() when your application

// is done to free memory allocated here.

//

// Params:

//    hWnd - a handle to the window which will use the class

//

// Return Values:

//    S_OK if successfull, otherwise a DirectInput error code 

//----------------------------------------------------------------------

HRESULT InputSystem::InitKeyboard(HWND hWnd)
{
	HRESULT hr;                  // Holds Direct Input error codes


	// Get a Direct Input device interface

	hr=DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, 
		IID_IDirectInput8,(void**)&pDI,NULL);
	if(FAILED(hr))
	{
		MessageBox(NULL,"Cannot create Direct Input device interface",
			"InputSystem::InitKeyboard()",MB_ICONERROR);
		return hr;
	}
  
	// Get a keyboard device interface

	hr=pDI->CreateDevice(GUID_SysKeyboard,&pKeyboard,NULL);
	if(FAILED(hr))
	{
		MessageBox(NULL,"Cannot create Direct Input keyboard device interface",
			"InputSystem::InitKeyboard()",MB_ICONERROR);
		return hr;
	}
    
	// Tell Direct Input the keyboard will be used and how to arrange data

	hr=pKeyboard->SetDataFormat(&c_dfDIKeyboard);
	if(FAILED(hr))
	{
		MessageBox(NULL,"Cannot set data format",
			"InputSystem::InitKeyboard()",MB_ICONERROR);
		return hr;
	}
    
	// Set access to keyboard 

	hr=pKeyboard->SetCooperativeLevel(hWnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
	if(FAILED(hr))
	{
		MessageBox(NULL,"Cannot set cooperative level",
			"InputSystem::InitKeyboard()",MB_ICONERROR);
		return hr; 
	}
	
	// Set the buffer description up

	DIPROPDWORD dipdw;

    dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
    dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
    dipdw.diph.dwObj        = 0;
    dipdw.diph.dwHow        = DIPH_DEVICE;
    dipdw.dwData            = BUFFER_SIZE; // Arbitary buffer size


	// Set up keyboard buffered mode

    hr=pKeyboard->SetProperty(DIPROP_BUFFERSIZE,&dipdw.diph);
	if(FAILED(hr))
	{
		MessageBox(NULL,"Cannot set keyboard to buffered mode",
			"InputSystem::InitKeyboard()",MB_ICONERROR);
		return hr;
	}

	// Acquire the keyboard

	hr=AcquireKeyboard();
	if(FAILED(hr))
	{
		MessageBox(NULL,"Cannot acquire keyboard",
			"InputSystem::InitKeyboard()",MB_ICONERROR);
		return hr; 
	}

	return S_OK;
}

//----------------------------------------------------------------------

// HRESULT InputSystem::AcquireKeyboard()

//

// Acquire the keyboard when your application comes in focus

//

// Params:

//    None

//

// Return Values:

//    S_OK if successfull, otherwise a DirectInput error code 

//----------------------------------------------------------------------

HRESULT InputSystem::AcquireKeyboard()
{
	if(pKeyboard)
		return pKeyboard->Acquire();
	
	return DIERR_GENERIC;
}

//----------------------------------------------------------------------

// HRESULT InputSystem::UnacquireKeyboard()

//

// Unacquire the keyboard when your application goes out of focus

//

// Params:

//    None

//

// Return Values:

//    S_OK if successfull, otherwise a DirectInput error code 

//----------------------------------------------------------------------

HRESULT InputSystem::UnacquireKeyboard()
{
	if(pKeyboard)
		return pKeyboard->Unacquire();
	
	return DIERR_GENERIC;
}

//----------------------------------------------------------------------

// HRESULT InputSystem::ReadKeyboard()

//

// Reads buffered keyboard strokes and puts the values into the

// keystate array class member to be used by the application.

// Input processing for the keyboard should be done inside the

// loop in this function so as to process input the same way it

// was buffered.

//  

// Params:

//    None

//

// Return Values:

//    S_OK if successfull, otherwise a DirectInput error code 

//----------------------------------------------------------------------

HRESULT InputSystem::ReadKeyboard()
{
	DWORD              dwElements;          // Number of buffered events

	DIDEVICEOBJECTDATA didod[BUFFER_SIZE];  // Receives buffered data

	DWORD              i;                   // Loop counter

    HRESULT            hr;                  // Holds Direct Input error codes


	// Set number of events to maximum

	dwElements=BUFFER_SIZE;
	
	// Don''t try to read the keyboard if the device is invalid

	if(!pKeyboard || !pDI)
	{
		return DIERR_GENERIC;
	}

	// Get the buffered data 

	hr=pKeyboard->GetDeviceData(sizeof(DIDEVICEOBJECTDATA),didod,&dwElements,0);
    if(hr != DI_OK) 
    {
		// Error could be caused by the keyboard not being acquired

        hr=AcquireKeyboard();
        while(hr==DIERR_INPUTLOST) 
            hr=AcquireKeyboard();
 
		// If the keyboard needed to be reacquired reads will be done 

		// on the next call to this function.

        return S_OK; 
    }

    if(FAILED(hr))
	{
		MessageBox(NULL,"Cannot get buffered keyboard data",
			"InputSystem::ReadKeyboard()",MB_ICONERROR);
		return hr; 
	}

    // Study each of the buffer elements and process them.

    for(i=0;i<dwElements;i++) 
    {
		if(didod[i].dwData & 0x80)
			keystate[didod[i].dwOfs]=1;
		else
			keystate[didod[i].dwOfs]=0;
		
		//--------------------------------------------

		// Call user defined input processing routine

		//--------------------------------------------

		ProcessInput(*this);
	}

    return S_OK;
}

//----------------------------------------------------------------------

// void InputSystem::Cleanup()

//

// Free up allocated memory and release interfaces. A call to this

// function should be made when your application is done using the

// object.

//  

// Params:

//    None

//

// Return Values:

//    None

//----------------------------------------------------------------------

void InputSystem::Cleanup()
{
  if(pKeyboard)
  {
	UnacquireKeyboard();
    pKeyboard->Release();
  }
  
  if(pDI)
    pDI->Release();
}

//----------------------------------------------------------------------

// void ProcessInput(InputSystem &input)

//

// This function is called by the InputSystem object every time

// the ReadKeyboard function is called and buffered input is retrieved.

// The user of the class should enter input processing routines here.

//  

// Params:

//    The calling InputSystem class

//

// Return Values:

//    None

//----------------------------------------------------------------------

void ProcessInput(InputSystem &input)
{
	//-------------------------------------------

	// Put your input processing routines here!!!

	//-------------------------------------------

	if(KeyDown(DIK_RETURN))
		PostQuitMessage(0);

}

  
Advertisement
What you''re describing:

Main program runs.
Main program calls a function to get keyboard data.
Input subsystem calls a callback function implemented in the main program for each keystroke.

Why not just have the main program pass a pointer to a buffer to the ReadKeyboard() function (which will populate it)? Then you avoid the headaches of callback functions and you can just have your main program iterate over the keystrokes.

Or if you''re building a specialized input system, your ReadKeyboard function can iterate over the keystrokes and convert them into game-commands specific for your game, then populate the buffer with game-commands.

In any case, what you''re doing should work, and my advice is to get something working, then move on to the next piece. If it needs optimizing later, do it later.

Domenic, Underdog
http://www.unitedunderdogs.com
Domenic, Underdoghttp://www.unitedunderdogs.com
quote:
Main program runs.
Main program calls a function to get keyboard data.
Input subsystem calls a callback function implemented in the main program for each keystroke.


Not really a callback function, as it is now it just calls
a plain old function and I already made a prototype and empty
implementation in the same module and left it up to the user of the class to fill it in.

quote:
Why not just have the main program pass a pointer to a buffer to the ReadKeyboard() function (which will populate it)? Then you avoid the headaches of callback functions and you can just have your main program iterate over the keystrokes.

Or if you''re building a specialized input system, your ReadKeyboard function can iterate over the keystrokes and convert them into game-commands specific for your game, then populate the buffer with game-commands.


Both of these ideas would work, but I really kind of wanted
all variables and functions that deal with the input to be
in the same module, of course nothing is stopping the user of the class to do it that way..I''ll have to think on that one
and try it out a bit.

quote:
In any case, what you''re doing should work, and my advice is to get something working, then move on to the next piece. If it needs optimizing later, do it later.

That would go against every class and every book I''ve learned from. Fixing it up later would cause all kinds of messes after code is built on top of it and depends on it functioning a certain way....but it is tempting.
,
Christopher

This topic is closed to new replies.

Advertisement