Boring Look@Keyboard/Mouse Button Input

Discuss programming topics for the various GPL'd game engine sources.
Post Reply
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Boring Look@Keyboard/Mouse Button Input

Post by Baker »

Another boring thread ...

Keyboard input works in this manner on Windows:

1. Designated message handler receives WM_KEYUP, WM_KEYDOWN, WM_SYSKEYUP, WM_SYSKEYDOWN. The first 7 bits of the lparam for the message handler contains the virtual key code for the physical key pressed on the keyboard (i.e. pressing 8 in the top row isn't the same physical key as pressing 8 on the keypad, the right shift key isn't the same as left shift key, etc.) and the Windows function ToAscii can be used to obtain the ASCII (0-255 keycode) which in the Windows implementation appears to be able to support: English, French, German, Italian, Portuguese, Danish, Dutch, Norwegian, Icelandic and Spanish (according to Wikipedia).

In something like Quake where you want to be able to deal with continuous press states, versus say key repeats and the timing issues this would present (example the +turnleft key where this would work differently based on control panel settings, had it been implemented another way), both the virtual key and the what that key represents are 2 separate pieces of information. Quake overcomes this with a fixed key map table, the disadvantage of which this that the hardcoded keys are only correct for, say, an English keyboard. [Quake 3 and, for example, DarkPlaces use the ToAscii method to correctly map the keys for non-English keyboards].

In Quake, there are 3 situations that should be nullified: ALT-SPACE if the Window is full-screen (arguably windowed mode too) as ALT-Space triggers the start menu (WM_SYSCHAR, returning a zero takes care of that.) StickyKeys, ToggleKeys, Filter keys are undesirable and those can be disabled by API (they must be re-enabled on exit). And the "Windows" key and "Context menu key" which can be nullified via SetWindowsHookEx and setting a function to handle it which returns 1 for those keys (VK_LWIN - left Windows key, VK_RWIN - right Windows key, VK_APPS --- context menu).

2. The console. In Quake, certain keys are often coded to behave in a specific way when the console is displayed. The arrow keys, for instance, always move the cursor. Some engines have the numeric keyboard always produce numbers in the console, ignoring the numlock key state.

3. In Quake, the keys that mapped to Quake button functions such as "+attack" or "+jump" also keep track of up to 2 keys that may have been pressed to trigger that function to ensure that a continuous "attack" or "jump" can be maintained even if two separate were pressed to do the same thing.

4. Overcoming timing: It is possible for a key to be pressed and released quickly enough both occur during the same frame (i.e. what you are getting 30 FPS on some giant map in a wide open area). In that event, Quake does something interesting: a button pressed and released within a frame receives a fraction of 0.25, a button pressed or released during a frame gets a fraction of 0.5 and a continuously held button receives a fraction of 1.0 --- these are used in combination with the amount of time for the frame (i.e. 100 frames per second = 1/100 of a second) for angle manipulation like pressing an arrow key to turn left or right or some other key to look up.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
andrewj
Posts: 133
Joined: Mon Aug 30, 2010 3:29 pm
Location: Australia

Re: Boring Look@Keyboard/Mouse Button Input

Post by andrewj »

Baker wrote:Another boring thread ...
I dunno why you said that.

If you wanna say stuff, then say stuff, and let other people decide if it's interesting or not.
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Boring Look@Keyboard/Mouse Button Input

Post by Baker »

andrewj wrote:
Baker wrote:Another boring thread ...
I dunno why you said that.

If you wanna say stuff, then say stuff, and let other people decide if it's interesting or not.
Fair enough. I'll refrain from that in the future.

I'm trying to unravel client-server communications and timings (then comes prediction which obviously requires collisions and 3D math). During the course of this, I realized I needed an exact understanding of all the inputs and how they are calculated precisely, and realized that the input code needs to be re-written entirely (either now or in the future, which means now because I'm "there" and don't want loose ends).

