Alrott SlimRG Asked:2020-05-15 06:49:33 +0000 UTC2020-05-15 06:49:33 +0000 UTC 2020-05-15 06:49:33 +0000 UTC 在控制台中捕获键盘快捷键 772 1)代码必须是跨平台的,或者2a代码选项(在Windows和Linux下) 2)我需要跟踪用户何时按下 Shift+Enter 3) 控制台程序 c++ 1 个回答 Voted Best Answer Dmitry Sokolov 2020-06-29T01:24:50Z2020-06-29T01:24:50Z 有可能是这样的: #include <iostream> #include <array> #include <vector> #include <set> #include <functional> template <typename T> void throw_if(T val, T err, const char* msg) { if (val == err) { throw std::runtime_error(msg); } } template <typename T> 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 <windows.h> 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<bool(Key_t, bool)> 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<INPUT_RECORD, 128> 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 <termios.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <linux/kd.h> 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<const char*> 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<bool(Key_t, bool)> 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<Key_t>& 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<Key_t> 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、Ubuntu 16.04 上测试。 在正常模式下,无法获取特殊键(Shift、Alt、Ctrl 等)的代码,因为 输入被缓冲并翻译成字符(或行)。因此,控制台被置于“原始”模式(没有缓冲和翻译)。 键码:Windows - Virtula-Key Codes,Linux - 单向键盘输入。 在 Windows 上使用控制台非常简单,阅读输入缓冲区事件文档中对此进行了描述。在 Linux 上 - 请参阅其中的答案和链接 - https://stackoverflow.com/a/29446193/2267114 ,加上showkey实用程序代码,以及ioctl 代码的可用描述。
有可能是这样的:
在 Win 10、Ubuntu 16.04 上测试。
在正常模式下,无法获取特殊键(Shift、Alt、Ctrl 等)的代码,因为 输入被缓冲并翻译成字符(或行)。因此,控制台被置于“原始”模式(没有缓冲和翻译)。
键码:Windows - Virtula-Key Codes,Linux - 单向键盘输入。
在 Windows 上使用控制台非常简单,阅读输入缓冲区事件文档中对此进行了描述。在 Linux 上 - 请参阅其中的答案和链接 - https://stackoverflow.com/a/29446193/2267114 ,加上showkey实用程序代码,以及ioctl 代码的可用描述。