DirectInput, как и DirectDraw, обходит традиционные механизмы Windows и обеспечивает прямой доступ к устройствам, не утрачивая аппаратной независимости. Поскольку Windows в этой схеме не используется, установленные в системе параметры устройств ввода (например, частота повтора символов для клавиатуры или чувствительность мыши) не влияют на DirectInput.
В зависимости от потребностей вашего приложения DirectInput может использоваться для получения данных двух видов: непосредственных (immediate) и буферизованных (buffered). Непосредственные данные описывают состояние устройства ввода на момент запроса данных, а буферизованные данные используют концепцию очереди для описания изменений в состоянии устройства (например, нажатий клавиш или осевых смещений).
С помощью непосредственных данных можно, например, определить, нажата ли некоторая клавиша в данный момент. Для клавиатуры получение непосредственных данных напоминает применение функции Win32 GetAsyncKeyState(). Непосредственные данные лучше всего работают при частом опросе устройства ввода (как правило, не реже 30 раз в секунду). Редкий опрос может привести к потере данных; если за время нахождения клавиши в нажатом состоянии клавиатура не опрашивалась, то приложение не узнает о наличии ввода.
С другой стороны, буферизованные данные получают весь ввод от устройства и помещают каждое событие в очередь. Ваше приложение может в любой момент просмотреть содержимое очереди. Каждый элемент содержит порядковый номер, по которому можно определить последовательность событий (если только события не произошли одновременно, в этом случае они будут иметь одинаковые порядковые номера). Буферизация данных предотвращает их потерю (это может произойти разве что при переполнении буфера).
Каждая форма получения данных обладает своими достоинствами и недостатками, и только вы можете решить, какая из них лучше подходит для вашего приложения. В некоторых приложениях встречаются обе формы. Например, буферизованные данные применяются для меню и общего управления приложением, а непосредственные данные — в оптимизированном ядре. В этой главе непосредственные данные используются в программе Qwerty, а буферизованные — в программе Smear.
Независимо от конкретной схемы приложение в какой-то момент должно получить данные. Чаще всего это делается путем опроса (polling) устройства через подходящие промежутки времени. Например, приложение может проверять наличие новых данных (непосредственных или буферизованных) при каждом обновлении экрана.
Кроме опроса устройств существует и альтернативный вариант — оповещение (notification). В этом случае программный поток (thread) блокируется и ожидает поступления оповещающего события. С наступлением такого события поток автоматически активизируется. Оповещение позволяет приложению реагировать на изменения в состоянии устройства ввода без расходов процессорного времени, связанных с опросом.
Для однопоточных приложений оповещение используется редко, потому что во время ожидания события поток блокируется и не может ничего делать. Если только вся работа приложения не сводится к получению ввода от пользователя, для оповещения потребуется по крайней мере два потока. В программах этой главы оповещение не используется, однако мы встретимся с ним в главе 7.
Чтобы приложение могло задать нужную степень контроля над устройством, в DirectInput используются уровни кооперации. DirectInput, как и DirectDraw, позволяет установить монопольный (exclusive) и совместный (nonexclusive) режим доступа для каждого устройства. Если приложение DirectInput обладает монопольным доступом к устройству ввода, то никакое другое приложение заведомо не сможет получить монопольного доступа к тому же устройству (хотя сможет получить совместный доступ).
DirectInput также позволяет задать уровень кооперации для активного (foreground) и фонового (background) режимов работы — эти два термина часто приводят к недоразумениям. Активный доступ (foreground access) означает, что приложение работает с устройством только тогда, когда обладает фокусом ввода (по аналогии с тем, как ввод с клавиатуры по умолчанию передается приложению, обладающему фокусом). Фоновый доступ (background access) означает, что приложение может обращаться к устройству независимо от того, обладает ли оно фокусом ввода. Интуитивно кажется, будто активный доступ обладает большими возможностями, но на самом деле это не так. В программах Qwerty и Smear используется совместный активный уровень кооперации.
Устройство, возвращающее информацию об осевых смещениях (например, мышь или джойстик), можно настроить так, чтобы оно возвращало относительные или абсолютные данные. Относительные осевые смещения описывают перемещение по данной оси по отношению к предыдущему положению, а абсолютные — текущую позицию по данной оси.
По умолчанию мышь возвращает относительные данные, а джойстик — абсолютные. В программе Smear для мыши используется установка по умолчанию, однако следует помнить о том, что тип данных можно изменить как для джойстика, так и для мыши.
Приложение DirectDraw в случае необходимости может уступить видеопамять другому приложению и восстановить ее, когда исходное приложение снова получит фокус. В DirectInput приложение тоже может потерять устройство и восстановить контроль над ним перед тем, как продолжить работу. В таких случаях говорят, что приложение
Некоторые устройства (особенно клавиатуры и мыши) регулярно захватываются и уступаются приложениями. Обычно они уступаются автоматически в тот момент, когда приложение теряет фокус. Когда ваше приложение опять получает фокус, оно должно снова захватить устройство.
DirectInput API
До выхода DirectX 3 библиотека DirectInput была построена на существующих функциях Win32 и не поддерживала ввода с клавиатуры или от мыши. В DirectX 3 появились COM-интерфейсы для клавиатуры и мыши, но все остальные устройства продолжали зависеть от функций Win32 (и особенно от функции joyGetPosEx()). В DirectX 5 зависимость DirectInput от Win32 полностью устранена, а все устройства ввода переведены на использование COM-интерфейсов. Работа с устройствами ввода реализована через три интерфейса:
• DirectInput
• DirectInputDevice
• DirectInputEffect
Первичным, или главным, является интерфейс DirectInput. Создание его экземпляра приводит к инициализации библиотеки, а все остальные интерфейсы DirectInput могут быть инициализированы лишь с помощью его функций.
Интерфейс DirectInputDevice представляет устройство ввода. Его функции выполняют инициализацию, настройку, захват и отпускание устройств. Что еще важнее, DirectInputDevice содержит функции для получения данных от устройства.
Интерфейс DirectInputEffect применяется для обслуживания устройств с обратной связью. В этой книге он не используется.
Инициализация DirectInput происходит в тот момент, когда вы получаете указатель на интерфейс DirectInput функцией DirectInputCreate(). Затем полученным интерфейсом можно воспользоваться для создания экземпляров интерфейса DirectInputDevice, составления списка доступных устройств и даже для вызова панели управления DirectInput. Интерфейс DirectInput содержит следующие четыре функции:
• CreateDevice()
• EnumDevice()
• GetDeviceStatus()
• RunControlPanel()
Функция CreateDevice() создает новые экземпляры интерфейса DirectInputDevice. Она получает три аргумента: GUID нужного устройства, адрес инициализируемого указателя на интерфейс и показатель агрегирования (aggregation) COM, который обычно должен быть равен нулю. Для системной мыши и клавиатуры в DirectX предусмотрены стандартные значения GUID:
• GUID_SysKeyboard
• GUID_SysMouse
Значения GUID остальных устройств можно получить функцией EnumDevices().
Функция EnumDevices() обнаруживает устройства ввода, установленные на данном компьютере. Она позволяет составить список устройств по их типу, по факту их текущего подключения к компьютеру и по тому, являются ли они устройствами с обратной связью. Для каждого устройства, обнаруженного функцией EnumDevices(), предоставляются два значения GUID: GUID экземпляра и GUID продукта. GUID экземпляра идентифицирует конкретное устройство, а GUID продукта — его тип. При вызове функции CreateDevice() используется GUID экземпляра.
С помощью функции GetDeviceStatus() можно определить, доступно ли устройство для DirectInput в данный момент. Код возврата DI_OK означает, что устройство подключено и доступно. Функция RunControlPanel() вызывает приложение DirectInput Control Panel. Точнее, она вызывает приложение DirectX Control Panel и активизирует вкладку DirectInput.
Доступ ко всем устройствам ввода, представленным в DirectInput, осуществляется через интерфейс DirectInputDevice. Интерфейс DirectInputDevice содержит следующие функции:
• Acquire()
• Unacquire()
• GetCapabilities()
• GetDeviceData()