Беда в том, что _getch() считывает некоторые символы дважды и т.о. такой, к примеру, код:
int i = 0;
_getch();
std::cout << i << std::endl;
_getch();
std::cout << i + 1 << std::endl;
_getch();
std::cout << i + 2 << std::endl;
_getch();
std::cout << i + 3 << std::endl;
std::system("pause");
работает некорректно при нажатии на, допустим, delete(игнорирует следующий _getch()).
Какие есть альтернативы или костыли непосредственно для Windows, чтобы все работало нормально? Заранее спасибо.
Особенность _getch
в следующем: если нажата 'обычная' клавиша (любой печатаемый символ) функция возвращает ascii-код нажатой клавиши, если же нажата специальная или управляющая клавиша (стрелки, f1-f12) тогда функция возвращает специальный число маркер и оставляет в буфере еще один символ (когда у вас _getch
срабатывал два раза).
Но на этом особенности не заканчиваются. Продемонстрирую на примере следущую возможную проблему:
Допустим у нас есть код: int a = _getch(); int b = _getch();
и мы хотим таким образом обработать нажатие специальных клавиш.
a
записывается одно значение._getch
в b
записывается значение из очереди.
Здесь и кроется проблема - это значение может быть идентично с каким-нибудь печатаемым символом. И разобрать был введен символ или стрелка невозможно.Собственно, решение. Пользуйтесь.
windows console input tools (заголовочный файл)
#include <conio.h> // _getch, _kbhit
// специальные клавиши
enum class keyboard : unsigned short
{
enter = 13,
escape = 27,
space = 32,
bspace = 8,
tab = 9,
del = 83,
left = 300, // для спец символов сами придумываем значения > 255 (для char)
up,
right,
down,
cup,
home,
end,
f1,
f2,
f3,
f4,
f5,
f6,
f7,
f8,
f9,
f10,
f11
};
// возвращает нажатый символ
int get_key();
// возвращает нажатый символ, если была нажата клавиша
// или 0 - если клавиша не нажата
int aget_key();
// очищает очередь нажатий с клавиатуры
void flush();
windows console input tools (реализация)
// включение заголовочного файла
// реальные ascii-кода
enum ascii_codes
{
ascii_Home = 71,
ascii_End = 79,
ascii_Right = 77,
ascii_Left = 75,
ascii_Up = 72,
ascii_Down = 80,
ascii_F1 = 59,
ascii_F2, // дальше +1
ascii_F3,
ascii_F4,
ascii_F5,
ascii_F6,
ascii_F7,
ascii_F8,
ascii_F9,
ascii_F10,
ascii_F11
};
// общая реализация, дальше будет понятно зачем
int _common_get_key()
{
auto key = _getch(); // считываем
if (_kbhit()) // проверяем есть ли в очереди еще символы
{ // если есть - значит нажата спец.клавиша
key = _getch(); // считываем следующий символ
// хардкодим
switch (key)
{
case ascii_Home: return static_cast<int>(keyboard::home);
case ascii_End: return static_cast<int>(keyboard::end);
// далее по аналогии
// ...
}
}
return key;
}
// flush реализация
void flush()
{
while (_kbhit()) // пока есть символы
_getch(); // извлекаем их
}
// get_key реализация
int get_key()
{
// дабы избежать неприятностей get_key всегда должен
// очищать очередь перед чтением
flush();
return _common_get_key();
}
// aget_key реализация
int aget_key()
{
if (_kbhit()) // клавиша нажата?
return _common_get_key(); // возврат без очистки очереди
// клавиша не нажата, по договоренности return 0
return 0;
}
Для комфортного использования перечисления keyboard
можно в заголовочный файл добавить следующие шаблоны:
tools
#include <xutility> // std::enable_if, std::is_same, std::is_integral, std::underlying_type
template <typename _Left,
typename _Right,
typename = std::enable_if_t<
(std::is_integral_v<_Left> && std::is_same_v<keyboard, std::decay_t<_Right>>)
||
(std::is_integral_v<_Right> && std::is_same_v<keyboard, std::decay_t<_Left>>)
>>
inline constexpr bool
operator==(_Left left, _Right right) noexcept
{
return (static_cast<std::underlying_type_t<keyboard>>(left)
== static_cast<std::underlying_type_t<keyboard>>(right));
}
template <typename _Left,
typename _Right,
typename = std::enable_if_t<
(std::is_integral_v<_Left> && std::is_same_v<keyboard, std::decay_t<_Right>>)
||
(std::is_integral_v<_Right> && std::is_same_v<keyboard, std::decay_t<_Left>>)
>>
inline constexpr bool
operator!=(_Left left, _Right right) noexcept
{
return (static_cast<std::underlying_type_t<keyboard>>(left)
!= static_cast<std::underlying_type_t<keyboard>>(right));
}
note: sfinae
Пример использования:
test
// includes
#include <iostream>
int main()
{
int key = 0;
do
{
// можно поэксперементировать с асинхронным вводом
key = get_key();
if (key == keyboard::left)
std::cout << "Left arrow was pressed\n";
else if (key == keyboard::enter)
std::cout << "Enter was pressed\n";
else if (key == 'F')
std::cout << "'F' key was pressed\n";
} while (key != keyboard::escape);
return 0;
}
Не забывайте, что это решение только для систем Windows.
Это не беда, а большая и нужная вещь :)
Как вы отличите, какой +, например, нажат - обычный или "серый"? Как вы отловите функциональные клавиши? Стрелки?
А он это все позволяет отлавливать! Именно возвращая для таких клавиш пару символов (т.е. возвращая в двух вызовах два байта).
А если вам просто для остановки - ну, напишите типа
_getch(); while(kbhit()) _getch();
(надо только посмотреть, там подчеркивание нужно или нет в kbhit
).
В общем, если это будет кому-то полезно: чтобы обрабатывались нажатия на стрелки в цикле типа:
while(1)
{
int button = _getch();
if(button == 80)//сделать что-то при нажатии на стрелку
}
Я добавил еще один _getch() в начале. И все заработало.
Поправьте, пожалуйста, если я несу какую-то чушь.
Виртуальный выделенный сервер (VDS) становится отличным выбором
mingw32 и mingw64 - как отличить в какой среде оно запущено ? Вариантов минимум два, или на Linux, или на Windows
ЗдравстуйтеЕсть необходимость собрать проект весом не более 1 мб, но 1 класс в проекте весит около 13мб
Подскажите, можно ли через WinApi определить кодировку переданной строки char* ? Мне нужно по переданной строке сравнивать является и она Utf8 или...
Возможно ли вообще использовать эту библиотеку на mac и как это сделать?