[.net] [XNA] Getting Text from keyboard

Started by
19 comments, last by Headkaze 16 years ago
Hello, Making a small online game in C#, using the XNA framework. I'd like to make it so you 'chat' in this game (using the keyboard)...but so far..I can't find any good way to get text that the user types in...it seems my only options are

KeyboardState keyboard = Keyboard.GetState();
keyboard.IsKeyPressed(Key);
There is no way that would ever work..unless i'm suppose to poll every key. the other way was the

keyboard.GetPressedKeys();
which doesn't give me help either. There has to be some simple way to get text that the user types in. For example if they wanted to chat..."Hi My name is Kyle"....how would I grab the text that they entered? I hope I made this clear enough.. Thanks, ArchG
Advertisement
Something like that is far beyond what XNA is. At it's core XNA is just a fancy wrapper around DirectX and XInput. So yes, you will have to manually check each key and add it to the end of a string. It sucks, but just make it a class that you can reuse and then it won't seem so bad.
thanks for the quick reply.

I found a solution that benryves made, it's in his journal if anyone is interested.
What you are looking for is Event Based Input rather than Immediate Data. My project, Thrust, has a few classes in the Thrust.Input library to handle these types of events for the keyboard, mouse and gamepad.
Here's some simple sample code I put together to address this issue, it needs to be refactored but should show a decent way to handle keyboard input.

private void UpdateInput()
{
oldKeyboardState = currentKeyboardState;
currentKeyboardState = Keyboard.GetState();

Keys[] pressedKeys;
pressedKeys = currentKeyboardState.GetPressedKeys();

foreach (Keys key in pressedKeys)
{
if( oldKeyboardState.IsKeyUp(key) )
{
if (key == Keys.Back) // overflows
textString = textString.Remove(textString.Length - 1, 1);
else
if (key == Keys.Space)
textString = textString.Insert(textString.Length, " ");
else
textString += key.ToString();
}
}
}
-------------------------------------------------------- What''s the difference between engineers and architects? Engineers build weapons; architects build targets. --------------------------------------------------------
Quote:Original post by NickGravelyn
Something like that is far beyond what XNA is. At it's core XNA is just a fancy wrapper around DirectX and XInput. So yes, you will have to manually check each key and add it to the end of a string. It sucks, but just make it a class that you can reuse and then it won't seem so bad.


