BlueMonkMN's picture

OpenTK.Input.Key Numeric Keypad Enter?

It appears that there is no enumeration member to represent the enter key on the numeric keypad. Is this true? Is it intentional? Do some platforms not distinguish between numeric keypad enter and the other enter key?


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

As far as I know, the .so files are links to the real library. On my system (Arch Linux), it is libX11.so -> libX11.so.6 -> libX11.so.6.2.0 and libxkbfile.so ->libxkbfile.so.1 -> libxkbfile.so.1.0.0.

With C/C++ code you can simply use -lX11 or -lxkbfile. With C#, [DllImport("libX11")] and [DllImport("libxkbfile")] should work fine.

BlueMonkMN's picture

Correct me if I'm wrong, but after much reading, I'm slowly coming to the realization that XKB seems designed only to deal with modifier keys and not individual character key presses. I assume that ideally we'd like to be able to have a more straightforward mechanism of monitoring all keys, not just modifier keys. So I think I'll switch to investigating monitoring /dev/input/event1. (How do I know which event* file is a keyboard?)

objarni's picture

Is the problem with I8N combined with modifier left/right keys even solved for windows..? Looks tough to combine those "two worlds" - low-level game access to keyboard state, and international key combination (a high-level OS service).

the Fiddler's picture
BlueMonkMN wrote:

Correct me if I'm wrong, but after much reading, I'm slowly coming to the realization that XKB seems designed only to deal with modifier keys and not individual character key presses. I assume that ideally we'd like to be able to have a more straightforward mechanism of monitoring all keys, not just modifier keys. So I think I'll switch to investigating monitoring /dev/input/event1. (How do I know which event* file is a keyboard?)

The general procedure is that you open the device, use ioctl to get the driver version and read/write data. For some reason, it's surprisingly difficult to find clear documentation on the interface. Here are some links that might be useful:

X11Joystick.cs from OpenTK that implements the necessary pinvokes in class UnsafeNativeMethods. Also check function OpenJoystick on line 129.
Using the Input Subsystem (with sample code - excellent)
http://www.mjmwired.net/kernel/Documentation/input/#263 (describes the event structure)

I have a hunch that Wine will have source code implementing exactly this, but I'm wary of its license.

The biggest difficulty is finding the correct ioctl codes, as they are C macros. For joystick input, I wrote a small C program that outputs the values of joystick ioctls (attached). The blog post above (the one with the sample code) lists all interesting ioctls.

AttachmentSize
test.c582 bytes
BlueMonkMN's picture

Well, this idea didn't get very far at all. Root access is required to access the events at that level, and I don't think most people are going to want to become root just to play a game.

the Fiddler's picture

Not even read-only access, e.g. with cat /dev/input/event0? Not good. Found this in the Panda3D documentation:

Quote:

To use raw mouse input under Linux, the panda program needs to open the device files /dev/input/event*. On many Linux distributions, the permission bits are set such that this is not possible. This is a flaw in these distributions.

There's another X11 extension called XInput (no, not the Microsoft API) that might be able to help. This API mess is starting to get silly.

Edit: Ok, I think there may be a simple solution in the form of XQueryKeymap. If the description is accurate this will return the state of all keyboard keys - which is what we want!

I'm pretty sure there it won't be gotcha-free, but I'm too exhausted to play with it right now.

BlueMonkMN's picture

I tried some complicated declarations first using "out" and "ref" parameters for the keys_return argument of XQueryKeymap, and none of that worked. Finally I just tried this and I'm starting to see results:

   [DllImport("libX11")] public static extern IntPtr XOpenDisplay(string display_name); 
   [DllImport("libX11")] public static extern void XQueryKeymap(IntPtr display, byte[] keys); 
...
   IntPtr display = XOpenDisplay(":0.0");
   byte[] keys = new byte[32];
   XQueryKeymap(display, keys);

BTW, is it correct to hard-code ":0.0"?

the Fiddler's picture

If you are editing OpenTK source, just use API.DefaultDisplay (if I remember the name correctly). Otherwise, you can use XOpenDisplay(null) to open the default display.

The complications arise when you try to use the code over an SSH connection, but you can safely ignore that for now.

BlueMonkMN's picture

OK, I've been kind of preoccupied with other things, but I finally got back to this for a little while. Here's what I've got so far:

enum KeyCodes
{
	Escape = 9,
	Digit1,
	Digit2,
	Digit3,
	Digit4,
	Digit5,
	Digit6,
	Digit7,
	Digit8,
	Digit9,
	Digit0,
	Minus,
	Equal,
	Backspace,
	Tab,
	Q,
	W,
	E,
	R,
	T,
	Y,
	U,
	I,
	O,
	P,
	LeftBracket,
	RightBracket,
	Enter,
	LeftCtrl,
	A,
	S,
	D,
	F,
	G,
	H,
	J,
	K,
	L, 
	SemiColon,
	Apostrophe,
	Grave,
	LeftShift,
	Backslash,
	Z,
	X,
	C,
	V,
	B,
	N,
	M,
	Comma,
	Period,
	Slash,
	RightShift,
	KeyPadAsterisk,
	LeftAlt,
	Space,
	CapsLock,
	F1,
	F2,
	F3,
	F4,
	F5,
	F6,
	F7,
	F8,
	F9,
	F10,
	NumLock,
	ScrollLock,
	KeyPad7,
	KeyPad8,
	KeyPad9,
	KeyPadMinus,
	KeyPad4,
	KeyPad5,
	KeyPad6,
	KeyPadPlus,
	KeyPad1,
	KeyPad2,
	KeyPad3,
	KeyPad0,
	KeyPadDot,
	F11=95,
	F12,
	Home,
	Up,
	PageUp,
	Left,
	Right=Left+2,
	End,
	Down,
	PageDown,
	Insert,
	Delete,
	KeyPadEnter,
	RightCtrl,
	Pause,
	SysRq,
	PrintScreen=SysRq,
	KeyPadSlash,
	RightAlt,
	LeftWin=115,
	RightWin,
	ContextMenu
}
class MainClass
{
	[DllImport("libX11")] public static extern IntPtr XOpenDisplay(string display_name); 
	[DllImport("libX11")] public static extern void XQueryKeymap(IntPtr display, System.UInt32[] keys); 
 
	public static void Main(string[] args)
	{
		XStuff();
	}
		public static void XStuff()
	{
		IntPtr display = XOpenDisplay(null);
		System.UInt32[] keys = new System.UInt32[8];
		System.UInt32[] keysOld = new System.UInt32[8];
		System.UInt32[] keysDiff = new System.UInt32[8];
		int downKey;
		do
		{
			XQueryKeymap(display, keys);
			for (int i=0; i < keys.Length; i++)
				keysDiff[i] = keysOld[i] ^ keys[i];
			keys.CopyTo(keysOld, 0);
			downKey = GetFirstBit(keysDiff);
			if (downKey > 0)
				Console.WriteLine("{0}={1} ", downKey, ((KeyCodes)downKey).ToString());
			System.Threading.Thread.Sleep(100);
		} while(downKey != (int)KeyCodes.Escape);
	}
 
	private static int GetFirstBit(System.UInt32[] data)
	{
		for(int arrayIdx=0; arrayIdx<data.Length; arrayIdx++)
		{
			if(data[arrayIdx] != 0)
			{
				for(int bitIdx = 0; bitIdx < 32; bitIdx++)
				{
					if((data[arrayIdx] & (1u << bitIdx)) != 0)
					{
						return (arrayIdx << 5) + bitIdx;
					}
				}
			}
		}
		return 0;
	}
}

next question is, how reliable are those key codes? Are they specific to my keyboard? They don't seem to match any include file. They follow a similar sequence to the key codes in include/linux/input.h, but they are offset by 8, and the sequence is quite different at the end. Is there some sort of configuration file in /etc/ or something that lists key codes for the current keyboard?

the Fiddler's picture

I was about to add keyboard support to the new GameWindow and you posted the code just in time. Great synchronization! :)

The key codes don't seem terribly accurate here, e.g. pressing right control here, results in a PageDown key and keypad enter -> Down. How did you create the enum?

In OpenTK, I use latin-1 X11 keysyms, XGetKeyboardMapping and XLookupKeysym to map event keycodes to keysyms. Truth be told, I haven't tested this approach on non-latin keyboards, but it seems to work relatively well when I switch between US, Colemak and Greek layouts here.

You can find the complete keysym list in X11/keysymdef.h and a C# translation in Source/OpenTK/Platform/X11/XKeyMap.cs.