RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 981510
Accepted
Alrott SlimRG
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 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. 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 代码的可用描述。

    • 10

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    根据浏览器窗口的大小调整背景图案的大小

    • 2 个回答
  • Marko Smith

    理解for循环的执行逻辑

    • 1 个回答
  • Marko Smith

    复制动态数组时出错(C++)

    • 1 个回答
  • Marko Smith

    Or and If,elif,else 构造[重复]

    • 1 个回答
  • Marko Smith

    如何构建支持 x64 的 APK

    • 1 个回答
  • Marko Smith

    如何使按钮的输入宽度?

    • 2 个回答
  • Marko Smith

    如何显示对象变量的名称?

    • 3 个回答
  • Marko Smith

    如何循环一个函数?

    • 1 个回答
  • Marko Smith

    LOWORD 宏有什么作用?

    • 2 个回答
  • Marko Smith

    从字符串的开头删除直到并包括一个字符

    • 2 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5