【问题标题】:Detect keydown?检测按键?
【发布时间】:2020-02-08 19:44:33
【问题描述】:

我想检测 Rust 中的 keydown 事件,然后检查是否按下了组合键,以便在此基础上执行进一步的操作。 所以基本上在我的 Rust 应用程序中支持键盘快捷键

我查看了一些板条箱,例如 ncurses,但它们不符合我的要求...

【问题讨论】:

  • 键盘处理是系统特定的,它取决于你正在编写什么样的程序:如果它是控制台或 GUI,如果它是 Windows、Linux、Mac、WebAssembly 或其他,如果你使用工具包或本机绑定...
  • 我想支持 Windows、Linux 和 Mac。我目前使用 web-view 作为我的 gui,并希望在系统级别检测按键。

标签: rust keyboard keyboard-events rust-cargo


【解决方案1】:

ANSI 终端(Linux、macOS)的最佳解决方案

如果您不需要 Windows 支持,那么最好是 termion

这是一个用于操作终端的库。您可以在其中检测关键事件甚至键盘快捷键。而且它也真的很轻!仅 22.78 kB(截至 1.5.5 版)。

这是我整理的一个快速程序,用于展示一些快捷方式。

将此代码添加到main.rs,将termion = "1.5.5" 添加到Cargo.toml 并以cargo run 开头!

use std::io::{stdin, stdout, Write};
use termion::event::Key;
use termion::input::TermRead;
use termion::raw::IntoRawMode;

fn main() {
    let stdin = stdin();
    //setting up stdout and going into raw mode
    let mut stdout = stdout().into_raw_mode().unwrap();
    //printing welcoming message, clearing the screen and going to left top corner with the cursor
    write!(stdout, r#"{}{}ctrl + q to exit, ctrl + h to print "Hello world!", alt + t to print "termion is cool""#, termion::cursor::Goto(1, 1), termion::clear::All)
            .unwrap();
    stdout.flush().unwrap();

    //detecting keydown events
    for c in stdin.keys() {
        //clearing the screen and going to top left corner
        write!(
            stdout,
            "{}{}",
            termion::cursor::Goto(1, 1),
            termion::clear::All
        )
        .unwrap();

        //i reckon this speaks for itself
        match c.unwrap() {
            Key::Ctrl('h') => println!("Hello world!"),
            Key::Ctrl('q') => break,
            Key::Alt('t') => println!("termion is cool"),
            _ => (),
        }

        stdout.flush().unwrap();
    }
}

跨平台解决方案

如果您需要支持 Windows 和所有其他平台,那么您可以使用 crossterm。这是一个相当不错的库,比 termion 重得多。它是 98.06 kB(从 0.16.0 版开始)。

这是与上面相同的程序,但使用 crossterm 编写。

将此代码添加到main.rs,将crossterm = "0.16.0" 添加到Cargo.toml 并尝试使用cargo run

//importing in execute! macro
#[macro_use]
extern crate crossterm;

use crossterm::cursor;
use crossterm::event::{read, Event, KeyCode, KeyEvent, KeyModifiers};
use crossterm::style::Print;
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType};
use std::io::{stdout, Write};

fn main() {
    let mut stdout = stdout();
    //going into raw mode
    enable_raw_mode().unwrap();

    //clearing the screen, going to top left corner and printing welcoming message
    execute!(stdout, Clear(ClearType::All), cursor::MoveTo(0, 0), Print(r#"ctrl + q to exit, ctrl + h to print "Hello world", alt + t to print "crossterm is cool""#))
            .unwrap();

    //key detection
    loop {
        //going to top left corner
        execute!(stdout, cursor::MoveTo(0, 0)).unwrap();

        //matching the key
        match read().unwrap() {
            //i think this speaks for itself
            Event::Key(KeyEvent {
                code: KeyCode::Char('h'),
                modifiers: KeyModifiers::CONTROL,
                //clearing the screen and printing our message
            }) => execute!(stdout, Clear(ClearType::All), Print("Hello world!")).unwrap(),
            Event::Key(KeyEvent {
                code: KeyCode::Char('t'),
                modifiers: KeyModifiers::ALT,
            }) => execute!(stdout, Clear(ClearType::All), Print("crossterm is cool")).unwrap(),
            Event::Key(KeyEvent {
                code: KeyCode::Char('q'),
                modifiers: KeyModifiers::CONTROL,
            }) => break,
            _ => (),
        }
    }

    //disabling raw mode
    disable_raw_mode().unwrap();
}

我不会撒谎,这比termion 解决方案更难阅读,但它的作用相同。我之前没有使用crossterm 的经验,所以这段代码实际上可能不是最好的,但它很不错。

正在寻找一种仅检测不带任何修饰符(ShiftControlAlt)的按键的方法?检查这个简化的代码:

//-- code --

loop {
    //--code--

    match read().unwrap() {
        Event::Key(KeyEvent {
            code: KeyCode::Char('a'),
            modifiers: KeyModifiers::NONE,
        }) => //--code--
    }

    //--code--
}

//--code--

这里重要的部分是KeyModifiers::NONE的使用。

【讨论】:

  • 一开始我很惊讶,但后来我发现termion目前不支持windows。不幸的是,这对我来说是一个交易障碍。
  • @SephReed 检查答案底部的编辑,应该有解决您问题的方法。
  • 我对 Rust 很陌生。回到我的 C/C++ 时代,我可以为此使用 getch()getchar() 和其他几个内置函数。在rust中,我必须使用第三方库来实现这个吗?是因为 Rust 仍处于起步阶段还是它的构建方式(功能非常少)?
  • @JamesPoulose 你仍然可以像这样在 Rust 中使用getchar()extern "C" { fn getchar() -> std::os::raw::c_int; } 并省略任何第三方库,但这样做显然是不安全的。
  • 不要使用KeyModifiers::empty(),因为这2个no_modifiers实际上是2个不同的变量。第一个被声明但从未使用过,第二个就像一个通配符,用于解构枚举。请改用KeyModifiers::NONE
【解决方案2】:

您可以使用console 作为简单的跨平台解决方案。

use console::Term;

fn main() {
    let stdout = Term::buffered_stdout();

    'game_loop: loop {
        if let Ok(character) = stdout.read_char() {
            match character {
                'w' => todo!("Up"),
                'a' => todo!("Left"),
                's' => todo!("Down"),
                'd' => todo!("Right"),
                _ => break 'game_loop,
            }
        }
    }
}

上面的 sn-p 显示了一个为平台匹配常见移动字符的基本示例。

【讨论】:

    猜你喜欢
    • 2012-10-11
    • 2016-08-01
    • 2013-08-17
    • 1970-01-01
    • 1970-01-01
    • 2012-10-10
    • 2014-07-27
    • 2013-12-14
    相关资源
    最近更新 更多