Archived

This topic is now archived and is closed to further replies.

How to find out if key WAS pressed

This topic is 5109 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

With Managed DirectInput, you obtain the keyboard state to KeyboardState class. This is done through the GetCurrentKeyboardState()-call. You can''t create instance of the KeyboardState class nor can you change its data, you can only read the booleans from it. Now if I want to read if particular key WAS pressed to do an action once, I have to do it like this: if(kb.KeyDown(Key.F) && bDrawFpsKey) { bDrawFps = !bDrawFps; bDrawFpsKey = false; } if(kb.KeyUp(Key.F)) bDrawFpsKey = true; I need one extra boolean, because without this check, the bDrawFps-boolean might be changed hundreds of times with single fast keyboard hit. My questions is how could this be done like this still using Managed DirectInput? if(kb.KeyPressed(Key.F)) bDrawFps = !bDrawFps; You cant loop through the KeyboardState class because it doesn''t contain method for getting enumerator. Also I think that I can''t loop through the Key-struct, so I''m completely out of ideas...

Share this post


Link to post
Share on other sites
unsigned char Old[256];
unsigned char New[256];

inline bool Released( unsigned int Key )
{
if( Old[ Key ] & 0x08 ) // 0x08?? Been a while..

{
if( New[ Key ] & 0x80 )
return false;
else
return true;
}
return false;
}

inline bool KeyLoop( void )
{
if( GetMyInputFromTheKeyboard() != SUCCESS )
{
if( Released( bDrawFpsKey ) )
// Yes released..


memcpy( Old, 256, New ); // Copy

}
}

I hope this helps..

By the way, I think you should learn about Buffered Input.
One, it won't miss any input if your game runs slow.
Two, you can discard this code and just check the previous buffer of the x. Learn about it, is my tip.


--
You're Welcome,
Rick Wong

- sitting in his chair doing the most time-consuming thing..

[edited by - Pipo DeClown on December 15, 2003 8:45:31 AM]

Share this post


Link to post
Share on other sites
Thanks for the code, but I''m using c#, sorry if I forgot to mention

This could easily be done like having two KeyboardStates. Like oldState and newState. Then with every loop compare if one key was is true in the oldState and false in the newState, it was pressed. The problem is that I can''t create a new KeyboardSate, because its ctor is private, and even if I could, I can''t set its values (they''re read-only). The values are set with call to the Managed DirectInput. Also I can''t iterate through the KeyboardState, because it is not enumerable. I could create for example an array of those pressed keys every iteration, but I would have to hardcode every keycheck to the code. I would rather do it by iterating through the Key-enumeration, but enums can''t be looped...

Is there any way I could do this?

Share this post


Link to post
Share on other sites
I just discovered that Managed DirectInput''s KeyboardState class behaves like a singleton, so I can''t have two separate instances of it. This makes things a bit harder...

Hasn''t anyone done something like this with c# and managed dx?

Share this post


Link to post
Share on other sites
It''s C++, but you should be able to port the concept easily enough... even if you don''t quite get all the boolean logic.


#ifndef INC_KEYBOARD_H_
#define INC_KEYBOARD_H_

// Keyboard -- Defines an interface to a keyboard device.

// (C)2003 David ''Redbeard'' Eccleston

//

// Obtains input from a keyboard device. Usually only one keyboard is present

// on a system, and as such, only one instance of a keyboard object should be

// created.

//

// Create a keyboard object with ''new'', then call Init(). You can now request

// keyboard data. When a keyboard object is lost, it attempts to regain

// itself anytime you request it to poll information.

//

// Usually, Poll() is called once per frame, before any keyboard data is

// required. After calling Poll(), the remaining functions can be used

// to determine the state of the keys on the keyboard.

//

// IsPressed(key) - returns TRUE if the requested key (DirectInput keyboard

// code) is currently pressed down.

//

// MakePressed(key) - can simulate a keypress that later code may act upon

//

// IsLatched(key) - returns TRUE if the key was pressed since the last time

