Thread: Direct Input MADNESS!

  1. #1
    Registered User
    Join Date
    Apr 2008
    Posts
    58

    Direct Input Keymap is Destroyed!!!

    I'm very confused with this particular error, and was hoping that someone here would be kind enough to tell me why this error is occuring...

    I have a simple Input class:
    Code:
    #ifndef _INPUT_H_
    #define _INPUT_H_
    
    #include <dinput.h>
    class InputDevice
    {
    public:
    	~InputDevice(void);
    	LPDIRECTINPUT8 Interface;
    	LPDIRECTINPUTDEVICE8 Device;
    protected:
    	InputDevice(void);
    private:
    };
    InputDevice::InputDevice(void)
    {
    }
    InputDevice::~InputDevice(void)
    {
    }
    #endif
    Then I have a simple KeyboardDevice Class and Keys class:
    Code:
    #ifndef _KEYBOARD_H_
    #define _KEYBOARD_H_
    #include "inputdevice.h"
    #include <iostream>
    using namespace std;
    class KeyboardDevice : public InputDevice
    {
    public:
    	KeyboardDevice(void);
    	~KeyboardDevice(void);
    	void Update(void);
    	bool Pressed(const unsigned short Key);
    	void Print(void);
    protected:
    	unsigned short state[256], pState[256];
    };
    struct Keys
    {
    	static const unsigned short 
    		Num1 = 0x01,
    		Num3 = 0x02,
    		Num5 = 0x03,
    		Num7 = 0x04,
    		Num9 = 0x05,
    		Dash = 0x06,
    		BackSpace = 0x07,
    		Q = 0x08,
    		E = 0x09,
    		T = 0x0A,
    		U = 0x0B,
    		O = 0x0C,
    		BracketLeft = 0x0D,
    		Return = 0x0E,
    		A = 0x0F,
    		D = 0x10,
    		G = 0x11,
    		J = 0x12,
    		L = 0x13,
    		Comma = 0x14,
    		ShiftLeft = 0x15,
    		Z = 0x16,
    		C = 0x17,
    		B = 0x18,
    		M = 0x19,
    		Period = 0x1A,
    		ShiftRight = 0x1B,
    		AltLeft = 0x1C,
    		F2 = 0x1E,
    		F4 = 0x1F,
    		F6 = 0x20,
    		F8 = 0x21,
    		NumPad8 = 0x24,
    		NumPadMinus = 0x25,
    		NumPad5 = 0x26,
    		NumPadPlus = 0x27,
    		NumPad2 = 0x28,
    		NumPad0 = 0x29,
    		CapsLock = 29,
    		Equals = 92,
    		Up = 100,
    		Down = 104;
    
    };
    #include "keyboarddevice.h"
    KeyboardDevice::KeyboardDevice(void)
    {
    }
    KeyboardDevice::~KeyboardDevice(void)
    {
    }
    void KeyboardDevice::Update(void)
    {
    	for(int i = 0; i < 256; i++) pState[i] = state[i];
        Device->GetDeviceState(256, (LPVOID)state);
    }
    bool KeyboardDevice::Pressed(const unsigned short Key)
    {
    	//if(state[DIK_ESCAPE] & 0x80) return true;
    	//else return false;
    	return (state[Key] & 0x806);// && !pState[Key & 0x80];
    }
    void KeyboardDevice::Print(void)
    {
    	for(int i = 0; i < 256; i++)
    	{
    		if((state[i] & 0x80) != 0)
    			cout << i << "\n";
    	} cout << "0x00\n";
    }
    
    #endif


    ...and I run it in this program:
    Code:
    // include the necessary header files
    #include <windows.h>
    #include <windowsx.h>
    #include <d3d9.h>
    #include <d3dx9.h>
    #include <dinput.h>
    #include "graphicdevice.h"
    #include "keyboarddevice.h"
    #include <iostream>
    // define the screen resolution and keyboard macros
    #define SCREEN_WIDTH 800
    #define SCREEN_HEIGHT 600
    // #define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
    // #define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)
    using namespace std;
    // include the DirectX Library files
    #pragma comment (lib, "d3d9.lib")
    #pragma comment (lib, "d3dx9.lib")
    #pragma comment (lib, "dinput8.lib")
    #pragma comment (lib, "dxguid.lib")
    
    GraphicDevice Graphics;
    KeyboardDevice Keyboard;
    D3DCOLOR color = D3DCOLOR_XRGB(0, 0, 0);
    
    // function prototypes
    void initD3D(HWND hWnd);
    void render_frame(void);
    void cleanD3D(void);
    void init_graphics(void);
    void init_light(void);
    void initDInput(HINSTANCE hInstance, HWND hWnd);    // sets up and initializes DirectInput
    void detect_input(void);    // gets the current input state
    void cleanDInput(void);    // closes DirectInput and releases memory
    
    // the WindowProc function prototype
    LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    
    
    // the entry point for any Windows program
    int WINAPI WinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPSTR lpCmdLine,
                       int nCmdShow)
    {
        HWND hWnd;
        WNDCLASSEX wc;
    
        ZeroMemory(&wc, sizeof(WNDCLASSEX));
    
        wc.cbSize = sizeof(WNDCLASSEX);
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.hInstance = hInstance;
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
        wc.lpszClassName = "WindowClass";
        wc.lpfnWndProc = WindowProc;
        RegisterClassEx(&wc);
    
        hWnd = CreateWindowEx(NULL, "WindowClass", "Our DirectInput Program",
                              WS_OVERLAPPEDWINDOW, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT,
                              NULL, NULL, hInstance, NULL);
    
        ShowWindow(hWnd, nCmdShow);
        // set up and initialize DirectX
        initD3D(hWnd);
        initDInput(hInstance, hWnd);    // initialize DirectInput
        MSG msg;
        while(TRUE)
        {
            while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
            if(msg.message == WM_QUIT)
                break;
    
            detect_input();    // update the input data before rendering
            render_frame();
        }
        // clean up DirectX and COM
        cleanD3D();
        cleanDInput();    // release DirectInput
        return msg.wParam;
    }
    // this is the main message handler for the program
    LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        switch(message)
        {
            case WM_DESTROY:
                {
                    PostQuitMessage(0);
                    return 0;
                } break;
        }
    
        return DefWindowProc (hWnd, message, wParam, lParam);
    }
    
    
    // this function initializes and prepares Direct3D for use
    void initD3D(HWND hWnd)
    {
    	Graphics.Interface = Direct3DCreate9(D3D_SDK_VERSION);
    
        D3DPRESENT_PARAMETERS d3dpp;
    
        ZeroMemory(&d3dpp, sizeof(d3dpp));
        d3dpp.Windowed = TRUE;
        d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
        d3dpp.hDeviceWindow = hWnd;
        d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
        d3dpp.BackBufferWidth = SCREEN_WIDTH;
        d3dpp.BackBufferHeight = SCREEN_HEIGHT;
        d3dpp.EnableAutoDepthStencil = TRUE;
        d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
    
        // create a device class using this information and the info from the d3dpp stuct
        Graphics.Interface->CreateDevice(D3DADAPTER_DEFAULT,
                          D3DDEVTYPE_HAL,
                          hWnd,
                          D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                          &d3dpp,
                          &Graphics.Device);
    }
    
    
    // this is the function used to render a single frame
    void render_frame(void)
    {
        Graphics.GetService()->GetService()->Device->Clear(0, NULL, D3DCLEAR_TARGET, color, 1.0f, 0);
        Keyboard.Print();
    
        Graphics.GetService()->GetService()->Device->EndScene(); 
        Graphics.GetService()->GetService()->Device->Present(NULL, NULL, NULL, NULL);
    }
    // this is the function that cleans up Direct3D and COM
    void cleanD3D(void)
    {
        Graphics.Device->Release();
        Graphics.Interface->Release();
    }
    // this is the function that puts the 3D models into video RAM
    void init_graphics(void)
    {
    }
    void init_light(void)
    {
    }
    // this is the function that initializes DirectInput
    void initDInput(HINSTANCE hInstance, HWND hWnd)
    {
        // create the DirectInput interface
        DirectInput8Create(hInstance,    // the handle to the application
                           DIRECTINPUT_VERSION,    // the compatible version
                           IID_IDirectInput8,    // the DirectInput interface version
                           (void**)&Keyboard.Interface,    // the pointer to the interface
                           NULL);    // COM stuff, so we'll set it to NULL
    
        // create the keyboard device
        Keyboard.Interface->CreateDevice(GUID_SysKeyboard,    // the default keyboard ID being used
                          &Keyboard.Device,    // the pointer to the device interface
                          NULL);    // COM stuff, so we'll set it to NULL
        Keyboard.Device->SetDataFormat(&c_dfDIKeyboard);
        Keyboard.Device->SetCooperativeLevel(hWnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
    }
    void detect_input(void)
    {
        Keyboard.Device->Acquire();
        Keyboard.Update();
    }
    void cleanDInput(void)
    {
        Keyboard.Device->Unacquire();    // make sure the keyboard is unacquired
        Keyboard.Interface->Release();    // close DirectInput before exiting
    }



    Now, the issue is:

    - I can ONLY check a limited number of keys. For some reason, the Direct Input defined preprocessor directives don't match up with the keys that are being pressed.

    For example:
    0x01 is SUPPOSED to be the escape key; INSTEAD it is read as the Num1 key!
    0x02 is SUPPOSED to be the Num1 key; INSTEAD it is read as the Num 3 key!


    Does anyone here have any idea why this would occur?! This is SERIOUSLY confusing me!

  2. #2
    Registered User
    Join Date
    Apr 2008
    Posts
    58

    Direct Input MADNESS!

    This is a different question, but is just as puzzling to me. In my last post (Found Here) I provided code snippets so you could see what I was doing.

    In the Directinput GetDeviceState method, I found that it copies the states into your array in a very odd manner. For example, the state of key "F2" is stored in my variable, state[30] with a value of 128. However, "F3" is ALSO stored in state[30], but with a value of 32768.

    I noticed that these two values are repeated. When I do
    Code:
    (state[key] & 128)
    , I will get one key, whereas
    Code:
    (state[key] & 32768)
    will give me a different key.

    This just seems very odd to me, and makes me think that I am using Directinput incorrectly. But if anyone can justify this seemingly odd form of obtaining key states, please do so here.

  3. #3
    Registered User valaris's Avatar
    Join Date
    Jun 2008
    Location
    RING 0
    Posts
    507
    I would use the macros/constants they have already defined. IE KEYDOWN() and DIK_KEY (IE DIK_E, DIK_D)...

  4. #4
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    It is because you are assigning ad-hoc hexadecimal values to keys.

    For instance if I wanted to re-define DIK_F to fire then I would do this:

    Code:
    enum KeyMap
    {
       FIRE = DIK_F,
       ...
       ...
    };
    
    
    if (m_pKeyboard->KeyDown(FIRE))
    {
       //Fire some type of weapon
    }

    I would advise you place the key mapping into an array that lines up with the DIK constants you are mapping to. You can use a map or a hash. You might hash the message "fire" to some DIK_ constant. If you do this in an array or a map you can change the mapping of the keys in your game at any time.

  5. #5
    Registered User
    Join Date
    Apr 2008
    Posts
    58
    I'm sort of doing that, but writing it in a function...my point is that the keys that are already defined aren't working for me for some unknown reason.

    For example, DIK_ESCAPE (which is defined as 0x01), is read as key Num1 on my computer, for some ungodly reason.

    I had to redefine all of the keys on my keyboard, one by one...very tedious and annoying. The signatures are ALL different though...which confuses me very very much. Windows and DirectInput BOTH define DIK_ESCAPE as 0x01...but my program reads state[1] as number 1...I just don't understand it.

  6. #6
    Registered User
    Join Date
    Apr 2008
    Posts
    58
    I actually originally had it that way, and it didn't work...the keys listed above are surprisingly the working ones! For some reason, DIK_whatever just won't work. It compiles just fine, the only problem is that the key doesn't correspond to the name!

    For example, if I replace Keys::Escape in my code with DIK_ESCAPE, it will be broken...it will read the input as key NUM1, rather than DIK_ESCAPE....so for me, it is the Direct Input defined keys that are giving me issues...

  7. #7
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    For key down you and the result with 0x80. Anything else is undefined and would be outside the scope of how DirectInput is to be used.

    Immediate Keyboard Data
    To retrieve the current state of the keyboard, call the IDirectInputDevice8::GetDeviceState method with a pointer to an array of 256 bytes that will hold the returned data.

    The IDirectInputDevice8::GetDeviceState method behaves in the same way as the Microsoft Win32 GetKeyboardState function. It returns a snapshot of the current state of the keyboard. Each key is represented by a byte in the array of 256 bytes whose address was passed as the lpvData parameter. If the high bit of the byte is set, the key is down. The array is most conveniently indexed with the Microsoft DirectInput Keyboard Device. (See also Interpreting Keyboard Data.)

    The following code example calls a function in response to the ESC key being down. Assume that lpdiKeyboard is an acquired DirectInput device.

    Code:
    BYTE  diKeys[256]; 
    if (lpdiKeyboard->GetDeviceState(256, diKeys) == DI_OK) 
    { 
    	if (diKeys[DIK_ESCAPE] & 0x80) DoSomething(); 
    }

  8. #8
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    If you do this:

    Code:
    if (m_pKeyboard->KeyDown(DIK_ESCAPE))
    {
      //do something
    }
    ...and KeyDown() AND's the keystate array with 0x80 to get the result then there is no way that DIK_ESCAPE can map to some other key. I cannot see your keyboard code so I'm not sure it is working correctly. You must call GetDeviceState() in order to get the current mouse and keyboard state. This must be done before every frame.

    Incidentally if you only acquire the device once it can actually fail. You should acquire continually until it succeeds or it's possible that the device will not be re-acquired and your app will not accept keyboard input. Normally it will succeed the first time but if it's a windowed app and you set the focus to another app window and come back...it could take a slight bit of time to re-acquire and your code does not account for this. It assumes the acquire succeeded when it may have failed. In my experience it appears that the keyboard is the primary device most affected by this.

    All DirectX functions return an HRESULT which can be checked via the FAILED() and SUCEEDED() macros. Normally I will choose either FAILED() or SUCEEDED() but I will not mix them in the code. If you choose one then use it all the time or your error conditions can become confusing.
    Last edited by VirtualAce; 09-02-2009 at 09:35 PM.

  9. #9
    Registered User
    Join Date
    Apr 2008
    Posts
    58

    Exclamation

    I didn't know about continual acquires, but fortunately, I am doing that. However, the GetDeviceState() function is copying information in a very odd way.


    I have my BYTE keystate[256] array, and have used GetDeviceState() on every update (which happens everytime time the program goes through the game loop). The problem is that certain keys are being stored in the same array location.

    For example:

    Code:
    if(keystate[30] & 128) //128 is 0x80
    will give me the state of F2. However, the following will give me the state of F3:
    Code:
    if(keystate[30] & 32768)

    So...as you can see, both keys are stored in the same array location. I have used multiple keyboards, and have been given the same result. The code posted in my first post should compile...so feel free to toy with it and see what happens when you use it.

  10. #10
    Registered User
    Join Date
    Apr 2008
    Posts
    58
    Yes, but I was under the impression that information about each key would be copied to a separate array location or byte. What would be the purpose of copying multiple keys to the same location...and how would I have been able to change the GetDeviceState() function in order to make it do that?!

    Also...now that I think about it, how is it even possible to have a value of 32768 in a byte?!
    ** Correction, I forgot that I wasn't using an actual byte. (BYTE is typedef'ed from an unsigned short)
    Last edited by arcaine01; 09-03-2009 at 12:17 PM.

  11. #11
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by arcaine01 View Post
    Yes, but I was under the impression that information about each key would be copied to a separate array location or byte. What would be the purpose of copying multiple keys to the same location...and how would I have been able to change the GetDeviceState() function in order to make it do that?!

    Also...now that I think about it, how is it even possible to have a value of 32768 in a byte?!
    ** Correction, I forgot that I wasn't using an actual byte. (BYTE is typedef'ed from an unsigned short)
    So if you change everything to use proper bytes (if your BYTE typedef is broken as such, then maybe using unsigned char) does that fix all those issues in your previous thread?

  12. #12
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    The moral of the story is that if you want to get things running properly, you should stick with the API defines and macros/functions to access everything. Even if you were to successfully process the data-structures "by-hand" (an interesting excercise, actually, if you've got some spare time on your hands, to be sure) it wouldn't be sufficient for production-quality code, since the underlying layout can change from system to system. In other words, aside from the purely academic excercise in curiosity, the approach is basically worthless.

  13. #13
    'Allo, 'Allo, Allo
    Join Date
    Apr 2008
    Posts
    639
    Unless you're retrieving joystick data, you shouldn't use DirectInput. Even MS actively discourages its use for anything other than Joysticks.
    Quote Originally Posted by http://msdn.microsoft.com/en-us/library/bb219802(VS.85).aspx
    The use of DirectInput for keyboard and mouse input is not recommended, Windows messages should be used instead.
    One of the DX MVP's sums up the reasons against it (it's under the to-do lists). Also, from Bubba's quote, it doesn't look like GetDeviceState does anything that GetKeyboardState doesn't, so the extra setup and maintainance required for the pleasure doesn't seem worth it.

  14. #14
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Quote Originally Posted by adeyblue View Post
    Unless you're retrieving joystick data, you shouldn't use DirectInput. Even MS actively discourages its use for anything other than Joysticks.


    One of the DX MVP's sums up the reasons against it (it's under the to-do lists). Also, from Bubba's quote, it doesn't look like GetDeviceState does anything that GetKeyboardState doesn't, so the extra setup and maintainance required for the pleasure doesn't seem worth it.
    Well, at least none of their other API's are broken - oh, wait...nevermind.

  15. #15
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Code:
    BYTE keystate[256]
    And you are ANDing this with 32768? Why? Where did you get that number? How did you think it was going to work when you just invented a number to use as a mask?

    They are not being stored in the same place. How do you suppose you are using a BYTE array yet ANDing that with a short? What bits are you trying to mask off? The mask has more bits than the source so I'm lost as to why you think you can AND it with any arbitrary value and get back meaningful information or assume it is meaningful.

    For the last time AND with 0x80 and nothing else.

    Here is what you are doing:

    1000000000000000
    AND
    (00000000
    to
    11111111)

    The correct mask is:
    10000000 or 0x80 or 128.
    Last edited by VirtualAce; 09-03-2009 at 04:47 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. can someone help me with these errors please code included
    By geekrockergal in forum C Programming
    Replies: 7
    Last Post: 02-10-2009, 02:20 PM
  2. I would love some input on my BST tree.
    By StevenGarcia in forum C++ Programming
    Replies: 4
    Last Post: 01-15-2007, 01:22 AM
  3. About aes
    By gumit in forum C Programming
    Replies: 13
    Last Post: 10-24-2006, 03:42 PM
  4. Structure and Linked List User Input Question
    By kevndale79 in forum C Programming
    Replies: 16
    Last Post: 10-05-2006, 11:09 AM