Search Tips Advanced Search |
| Delphi Graphics and Game Programming Exposed with DirectX 7.0 by John Ayres Wordware Publishing, Inc. ISBN: 1556226373 Pub Date: 12/01/99
Search this book:
Previous | Table of Contents | Next
#### Keyboard Input Of all input devices, the keyboard is perhaps the least complex. Having been the standard for input since almost the dawn of computers themselves, reading keyboard input is very straightforward and easy. DirectInput approaches the keyboard not as a text input device, but as a gamepad with many buttons (according to the DirectInput documentation). As such, you gain the benefit of being able to differentiate between the left and right versions of identical keyboard buttons, such as Shift, Alt, and Ctrl. However, DirectInput completely ignores such control panel settings as repeat rate and other keyboard properties. Thus, pressing a key registers as a single key press, regardless of how long it has been held down. Similarly, releasing a key also registers as a singular event. Therefore, you must specifically write your code to respond to a key being held down, starting when a key press is noted and ending when the key has been detected as released (if necessary). Windows Functions Windows provides developers with the GetAsyncKeyState function, which is very similar to retrieving immediate keyboard data using DirectInput. GetAsyncKeyState simply indicates if a specific key is pressed or released at the time of the function call. GetAsyncKeyState is defined as:
    function GetAsyncKeyState(

    vKey: Integer             // a virtual key code

    ): SHORT;                 // returns a key press state code
This function’s only parameter takes a virtual key code, such as VK_LEFT for the left cursor key. It returns a 16-bit value that indicates, if the most significant bit is set, the specified key is currently pressed. For applications that only require a modicum of keyboard input, this function may be more than adequate. The following example demonstrates the use of this function. Listing 7-2: Retrieving keyboard input through GetAsyncKeyState
    procedure TForm1.Timer1Timer(Sender: TObject);

    begin

      {poll the keyboard to check the state of various keys. the program can

       respond to multiple keys changing states simultaneously}

      if (GetAsyncKeyState(VK_LEFT) and $8000) = $8000 then

        shpLeft.Brush.Color := clRed

      else

        shpLeft.Brush.Color := clMaroon;

      if (GetAsyncKeyState(VK_RIGHT) and $8000) = $8000 then

        shpRight.Brush.Color := clRed

      else

        shpRight.Brush.Color := clMaroon;

      if (GetAsyncKeyState(VK_UP) and $8000) = $8000 then

        shpUp.Brush.Color := clRed

      else

        shpUp.Brush.Color := clMaroon;

      if (GetAsyncKeyState(VK_DOWN) and $8000) = $8000 then

        shpDown.Brush.Color := clRed

      else

        shpDown.Brush.Color := clMaroon;

    end;
Unfortunately, this method cannot distinguish between left and right versions of certain keyboard keys, such as Shift, Alt, etc. Although easy to implement, it must still go through the Windows operating system to retrieve input data, which for many games may be too slow. For the best response times and total keyboard input control, we must use DirectInput. DirectInput Initialization We must follow most of the steps outlined above to initialize DirectInput to receive keyboard data. In most cases, we only need to determine if a particular button is being pressed at a specific time, so immediate data retrieval is the perfect solution. This being the case, initialization of DirectInput is very straightforward. The first order of business is creating the DirectInput object. We can then immediately create an IDirectInputDevice object for the primary keyboard using the GUID_SysKeyboard value. Since it’s not likely that the keyboard will have force feedback capabilities, we’ll skip querying for an IDirectInputDevice2 object. We must then set the data format to c_dfDIKeyboard, the standard keyboard data format. We’ll set the cooperative level for foreground, non-exclusive, and finally acquire the device so we can retrieve data. That’s all there is to initializing DirectInput for immediate keyboard data retrieval. These steps are illustrated in the example below.
| Note: Remember, the keyboard cannot be acquired in an exclusive cooperative level.
Listing 7-3: Initializing DirectInput for keyboard data retrieval
      {create the DirectInput object}

      DirectInputCreate(hInstance, DIRECTINPUT_VERSION, FDirectInput, nil);

      {create the DirectInputDevice object for the primary keyboard}

      FDirectInput.CreateDevice(@GUID_SysKeyboard, FDirectInputDevice, nil);

      {set the data format for keyboard data}

      FDirectInputDevice.SetDataFormat(c_dfDIKeyboard);

      {set the appropriate cooperative level}

      FDirectInputDevice.SetCooperativeLevel(Handle, DISCL_FOREGROUND or

                                             DISCL_NONEXCLUSIVE) );

      {acquire the keyboard}

      FDirectInputDevice.Acquire;