Eh? Even DirectX has support for event based input (kinda; directInput had a toggle to do BufferedInput, which completely solves the cons about poll-based input. Furthermore, DirectX does not have methods to create a Window, DirectX programmers had to create their window with Win32 and then build a DirectX context into it -- XNA does this for you.

Quote:Original post by ArchG
For example if they wanted to chat..."Hi My name is Kyle"....how would I grab the text that they entered?


You can't.

Currently, XNA sucks for getting input, even for simple games (like Tetris); it's like a trademark suckyness similar to RPGMaker2000 games wherein you find yourself saying "this game must be made in RPGMaker2000 because of {some suckyness}. You can alleviate this by building a event-based system overtop of the poll-based system (shoving the frequncy reader on a seperate thread), but that won't solve all the issues. Lack of event-based input is one of the primary issues I've had with XNA. The best solution thus far is to embed XNA's MDX components into a WinForms application, and use WinForms as the framework for event-based input. This way ditch everything that differentiates XNA from MDX; no more XNA Input, no more XNA Window, and no more XNA Game class. You'll have to resort back to handling your own devicelost exceptions, and the like, just as you would with MDX.

benryves has a couple solutions. One is embedding XNA into a WinForm (which I do aswell), another is a GlobalKeyHook. Considering both of these solutions rely on hacks&/otherwise_abusive_methods I'd go with the WinForms if but for the sole reason that you won't find yourself locked out of other important features (Such as Input Method Editors).

[Edited by - Thevenin on August 21, 2007 6:22:28 PM]
Quote:Original post by Thevenin
Quote:Original post by NickGravelyn
Something like that is far beyond what XNA is. At it's core XNA is just a fancy wrapper around DirectX and XInput. So yes, you will have to manually check each key and add it to the end of a string. It sucks, but just make it a class that you can reuse and then it won't seem so bad.


Eh? Even DirectX has support for event based input (kinda; directInput had a toggle to do BufferedInput, which completely solves the cons about poll-based input. Furthermore, DirectX does not have methods to create a Window, DirectX programmers had to create their window with Win32 and then build a DirectX context into it -- XNA does this for you.

Like I said, it's a fancy wrapper. It tosses in the game loop, window management, and a few other goodies.

Quote:Currently, XNA sucks for getting input, even for simple games (like Tetris); it's like a trademark suckyness similar to RPGMaker2000 games wherein you find yourself saying "this game must be made in RPGMaker2000 because of {some suckyness}. You can alleviate this by building a event-based system overtop of the poll-based system (shoving the frequncy reader on a seperate thread), but that won't solve all the issues. Lack of event-based input is one of the primary issues I've had with XNA. The best solution thus far is to embed XNA's MDX components into a WinForms application, and use WinForms as the framework for event-based input. This way ditch everything that differentiates XNA from MDX; no more XNA Input, no more XNA Window, and no more XNA Game class. You'll have to resort back to handling your own devicelost exceptions, and the like, just as you would with MDX.

benryves has a couple solutions. One is embedding XNA into a WinForm (which I do aswell), another is a GlobalKeyHook. Considering both of these solutions rely on hacks&/otherwise_abusive_methods I'd go with the WinForms if but for the sole reason that you won't find yourself locked out of other important features (Such as Input Method Editors).

I think the decision to go with a polling system instead of event based is that it simply doesn't make much sense for a vast majority of games. Beyond that it then becomes harder to get those inputs in various places. Right now you can call Keyboard.GetState() anywhere you are using the Input namespace. If you had an event system, you'd have to tie in events for anything reading the input or start passing around the keyboard state. I believe XNA is better with the polling system.

Beyond that I was able to build a simple event based system with key repeats and code to then interpret the inputs into text in a matter of about a half hour. It's not too challenging.

I would love to see how other people have implemented reading keyboard input in XNA. I'll post some updated code one I add some more functionality and error checking.
-------------------------------------------------------- What''s the difference between engineers and architects? Engineers build weapons; architects build targets. --------------------------------------------------------
I'm feeling generous. Here's a dump of code from my project (caution: may be missing required definitions - I almost forgot the Win32 DllImport stuff).

It should be noted that the key-to-char conversion is a pain and the snippets below is only known to work for QWERTY English. I'm not sure if Dvorak layouts work or not.

The code below is broken into two parts: A KeyboardTracker class that tracks keydown / keyup changes, and a few functions from a GuiManager class that convert that into events similar to how Systems.Windows.Forms control keyboard handling works.

namespace Win32{  public enum VKey  {    // Tons of other VK_ codes removed.    CAPITAL =       0x14,    NUMLOCK =       0x90,    SCROLL =        0x91,  }    public static class User  {    [DllImport("user32.dll")]    public static extern short GetKeyState(VKey vkey);  }}public enum KeyState{	Up, Down, NewUp, NewDown}public class KeyboardTracker{	static TimeSpan keyDelayTime;	static TimeSpan keyRepeatTime;		KeyState[] keyStates;	Keys lastPressedKey;	TimeSpan nextRepeatTime;	int repeatSignal;		bool capsLock;	bool numLock;	bool scrollLock;		public bool AltDown	{		get { return keyStates[(int)Keys.LeftAlt] == KeyState.Down || keyStates[(int)Keys.RightAlt] == KeyState.Down; }	}		public bool CtrlDown	{		get { return keyStates[(int)Keys.LeftControl] == KeyState.Down || keyStates[(int)Keys.RightControl] == KeyState.Down; }	}	public bool ShiftDown	{		get { return keyStates[(int)Keys.LeftShift] == KeyState.Down || keyStates[(int)Keys.RightShift] == KeyState.Down; }	}		public bool CapsLock   { get { return capsLock;   } }	public bool NumLock    { get { return numLock;    } }	public bool ScrollLock { get { return scrollLock; } }		public KeyState[] KeyStates	{		get { return keyStates; }	}		public Keys LastPressedKey	{		get { return lastPressedKey; }	}		public int RepeatSignal	{		get { return repeatSignal; }	}		static KeyboardTracker()	{		keyDelayTime  = new TimeSpan(0, 0, 0, 0, 250 + 250*SystemInformation.KeyboardDelay);		keyRepeatTime = new TimeSpan(0, 0, 0, 0, 33 + (400-33) * (31 - SystemInformation.KeyboardSpeed) / 31);	}		public KeyboardTracker()	{		Keys[] values = (Keys[])Enum.GetValues(typeof(Keys));				int maxIndex = 0;		foreach (Keys key in values)		{			if ((int)key > maxIndex)				maxIndex = (int)key;		}				lastPressedKey = Keys.None;				keyStates = new KeyState[maxIndex+1];	}		public void Update(GameTime gameTime)	{		KeyState[] tempStates = new KeyState[keyStates.Length];				KeyboardState state = Keyboard.GetState();				capsLock   = (Win32.User.GetKeyState(Win32.VKey.CAPITAL) & 1) != 0;		numLock    = (Win32.User.GetKeyState(Win32.VKey.NUMLOCK) & 1) != 0;		scrollLock = (Win32.User.GetKeyState(Win32.VKey.SCROLL)  & 1) != 0;				Keys[] keys = state.GetPressedKeys();				for (int i=0; i<keys.Length; ++i)		{			tempStates[(int)keys] = KeyState.Down;		}				repeatSignal = 0;				for (int i=0; i<keyStates.Length; ++i)		{			switch (keyStates)			{				case KeyState.Up:					if (tempStates == KeyState.Down) keyStates = KeyState.NewDown;					break;								case KeyState.Down:					if (tempStates == KeyState.Up) keyStates = KeyState.NewUp;					break;								case KeyState.NewDown:					if (tempStates == KeyState.Down) keyStates = KeyState.Down;					else                                keyStates = KeyState.NewUp;					break;								case KeyState.NewUp:					if (tempStates == KeyState.Up) keyStates = KeyState.Up;					else                              keyStates = KeyState.NewDown;					break;			}						if (keyStates == KeyState.NewDown)			{				Keys newPressedKey = (Keys)i;								if (newPressedKey != lastPressedKey)				{					nextRepeatTime = gameTime.TotalRealTime + keyDelayTime;					repeatSignal = 1;				}				lastPressedKey = newPressedKey;			}		}				if (keyStates[(int)lastPressedKey] == KeyState.Up || keyStates[(int)lastPressedKey] == KeyState.NewUp)		{			lastPressedKey = Keys.None;		}		else		{			while (gameTime.TotalRealTime > nextRepeatTime)			{				repeatSignal++;				nextRepeatTime += keyRepeatTime;			}		}	}		public static char TranslateChar(Keys key, bool shift, bool capsLock, bool numLock)	{		switch (key)		{			case Keys.A: return TranslateAlphabetic('a', shift, capsLock);			case Keys.B: return TranslateAlphabetic('b', shift, capsLock);			case Keys.C: return TranslateAlphabetic('c', shift, capsLock);			case Keys.D: return TranslateAlphabetic('d', shift, capsLock);			case Keys.E: return TranslateAlphabetic('e', shift, capsLock);			case Keys.F: return TranslateAlphabetic('f', shift, capsLock);			case Keys.G: return TranslateAlphabetic('g', shift, capsLock);			case Keys.H: return TranslateAlphabetic('h', shift, capsLock);			case Keys.I: return TranslateAlphabetic('i', shift, capsLock);			case Keys.J: return TranslateAlphabetic('j', shift, capsLock);			case Keys.K: return TranslateAlphabetic('k', shift, capsLock);			case Keys.L: return TranslateAlphabetic('l', shift, capsLock);			case Keys.M: return TranslateAlphabetic('m', shift, capsLock);			case Keys.N: return TranslateAlphabetic('n', shift, capsLock);			case Keys.O: return TranslateAlphabetic('o', shift, capsLock);			case Keys.P: return TranslateAlphabetic('p', shift, capsLock);			case Keys.Q: return TranslateAlphabetic('q', shift, capsLock);			case Keys.R: return TranslateAlphabetic('r', shift, capsLock);			case Keys.S: return TranslateAlphabetic('s', shift, capsLock);			case Keys.T: return TranslateAlphabetic('t', shift, capsLock);			case Keys.U: return TranslateAlphabetic('u', shift, capsLock);			case Keys.V: return TranslateAlphabetic('v', shift, capsLock);			case Keys.W: return TranslateAlphabetic('w', shift, capsLock);			case Keys.X: return TranslateAlphabetic('x', shift, capsLock);			case Keys.Y: return TranslateAlphabetic('y', shift, capsLock);			case Keys.Z: return TranslateAlphabetic('z', shift, capsLock);						case Keys.D0: return (shift) ? ')' : '0';			case Keys.D1: return (shift) ? '!' : '1';			case Keys.D2: return (shift) ? '@' : '2';			case Keys.D3: return (shift) ? '#' : '3';			case Keys.D4: return (shift) ? '$' : '4';			case Keys.D5: return (shift) ? '%' : '5';			case Keys.D6: return (shift) ? '^' : '6';			case Keys.D7: return (shift) ? '&' : '7';			case Keys.D8: return (shift) ? '*' : '8';			case Keys.D9: return (shift) ? '(' : '9';						case Keys.Add:      return '+';			case Keys.Divide:   return '/';			case Keys.Multiply: return '*';			case Keys.Subtract: return '-';			case Keys.Space: return ' ';			case Keys.Tab:   return '\t';			case Keys.Decimal: if (numLock && !shift) return '.'; break;			case Keys.NumPad0: if (numLock && !shift) return '0'; break;			case Keys.NumPad1: if (numLock && !shift) return '1'; break;			case Keys.NumPad2: if (numLock && !shift) return '2'; break;			case Keys.NumPad3: if (numLock && !shift) return '3'; break;			case Keys.NumPad4: if (numLock && !shift) return '4'; break;			case Keys.NumPad5: if (numLock && !shift) return '5'; break;			case Keys.NumPad6: if (numLock && !shift) return '6'; break;			case Keys.NumPad7: if (numLock && !shift) return '7'; break;			case Keys.NumPad8: if (numLock && !shift) return '8'; break;			case Keys.NumPad9: if (numLock && !shift) return '9'; break;						case Keys.OemBackslash:     return shift ? '|' : '\\';			case Keys.OemCloseBrackets: return shift ? '}' : ']'; 			case Keys.OemComma:         return shift ? '<' : ','; 			case Keys.OemMinus:         return shift ? '_' : '-'; 			case Keys.OemOpenBrackets:  return shift ? '{' : '['; 			case Keys.OemPeriod:        return shift ? '>' : '.'; 			case Keys.OemPipe:          return shift ? '|' : '\\';			case Keys.OemPlus:          return shift ? '+' : '='; 			case Keys.OemQuestion:      return shift ? '?' : '/'; 			case Keys.OemQuotes:        return shift ? '"' : '\'';			case Keys.OemSemicolon:     return shift ? ':' : ';'; 			case Keys.OemTilde:         return shift ? '~' : '`'; 		}				return (char)0;	}		public static char TranslateAlphabetic(char baseChar, bool shift, bool capsLock)	{		return (capsLock ^ shift) ? char.ToUpper(baseChar) : baseChar;	}}


And the relevant sections of a System.Windows.Forms GUI manager emulator...

public class KeyEventArgs{	public bool capsLock;	public bool numLock;	public bool scrollLock;		public bool alt;	public bool ctrl;	public bool shift;		public char character;	public readonly Keys key;		public KeyEventArgs(Keys key, bool alt, bool ctrl, bool shift, bool capsLock, bool numLock, bool scrollLock)	{		this.key = key;		this.alt = alt;		this.ctrl = ctrl;		this.shift = shift;		this.capsLock = capsLock;		this.numLock = numLock;		this.scrollLock = scrollLock;				character = KeyboardTracker.TranslateChar(key, shift, capsLock, numLock);	}}public class KeyPressEventArgs{	public bool handled;	public char keychar;	public Keys key;		public KeyPressEventArgs(Keys key, char keychar)	{		this.key = key;		this.keychar = keychar;	}}public class PreviewKeyDownEventArgs{	KeyEventArgs kea;	bool isInputKey;		public KeyEventArgs KeyEventArgs	{		get { return kea; }	}		public bool IsInputKey	{		get { return isInputKey; }		set { isInputKey = value; }	}		public PreviewKeyDownEventArgs(KeyEventArgs kea)	{		this.kea = kea;	}}public delegate void KeyEventHandler(object sender, KeyEventArgs kea);public delegate void KeyPressEventHandler(object sender, KeyPressEventArgs kea);public delegate void PreviewKeyDownEventHandler(object sender, PreviewKeyDownEventArgs pkea);// The following code is snippets from class 'GuiManager'// (which has so many other methods that I just ripped the keyboard functions out by themselves).// The GuiManager is a class derived from a System.Windows.Forms.Control look-alike class// from which I derive everything in my GUI system.  The GuiManager originally was// a stand-alone class, but composite child control handling led me to just derive// from the Control class instead.// An important part to note is that UpdateKeyboard should only be called// if the application window has focus / is active.KeyboardTracker keyboardTracker;void UpdateKeyboard(GameTime gameTime){	keyboardTracker.Update(gameTime);		if (focusControl != null)	{		for (int i=0; i<keyboardTracker.KeyStates.Length; ++i)		{			switch (keyboardTracker.KeyStates)			{				case KeyState.NewUp:OnKeyUp(new KeyEventArgs((Keys)i, keyboardTracker.AltDown, keyboardTracker.CtrlDown, keyboardTracker.ShiftDown,keyboardTracker.CapsLock, keyboardTracker.NumLock, keyboardTracker.ScrollLock));					break;								case KeyState.NewDown:					if (keyboardTracker.LastPressedKey != (Keys)i) // Prevent two KeyDowns for the newest key.					{OnKeyDown(new KeyEventArgs((Keys)i, keyboardTracker.AltDown, keyboardTracker.CtrlDown, keyboardTracker.ShiftDown,keyboardTracker.CapsLock, keyboardTracker.NumLock, keyboardTracker.ScrollLock));					}					break;			}		}				// Key Repeats		if (keyboardTracker.LastPressedKey != Keys.None)		{char character = KeyboardTracker.TranslateChar(keyboardTracker.LastPressedKey, keyboardTracker.ShiftDown, keyboardTracker.CapsLock, keyboardTracker.NumLock);						for (int i=0; i<keyboardTracker.RepeatSignal; ++i)			{OnKeyDown(new KeyEventArgs(keyboardTracker.LastPressedKey, keyboardTracker.AltDown, keyboardTracker.CtrlDown, keyboardTracker.ShiftDown,keyboardTracker.CapsLock, keyboardTracker.NumLock, keyboardTracker.ScrollLock));								if (character != '\0')				{OnKeyPress(new KeyPressEventArgs(keyboardTracker.LastPressedKey, character));				}			}		}	}}public override void OnKeyUp(KeyEventArgs kea){	if (focusControl.IsInputKey(kea.key))	{		focusControl.OnKeyUp(kea);	}}public override void OnKeyDown(KeyEventArgs kea){	if (focusControl.IsInputKey(kea.key))	{		focusControl.OnKeyDown(kea);	}	else	{		if (kea.key == Keys.Tab)		{			Control newTabStop = GetNextTabStop(focusControl, !kea.shift);						if (newTabStop != null)			{				Focus = newTabStop;			}		}	}}public override void OnKeyPress(KeyPressEventArgs kpea){	if (focusControl.IsInputKey(kpea.key))	{		focusControl.OnKeyPress(kpea);	}}



(edit) Oops... forgot that the key preview stuff wasn't done yet. I removed it from the snippets.

(edit2) Trying to make the stupid 'source' blocks stay limited horizontally.
I talked to Thevenin about this whole thing tonight, and decided to fix it in the simplest, most direct way I could think of. Took a little while to install the various XNA bits, but that plus a half hour of poking around with some code finally solved things. Here's a mostly cleaned up version of the code, although it's still very much a first draft.
using System;using System.Runtime.InteropServices;using Microsoft.Xna.Framework;using Microsoft.Xna.Framework.Input;namespace EventInput{	public class CharacterEventArgs : EventArgs	{		private readonly char character;		private readonly int lParam;		public CharacterEventArgs( char character, int lParam )		{			this.character = character;			this.lParam = lParam;		}		public char Character		{			get { return character; }		}		public int Param		{			get { return lParam; }		}		public int RepeatCount		{			get { return lParam & 0xffff; }		}		public bool ExtendedKey		{			get { return ( lParam & ( 1 << 24 ) ) > 0; }		}		public bool AltPressed		{			get { return ( lParam & ( 1 << 29 ) ) > 0; }		}		public bool PreviousState		{			get { return ( lParam & ( 1 << 30 ) ) > 0; }		}		public bool TransitionState		{			get { return ( lParam & ( 1 << 31 ) ) > 0; }		}	}	public class KeyEventArgs : EventArgs	{		private Keys keyCode;		public KeyEventArgs( Keys keyCode )		{			this.keyCode = keyCode;		}		public Keys KeyCode		{			get { return keyCode; }		}	}	public delegate void CharEnteredHandler( object sender, CharacterEventArgs e );	public delegate void KeyEventHandler( object sender, KeyEventArgs e );	public static class EventInput	{		/// <summary>		/// Event raised when a character has been entered.		/// </summary>		public static event CharEnteredHandler CharEntered;		/// <summary>		/// Event raised when a key has been pressed down. May fire multiple times due to keyboard repeat.		/// </summary>		public static event KeyEventHandler KeyDown;		/// <summary>		/// Event raised when a key has been released.		/// </summary>		public static event KeyEventHandler KeyUp;		delegate IntPtr WndProc( IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam );		static bool initialized;		static IntPtr prevWndProc;		static WndProc hookProcDelegate;		static IntPtr hIMC;		//various Win32 constants that we need		const int GWL_WNDPROC = -4;		const int WM_KEYDOWN = 0x100;		const int WM_KEYUP = 0x101;		const int WM_CHAR = 0x102;		const int WM_IME_SETCONTEXT = 0x0281;		const int WM_INPUTLANGCHANGE = 0x51;		const int WM_GETDLGCODE = 0x87;		const int WM_IME_COMPOSITION = 0x10f;		const int DLGC_WANTALLKEYS = 4;		//Win32 functions that we're using		[DllImport( "Imm32.dll" )]		static extern IntPtr ImmGetContext( IntPtr hWnd );		[DllImport( "Imm32.dll" )]		static extern IntPtr ImmAssociateContext( IntPtr hWnd, IntPtr hIMC );		[DllImport( "user32.dll" )]		static extern IntPtr CallWindowProc( IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam );		[DllImport( "user32.dll" )]		static extern int SetWindowLong( IntPtr hWnd, int nIndex, int dwNewLong );		/// <summary>		/// Initialize the TextInput with the given GameWindow.		/// </summary>		/// <param name="window">The XNA window to which text input should be linked.</param>		public static void Initialize( GameWindow window )		{			if( initialized )				throw new InvalidOperationException( "TextInput.Initialize can only be called once!" );			hookProcDelegate = new WndProc( HookProc );			prevWndProc = (IntPtr) SetWindowLong( window.Handle, GWL_WNDPROC,				(int) Marshal.GetFunctionPointerForDelegate( hookProcDelegate ) );			hIMC = ImmGetContext( window.Handle );			initialized = true;		}		static IntPtr HookProc( IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam )		{			IntPtr returnCode = CallWindowProc( prevWndProc, hWnd, msg, wParam, lParam );			switch( msg )			{				case WM_GETDLGCODE:					returnCode = (IntPtr) ( returnCode.ToInt32() | DLGC_WANTALLKEYS );					break;				case WM_KEYDOWN:					if( KeyDown != null )						KeyDown( null, new KeyEventArgs( (Keys) wParam ) );					break;				case WM_KEYUP:					if( KeyUp != null )						KeyUp( null, new KeyEventArgs( (Keys) wParam ) );					break;				case WM_CHAR:					if( CharEntered != null )						CharEntered( null, new CharacterEventArgs( (char) wParam, lParam.ToInt32() ) );					break;				case WM_IME_SETCONTEXT:					if( wParam.ToInt32() == 1 )						ImmAssociateContext( hWnd, hIMC );					break;				case WM_INPUTLANGCHANGE:					ImmAssociateContext( hWnd, hIMC );					returnCode = (IntPtr) 1;					break;			}			return returnCode;		}	}}

So, general things of note:
* It picks up key up/down messages, and it's event based. It's also buffered, so you won't lose presses regardless of poll frequency.
* The CharEntered event does full textual translation. It understands keyboard layouts, it's aware of modifiers like shift, etc. If you type a capital letter, you get a capital letter.
* The whole thing is IME enabled. In other words, East Asian languages will function properly.

Usage is about as simple as it gets. Call EventInput.Initialize from your Game class initialize, and pass this.Window. Hook the events from there and you're done. There's nothing to create or keep track of, since it's a single static class.

[Edited by - Promit on August 22, 2007 4:44:06 PM]
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.

This topic is closed to new replies.

Advertisement