В сегодняшнем уроке мы рассмотрим получение данных ввода с устройства мышь, используя DirectInput.

Сразу рассмотрим код инициализации DirectInput для мышки:

IDirectInput8* di;           // объект DirectInput
IDirectInputDevice8* didev;  // устройство DirectInput

DirectInput8Create(hInstance,  // экземпляр приложения
  DIRECTINPUT_VERSION, // версия DirectInput
  IID_IDirectInput8,   // глобальный идентификатор (COM)
  (void**)&di,         // адрес указателя объекта DirectInput
  NULL);

di->CreateDevice(GUID_SysMouse,  // устройство ввода  с мыши
  &didev,                        // адрес указателя  на устройство
  NULL);

didev->SetDataFormat(&c_dfDIMouse);
didev->SetCooperativeLevel(hWnd,DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
didev->Acquire();

Различия по сравнению с инициализацией DirectInput для клавиатуры заключаются в передаваемых аргументах методов IDirectInput8::CreateDevice и IDirectInputDevice8::SetDataFormat.

Для перехвата ввода с мышки, также как и для перехвата ввода с клавиатуры, необходимо, чтобы был получен доступ к устройству - методом IDirectInputDevice8::Acquire. Поэтому если наша программа теряет фокус, а потом снова становится активной, мы должны предусмотреть подобную ситуацию

В DirectInput существует два способа получить ввод мыши. Здесь мы рассмотри только один:

Немедленный режим (immediate mode) ввода данных с мыши в DirectInput

Как вы, наверное, помните, ввод с клавиатуры мы получали с помощью метода IDirectInputDevice8::GetDeviceState. Этот же метод используется и для получения ввода с мышки. Давайте посмотрим на определение:

HRESULT GetDeviceState(
  DWORD cbData,
  LPVOID lpvData
);

Get device state переводится как - получить состояние устройства.

cpData
Размер буфера, в который будет сохранено текущее состояние устройства.

lpvData
Адрес буфера.

Итак, в этот метод передаётся адрес буфера, в котором будет сохранено текущее состояние устройства ввода, и размер этого буфера. Для клавиатуры мы создавали простой массив из 256-и элементов. Для мышки в DirectInput используется специальная структура - DIMOUSESTATE (Direct Input Mouse State - состояние мышки в DirectInput). Поэтому нам нужно объявить соответствующую переменную до основного цикла (и обнулить все её поля):

DIMOUSESTATE dims;
ZeroMemory(&dims,sizeof(dims));
Теперь посмотрим,  из чего состоит эта структура:

typedef struct DIMOUSESTATE {
  LONG lX;
  LONG lY;
  LONG lZ;
  BYTE rgbButtons[4];
} DIMOUSESTATE, *LPDIMOUSESTATE;

Как видите, в структуре хранится перемещение мышки по трём осям (обратите внимание на ось z, которая отвечает за прокрутку колёсика мышки) и 4 кнопки (массив rgbButtons):
rgbButtons[0] - левая кнопка мыши.
rgbButtons[1] - правая кнопка мыши.
rgbButtons[2] - колёсико мыши.

Вот в общем-то и всё. Теперь мы можем очень легко взять нужную нам информацию из структуры DIMOUSESTATE.

Код довольно простой (если вы читали урок о вводе с клавиатуры в DirectInput). Думаю, вы сами разберётесь:

didev->GetDeviceState(sizeof(DIMOUSESTATE),&dims);

dx = dims.lX;
dy = dims.lY;
dz = dims.lZ;
if (dims.rgbButtons[0] & 0x80)
{
  for (int i = 0; i < 6; i++)
    vertices[i].color = 0xffff0000;
}
if (dims.rgbButtons[1] & 0x80)
{
  for (int i = 0; i < 6; i++)
    vertices[i].color = 0xff00ff00;
}
if (dims.rgbButtons[2] & 0x80)
{
  for (int i = 0; i < 6; i++)
    vertices[i].color = 0xff0000ff;
}

for (int i = 0; i < 6; i++)
{
  vertices[i].x += dx;
  vertices[i].y -= dy;
}

matWorld._43 += dz/10;
dev->SetTransform(D3DTS_WORLD, &matWorld);

Сначала мы получаем текущее состояние мышки.

После этого идёт проверка на нажатие клавиш мышки, и если была нажата одна из кнопок, мы перекрашиваем все вершины в соответствующий цвет: левая кнопка мышки - красный, правая кнопка мыши - зелёный, колёсико мышки - синий.

Затем идёт код перемещения всех вершин квадрата с помощью полей lX и lY. Далее мы приближаем или удаляем квадрат к камере, изменяя элемент _43 (четвёртая строка, третий столбец) матрицы matWorld на значение lZ (прокрутка колёсика мышки). При этом поле lZ мы делим на 10. Это необходимо, чтобы квадрат не приближался/удалялся слишком быстро. В конце мы передаём изменившуюся матрицу matWorld в метод SetTransform.

Теперь выясним почему данный способ получения ввода мыши называется - немедленным (immediate mode). DirectInput получает информацию о состоянии мыши двенадцать раз в секунду. Обратите внимание на наш код: мы опрашиваем устройство с помощью метода IDirectInputDevice8::GetDeviceState, помещаем состояние устройства в структурную переменную dims, ну а затем смотрим, какие поля данной переменной нам нужны.

И всё бы хорошо, но представим ситуацию, когда у нас получается выводить на экран только один кадр в секунду (допустим, нам нужно построить очень сложную сцену). А это значит, что за секунду мы успеем выполнить только одну итерацию основного цикла, что в свою очередь означает только один вызов метода IDirectInputDevice8::GetDeviceState за секунду. Это приведёт к тому, что мы потеряем одиннадцать промежуточных состояний устройства (мышки).

Собственно, использование только метода IDirectInputDevice8::GetDeviceState и есть этот самый немедленный режим ввода данных с устройства мышь. Т.е. мы получаем состояние устройства в какой-то конкретный момент времени, при этом возможна потеря промежуточных состояний (если метод GetDeviceState вызывается меньше двенадцати раз в секунду).

Чтобы получить все данные с устройства (все состояния) используется буферизованный режим. При этом используется метод IDirectInputDevice8::GetDeviceData.

Буферизованный режим ввода данных с мышки мы рассмотрим позднее.