forked from alacritty/alacritty
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for synchronized terminal updates
This adds support for the synchronized updates escape sequence (`DCS = 1 s`/`DCS = 2 s`). This allows terminal applications to freeze Alacritty's rendering temporarily to prevent excessive redraws and flickering. There seem to be two possible approaches for implementing this escape sequence. Some terminal emulators like Kitty have an internal escape sequence buffer which reads every byte, then upon receiving the freezing escape they stop reading from this buffer and keep going when the terminal is unfrozen or the buffer is full. The alternative implementation, which this patch has taken, is to freeze the rendering completely, while still updating the grid state in the background. The approach taken in this patch has the advantage that the number of bytes written can be optimally compressed, there's no need for an additional buffer and no risk of running out of buffer space and having to end synchronization prematurely. On the other hand since only the visible part of the terminal can be frozen because of performance and memory limitations, many user interactions with the terminal need to forcefully interrupt the synchronized update. While the advantages and disadvantages seem somewhat on par for both approaches and most limitations shouldn't be noticeable with the synchronized updates under normal operating conditions, the rendering freeze was much easier to implement efficiently in Alacritty. Since this is an escape sequence, all the rendering freezes must be handled by `alacritty_terminal` internally. Because of this a new intermediate `RenderableContent` struct has been introduced which contains all the information necessary for rendering the visible terminal region. This single interface allows for decoupling the integration between terminal and rendering and at the same time makes it possible to clone the entire structure to preserve during a freeze. Fixes alacritty#598.
- Loading branch information
1 parent
c566f78
commit 0c909e9
Showing
33 changed files
with
1,304 additions
and
1,290 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
use std::time::{Instant, Duration}; | ||
|
||
use crate::config::bell::{BellAnimation, BellConfig}; | ||
|
||
pub struct VisualBell { | ||
/// Visual bell animation. | ||
animation: BellAnimation, | ||
|
||
/// Visual bell duration. | ||
duration: Duration, | ||
|
||
/// The last time the visual bell rang, if at all. | ||
start_time: Option<Instant>, | ||
} | ||
|
||
impl VisualBell { | ||
/// Ring the visual bell, and return its intensity. | ||
pub fn ring(&mut self) -> f64 { | ||
let now = Instant::now(); | ||
self.start_time = Some(now); | ||
self.intensity_at_instant(now) | ||
} | ||
|
||
/// Get the currently intensity of the visual bell. The bell's intensity | ||
/// ramps down from 1.0 to 0.0 at a rate determined by the bell's duration. | ||
pub fn intensity(&self) -> f64 { | ||
self.intensity_at_instant(Instant::now()) | ||
} | ||
|
||
/// Check whether or not the visual bell has completed "ringing". | ||
pub fn completed(&mut self) -> bool { | ||
match self.start_time { | ||
Some(earlier) => { | ||
if Instant::now().duration_since(earlier) >= self.duration { | ||
self.start_time = None; | ||
} | ||
false | ||
}, | ||
None => true, | ||
} | ||
} | ||
|
||
/// Get the intensity of the visual bell at a particular instant. The bell's | ||
/// intensity ramps down from 1.0 to 0.0 at a rate determined by the bell's | ||
/// duration. | ||
pub fn intensity_at_instant(&self, instant: Instant) -> f64 { | ||
// If `duration` is zero, then the VisualBell is disabled; therefore, | ||
// its `intensity` is zero. | ||
if self.duration == Duration::from_secs(0) { | ||
return 0.0; | ||
} | ||
|
||
match self.start_time { | ||
// Similarly, if `start_time` is `None`, then the VisualBell has not | ||
// been "rung"; therefore, its `intensity` is zero. | ||
None => 0.0, | ||
|
||
Some(earlier) => { | ||
// Finally, if the `instant` at which we wish to compute the | ||
// VisualBell's `intensity` occurred before the VisualBell was | ||
// "rung", then its `intensity` is also zero. | ||
if instant < earlier { | ||
return 0.0; | ||
} | ||
|
||
let elapsed = instant.duration_since(earlier); | ||
let elapsed_f = | ||
elapsed.as_secs() as f64 + f64::from(elapsed.subsec_nanos()) / 1e9f64; | ||
let duration_f = self.duration.as_secs() as f64 | ||
+ f64::from(self.duration.subsec_nanos()) / 1e9f64; | ||
|
||
// Otherwise, we compute a value `time` from 0.0 to 1.0 | ||
// inclusive that represents the ratio of `elapsed` time to the | ||
// `duration` of the VisualBell. | ||
let time = (elapsed_f / duration_f).min(1.0); | ||
|
||
// We use this to compute the inverse `intensity` of the | ||
// VisualBell. When `time` is 0.0, `inverse_intensity` is 0.0, | ||
// and when `time` is 1.0, `inverse_intensity` is 1.0. | ||
let inverse_intensity = match self.animation { | ||
BellAnimation::Ease | BellAnimation::EaseOut => { | ||
cubic_bezier(0.25, 0.1, 0.25, 1.0, time) | ||
}, | ||
BellAnimation::EaseOutSine => cubic_bezier(0.39, 0.575, 0.565, 1.0, time), | ||
BellAnimation::EaseOutQuad => cubic_bezier(0.25, 0.46, 0.45, 0.94, time), | ||
BellAnimation::EaseOutCubic => cubic_bezier(0.215, 0.61, 0.355, 1.0, time), | ||
BellAnimation::EaseOutQuart => cubic_bezier(0.165, 0.84, 0.44, 1.0, time), | ||
BellAnimation::EaseOutQuint => cubic_bezier(0.23, 1.0, 0.32, 1.0, time), | ||
BellAnimation::EaseOutExpo => cubic_bezier(0.19, 1.0, 0.22, 1.0, time), | ||
BellAnimation::EaseOutCirc => cubic_bezier(0.075, 0.82, 0.165, 1.0, time), | ||
BellAnimation::Linear => time, | ||
}; | ||
|
||
// Since we want the `intensity` of the VisualBell to decay over | ||
// `time`, we subtract the `inverse_intensity` from 1.0. | ||
1.0 - inverse_intensity | ||
}, | ||
} | ||
} | ||
|
||
pub fn update_config(&mut self, bell_config: &BellConfig) { | ||
self.animation = bell_config.animation; | ||
self.duration = bell_config.duration(); | ||
} | ||
} | ||
|
||
impl From<&BellConfig> for VisualBell { | ||
fn from(bell_config: &BellConfig) -> VisualBell { | ||
VisualBell { | ||
animation: bell_config.animation, | ||
duration: bell_config.duration(), | ||
start_time: None, | ||
} | ||
} | ||
} | ||
|
||
fn cubic_bezier(p0: f64, p1: f64, p2: f64, p3: f64, x: f64) -> f64 { | ||
(1.0 - x).powi(3) * p0 | ||
+ 3.0 * (1.0 - x).powi(2) * x * p1 | ||
+ 3.0 * (1.0 - x) * x.powi(2) * p2 | ||
+ x.powi(3) * p3 | ||
} |
Oops, something went wrong.