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

Direct Input: Keyboard problems

Started by Destroyer Apr 16, 2002 at 9:58 PM 7 replies 1.1k views
Original Post
Destroyer
Destroyer
Hi, I'm having some problems with DirectInput and the keyboard. It's very similiar to the code used in "Multiplayer Game Programming" The main problems arise from this very function.
      


HRESULT CKeyboard::hrCheckForInput(void)
{
	DIDEVICEOBJECTDATA		didKeyboardBuffer[KEYBOARD_BUFFERSIZE];
	DWORD dwItems = KEYBOARD_BUFFERSIZE;

	WORD wAscii;

	if(!m_lpdidKeyboard)
		return E_FAIL;

	m_lpdidKeyboard->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), didKeyboardBuffer, &dwItems, 0);

	if(dwItems)
	{
		for(UINT index = 0; index < dwItems; index++)
		{ 

			static UCHAR State[256];

			if(GetKeyboardState(State) == false)
				return E_FAIL;

			UINT vk = MapVirtualKeyEx(didKeyboardBuffer[index].dwOfs, 1, m_hkllayout);
			   
			ToAsciiEx(vk, didKeyboardBuffer[index].dwOfs, State, &wAscii, 0, m_hkllayout);

			if(didKeyboardBuffer[index].dwData & 0x80)
			{
		
					m_bascKeys[wAscii] = true;
                                  if(didKeyboardBuffer[index].dwOfs >= 0 && didKeyboardBuffer[index].dwOfs < 256)
					m_bdiks[didKeyboardBuffer[index].dwOfs] = true;
			}
			else
			{
				m_bascKeys[wAscii] = false;

                            if(didKeyboardBuffer[index].dwOfs >= 0 && didKeyboardBuffer[index].dwOfs < 256)
				m_bdiks[didKeyboardBuffer[index].dwOfs] = false;
			}
		}
	}
	else
		return E_FAIL;

	return DI_OK;
}
  

In case you're wondering - this is how I check for input

[source]     

bool CKeyboard::bisKeydowndik(UCHAR idik)
{
	return m_bdiks[idik];
}

bool CKeyboard::bisKeydownch(char cascii)
{
	return m_bascKeys[cascii];
}

  
This problem arises when I exit the program... The line m_bdiks[didKeyboardBuffer[index].dwOfs] = true; is causing some problems. When I exit the didKeyboardBuffer[index].dwOfs value has some insanely high number. So I had to put that if statement above to avoid from going out of bounds of the array. However, this still causes it to crash... A dialogue box appears with the message that there was "DAMAGE: after Normal block (#21) at 0x00A00430". Trying to debug that problem was a nightmare... all I saw was a lot of assembly... what could be going wrong? Another problem is I can't seem to be able to check for capital letters for the ascii version of my "Keydown" functions... the lower case letters work, while the upper case don't. I tried searching the forums for help, but alas I found no sucess... I tried going to www.LostLogic.com to see support for this problem in his book, but I found nothing as well. Thanks in advance for any help, Destroyer //---------------------------- I'm almost never right, and almost always left... ---------------------------- [edited by - Destroyer on April 16, 2002 11:00:40 PM] [edited by - Destroyer on April 16, 2002 11:01:48 PM]
Dooster
Dooster
Seems to me like you're making an easy task harder than it should be. I haven't read that book, but my DI-Keyboard code looks nothing like that, is really simple, but works perfectly well. I created a wrapped class that inits DirectInput, and allow you to initialize and cleanup all major devices, like Keyboard, Mouse and Joysticks etc.

Imagine you have initialized DirectInput and created/acquired the keyboard, these are the two functions I wrote to handle the updating of the keyboard state and the checking for a keydown:


      
LPDIRECTINPUTDEVICE8 m_Keyboard; // Keyboard Device

BYTE m_KeyBuffer[256]; // Keyboard State Buffer


#define KEYDOWN(name, key) (name[key] & 0x80)

void Update_Keyboard(void)
{
HRESULT hr;

if( m_Keyboard )
{
// Update Keyboard State Buffer

hr = m_Keyboard->GetDeviceState( sizeof(m_KeyBuffer), (LPVOID)&m_KeyBuffer );
if FAILED(hr)
{
// Recover Device If Lost

while( hr == DIERR_INPUTLOST )
{
hr = m_Keyboard->Acquire();
}
}
}
}

bool Keydown(BYTE key)
{
if( KEYDOWN( m_KeyBuffer, key ) )
return(true);

return(false);
}


You call Update_Keyboard() in your Input loop prior to checking the keys, then use the Keydown() function to check to see if a key is down. Ie:


  
Update_Keyboard();

If( Keydown(DIK_ESCAPE) )
{
// Quit

PostQuitMessage(0);
}


Also do a search in the DX SDK documentation for "Interpreting Keyboard Data" for help on more elaborate handling