// the key''s latch was cleared. When a latch is cleared, the key is not

// latched again until the key goes up, and is pressed down again.

//

// JustLatched(key) - returns TRUE is the key was latched on the last Poll()

// call. If you don''t clear the latch, you can determine that the key wasn''t

// just pressed using this method. Very odd, unsure when it would be useful,

// but it was easy to support, so it''s here.

//

// ReadAndClearLatch(key) - Reads the latch and clears it. If no other parts

// of your app will check the same latched key, this is useful in that you

// won''t have to make a second call to clear the latched key.

//

// ClearLatch() - Clears the latch of a key. Allows you to determine the

// next time the key is pressed, and to not think the key was pressed again

// when it has been down for more than a single Poll() call.

//

// ClearAllLatches() - Clears the latches of all keys on the keyboard

//

// ClearAllLatchesAndKeysDown() - Stops any further keyboard activity until

// the next poll.


class Keyboard
{
public:
LPDIRECTINPUTDEVICE8 m_pKeyboard;
BYTE m_bKeyLatch[256];

BOOL Init(LPDIRECTINPUT8 pDInput, HWND hWnd);
void Poll();
BYTE IsPressed(int nKey) {return m_bKeyLatch[nKey] & 0x80;}
void MakePressed(BYTE nKey) {m_bKeyLatch[nKey] |= 0x80;}
BYTE IsLatched(int nKey) {return m_bKeyLatch[nKey] & 0x40;}
BYTE JustLatched(int nKey) {return m_bKeyLatch[nKey] & 0x20;}
BYTE ReadAndClearLatch(int nKey) {BYTE l = m_bKeyLatch[nKey]; m_bKeyLatch[nKey] &= ~0x40; return l&0x40;}
void ClearLatch(int nKey) {m_bKeyLatch[nKey] &= ~0x40;}
void ClearAllLatches();
void ClearAllLatchesAndKeysDown();

Keyboard();
~Keyboard();
};

#endif


#include "keyboard.h"

Keyboard::Keyboard()
{
m_pKeyboard = 0;
}

Keyboard::~Keyboard()
{
if (m_pKeyboard)
{
m_pKeyboard->Unacquire();
m_pKeyboard->Release();
}
}

void Keyboard::ClearAllLatches()
{
DWORD *l, n;

n = 256/4;
l = (DWORD *)m_bKeyLatch;
while (n--)
{
*l &= ~0x40404040;
l++;
}
}

void Keyboard::ClearAllLatchesAndKeysDown()
{
memset(m_bKeyLatch, 0, sizeof(m_bKeyLatch));
}

BOOL Keyboard::Init(LPDIRECTINPUT8 pDInput, HWND hWnd)
{
HRESULT hr;

ClearAllLatchesAndKeysDown();

if( FAILED( hr = pDInput->CreateDevice( GUID_SysKeyboard, &m_pKeyboard, NULL ) ) )
{
// g_Errors.Add("Create keyboard");

// g_Errors.Add((char *)DXGetErrorString9(hr));

return FALSE;
}

// This tells DirectInput that we will be passing an array

// of 256 bytes to IDirectInputDevice::GetDeviceState.

if( FAILED( hr = m_pKeyboard->SetDataFormat( &c_dfDIKeyboard ) ) )
{
// g_Errors.Add("Set keyboard format");

// g_Errors.Add((char *)DXGetErrorString9(hr));

return FALSE;
}

if (FAILED(hr = m_pKeyboard->SetCooperativeLevel( hWnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND )))
{
// g_Errors.Add("Set keyboard cooperative level ");

// g_Errors.Add((char *)DXGetErrorString9(hr));

return FALSE;
}

m_pKeyboard->Acquire();

return TRUE;
}

void Keyboard::Poll()
{
HRESULT hr;
int n;
DWORD *down, *latch, d, l;
BYTE bKeys[256];

hr = m_pKeyboard->GetDeviceState(256, bKeys);
if (FAILED(hr))
{
// We''ll get a 1 frame lag on re-acquiring the keyboard, but hey, not a big deal.

// Attempt to reacquire, and return so we don''t use the uninitialized key states.

hr = m_pKeyboard->Acquire();
return;
}

// Now handle latched and just latched, and copy keydowns

n = 256/4;
down = (DWORD *)bKeys;
latch = (DWORD *)m_bKeyLatch;
while (n--)
{
d = *down & 0x80808080; // Only look at the down flag

l = *latch & 0xC0C0C0C0; // Keep previous down keys, and latches, but clear just latched


// Remember current set of down keys, keep previous uncleared latches,

// and add any keys that are newly down to both latch and just latched.

*latch = d | (l & 0x40404040) | ((d&(~l))>>1) | ((d&(~l))>>2);

down++;
latch++;
}
}


#if 0

// Test sample app - all 3 methods of interpreting keys


void Init()
{
g_pKeyboard = new Keyboard();
if (!g_pKeyboard)
return E_FAIL;

if (!g_pKeyboard->Init())
return E_FAIL;
}

void Cleanup()
{
if (g_pKeyboard)
delete g_pKeyboard;
g_pKeyboard = 0;
}

void ReadKeys()
{
static float x=0, y=0;
static int mode=0;

g_Keyboard->Poll();

if (g_pKeyboard->IsLatched(DIK_T))
{
mode = (mode+1)%3;
g_pKeyboard->ClearAllLatches();
}
if (mode==0)
{
if (g_pKeyboard->IsPressed(DIK_UP))
y -= 1;
if (g_pKeyboard->IsPressed(DIK_DOWN))
y += 1;
if (g_pKeyboard->IsPressed(DIK_LEFT))
x -= 1;
if (g_pKeyboard->IsPressed(DIK_RIGHT))
x += 1;
}
else if (mode==1)
{
if (g_pKeyboard->IsLatched(DIK_UP))
{
y -= 5;
g_pKeyboard->ClearLatch(DIK_UP);
}
if (g_pKeyboard->IsLatched(DIK_DOWN))
{
y += 5;
g_pKeyboard->ClearLatch(DIK_DOWN);
}
if (g_pKeyboard->IsLatched(DIK_LEFT))
{
x -= 5;
g_pKeyboard->ClearLatch(DIK_LEFT);
}
if (g_pKeyboard->IsLatched(DIK_RIGHT))
{
x += 5;
g_pKeyboard->ClearLatch(DIK_RIGHT);
}
}
else if (mode==2)
{
if (g_pKeyboard->JustLatched(DIK_UP))
y -= 5;
if (g_pKeyboard->JustLatched(DIK_DOWN))
y += 5;
if (g_pKeyboard->JustLatched(DIK_LEFT))
x -= 5;
if (g_pKeyboard->JustLatched(DIK_RIGHT))
x += 5;
}
}
#endif

Share this post


Link to post
Share on other sites
Yep The problem is not that I wouldn''t understand how to do this, I just think it is impossible :D

The REAL problem is that the key data is obtained to a SINGLETON CLASS! The c# version of directinput doesn''t use byte arrays, it uses key enum and single sealed class to hold the data. I''ve tried to modify it from every direction, but the compiler is always saying that something is private/readonly, you can''t do this and that etc... I think this is impossible to do.

Share this post


Link to post
Share on other sites
The only thing you need to change is how I query DInput on if keys are down... i''m not modifying it''s values or making duplicate keyboard interfaces or anything.

Replace where I GetDeviceState() and get back 256 values into whatever you need. ie: loop for 256 values, and ask managed dinput for "Is this key down?". If the key is down, set bit 7 (0x80), in a byte array, otherwise clear it. The rest of the code will work, regardless of anything else.

Managed C# may not like casting byte arrays to long arrays of quarter their size, so when I''m clearing, or calculating latches you might have to loop 256 times, and do one byte of work at a time... Not a big deal.

Share this post


Link to post
Share on other sites