summaryrefslogtreecommitdiff
path: root/alacritty_terminal/src/event_loop.rs
diff options
context:
space:
mode:
authorChristian Duerr <contact@christianduerr.com>2021-01-25 22:23:08 +0100
committerChristian Duerr <contact@christianduerr.com>2021-02-24 16:16:38 +0000
commit9575aed68197ba783e50b922a6c3f9a78b8c7048 (patch)
tree5978f62ca3d0744864e483b00baff21e6f1c63d1 /alacritty_terminal/src/event_loop.rs
parent64abd7fb43febc53e27e63411a1eb06c633302bb (diff)
downloadalacritty-9575aed68197ba783e50b922a6c3f9a78b8c7048.tar.gz
alacritty-9575aed68197ba783e50b922a6c3f9a78b8c7048.zip
Add support for synchronized updates
This implements support for temporarily freezing the terminal grid to prevent rendering of incomplete frames. This can be triggered using the escapes `DCS = 1 s` (start) and `DCS = 2 s` (end). The synchronization is implemented by forwarding all received PTY bytes to a 2 MiB buffer. This should allow updating the entire grid even if it is fairly dense. Unfortunately this also means that another branch is necessary in Alacritty's parser which does have a slight performance impact. In a previous version the freezing was implemented by caching the renderable grid state whenever a synchronized update is started. While this strategy makes it possible to implement this without any performance impact without synchronized updates, a significant performance overhead is introduced whenever a synchronized update is started. Since this can happen thousands of times per frame, it is not a feasible solution. While it would be possible to render at most one synchronized update per frame, it is possible that another synchronized update comes in at any time and stays active for an extended period. As a result the state visible before the long synchronization would be the first received update per frame, not the last, which could lead to the user missing important information during the long freezing interval. Fixes #598.
Diffstat (limited to 'alacritty_terminal/src/event_loop.rs')
-rw-r--r--alacritty_terminal/src/event_loop.rs41
1 files changed, 24 insertions, 17 deletions
diff --git a/alacritty_terminal/src/event_loop.rs b/alacritty_terminal/src/event_loop.rs
index 8a6441ce..09c71668 100644
--- a/alacritty_terminal/src/event_loop.rs
+++ b/alacritty_terminal/src/event_loop.rs
@@ -7,6 +7,7 @@ use std::io::{self, ErrorKind, Read, Write};
use std::marker::Send;
use std::sync::Arc;
use std::thread::JoinHandle;
+use std::time::Instant;
use log::error;
#[cfg(not(windows))]
@@ -58,16 +59,6 @@ struct Writing {
written: usize,
}
-/// All of the mutable state needed to run the event loop.
-///
-/// Contains list of items to write, current write state, etc. Anything that
-/// would otherwise be mutated on the `EventLoop` goes here.
-pub struct State {
- write_list: VecDeque<Cow<'static, [u8]>>,
- writing: Option<Writing>,
- parser: ansi::Processor,
-}
-
pub struct Notifier(pub Sender<Msg>);
impl event::Notify for Notifier {
@@ -91,10 +82,15 @@ impl event::OnResize for Notifier {
}
}
-impl Default for State {
- fn default() -> State {
- State { write_list: VecDeque::new(), parser: ansi::Processor::new(), writing: None }
- }
+/// All of the mutable state needed to run the event loop.
+///
+/// Contains list of items to write, current write state, etc. Anything that
+/// would otherwise be mutated on the `EventLoop` goes here.
+#[derive(Default)]
+pub struct State {
+ write_list: VecDeque<Cow<'static, [u8]>>,
+ writing: Option<Writing>,
+ parser: ansi::Processor,
}
impl State {
@@ -261,8 +257,8 @@ where
}
}
- if processed > 0 {
- // Queue terminal redraw.
+ // Queue terminal redraw unless all processed bytes were synchronized.
+ if state.parser.sync_bytes_count() < processed && processed > 0 {
self.event_proxy.send_event(Event::Wakeup);
}
@@ -325,13 +321,24 @@ where
};
'event_loop: loop {
- if let Err(err) = self.poll.poll(&mut events, None) {
+ // Wakeup the event loop when a synchronized update timeout was reached.
+ let sync_timeout = state.parser.sync_timeout();
+ let timeout = sync_timeout.map(|st| st.saturating_duration_since(Instant::now()));
+
+ if let Err(err) = self.poll.poll(&mut events, timeout) {
match err.kind() {
ErrorKind::Interrupted => continue,
_ => panic!("EventLoop polling error: {:?}", err),
}
}
+ // Handle synchronized update timeout.
+ if events.is_empty() {
+ state.parser.stop_sync(&mut *self.terminal.lock(), &mut self.pty.writer());
+ self.event_proxy.send_event(Event::Wakeup);
+ continue;
+ }
+
for event in events.iter() {
match event.token() {
token if token == channel_token => {