Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] cursive with crossterm or termion backends redraws the screen every time #667

Open
vi opened this issue Jul 29, 2022 · 4 comments
Open
Labels

Comments

@vi
Copy link

vi commented Jul 29, 2022

Describe the bug

With default ncurses backend, cursive applications feel smooth and does not blink in e.g. Alacritty.
But with other backends (crossterm and termion), it seems like it just redraws the entire screen each time something is updated.

Also in scenario where ncurses backend write only 11 KB to the tty, but with the pure-Rust backends I see 174 KB of data. This may become problematic if TUI applications are accessed over SSH with bad network.

To Reproduce

  1. Build some cursive application with multiple backends.
Example application ``` extern crate cursive_table_view;

use std::cmp::Ordering;

use cursive::{
align::HAlign,
traits::{Nameable, Resizable},
views::{TextView, LinearLayout, TextArea},
Cursive, With,
};
use cursive_table_view::{TableView, TableViewItem};

#[derive(Copy, Clone, PartialEq, Eq, Hash)]
enum MyColumn {
Name,
Count,
Rate,
}

#[derive(Clone, Debug)]
struct Foo {
name: String,
count: usize,
rate: usize,
}

impl TableViewItem for Foo {
fn to_column(&self, column: MyColumn) -> String {
match column {
MyColumn::Name => self.name.to_string(),
MyColumn::Count => format!("{}", self.count),
MyColumn::Rate => format!("{}", self.rate),
}
}

fn cmp(&self, other: &Self, column: MyColumn) -> Ordering
where
    Self: Sized,
{
    match column {
        MyColumn::Name => self.name.cmp(&other.name),
        MyColumn::Count => self.count.cmp(&other.count),
        MyColumn::Rate => self.rate.cmp(&other.rate),
    }
}

}

fn main() {
let mut siv = cursive::default();

// Configure the actual table
let table = TableView::<Foo, MyColumn>::new()
    .column(MyColumn::Name, "Name", |c| c.width(20))
    .column(MyColumn::Count, "Count", |c| c.align(HAlign::Center))
    .column(MyColumn::Rate, "Rate", |c| {
        c.ordering(Ordering::Greater).align(HAlign::Right).width(20)
    })
    .default_column(MyColumn::Name)
    .with(|table| {
        table.insert_item(Foo {
            count: 3,
            name: "QQQ".to_owned(),
            rate: 4,
        });
        table.insert_item(Foo {
            count: 2,
            name: "WWW".to_owned(),
            rate: 40,
        });
        table.insert_item(Foo {
            count: 0,
            name: "EEE".to_owned(),
            rate: 0,
        });
    })
    .on_submit(|siv: &mut Cursive, _row: usize, index: usize| {
        siv.call_on_name("q", |t: &mut TableView<Foo, MyColumn>| {
            if let Some(i) = t.borrow_item_mut(index) {
                i.count += 1;
            }
        });
    });

let linear_layout = LinearLayout::vertical()
    .child(TextView::new("Top of the page"))
    .child(TextArea::new().fixed_height(8).full_width())
    .child(table.with_name("q").full_height().full_width());

siv.add_fullscreen_layer(linear_layout.full_screen());

siv.run();

}

</details>

2. Run those multiple versions on Linux (e.g. in Alacritty + tmux)
3. Compare how the feel

**Expected behavior**

Applications differ only by their filesize and dynamic dependencies, maybe some tricky feature support.

**Environment**
* Linux Debian, Xorg
* Backend used: ncurses, crossterm, termion
* Current locale: C.UTF-8
* Cursive version: 0.18.0

**Other notes**

If this cannot be fixed easily, then it should be documented in ["Backends" wiki article](https://github.com/gyscos/cursive/wiki/Backends).
This blinking and tty bandwidth usage may be overseen by deploying developer, who might think that by switching to crossterm/termion one just gains easy deployment without any apparent loss in functionality. 
@vi vi added the bug label Jul 29, 2022
@gyscos
Copy link
Owner

gyscos commented Jul 29, 2022

Hi - indeed, right now we re-draw the screen on any change or event.

ncurses includes some internal caching to only re-draw the changes, which the other backends don't do at the moment.
However, there exists a third-party backend wrapper that brings this feature: https://github.com/agavrilov/cursive_buffered_backend

Eventually I plan on bringing this in cursive itself, but for now this wrapper may help your performance issue.

@vi
Copy link
Author

vi commented Jul 29, 2022

Tried cursive_buffered_backend - flickering disappeared and bytes written to tty dropped from 174kb to 15kb. Both with crossterm and termion underlying backends. Also works with curses::n backend, but with no visible change.

@hrkfdn
Copy link

hrkfdn commented Sep 11, 2022

@gyscos Would a PR to integrate this into the Termion Backend help?

@gyscos
Copy link
Owner

gyscos commented Sep 11, 2022

The buffered backend would benefit both the termion and crossterm backends, and really could be integrated around the Printer level.
The Printer itself may benefit from a bit of re-structuring too (as discussed a bit in #652), so I might include some buffering like that when I get there.

hrkfdn added a commit to hrkfdn/ncspot that referenced this issue Sep 11, 2022
This is reported to be occuring with the Cursive Termion backend as it redraws
the screen every time, resulting in flickering for some users.

Should be removed once Cursive has integrated this.

See also:
- gyscos/cursive#142
- gyscos/cursive#667

Fixes #934
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants