Only re-render when an event happened

For now we don't even filter out invisible events, but it already cuts down
considerably on CPU usage.
This commit is contained in:
2023-11-04 10:08:09 +01:00
parent 5b4b9d01df
commit e04c58fec2
4 changed files with 49 additions and 17 deletions

View File

@ -8,7 +8,10 @@ use serde::{
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Action {
Tick,
/// Apply any pending update to the screen
Render,
/// Notify there is a pending update
ShouldRender,
Resize(u16, u16),
Suspend,
Resume,

View File

@ -205,7 +205,22 @@ impl App {
}));
}
let mut changes_since_last_render = true;
loop {
if changes_since_last_render {
tui.draw(|f| {
for component in self.components.iter_mut() {
let r = component.draw(f, f.size(), &self.buffers);
if let Err(e) = r {
action_tx
.send(Action::Error(format!("Failed to draw: {:?}", e)))
.unwrap();
}
}
})?;
changes_since_last_render = false;
}
tokio::select! {
e = tui.next() => {
if let Some(e) = e {
@ -221,9 +236,9 @@ impl App {
let (client, sync_response) = sync_response.expect("sync_responses_rx unexpectedly closed");
self.handle_sync_response(&action_tx, client, sync_response).await.context("Error while handling sync response")?;
}
poll_updates = futures::future::join_all(
self.buffers.iter_mut().map(|buf| buf.poll_updates())
) => {}
poll_updates = self.buffers.poll_updates() => {
changes_since_last_render = true;
}
sync_result = sync_results.next() => {
if !self.should_quit.load(Ordering::Acquire) {
panic!("Sync ended unexpected: {:?}", sync_result);
@ -243,18 +258,21 @@ impl App {
Action::Suspend => self.should_suspend = true,
Action::Resume => self.should_suspend = false,
Action::NextBuffer => {
changes_since_last_render = true;
// self.buffer implements wrap-around itself
self
.buffers
.set_active_index(self.buffers.active_index() as isize + 1)
},
Action::PreviousBuffer => {
changes_since_last_render = true;
// self.buffer implements wrap-around itself
self
.buffers
.set_active_index(self.buffers.active_index() as isize - 1)
},
Action::Resize(w, h) => {
changes_since_last_render = true;
tui.resize(Rect::new(0, 0, w, h))?;
tui.draw(|f| {
for component in self.components.iter_mut() {
@ -267,19 +285,14 @@ impl App {
}
})?;
},
Action::ShouldRender => {
changes_since_last_render = true;
},
Action::Render => {
tui.draw(|f| {
for component in self.components.iter_mut() {
let r = component.draw(f, f.size(), &self.buffers);
if let Err(e) = r {
action_tx
.send(Action::Error(format!("Failed to draw: {:?}", e)))
.unwrap();
}
}
})?;
// Merely used as a way to signify it's time to render any pending update
},
Action::RunCommand(ref command_line) => {
changes_since_last_render = true;
log::info!("Got command: {command_line}");
crate::commands::run_command(&command_line, &self, &action_tx)?;
},
@ -291,6 +304,7 @@ impl App {
};
}
}
if self.should_suspend {
tui.suspend()?;
action_tx.send(Action::Resume)?;

View File

@ -14,6 +14,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use futures::stream::FuturesUnordered;
use futures::StreamExt;
use matrix_sdk::async_trait;
use nonempty::NonEmpty;
@ -23,10 +25,13 @@ mod room;
pub use room::RoomBuffer;
#[async_trait]
pub trait Buffer: Send {
pub trait Buffer: Send + Sync {
/// A short human-readable name for the room, eg. to show in compact buflist
fn short_name(&self) -> String;
async fn poll_updates(&mut self) {}
/// Returns if there are any updates to apply.
async fn poll_updates(&mut self) {
std::future::pending().await
}
fn content(&self) -> Vec<ratatui::text::Text>; // TODO: make this lazy, only the last few are used
/// Called when the user is being showned the oldest items this buffer returned.
///
@ -47,6 +52,16 @@ impl Buffers {
}
}
pub async fn poll_updates(&mut self) {
self
.iter_mut()
.map(|buf| buf.poll_updates())
.collect::<FuturesUnordered<_>>()
.next()
.await
.expect("poll_updates reached the end of the never-ending stream");
}
pub fn iter(&self) -> impl Iterator<Item = &dyn Buffer> {
self.buffers.iter().map(|buffer_box| &**buffer_box)
}

View File

@ -96,7 +96,7 @@ impl Component for Home {
// Shift-Enter events (at least on Foot via SSH).
// However, tui-textarea implements Alt-Tab to insert a newline, so it's okay.
self.textarea.insert_newline();
Ok(Some(Action::Render))
Ok(Some(Action::ShouldRender))
},
_ => {
assert!(
@ -104,7 +104,7 @@ impl Component for Home {
"backlog.handle_key_events returned Some"
);
self.textarea.input(key);
Ok(Some(Action::Render))
Ok(Some(Action::ShouldRender))
},
}
}