1) Код должен быть или кроссплатформенный, или 2а варианта кода (под Windows и Linux)
2) Мне нужно отследить, когда пользователь нажмет Shift+Enter
3) Программа консольная
Можно так:
#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, 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.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Могут ли возникнуть проблемы с работой со строками если использовать не char* /const char*, а unsigned char*/const unsigned char*? Если все правильно, к примеру, в UTF-8,...