Reading Input Data Retrieving immediate data from any device involves a single call to the IDirectInputDevice’s GetDeviceState method. This method returns a snapshot of the state of the device at the time the function was called, including all pressed keys, axis movements, etc. The GetDeviceState function is defined as:
    function GetDeviceState(

    cbData: DWORD;                    // the size of the data structure

    lpvData: Pointer                  // a pointer to the appropriate data structure

    ): HResult;                       // returns a DirectX error code
Remember the call to the SetDataFormat method? This indicated to DirectInput the structure in which to report device data. The value passed to SetDataFormat indicates the data structure to be passed into GetDeviceState in the lpvData parameter. For keyboards, we use the TDIKeyboardState structure. The first parameter must be set to the size of this data structure. The TDIKeyboardState structure itself is simply a 256-entry array of bytes. Each entry corresponds to a specific key on the keyboard. The array can be indexed using the DIK_* constants defined in the DInput.pas file for each specific key. If the hit bit of this byte is set, the key is currently down; otherwise it is up. Therefore, to retrieve the current state of all keyboard keys, we simply call the GetDeviceState method of the IDirectInputDevice object we just created and check the high bit of the keys with which we are concerned. We must check for input data each time we need it, a technique known as polling as we discussed when we looked at polling versus event notification. The following example demonstrates this technique. Listing 7-4: Retrieving immediate keyboard input data
    procedure TfrmDXAppMain.DrawSurfaces;

    var

      KeyboardState: TDIKeyboardState;

      SourceRect: TRect;

    begin

    .

    .

    .

      {retrieve the current state of the keyboard as immediate data}

      FDirectInputDevice.GetDeviceState(SizeOf(KeyboardState), @KeyboardState);

      {if any of the cursor keys are held down, display an appropriate graphic}

      if (KeyboardState[DIK_UP] and $80) = $80 then

      begin

        SourceRect := Rect(0, 110, 110, 110+110);

        FBackBuffer.BltFast(265, 75, FImage, SourceRect, DDBLTFAST_NOCOLORKEY or

                            DDBLTFAST_WAIT);

      end;

      if (KeyboardState[DIK_DOWN] and $80) = $80 then

      begin

        SourceRect := Rect(2*110, 110, (2*110)+110, 110+110);

        FBackBuffer.BltFast(265, 295, FImage, SourceRect, DDBLTFAST_NOCOLORKEY or

                            DDBLTFAST_WAIT);

      end;

      if (KeyboardState[DIK_LEFT] and $80) = $80 then

      begin

        SourceRect := Rect(3*110, 110, (3*110)+110, 110+110);

        FBackBuffer.BltFast(155, 185, FImage, SourceRect, DDBLTFAST_NOCOLORKEY or

                            DDBLTFAST_WAIT);

      end;

      if (KeyboardState[DIK_RIGHT] and $80) = $80 then

      begin

        SourceRect := Rect(110, 110, 110+110, 110+110);

        FBackBuffer.BltFast(375, 185, FImage, SourceRect, DDBLTFAST_NOCOLORKEY or

                            DDBLTFAST_WAIT);

      end;

    end;
Previous | Table of Contents | Next

Products | Contact Us | About Us | Privacy | Ad Info | Home Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc. All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.