#cpp #консоль #ввод
1) Код должен быть или кроссплатформенный, или 2а варианта кода (под Windows и Linux) 2) Мне нужно отследить, когда пользователь нажмет Shift+Enter 3) Программа консольная
Ответы
Ответ 1
Можно так: #include#include #include #include #include template void throw_if(T val, T err, const char* msg) { if (val == err) { throw std::runtime_error(msg); } } template void throw_if_not(T val, T ok, const char* msg) { if (val != ok) { throw std::runtime_error(msg); } } enum class Key_t { Unknown, Shift, Return, Esc }; #if defined(_MSC_VER) #include Key_t to_Key_t(WORD vk) { switch (vk) { case VK_SHIFT: return Key_t::Shift; case VK_RETURN: return Key_t::Return; case VK_ESCAPE: return Key_t::Esc; default: return Key_t::Unknown; } } void read_kb(std::function key_handler) { auto stdout_handle = GetStdHandle(STD_INPUT_HANDLE); throw_if(stdout_handle, INVALID_HANDLE_VALUE, "could not get STD_INPUT_HANDLE"); DWORD orig_mode; throw_if(GetConsoleMode(stdout_handle, &orig_mode), FALSE, "could not get console mode"); throw_if(SetConsoleMode(stdout_handle, orig_mode | ENABLE_WINDOW_INPUT), FALSE, "could not set console mode"); try { for (bool done = false; !done; ) { std::array ir; DWORD count; throw_if(ReadConsoleInput(stdout_handle, ir.data(), ir.size(), &count), FALSE, "coud not read console input"); std::for_each(std::cbegin(ir), std::cbegin(ir) + count, [&done, key_handler](const INPUT_RECORD& r) { if (r.EventType == KEY_EVENT) { const auto& e = r.Event.KeyEvent; done = key_handler(to_Key_t(e.wVirtualKeyCode), e.bKeyDown); } }); } } catch (...) { SetConsoleMode(stdout_handle, orig_mode); throw; } SetConsoleMode(stdout_handle, orig_mode); } #else #include #include #include #include #include bool is_a_console(int fd) { char arg = 0; return (isatty(fd) && ioctl(fd, KDGKBTYPE, &arg) == 0 && ((arg == KB_101) || (arg == KB_84))); } int open_a_console(const char *fn) { int fd = open(fn, O_RDWR); if (fd < 0) { fd = open(fn, O_WRONLY); } if (fd < 0) { fd = open(fn, O_RDONLY); } if (fd < 0) { return -1; } return fd; } int getfd() { int fd; std::vector fn = { "/proc/self/fd/0", "/dev/tty", "/dev/tty0", "/dev/vc/0", "/dev/systty", "/dev/console" }; for (const auto& s : fn) { fd = open_a_console(s); if (is_a_console(fd)) { return fd; } close(fd); } for (fd = 0; fd < 3; fd +=1) if (is_a_console(fd)) { return fd; } throw std::runtime_error("could not get console fd"); } Key_t to_Key_t(const char c) { switch (c) { case 1: return Key_t::Esc; case 42: case 54: return Key_t::Shift; default: return Key_t::Unknown; } } void read_kb(std::function key_handler) { int fd = getfd(); termios orig; throw_if_not(tcgetattr(fd, &orig), 0, "could not get attr"); int kbmode_orig; throw_if_not(ioctl(fd, KDGKBMODE, &kbmode_orig), 0, "could not get KDGKBMODE"); termios raw = orig; raw.c_lflag &= ~(ECHO | ICANON); raw.c_cc[VMIN] = 0; raw.c_cc[VTIME] = 1; tcsetattr(fd, TCSANOW, &raw); throw_if_not(ioctl(fd, KDSKBMODE, K_RAW), 0, "could not set KDSKBMODE"); try { for (bool done = false; !done; ) { char c = 0; if (read(fd, &c, 1) == 1) { done = key_handler(to_Key_t(c & 0x7F), !(c & 0x80)); } } } catch (...) { tcsetattr(fd, TCSANOW, &orig); ioctl(fd, KDSKBMODE, kbmode_orig); throw; } tcsetattr(fd, TCSANOW, &orig); ioctl(fd, KDSKBMODE, kbmode_orig); } #endif bool is_Shift_Return(const std::set & keys) { return keys.size() == 2 && keys.find(Key_t::Shift) != std::end(keys) && keys.find(Key_t::Return) != std::end(keys); } int main() { try { std::set keys; read_kb([&keys](Key_t key, bool is_down) { if (is_down) { keys.insert(key); } else { keys.erase(key); } std::cout << (is_Shift_Return(keys) ? "Shift+Return" : "Unknown") << " key is " << (is_down ? "down" : "up") << std::endl; return (key == Key_t::Esc); }); } catch (const std::exception& e) { std::cout << "Error: " << e.what() << std::endl; return 1; } return 0; } Проверено на Win 10, Ubintu 16.04. В обычном режиме коды специальных клавиш (Shift, Alt, Ctrl, и т.п.) получить не удастся, т.к. ввод буферизируется и транслируется в символы (или строки). Поэтому консоль переводится в "сырой" режим (без буферизации и трансляции). Коды клавиш: Windows - Virtula-Key Codes, Linux - одним из способов Keyboard input. Работа с консолью в Windows - все достаточно тривиально, и описано в доках Reading Input Buffer Events. В Linux - см. ответ и ссылки из него - https://stackoverflow.com/a/29446193/2267114, плюс код утилиты showkey, плюс доступное описание кодов ioctl.
Комментариев нет:
Отправить комментарий