如何减少端子重新绘制时的闪烁



我有一个程序,它显示在并行中运行的一些命令的状态

fmt    ✔
clippy cargo clippy --tests --color always ... 
tests  cargo test --color always ..

该程序是我的第一个依赖于多线程的程序,我有一些线程在程序"启动"后立即运行这些程序;可用";,我有一个线程(主线程(专门用于等待新的结果(这是非常罕见的,因为作业往往运行至少几秒钟,而且作业相对较少,最多10个并行(和删除&循环印刷事物的状态。

在软件的这一部分,我不打印命令的输出,只打印正在运行的命令和一些ascii微调器。

我不知道这些事情应该怎么做,所以我设法将重绘限制在至少40毫秒:

const AWAIT_TIME: Duration = std::time::Duration::from_millis(40);
fn delay(&mut self) -> usize {
let time_for = AWAIT_TIME
- SystemTime::now()
.duration_since(self.last_occurence)
.unwrap();
let millis: usize = std::cmp::max(time_for.as_millis() as usize, 0);
if millis != 0 {
sleep(time_for);
}
self.last_occurence = SystemTime::now();
millis
}
while let Some(progress) = read(&rx) { ... }    
job_display.refresh(&tracker, delay);
delay = job_starter.delay();

因此,我最终跟踪了写入的行数和字符数,并将其全部删除:


struct TermWrapper {
term: Box<StdoutTerminal>,
written_lines: u16,
written_chars: usize,
}
...
pub fn clear(&mut self) {
(0..self.written_lines as usize).for_each(|_| {
self.term.cursor_up().unwrap();
self.term.carriage_return().unwrap();
self.term.delete_line().unwrap();
});
self.written_lines = 0;
self.written_chars = 0;
}

它可以工作,但它往往会闪烁,尤其是在嵌入式终端中。

我的下一个想法是存储打印字符串的哈希,如果可以的话,跳过重画。

是否有一些已知的模式可以应用于获得更好的输出?

我可以使用哪些常用策略?

在更新终端时保证不闪烁的最低要求是:不要发送一个东西,然后用其他东西覆盖它(在绘图的单个"框架"内(。在清除的情况下,我们可以更具体地重申该规则:不要清除要放入文本的区域。相反,只清除您知道没有放入文本的地区(以防之前有文本(。

传统的终端命令集包含一个非常有用的工具:"清除行尾"命令。你可以使用它的方式是:

  1. 将光标移动到要替换中文本的行的开头
  2. 编写文本,结尾不带换行符或CRLF
  3. 写下"清除到行尾"。(在crossterm中,这是ClearType::UntilNewLine。(

发送clear命令后,该行的其余部分将被清除(就像您碰巧写入了完全填充该行的确切空格数一样(。通过这种方式,你需要跟踪你在哪行写,但你不需要跟踪你写的每个字符串的确切宽度


除此之外的下一步,对任意2D屏幕布局都很有用,就是记住之前发送到终端的文本,只发送需要更改的内容——在Rust中,tui机箱提供了这一功能,您还可以找到与众所周知的C库curses的绑定,以实现同样的目的。

最新更新