Herramientas de usuario

Herramientas del sitio


fw:dx:dinput

¡Esta es una revisión vieja del documento!


DirectInput

Es la parte de DirectX que interactua directamente con los dispositivos de entrada (teclado, ratón, joystick…), utilizándolo obtienes mayor flexibilidad en la obtención de datos introducidos por el usuario. Sus ventajas son que al tratarse directamente con el dispositivo se desvinculan a tu aplicación las configuraciones realizadas por el usuario en el sistema operativo, además no funciona por eventos sino que a cada momento del tiempo el dispositivo tiene un estado distinto. Seguiremos los siguientes pasos para una correcta utilización:

  1. Se crea el objeto DirectInput (Objeto que gestiona el motor de DirectInput).
  2. Se miran los dispositivos de entrada de los que se disponen.
  3. Por cada dispositivo que queramos tratar creamos un objeto DirectInputDevice (Objeto que representa un dispositivo).
  4. Configuramos cómo vamos a tratar el dispositivo.
  5. Recogemos y tratamos los datos.
  6. Cerramos DirectInput.

Recuerda:

  • Para poder programar con DInput necesitaremos linkar las siguientes librerías: dxguid.lib y dinput8.lib.
  • Tenemos que incluir en nuestro .cpp el fichero dinput.h

Código general que se ha utilizado para los ejemplos:

#include <dinput.h>
#define MSGBOX(body) MessageBox(NULL,body,"",MB_OK);

Utilizando DInput

Creando el objeto DInput

Para crear un objeto utilizaremos la función: DirectInput8Create, a esta se le pasan los siguientes argumentos:

  • El HINSTANCE de la aplicación que lo crea (parámetro que viene en el WinMain).
  • La flag DIRECTINPUT_VERSION que indica la versión de DInput.
  • Tipo de interface que ha de retornar, utilizaremos: IID_IDirectInput8.
  • Referencia al objeto LPDIRECTINPUT8 que queremos crear.
  • NULL → Es para compativilidades con objetos COM.
LPDIRECTINPUT8 objDI;
if (FAILED(DirectInput8Create(inst, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&objDI, NULL)))
	MSGBOX("Error al crear el objeto DInput");

Crear dispositivos

Crear objeto dispositivo

Para crear un objeto dispositivo tendremos que declarar primero un objeto de este tipo (LPDIRECTINPUTDEVICE8) y luego llamar a la función CreateDevice del objeto LPDIRECTINPUT8 ya creado anteriormente. Esta función recoge los siguientes parámetros:

  • El identificador del dispositivos, podemos coger los “por defecto” que son: GUID_SysKeyboard y GUID_SysMouse.
  • Referencia al objeto LPDIRECTINPUTDEVICE8 que se quiere crear.
  • NULL.

Cómo serán los datos?

Tenemos que asignar al dispositivo un formato de datos, es decir, indicar cómo vamos a tratar con él; tenemos los siguientes: c_dfDIJoystick, c_dfDIJoystick2, c_dfDIKeyboard, c_dfDIMouse, y c_dfDIMouse2. Son variables globales que nos liberan del tedio de hacerlo por nuestra cuenta, sólo tenemos que pasar la elegida como referencia al método SetDataFormat del dispositivo que queramos.

Nivel cooperativo

Esto es cómo el dispositivo va a comunicarse con el Sistema Operativo, para indicarlo, en el objeto del dispositivo (LPDIRECTINPUTDEVICE8) llamamos al método SetCooperativeLevel pasándole:

  1. El HWND de la aplicación.
  2. Y dos de los siguientes flags:
    • DISCL_NONEXCLUSIVE o DISCL_EXCLUSIVE.
    • DISCL_FOREGROUND o DISCL_BACKGROUND.
    • Y si el dispositivo es un keyboard también podemos indicar que se desactive la tecla de windows pasándole otra flag: DISCL_NOWINKEY (en combinación con DISCL_NONEXCLUSIVE).

Básicamente con esto indicas si se seguirá el dispositivo cuando deje de estar en primer plano (foreground) o no; y si será exclusivo (que bloqueará el dispositivo para si mismo) o no. Podemos utilizar la siguiente combinación:

  • Para el teclado\ratón: DISCL_FOREGROUND | DISCL_NONEXCLUSIVE
  • Para el ratón si queremos manejar nosotros el cursor de windows (que desaparecerá): DISCL_EXCLUSIVE | DISCL_FOREGROUND

