关于控制台:在原始模式下使用termion时,如何创建新行?

How do I create a new line when using termion in raw mode?

我正在使用术语箱来像事件监听器一样使用termion::raw::IntoRawMode捕获用户输入键。我无法在控制台中打印新行,因为stdout处于"原始模式",并且\
无法识别为新行。我不知道如何在调用draw_user方法时禁用原始模式,然后返回原始模式以再次监听键事件。

第二行以空格开头,但我不知道为什么:

enter image description here

这是我的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
extern crate termion;

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

fn main() {
    let mut x: u16 = 1;
    let mut y: u16 = 1;
    // Get the standard input stream.
    let stdin = stdin();
    // Get the standard output stream and go to raw mode.
    let mut stdout = stdout().into_raw_mode().unwrap();
    write!(
        stdout,
       "{}{}{}",
        // Clear the screen.
        termion::clear::All,
        // Goto (1,1).
        termion::cursor::Goto(1, 1),
        // Hide the cursor.
        termion::cursor::Hide
    ).unwrap();
    // Flush stdout (i.e. make the output appear).
    stdout.flush().unwrap();

    for c in stdin.keys() {
        // Clear the current line.
        write!(
            stdout,
           "{}{}{}",
            termion::cursor::Goto(1, 1),
            termion::clear::CurrentLine,
            termion::clear::BeforeCursor
        ).unwrap();

        // Print the key we type...
        match c.unwrap() {
            // Exit.
            Key::Char('q') => break,
            Key::Ctrl('c') => break,
            Key::Left => {
                x -= 1;
                draw_user(&mut x, &mut y);
            }
            Key::Right => {
                x += 1;
                draw_user(&mut x, &mut y);
            }
            Key::Up => {
                y -= 1;
                draw_user(&mut x, &mut y);
            }
            Key::Down => {
                y += 1;
                draw_user(&mut x, &mut y);
            }
            _ => println!(""),
        }
        stdout.flush().unwrap();
    }
    // Show the cursor again before we exit.
    write!(
        stdout,
       "{}{}",
        termion::cursor::Show,
        termion::clear::AfterCursor
    ).unwrap();
    // Flush again.
}

fn draw_user(x: &mut u16, y: &mut u16) {
    let termsize = termion::terminal_size().ok();
    let termwidth = termsize.map(|(w, _)| w - 10).unwrap();
    let termheight = termsize.map(|(_, h)| h).unwrap();
    if *x < 1 {
        *x = 1;
    }
    if *x > termwidth {
        *x = termwidth;
    }
    if *y < 1 {
        *y = 1;
    }
    if *y > termheight {
        *y = termheight;
    }
    //println!("x:{}, y:{}", *x, *y);
    for h in 1..termheight + 1 {
        for w in 1..termwidth + 1 {
            //print!("w:{}",w);
            if h == *y && w == *x {
                print!("?");
            } else {
                print!("*");
            }
        }
        println!("");
    }
}


严格来说,换行符\
的意思是"转到同一位置的下一行"。为了从行的开头开始,您需要添加回车符\
,这意味着"转到当前行的开头"。这就是为什么Windows中的文本文件使用\
\
组合来标记行尾的原因。 Unix和MacOS认为,这种区分对计算机文件没有意义,并且在空间有限的时候占用了一个额外的字节,因此他们决定在文本文件中使用单个字符。出于同样的原因,计算机控制台有两种模式,一种是在收到换行符时自动添加回车符(以减少通过串行线路连接控制台时传输的数据量),另一种是严格应用它们被发送(用于细粒度控制)。由于您已调用stdout().into_raw_mode(),因此您处于第二种模式,需要手动输出回车。

此行为是从旧的机械打字机继承而来的,其中"换行"是使纸张前进一行的键,而"回车"则是一个允许手动将车架移回到起始位置的手柄。