Abstracting Input in Win32

Started by
12 comments, last by Evil Steve 15 years, 3 months ago
I am trying to get keyboard and mouse input from the Win32 message pump to update my data, graphics and audio nicely. I figured I should create a library for the input classes (mouse, keyboard, etc) and have the message handlers, called by WndProc, update the input classes. Afterward, the data could query the input classes for the results to get updated. Does this sound reasonable? Are there any design patterns that might help accomplish this (e.g. command, visitor, etc.)? Is there another more standard way I should know about? I haven't implemented anything like this for a game/tool in Win32 and would like to know "the right way." Thanks
Advertisement

why you don't want to use direct input instead of win32 messages? it gives more power.


Quote:Original post by nfnm

why you don't want to use direct input instead of win32 messages? it gives more power.


I am making a game tool, not a game, so the input will be interacting with buttons, menus, etc. as well as the D3D window. I have heard using DirectInput in combination with the Win32 message pump is a real head ache. Can you bypass the Win32 message pump and just use DirectInput?
Win32 applications receive input via message callbacks. As you know these are processed by the WndProc. My WndProc is a static function member of my win32 window object (Window), the Window pointer is stored in the win32 user data.

The Window allows the user to store an IWindowInput interface pointer. It is this IWindowInput which receives the keyboard and mouse inputs from win32.

class IWindowInput{public:  virtual ~IWindowInput() = 0;  enum KeyCodes { KEY_ESCAPE, /*include rest here*/ };  enum Button { BUTTON_LEFT, BUTTON_RIGHT, BUTTON_CENTER };  //these return true if handled  virtual bool KeyDown(KeyCodes keycode){ return false; }  virtual bool KeyUp(KeyCodes keycode){ return false; }  virtual bool MouseDown(Button button, int x, int y){ return false; }  virtual bool MouseUp(Button button, int x, int y){ return false; }};//Example usage:class Application : public IWindowInput{public:  Application() { window.SetInputHandler(this); }  virtual bool KeyDown(IWindowInput::Keycodes keycode)  {     if (keycode == IWindowInput::KEY_ESCAPE)      {       exit();       return true;     }     return false;  }private:  Win32Window window;};


So that explains the low level abstraction, pushing messages into the system. The next step is efficiently sharing and using the current state of the input devices.

My input system is a member of the Application, and all the input messages are basically passed directly into this system. Internally the input system may acquire additional input devices such as joysticks, gamepads, anything else that isn't a window message. It could even acquire the mice and keyboards directly via directinput or something.

Anyways, there is 1 major method of the input system. I call it "LockInput." LockInput takes the current state of the input devices, either updated via messages or polling the devices directly, and it applys logic based on the previous state of the devices to generate a couple output structures includeing Pressed, Held, Released, Current. These are child objects to each input device, for example:

inputManager.LockInput();
const Mouse::State &mouse_held = inputManager.GetMouse(0).GetHeld();
const Keyboard::State &keys_pressed = inputManager.GetKeyboard(0).GetPressed();

You could do all kinds of things with how you actually retrieve the input devices but you get the idea I think.

(for ex: inputManager.GetDevice<Mouse>(0)... )

If you implement boolean operators for your State structures then you can do this pretty easily.

void Device::LockInput(){  pressed_state = !prev_state && current_state;  released_state = prev_state && !current_state;  held_state = prev_state && current_state;  prev_state = current_state;}


[Edited by - bzroom on December 20, 2008 3:02:22 PM]
Quote:I have heard using DirectInput in combination with the Win32 message pump is a real head ache.

There's no reason why this should be overly difficult, you just need to have a common denominator that these two systems boil into.

Quote:Can you bypass the Win32 message pump and just use DirectInput?

Definitely. You'll need the message pump to keep your window alive and respond to other system events. But for input, you can ignore the win32 messages completely. In this case, ignore the first half of my previous post.

Quote:I am making a game tool, not a game, so the input will be interacting with buttons, menus, etc. as well as the D3D window.

I hope for this you'd decide to use .NET or wxWidgets or something. If you're going to roll your own then the win32 messages / direct input will set you right.. Your input handler from one of those gui libraries would be a reasonable place to inject your messages into the application from. Wether it's a win32 wndproc or a MouseDownEvent handler in .net, it's going to be the same after that.
Direct Input, is longer supported by Microsoft and it just wraps the Win32 messages anyways. Using it is just putting another layer thats pretty much useless and can actually slow you down slightly.

Just use Win32 or make your own OS independent class that handles input and write OS specific code, like a lot of people do (who support multiple platforms).

"Those who would give up essential liberty to purchase a little temporary safety deserve neither liberty nor safety." --Benjamin Franklin

Quote:I hope for this you'd decide to use .NET or wxWidgets or something.


I would like to use .NET but I will be needing to embed D3D windows in the app and managed DirectX is no longer supported by Microsoft. AFAIK, apart from XNA (which is pretty high level from what I understand) there is no Microsoft supported method for integrating D3D windows in managed apps. There is SlimDX but that is contributed to by volunteers with day jobs, so the support can not be as complete as for strictly Microsoft libraries. Even the SlimDX wiki says this.
Is there a reason no one mentioned this?

BYTE keyState[256];BYTE keyPrevState[256];POINT cursor;void init() {	ZeroMemory(keyState, sizeof(keyState));	ZeroMemory(keyPrevState, sizeof(keyPrevState));	ZeroMemory(&cursor, sizeof(cursor));}void update() {	RECT clientRect;	GetClientRect(window, &clientRect);	//	GetCursorPos(&cursor);	ScreenToClient(window, &cursor);	memcpy(&keyPrevState, &keyState, sizeof(keyPrevState));	GetKeyboardState(keyState);}bool keyDown(int k) {	return (keyState[k] & 0x80) && 1;}bool keyReleased(int k) {	return (!(keyState[k] & 0x80) && (keyPrevState[k] & 0x80));}bool keyTyped(int k) {	return ((keyState[k] & 0x80) && !(keyPrevState[k] & 0x80));}
Quote:Original post by impulsionaudio
I would like to use .NET but I will be needing to embed D3D windows in the app and managed DirectX is no longer supported by Microsoft. AFAIK, apart from XNA (which is pretty high level from what I understand) there is no Microsoft supported method for integrating D3D windows in managed apps.


This is not a problem. Using C++ CLI, managed C++, with unmanaged directx is fine. Say you want your rendering to be drawn to a picturebox in .net (C++ windows application). You'll use the myPictureBox->Handle.ToPointer() to create your d3d device. Basically, unmanaged and managed c++ can live happily along side each other. Having never tried it with a directx input library, i can't say it'll be as easy for input. But I'm going to have to believe it is.

But as mentioned Directinput is no more. I believe it is XInput these days.

Also anda pointed out a good way to use the win32 api in a polling method (without messages).
Quote:This is not a problem. Using C++ CLI, managed C++, with unmanaged directx is fine. Say you want your rendering to be drawn to a picturebox in .net (C++ windows application). You'll use the myPictureBox->Handle.ToPointer() to create your d3d device. Basically, unmanaged and managed c++ can live happily along side each other. Having never tried it with a directx input library, i can't say it'll be as easy for input. But I'm going to have to believe it is.


This is very interesting. I am going to have to give this a try.

This topic is closed to new replies.

Advertisement