[edited by - Dooster on April 17, 2002 10:23:53 AM]
TheFez
TheFez
He''s doing buffered mode (not sure why he''s doing it the way he is, but its buffered) and thats why it doesn''t look like yours. I''m confused as to why you need the ascii like that but oh well. If its only doing this when its closing your most likely quiting, but your code is continuing on to this portion. So when it tries to get the device data, it crashes. I would just go through your code and make sure that you stop when you think you stop. (I can''t really be that helpful, sorry, but since I can''t see all of your code this is all I can suggest)
Dooster
Dooster
I know, however the docs the best source of info on that, check out the samples and you can almost copy/paste the functionality. Also is it vitally important that the key data *is* buffered in your app? ... Personally I''ve found it to be much more effective (your milage may vary of course based on your needs) to check the state of keys immediately prior to using them as opposed to buffering key data in the loop.

My project has a pretty intricate scene and key data is always processed where it should be immediately no matter the speed without the need for buffered data which only adds to the soup.

Of course I don''t know Destroyers product intentions which may vary wildly from my own, but thats my opinion on the immediate/buffered key data.
Blaster
Blaster
About the insanely high values, I just read in the SDK doc something about random values.
In "Programming tips and tools" in the DirectInput section, it says:
"GetDeviceData and an Unacquired Device
When using the debug version of Microsoft DirectPlay®, you may notice some unusual behavior from the GetDeviceData method when a device is unacquired. If you call GetDeviceData and the device is not acquired, random bytes will be placed in the device data buffer and the value of the pdwInOut parameter will not be changed. Therefore, if your application does not check for the DPNERR_UNACQUIRED error code, your application will mistakenly determine that there is data in the buffer."

I hope it will help.

----------------
Blaster
Computer game programmer and part time human being
Strategy First - http://www.strategyfirst.com
BlasterSoft - http://www.blastersoft.com
Destroyer
Destroyer
Hi,

Why did I decide to use buffered input? The original argument given in the book was that in between frames in your main loop you''d probably have something to force the computer to wait for a certain amount of time before animating, moving, or whatever. In the small interval of time in between the two frames, if you press a key fast enough it might get lost.

Thinking back on it it''s sorta dumb... - but logical. It seems though it''s becoming more trouble then it''s worth. So I''ll go back to using immediate data instead, it''s much easier.

Why did I want ASCII codes... well if I need some sort of typing in my game it''s useful to have the ascii values to compare strings, etc.

There''s tons of post on coverting scan codes to ascii, just have a quick search on the forums .

My only problem with this was getting capital letters, without having to check for DIK_SHIFT then the ascii values, is there any other way... ?

Thanks a lot all,

Destroyer

//----------------------------
I''m almost never right,
and almost always left...
----------------------------\\
Dooster
Dooster
If you''re checking the character codes, simply check the code to see if it represents the lower case or capital character, as they have different codes. So in theory, it should already be supported by your intentions.
Destroyer
Destroyer
Oddly enough, it doesn't... I tried doing bisKeydownch('W'); and different capital letters, and I tried holding shift and the key and it didn't do anything, where as bisKeydownch('w'); and the rest of the lower case letters did.

As an aside... now that I've switched from buffered to immediate data, I'm finding it difficult to check asii values.

Here's the new code I use


  
bool CKeyboard::bisKeydownch(UCHAR cascii)
{
WORD wvkKey = VkKeyScanEx(cascii, m_hkllayout);
UCHAR chvkKey = (UCHAR)wvkKey;

if(m_chdiks[chvkKey] & 0x80)
return true;

return false;
}


It doesn't work
Any ideas on how to get around this problem? Also note that m_hkllayout has already been initialized with GetKeyboardLayout();

Thanks,

Destroyer

//----------------------------
I'm almost never right,
and almost always left...
----------------------------\

[edited by - Destroyer on April 17, 2002 10:18:39 PM]
Dooster
Dooster
Well to be honest whenever I work with retrieving characters for use as the user inputs them I trap the WM_CHAR message, which despite speed concerns is fast enough for entering information in the menu etc, I''ve even used it for retrieving input commands at runtime for a console.

However I never use it for player input. For that when I want to check the difference between upper and lower case through the DirectInput Keyboard constants I do the follow (using my previous sample as reference):


  
if( Keydown(DIK_A) && Keydown(DIK_LSHIFT) ) // or DIK_RSHIFT

{
// User is pressing A with left shift, thus caps

}


You could also check the Caps Lock state to also help you determine if input should be lower or caps. The downside is you need to check for specific key combinations which may not be good for entering loose text, for things like "Enter Name". But as mentioned before I''ve found the WM_CHAR message fine for this, and it DOES automatically detect lower/caps keys

When retrieving a character from the WM_CHAR (also use WM_KEYDOWN too) message I normally send its value to the handler (Ie, CGameEditBox), from which it''ll build up the string.

But if you''re looking to tell the difference for game input and you *know* which keys the player will be using, then checking the key and shift state works perfectly fine. I guess its down to how you want your input to work.

Topic Locked

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

Sign in to reply to this topic.