nc_std

DirectInput для клавиатуры.

последнее обновление 23.10.2003

Речь пойдет о том, как можно использовать средства DirectX для того, чтобы получать информацию о состоянии клавиш клавиатуры. Для компиляции этого кода необходимы хеадеры и библиотеки из DX8SDK: dinput8.lib и dxguid.lib.
За основу своей реализации я взял работу Mr.Snow, дополнил ее новыми возможностями и переделал некоторые функции. Структура осталась практически без изменений, и, ИМХО, она лучшая из того, что мне доводилось видеть.

Реализация

class CncInput
{
private:
  LPDIRECTINPUT8 Input;

  void Acquire();
  void Unacquire();

public:
  CncInput();
  ~CncInput();
  int  Create(HINSTANCE hInst);
  int  CreateKeyboard(HWND hWnd);
  void Destroy();
  void Update();

  class CncKeyboard
  {
    friend class CncInput;
    private:
      LPDIRECTINPUTDEVICE8 Device;
      bool                 Use;
      unsigned char*       KeysDown;
      unsigned char*       KeysUp;

      void Update();
      bool IsComboKeyPress(int iComboKey);

    public:
      bool IsBtnDown(BYTE key);
      bool IsBtnUp(BYTE key);
      bool IsBtnPress(BYTE key);
      BYTE WhatDown();
      char WhatAlphaNumDown();
      bool IsComboDown(int iComboKey, BYTE key);
      bool IsComboPress(int iComboKey, BYTE key);
      bool IsComboUp(int iComboKey, BYTE key);

  } Kbd;
};

Данная реализация позволяет отслеживать первое нажатие на клавишу, отпускание клавиши и получать текущее состояние: функции IsBtnDown(), IsBtnUp() и IsBtnPress() соответственно. Параметр key - это код клавиши, пользуйтесь константами, объявленными в "dinput.h". Функция WhatDown() позволяет отработать ситуацию "press any key", или "нажмите любую клавишу".

Пример использования

// в WinMain или в функции инициализации стартовых настроек программы
  if(ncInput.Create(hInstance)) ncInput.CreateKeyboard(hWnd);

// hot key test
int HotKeyTest()
{
  ncInput.Update();
  static bool iWireMode = 0;
  if(ncInputKbd.IsBtnDown(DIK_ESCAPE))
  {
     PostQuitMessage(0);
     return 1;
  }
  if(ncInputKbd.IsBtnDown(DIK_W))
  {
    iWireMode = !iWireMode;
    if(iWireMode)
      glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
    else
      glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
  }
  return 0;
}

Иногда нужно получить символьное обозначение нажатой клавиши, например, когда пользователь вводит свое имя. Для этого была создана функция WhatAlphaNumDown(), которая возвращает символ нажатой клавиши. Код функции:

// alfa num key test down
char CncInput::CncKeyboard::WhatAlphaNumDown()
{
  bool bShift = IsBtnPress(DIK_LSHIFT)||IsBtnPress(DIK_RSHIFT);
  for (int i = 0; i < sizeof(inpAlphaMap); i++)
  if (IsBtnDown(inpAlphaMap[i]))
  	return bShift ? inpAlphaCharShift[i] : inpAlphaChar[i];
  return 0;
}

Для работы функции необходимы следующие массивы:

BYTE inpAlphaMap[] =
{
	DIK_Q,DIK_W,DIK_E,DIK_R,DIK_T,DIK_Y,DIK_U,
	DIK_I,DIK_O,DIK_P,DIK_A,DIK_S,DIK_D,DIK_F,DIK_G,DIK_H,DIK_J,
	DIK_K,DIK_L,DIK_Z,DIK_X,DIK_C,DIK_V,DIK_B,DIK_N,DIK_M,DIK_MINUS,
	DIK_0,DIK_1,DIK_2,DIK_3,DIK_4,DIK_5,DIK_6,DIK_7,DIK_8,DIK_9
};
char* inpAlphaChar      = "qwertyuiopasdfghjklzxcvbnm-0123456789";
char* inpAlphaCharShift = "QWERTYUIOPASDFGHJKLZXCVBNM_)!@#$%^&*(";

Я не смог средствами DirectInput принимать ввод пользователя с учетом выбранного языка, для этого приходится использовать WinAPI. Примерно так:

  BYTE ks[256];
  GetKeyboardState(ks);
  DWORD vk = MapVirtualKey(i, 1);
  wchar_t ch[10];
  ToUnicode(vk, i, ks, ch, 10, 0);

, где i - скан код нажатой клавиши, в данном случае можно использовать код DirectInput'a. Состояние шифт-клавиш - учитывается. Необходимо помнить, что для того чтобы работала функция GetKeyboardState() клавиатурный девайс должен быть создан не в эксклюзивном режиме.

Поидее можно вместо DirectInput пользоваться аналогией из WinAPI (GetKeyboardState), но такая технология не поддерживает уникальности клавиш, т.е. дополнительная клавиатура будет возвращать различные коды клавиш при включенном или выключенном NumLock.

Иногда бывает нужно обрабатывать нажатие не одной клавиши а целой комбинации, например Ctrl+O. Реализовать проверку на такую комбинацию можно следующим образом:

if ( (Kbd.IsBtnPress(DIK_LCONTROL) || Kbd.IsBtnPress(DIK_RCONTROL))
  && Kbd.IsBtnDown(DIK_O))
  ... // обработка

Запись получилась слишком громоздская. Для удобства были введены функции IsComboDown(), IsComboPress() и IsComboUp(). Первый параметр этих функций флаг, указывающий какие управляющие клавиши участвуют в комбинации, второй - код кнопки, замыкающей комбинацию.

Флаг может принимать одно или комбинацию из следующих значений:
COMBO_ALT, COMBO_RALT, COMBO_LALT, (любой альт, только правый и только левый)
COMBO_CTRL, COMBO_RCTRL, COMBO_LCTRL,
COMBO_SHIFT, COMBO_RSHIFT, COMBO_LSHIFT

Пример использования:

if ( (Kbd.IsComboDown(COMBO_CTRL, DIK_O))
  ... // обработка

Запись стала короче и понятнее.

Вот, в принципе, и все.
исходный код.

Всем успехов в труде и творчестве!




С вопросами и предложениями прошу обращаться на мыло Cupper, или оставлять в Гостевой книге
Hosted by uCoz