I kinda of don't want to be doing this. I briefly looked at both SDL and Java to see how they handle everything and determined that SDL's way is nice and simple but insufficient ultimately and I do not like how Java handles input (No I don't mean use Java, I mean looked at the source code not wanting to re-invent the wheel and discovered the Java input scheme sucks too).

I did the above because multi-platform and as much international keyboard support as is possible is important to me.

And there is no magic bullet. I decided the following keys are not worth being in an input scheme: numlock, printscreen, scrolllock, the numeric keypad except to emulate the calculator keys if the console is open or to emulate the arrow keys if it is not (
maybe adding some new direction "keys", hehehe. For example in a klook scheme, why is there no "look up and to the left key?").

I also determined the idea of supporting SHIFT, R_SHIFT, L_SHIFT is stupid ... the way to do it is R_SHIFT and L_SHIFT. Except ... backwards compatibility enters the equation.


The incomplete skeleton looks like this so far ...

Code: Select all

   	{1,		"INSERT",			K_INSERT,			0, 82,		VK_INSERT,	0x2D,	NULL,	NSInsertFunctionKey, 		0xF727		},
   	{2,		"DELETE",			K_DELETE,			0, 83,		VK_DELETE,	0x2E,	NULL,	NSDeleteFunctionKey, 		0xF728		},
   	{3,		"HOME",				K_HOME,				0, 71,		VK_HOME,	0x24,	NULL,	NSHomeFunctionKey,			0xF729		},
   	{4,		"END",				K_END,				0, 79,		VK_END,		0x23,	NULL,	NSEndFunctionKey,			0xF72B		},
   	{5,		"PAGEUP",			K_PAGEUP,			0, 73,		VK_PRIOR,	0x21,	NULL,	NSPageUpFunctionKey,		0xF72C		},
   	{6,		"PAGEDOWN",			K_PAGEDOWN,			0, 81,		VK_NEXT,	0x22,	NULL,	NSPageDownFunctionKey,		0xF72D		},
   	{7,		"CAPSLOCK",			K_CAPSLOCK,			0,	0,		VK_CAPITAL,	0x14,	NULL,	Unknown at the moment					}
   	{8,		"BACKSPACE",		K_BACKSPACE,		0,  0,		VK_BACK,	0x08,	NULL,	Unknown at the moment					},
   	{9,		"TAB",				K_TAB,				0,	0,		VK_TAB,		0x09,	NULL,	Unknown at the moment					},
   	{13,	"ENTER",			K_ENTER,			0,  0		VK_RETURN,	0x0D,	NULL,	Unknown at the moment					},
   	{14,	"F1",				K_F1,				0, 59,		VK_F1,		0x70,	NULL,	NSF1FunctionKey,			0xF704		},
   	{15,	"F2",				K_F2,				0, 60,		VK_F2,		0x71,	NULL,	NSF2FunctionKey,			0xF705		},
   	{16,	"F3",				K_F3,				0, 61,		VK_F3,		0x72,	NULL,	NSF3FunctionKey,			0xF706		},
   	{17,	"F4",				K_F4,				0, 62,		VK_F4,		0x73,	NULL,	NSF4FunctionKey,			0xF707		},
   	{18,	"F5",				K_F5,				0, 63,		VK_F5,		0x74,	NULL,	NSF5FunctionKey, 			0xF708		},
   	{19,	"F6",				K_F6,				0, 64,		VK_F6,		0x75,	NULL,	NSF6FunctionKey, 			0xF709		},
   	{20,	"F7",				K_F7,				0, 65,		VK_F7,		0x76,	NULL,	NSF7FunctionKey,			0xF70A		},
   	{21,	"F8",				K_F8,				0, 66,		VK_F8,		0x77,	NULL,	NSF8FunctionKey,			0xF70B		},
   	{22,	"F9",				K_F9,				0, 67,		VK_F9,		0x78,	NULL,	NSF9FunctionKey,			0xF70C		},
   	{23,	"F10",				K_F10,				0, 68,		VK_F10,		0x79,	NULL,	NSF10FunctionKey,			0xF70D		},
   	{24,	"F11",				K_F11,				0, 133,		VK_F11,		0x7A,	NULL,	NSF11FunctionKey,			0xF70E		},
   	{25,	"F12",				K_F12,				0, 134,		VK_F12,		0x7B,	NULL,	NSF12FunctionKey,			0xF70F		},
   	{26,	"PAUSE",			K_PAUSE,			0,	0,		VK_PAUSE,	0x13,	NULL,	NSPauseFunctionKey,			0xF730		},
   	{27,	"ESCAPE",			K_ESCAPE,			0,	0,		VK_ESCAPE,	0x1B,	NULL,	an easy way to capture is unknown		},
   	{28,	"UPARROW",			K_UPARROW,			0, 72,		VK_UP,		0x26,	NULL,	NSUpArrowFunctionKey,		0xF700		},
   	{29,	"DOWNARROW",		K_DOWNARROW,		0, 80		VK_DOWN,	0x28,	NULL,	NSDownArrowFunctionKey,		0xF701		},
   	{30,	"LEFTARROW",		K_LEFTARROW,		0. 75		VK_LEFT,	0x25,	NULL,	NSLeftArrowFunctionKey,		0xF702		},
   	{31,	"RIGHTARROW",		K_RIGHTARROW,		0, 77,		VK_RIGHT,	0x27,	NULL,	NSRightArrowFunctionKey,	0xF703		},
	{32,	"SPACE",			K_SPACE,			0,	0,		VK_SPACE, 	0x20,	NULL,											},	
More or less I am completely unsatisfied with the input scheme in Quake. And feel it is insufficient. Quake really isn't playable on a keyboard and I think it should be --- someway and somehow --- made to have simpler controls. Including double-tapping +forward to get a jump, why have an extra key for jump?

Add: I've said this before but I think the design of modern keyboards is "bad". For instance, function keys can't be taken seriously these days in my opinion. The arrow keys, there are 4 of them. Sigh ..
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Boring Look@Keyboard/Mouse Button Input

Post by Spike »

you'll need to remove all associations between scan code and unicode char values, and to query the system for which unicode char should be generated from the sequence of scan codes given.

Its generally assumed that its always possible to type in latin chars, and that there'll always be a shift, alt, and ctrl key, but there are other modifiers also.
For example, there are 'accent' chars on certain keyboards, which do not work as ctrl+v does, but function sequentially. Press 'grave' and then 'a' and you get an 'a-with-grave-accent' unicode value.
However, you do still have to be able to cope with certain buttons, like 'uparrow' which has no unicode character value associated with it.
Imho, the only sane choice is to recognise control characters (ctrl, escape, uparrow, etc) by scancode, and only if their unicode value is unspecified, and for any printable stuff, use only the unicode value.
Which is all well and good until you come to binds. Which will likely need to use scan codes (with your own modifier key support).
Why not use unicode chars for binds? Because people don't expect shift+w to be a different bind from w, nor altgr+e to be 'whatever é is bound to' instead of 'whatever altgr is bound to, and whatever e is bound to'. Trust me. Users complain if their other buttons do weird things just because they're trying to strafe.
The other reason, of course, is that not all systems provide unicode char values for key releases. You would need to use scan codes for this anyway (tracking which scancode provides which unicode bind, and releasing that unicode binding when the scancode that matches it is released, though even with that you still have the problem that ctrl+r will still be 'down' even when ctrl is released, but perhaps that's what you want).

I cannot possibly begin to explain how a chinese keyboard works, as I have never had reason to use one.


However, input is only a third of the problem.
The other thirds are:
printing unicode chars, which font to use, and how that font is activated. Its pointless to accept weird accents and stuff if noone can actually see them once entered.
networking and compatibility - utf8 perhaps? that'll break with quake's 'highchars'. Special flags to say what text decoding algo to use perhaps? Lets hope your servers don't mangle it when handling 'say' commands. You'll still need to bounce it off servers (and probably support it in the mod too). Either way, you'll likely need to separate internal representation from network representation, at least if network/mod compat is desired.
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Boring Look@Keyboard/Mouse Button Input

Post by Baker »

Spike wrote:Which is all well and good until you come to binds. Which will likely need to use scan codes (with your own modifier key support).
Why not use unicode chars for binds? Because people don't expect shift+w to be a different bind from w, nor altgr+e to be 'whatever é is bound to
To keep a separation from the physical key state "is the actual keyboard key down or up" versus the interpretation, heheh.

Like say, left shift versus right shift. It is a bit of mess, hence not really thinking of this as a joyful task.

But I'm making progress :D
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
taniwha
Posts: 401
Joined: Thu Jan 14, 2010 7:11 am
Contact:

Re: Boring Look@Keyboard/Mouse Button Input

Post by taniwha »

Heh, I went the other way with key support: instead of dropping things like scroll-lock etc, I added things like hankaku/zenkaku, muhenkan, ... (I have a Japanese keyboard and hankaku/zenkaku is where ~/` is on US keyboards. Most inconvenient :P). Actually, my current kb is a Japanese MS Natural Ergonomic Keyboard 4000. I made it so that if X Windows responds to the key, QF knows about it. Unfortunatly, the five favorites keys and the zoom thingy in the middle don't work (linux kernel issue). Same with "Spell Check".
Leave others their otherness.
http://quakeforge.net/
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Boring Look@Keyboard/Mouse Button Input

Post by Baker »

Heh, I went the other way with key support: instead of dropping things like scroll-lock etc, I added things like hankaku/zenkaku, muhenkan, ... (I have a Japanese keyboard and hankaku/zenkaku is where ~/` is on US keyboards. Most inconvenient :P)
I've always admired Japanese culture as I perceive it (work ethic, value of science and math) so I thought maybe at one point I would learn Japanese. But in college I took history of modern China course, was exposed to Mandarin and Cantonese ... and I realized I wouldn't reasonably have what it takes to learn to read Japanese.
taniwha wrote:Heh, I went the other way with key support: instead of dropping things like scroll-lock etc, I added things
I look through things through the eye of utility and likely outcomes. So my approach was ...

Look at screenshots of different keyboards. Mac keyboards don't have some of the keys I've mentioned (numlock). Neither this Windows 7 laptop nor my Macbook Pro have the F1-F12 row of keys how it traditionally exists. On both, I have to press a FN key and the top row keys to get a function key. Scrolllock isn't on this Win 7 laptop keyboard anywhere I can see. And it is becoming a laptop world very quickly. I have difficulty imagining someone really using numlock or scroll-lock as a bind. At the same time, I see LSHIFT and RSHIFT as very likely candidates. And those "Windows logo" keys are in very convenient places for usage, so I want to make use of those. On a Mac, there are similar keys in those places. [Keyboard support, well, due to number of Europeans in the Quake community, I'd like to try to have that covered. Although my first interaction with international support was actually with Vegetous (a Brazilian).]

Speaking of LEFT SHIFT / RIGHT SHIFT. Apparently, the actual keyboard itself can limit the ability to use those keys in the exact same manner as the other keys. Pressing both at the same time and releasing either one releases BOTH on the laptop. I've run into keyboard before where pressing in excess of 3 keys at the same time gets ignored so I am hardly surprised.

Code: Select all

// Baker: I can run this every frame on this Win 7 laptop and if I press both shift keys and release one, both are released even though I am still holding one down  :( 
fbool IN_ExtendedKeyFix (void) //int* wParam, int lParam, fbool newstate)
{
		fbool LSHIFT_PRESSED = Key_isPressed (K_LEFTSHIFT); // Just checks my keydown[256] array (except I won't allow a platform specific files to access
		fbool RSHIFT_PRESSED = Key_isPressed (K_RIGHTSHIFT);   //  non-platform specific variables, nor the other way around.  Hence the function call.
		fbool LSHIFT_ISDOWN = (GetKeyState(VK_LSHIFT) & 0x8000);
		fbool RSHIFT_ISDOWN = (GetKeyState(VK_RSHIFT) & 0x8000);

		Key_Event (K_LEFTSHIFT, LSHIFT_ISDOWN);
		Key_Event (K_RIGHTSHIFT, RSHIFT_ISDOWN);
		return True;
}
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Boring Look@Keyboard/Mouse Button Input

Post by Spike »

keyboards tend to have some weird criscrossy matrix of wires connecting all across the keyboard. it generally figures out which button was pressed based upon resistance or something. This means it can't cope with you pressing multiple keys at once.

From memory, the USB keyboard protocol accepts up to 8 modifiers and 6? keys held at once. Its not possible for the keyboard to report that more keys than that are held, even if there's a separate thingie for each button.
Keyboards also have weird mapping inside them to cope with the somewhat random reordering of keys on modern keyboards. Eg for keys that were added after the original keyboards - they're all vaugely compatible with some original spec.
Standard PS2 controllers also have a tendancy to remap keys in hardware, for dos compatibility.
'Function' keys on laptop keyboards are part of the keyboard, rather than the operating system. They result in different scan codes, which may or may not be handled by the bios.
With all the remapping all over the place, its somewhat unsurprising that you are limited to 3 keys.

On windows, you can use ToUnicode to get the unicode char value from pressed keys. Like I say though, you'll still need to cope with keys which are non-printable. And of course cope with weird keymaps where '1' is actually '&', while 'shift+1' is required to actually get a '1' (eg: french) - the issue is how you go from the string name of the key to the scancode. I've yet to see docs for that with android... Windows at least has a 'VkKeyScan' function if you don't mind being awkward-to-implement system dependancies.
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Boring Look@Keyboard/Mouse Button Input

Post by Baker »

Not sure I care, but this supposedly disables the capslock key:

http://www.codeguru.com/cpp/misc/misc/k ... k-keys.htm

My only interest in the above to allow normal usage of the capslock key as a bind in-game without any other effects.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Boring Look@Keyboard/Mouse Button Input

Post by Spike »

presumably you only want to disable capslock for binds. either way it won't affect scan codes, so if that's what your binds are based upon then capslock won't affect anything anyway.
if your binds are based upon the unicode char value, then you'll have major issues with shift - capslock isn't special.
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Boring Look@Keyboard/Mouse Button Input

Post by Baker »

After a ton of effort, I think I might have a semi-plausable scheme for dealing at least with most of the various European keyboards without special treatment. At least on Windows by addressing wParam and not keymapping lParam. Don't have a final plan for dealing with, say, the French keyboard numbers row yet.

Quake likes to map lparam. ezQuake has different available hardcoded keymaps. I've got more things to do before I give it a test run and see. I'm kind of wondering if a OS X has the kind of functions Windows so this is plausible to do on that operating system.
Spike wrote:presumably you only want to disable capslock for binds.
Yeah, why should it flash the light if it is bound. It really isn't being used as a capslock key in that case.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Boring Look@Keyboard/Mouse Button Input

Post by Spike »

From FTE:

Code: Select all

		BYTE	keystate[256];
		WCHAR	wchars[2];
		GetKeyboardState(keystate);
		if (ToUnicode(wParam, HIWORD(lParam), keystate, wchars, sizeof(wchars)/sizeof(wchars[0]), 0) > 0)
		{
			//ignore if more than one char, its probably a compose and > 65535 anyway. we can't represent that.
			unicode = wchars[0];
		}
		else unicode = 0;
the above code should work for any western keymap (anything that doesn't need surrogates).
You can map unicode to iso8859-1 by just truncating it (though if its <32 or > 127 then it won't work nicely with quake's charset).
I wonder what happens if you disable control chars from the keystate array. I guess control chars would have no unicode value though.

sdl directly has a unicode field in its key up/down events.

you cannot depend upon release events for unicode chars, you can only depend upon the scan codes matching.
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Boring Look@Keyboard/Mouse Button Input

Post by Baker »

Spike wrote:From FTE:

Code: Select all

		BYTE	keystate[256];
		WCHAR	wchars[2];
		GetKeyboardState(keystate);
		if (ToUnicode(wParam, HIWORD(lParam), keystate, wchars, sizeof(wchars)/sizeof(wchars[0]), 0) > 0)
		{
			//ignore if more than one char, its probably a compose and > 65535 anyway. we can't represent that.
			unicode = wchars[0];
		}
		else unicode = 0;
the above code should work for any western keymap (anything that doesn't need surrogates).
You can map unicode to iso8859-1 by just truncating it (though if its <32 or > 127 then it won't work nicely with quake's charset).
I wonder what happens if you disable control chars from the keystate array. I guess control chars would have no unicode value though.

sdl directly has a unicode field in its key up/down events.

you cannot depend upon release events for unicode chars, you can only depend upon the scan codes matching.
Yeah, I don't plan on using key releases for a scenario like the above (I'm using ToAscii, little difference from using the ToUnicode by I wasn't aware of the ToUnicode function until your post ....), I plan on only using them if the input is directed to the console.

The missing piece is I'd like to be able to figure out what a wParam scancode represents for the keys like <, >, ., /, [, } and a few others like the British pound sign and some other characters that frankly, I don't know their names. I should be able to use MapVirtualKey specifying the wParam but it requires the lParam too ... which I think is just the shift bits so this would probably just get a zero.

This would allow for on-screen representation of what the unshifted key is (without a shift, ctrl, alt state or I guess some keyboards have a CTRL+ALT key? Like maybe French.) I know the part about Quake not being able to represent it with the character set, obviously.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Boring Look@Keyboard/Mouse Button Input

Post by Spike »

Con_Printf("0x%x 0x%x\n", wParam, HIWORD(lParam));

3 down -> 0x33 0x4
3 up -> 0x33 0xc004

shift down -> 0x10 0x36
£ (aka: 3) down -> 0x33 0x4
£ (aka: 3) up -> 0x33 0xc004
shift up -> 0x10 0xc036

same with punctuation, so no real difference between virtual keys and the scan codes, other than the scan codes have an extra bit or two saying that it was released, and the actual values are different.

At the hardware level, the scancode is a somewhat sequentially increasing value, based upon really old keyboard layouts, thus escape is 1, and the number keys come next. Just sequentially allocated based upon the original keyboard layouts before f1 etc were added.
Windows virtual keys seem to be a slightly more ascii compatible mapping of the same scan codes, but only really make any sense for alphanumeric chars. use punctuation and its pointless.
Still, different hardware has different scancodes, so I suppose its useful as a way to work around that.

You can always switch your own keyboard layout to a european one to test it yourself. Beware that windows can switch keymaps automatically between different programs. shift+alt will generally cycle the current keymap, if you get stuck trying to find some key.
Post Reply