Translating Window's keyboard messages into usable ASCII values

Started by
2 comments, last by helix 18 years, 8 months ago
In the current incarnation of our engine, we just take in all the virtual keycodes passed in via WM_KEYDOWN and WM_KEYUP and do some manual translation from the virtual keycodes into ASCII. This leads to some problems so I want to just pass in the expected ASCII values from the message proc level and let Windows do all the translations while ignoring the keys we're not interested in. I did some research and here's what I came up with so far. By calling TranslateMessage on a WM_KEYDOWN or WM_KEYUP message, Windows will place a WM_CHAR or WM_DEADCHAR message in the front of the message stack with the translated ASCII value of the source message. Our message proc was already calling TranslateMessage but was not handling the WM_CHAR messages. So I commented out the WM_KEYUP and WM_KEYDOWN and added support for WM_CHAR as so:

case WM_CHAR:
/* KF_AutoRepeat is based on the assumption from this statement on MSDN:

	Previous Key-State Flag:
	The previous key-state flag indicates whether the key that generated
	the keystroke message was previously up or down. It is 1 if the key was
	previously down and 0 if the key was previously up. You can use 
	this flag to identify keystroke messages generated by the keyboard's
	automatic repeat feature. This flag is set to 1 for WM_KEYDOWN and
	WM_SYSKEYDOWN keystroke messages generated by the automatic repeat
	feature. It is always set to 0 for WM_KEYUP and WM_SYSKEYUP messages.

So that tells me that a keydown would generate 0 because there was no previous
state and it explicitly says 0 for keyup.  It also says that the flag is set
to 1 for keydowns generated by autorepeat.
*/
	static const uint32 KF_AutoRepeat = 0x1E;	// Previous key state

	// Check for autorepeat message and ignore if desired
	//NOTE: The commented part here I found online.  I don't quite follow it...
	bool bAutoRepeat = (HIWORD(lParam) & KF_AutoRepeat) != 0;//KF_REPEAT == (HIWORD(lParam) & (KF_UP | KF_REPEAT)) )
	if( !m_bAutoRepeat && bAutoRepeat )
		return 0;

/* This next bit of code is based on this statement from MSDN:

	Transition-State Flag:
	The transition-state flag indicates whether pressing a key or
	releasing a key generated the keystroke message. This flag is always
	set to 0 for WM_KEYDOWN and WM_SYSKEYDOWN messages; it is always set
	to 1 for WM_KEYUP and WM_SYSKEYUP messages.
*/
	bool bKeyDown = (HIWORD(lParam) & KF_UP) == 0;

	// Some other code to take my wParam (the translated ASCII value) and do stuff
	FunctionToHandleKeyInput( wParam, bKeyDown );

	return 0;
break;





This doesn't appear to work quite right. It is correctly translating the keys into the values I want but the key up/down and auto repeat don't behave. Instead of ignoring autorepeat messages, it appears to be endlessly sending them to me disguised as regular keypresses (or my lParam checks are incorrect). So by pressing one key down, I get that char constantly being sent. I investigated the possibility of the ToAscii function but it requires a key state buffer which I don't really want to have to maintain. I'd like to just drop in some simple code like I have above to our existing engine. I really don't think the solution I'm looking for is that complicated and I know one has to exist or else this WM_CHAR message is bunk. Does anyone have any ideas for me? I used the following MSDN webpage for reference: MSDN Article: About Keyboard Input
Advertisement
KF_UP is the bit that indicates if the key was released of pressed. You could therefore make a test for if the key is being pressed. KF_REPEAT is the bit that indicates if this message is from the automatic repeat feature.
(HIWORD(lParam) & KF_UP) == 0 // The key is being pressed(HIWORD(lParam) & KF_REPEAT) != 0 // This is an auto repeat message

The above two tests can be combined as:
KF_REPEAT == (HIWORD(lParam) & (KF_UP | KF_REPEAT))

I wounder why you use 0x1E as the value of KF_AutoRepeat? You should be fine with just a test for HIWORD(lParam) & KF_REPEAT != 0
What exactly are you doing?

If you're doing something like input for gameplay (like a "walk foward" key), you should be using WM_KEYDOWN and WM_KEYUP events exclusivly. You should be using virtual key codes and not ASCII for this anyway.

And then if you are having a user type stuff, you need to use the WM_CHAR message - which will match the user's keyboard settings - including for ASCII mapping and repeat rate. By using this message you give the expected behaviour for text entry.

The two kinds of message are not really interchangable.
I currently just care about text entry for a chat window. This game only uses mouse input so it doesn't matter too much. However, I will want to reuse this system in future games that will definately be using key input for gameplay. So the end result will need to also support the virtual key codes (which I believe are still a part of the WM_CHAR message). But at the moment, I just want to figure out the issue I'm having with the WM_CHAR messages.

The problem with my code currently is that a normal keystroke will end up with a never ending stream of that keypress message (even though the key has been pressed and released long ago). According to the doc I linked to, the key up and key down should still be valid and testable using a WM_CHAR message. If I handled both the WM_CHAR and the WM_KEYUP/KEYDOWN messages, then I'll have duped messages being handled which is bad. So what I'm trying to do is get the ASCII values of the keypresses for my text entry so I don't have to have some hacked up virtual key translation function like I do now.

You can read my comment in the source block for my reasoning behind using the "previous key state flag". I did try also using the autorepeat flag as well but still have the same problem (I've tried a number of different tests and such to try to get it to work). It's odd because I'm typing normally so auto repeat should never have started anyway.

There's a KeyHandler class in the engine that I'm not currently using because it makes assumptions based on the virtual key code and the ascii values get munged and things just don't work right. I think if this WM_CHAR thing doesn't pan out, I might just have to fix it and remove the lame translate key functionality and use the ToAscii function instead (as it does store a keystate buffer already). That might work... But I still want to figure out how to properly use WM_CHAR messages though.

This topic is closed to new replies.

Advertisement