Adquirir el dispositivo

Esto es, como ya lo tenemos configurado se llama al método Acquire de este y podremos comenzar a utilizarlo.

Código

void createKeyboard () {
	objDI->CreateDevice(GUID_SysKeyboard, &keyb, NULL);
	keyb->SetDataFormat(&c_dfDIKeyboard); 
	keyb->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE | DISCL_NOWINKEY);
	keyb->Acquire ();
}
 
void createMouse () {
	objDI->CreateDevice(GUID_SysMouse, &mouse, NULL);
	mouse->SetDataFormat(&c_dfDIMouse); 
	mouse->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
	mouse->Acquire ();
}

Cerrando DInput

Una vez acabemos de utilizar DInput debemos liberar los dispositivos (llamando a Unadquire y luego a Release) y luego liberar el objeto DInput, llamando al Release de este:

keyb->Unacquire();
keyb->Release();
 
mouse->Unacquire();
mouse->Release();
 
objDI->Release();
 
keyb = NULL;
mouse = NULL;
objDI = NULL;

Perdiendo el dispositivo

Una vez tengas funcionando tu aplicación, si esta pierde el foco (aunque sea por un messagebox propio) tendrás que volver a cargar el objeto dispositivo. Puedes detectar que la aplicación ha perdido el foco mediante el mensaje WM_ACTIVATEAPP al WindowProcedure. Sabrás si se ha activado\desactivado mediante el wParam.
Notarás, también, que has perdido algún dispositivo porque métodos de este ya no devolverán DI_OK.

Recoger datos...

... del teclado

Los datos del teclado vienen dados en un buffer donde se guarda el estado actual de cada tecla. Para recoger dicho buffer únicamente has de declarar un array de bytes de 256 (nº de teclas) y llamar a la función GetDeviceState del objeto dispositivo pasándole como primer parámetro el tamaño del buffer (256 o sizeof(buffer)).
Para saber si se ha presionado la tecla que queremos existen unas constantes de DInput con los nombres de las teclas (DIK_ESCAPE, DIK_LEFT…), son números que corresponden a una posición del buffer. Para saber si una tecla en concreto se ha pulsado el contenido en el buffer de la posición correspondiente añadiendole lógicamente (&) un valor 0x80 tiene que dar como valor booleano true.

#define KEYDOWN(name, key) (name[key] & 0x80) 
...
byte buffer[256];
HRESULT  hr = keyb->GetDeviceState(sizeof(buffer), (LPVOID)&buffer);
if (hr == DI_OK) {
	if (KEYDOWN(buffer, DIK_ESCAPE))
		...
	if (KEYDOWN(buffer, DIK_LEFT))
		...
}

... del mouse

Para controlar la posición del mouse necesitarás de una nueva estructura: DIMOUSESTATE. Un objeto de esta es lo que has de pasar al GetDeviceState del objeto dispositivo del mouse junto con un sizeof:

DIMOUSESTATE msdata;
mouse->GetDeviceState(sizeof(msdata), &msdata);

Las propiedades que contiene son: lX e lY. En ellas encontrarás cuanto se ha movido el mouse en esas coordenadas desde la última vez que se hizo un GetDeviceState. También contiene un array de bytes con 4 posiciones correspondientes a 4 botones del ratón, para saber si se ha pulsado algún botón sólo has de añadir lógicamente (&) el valor 0x80 a la posición de dicho array correspondiente al botón (izquierdo = 0, derecho = 1…) y te dará un valor booleano que indica si ha sido pulsado o no. Otro valor, el lZ, indica cuanto ha sido movida la rueda.

DIMOUSESTATE msdata;
mouse->GetDeviceState(sizeof(msdata), &msdata);
mouseX += msdata.lX;
mouseY += msdata.lY;
if (msdata.rgbButtons[0] & 0x80)
  ...
if (msdata.lZ > 0)
  ...

Notas

  • Cuando llamamos a funciones de DInput estas han de retornar DI_OK si todo ha salido bien. Aún así, y para una sencilla comprobación, los programadores de DirectX nos proporcionan una macro llamada FAILED que indica si ha fallado la acción:
if (FAILED(DirectInput8Create(inst, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&objDI, NULL)))
	MSGBOX("Error al crear el objeto DInput");
fw/dx/dinput.1209229886.txt.gz · Última modificación: 2020/05/09 09:24 (editor externo)