Using the Keyboard Device

Each of the devices you can use in DirectInput have their own Device object, much like each graphics card and sound card have their own device objects. There are normally more DirectInput devices than the others though.

Creating a device requires a GUID to be passed in that is the same as the instance GUID you receive when enumerating your devices. It can also be a member of the SystemGuid object if you only want to create the default keyboard or mouse device. The devices that are created are generic enough to retrieve data from any device support by DirectInput.

Our first look at the device will start with the most common device, the keyboard. You will obviously need a device variable to use, so go ahead and declare one now:

 private Device device = null; 

Now, there are two ways you can use DirectInput. You can use a loop (the normal "game loop" mechanism) and get the current state of the device each frame. You can also set DirectInput up to notify you when the device state has changed so that you can update the game's state. This code will investigate the former mechanism first. Add the following initialization function:

 private bool running = true; public void InitializeInput() {     // Create our keyboard device     device = new Device(SystemGuid.Keyboard);     device.SetCooperativeLevel(this, CooperativeLevelFlags.Background |          CooperativeLevelFlags.NonExclusive);     device.Acquire();     while(running)     {         UpdateInputState();         Application.DoEvents();     } } 

As you can see here, this is the entire "input" loop. You first create the device, using the standard keyboard GUID. If you remember from the DirectSound section, you must set the cooperative level of the devices, since they are shared throughout the system. The various flags that can be used (and combined) for the cooperative levels can be seen in Table 15.1.

Table 15.1. DirectInput Cooperative Levels

FLAG

DESCRIPTION

Background

The device can be used in the background, and can be acquired at any time, even if the associated window isn't the active window.

Foreground

The device can only be used if the window is in the foreground. When this window loses focus, any acquired device will be unacquired.

Exclusive

The device requires exclusive access. While acquired exclusively, no other application can acquire the device exclusively. Nonexclusive acquires are still allowed. For security reasons, exclusive and background flags are not allowed to be used together on certain devices, like keyboards and mice.

NonExclusive

The device can be shared among many applications and does not require exclusive access.

NoWindowsKey

Disables the windows key.

For this application you can use the foreground and non-exclusive flags. Before you can actually receive any data from the device, you will first need to acquire it. This allows you to retrieve the data from the device.

Finally, you go into a loop; as long as the application is running, you will update the input state of the device and call DoEvents (to allow the system to stay responsive). Before you write the UpdateInputState method, you will need to have something to hold the text you'll be writing. Create a text box on your form, with the multiline property and read-only property set to true, and the Dock property set to fill. Now you can add the following method to update this textbox:

 private void UpdateInputState() {     // Check the keys currently pressed first     string pressedKeys = "Using GetPressedKeys(): \r\n";     foreach(Key k in device.GetPressedKeys())         pressedKeys += k.ToString() + " ";     textBox1.Text = pressedKeys; } 

Only two last things you need to do before you can try this application out. If you notice, the loop will run forever until the running variable is false, but that variable is never set to false anywhere. You want the application to quit when the form is closed, so you should set the variable to false there:

 protected override void OnClosed(EventArgs e) {     running = false; } 

It might help if you actually call this InitializeInput method sometime as well. Update the main method as follows:

 static void Main() {     using (Form1 frm = new Form1())     {         frm.Show();         frm.InitializeInput();     } } 

With all of these things out of way, you can now run this application. Notice how anytime you press a key, it shows up in the text box. You can also notice that all of the keys are named already, with no extra code required from you. The keys show up only while they are held down, and as soon as you let them go, they disappear from the UI again.

HOLDING DOWN TOO MANY KEYS

Most keyboards can only maintain the state of five or so keys simultaneously. Holding more than this number of keys will result in the extra keys being "ignored."

While you can see that this method works, since there wasn't a game loop running already, the looping seems a little out of place. What would be better is for DirectInput to notify you when the device state has changed so that you can do the checks then. You can start a separate thread to wait for these notifications from DirectInput. Create a new method to initialize input from Listing 15.3.

Listing 15.3 Initializing DirectInput and a Second Thread
 private System.Threading.AutoResetEvent deviceUpdated; private System.Threading.ManualResetEvent appShutdown;  public void InitializeInputWithThread() {     // Create our keyboard device     device = new Device(SystemGuid.Keyboard);     device.SetCooperativeLevel(this, CooperativeLevelFlags.Background |          CooperativeLevelFlags.NonExclusive);     deviceUpdated = new System.Threading.AutoResetEvent(false);     appShutdown = new System.Threading.ManualResetEvent(false);     device.SetEventNotification(deviceUpdated);     System.Threading.Thread threadLoop = new System.Threading.Thread(         new System.Threading.ThreadStart(this.ThreadFunction));     threadLoop.Start();     device.Acquire(); } 

The basic premise of this method is the same as the last one; however, you've declared two wait handle variables. One of these is an AutoResetEvent that you will pass in to DirectInput; the other is a ManualResetEvent that you will use to notify the thread when the application is shutting down. You will also need a thread to sit around and wait for one of these events to be fired, so you create that as well and start that thread. So what does the thread function actually look like?

 private void ThreadFunction() {     System.Threading.WaitHandle[] handles =         { deviceUpdated, appShutdown };     // Continue running this thread until the app has closed     while(true)     {         int index = System.Threading.WaitHandle.WaitAny(handles);         if (index == 0)         {             UpdateInputState();         }         else if (index == 1)         {             return;         }     } } 

This function is pretty much par for the course when dealing with multithreaded applications, and since you're not dealing with writing robust multithreaded code in this book, you can skip most of the mumbo jumbo. If the DirectInput device signals the event to notify you that the device has been updated, you call the UpdateInputState method; if our other event has been fired, return from this function, which will abort the thread.

Just like our looping method, you need to do two more things to "finish" this way. You need to set our event on application shut down by firing that event. Replace the OnClosed override with this one:

 protected override void OnClosed(EventArgs e) {     if (appShutdown != null)         appShutdown.Set(); } 

You also need to modify our main function to call our new initialize method. Update the main function once more as follows:

 static void Main() {     using (Form1 frm = new Form1())     {         frm.Show();         frm.InitializeInputWithThread();         Application.Run(frm);     } } 

OPENING THE DEVICE-SPECIFIC CONTROL PANEL

Just like the manager class, there is a method on the device class called "RunControlPanel" as well. This method will open the device-specific control for that device. If you have a keyboard device created, this method will open the keyboard properties; for a mouse device, this will open the mouse properties. It can be useful to allow calibration or changing of the options.

Most of the time, though, you don't want to get a list of the currently pressed keys; you want to detect if a certain key was pressed. In this scenario, you want to get the state of the keys and check this state against what you're looking for. For example, to see if the escape key was pressed, you would do something like

 KeyboardState state = device.GetCurrent KeyboardState(); if (state[Key.Escape]) {     /* Escape was pressed */ } 


Managed DirectX 9 Graphics and Game Programming, Kick Start
Managed DirectX 9 Kick Start: Graphics and Game Programming
ISBN: B003D7JUW6
EAN: N/A
Year: 2002
Pages: 180
Authors: Tom Miller

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net