diff options
Diffstat (limited to 'src/term')
-rw-r--r-- | src/term/mod.rs | 121 |
1 files changed, 120 insertions, 1 deletions
diff --git a/src/term/mod.rs b/src/term/mod.rs index b0ca2a59..a7cda171 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -18,12 +18,13 @@ use std::ops::{Deref, Range, Index, IndexMut}; use std::ptr; use std::cmp::min; use std::io; +use std::time::{Duration, Instant}; use ansi::{self, Color, NamedColor, Attr, Handler, CharsetIndex, StandardCharset}; use grid::{BidirectionalIterator, Grid, ClearRegion, ToRange}; use index::{self, Point, Column, Line, Linear, IndexRange, Contains, RangeInclusive, Side}; use selection::{Span, Selection}; -use config::{Config}; +use config::{Config, VisualBellAnimation}; pub mod cell; pub use self::cell::Cell; @@ -299,6 +300,119 @@ pub struct Cursor { charsets: Charsets, } +pub struct VisualBell { + /// Visual bell animation + animation: VisualBellAnimation, + + /// Visual bell duration + duration: Duration, + + /// The last time the visual bell rang, if at all + start_time: Option<Instant>, +} + +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 +} + +impl VisualBell { + pub fn new(config: &Config) -> VisualBell { + let visual_bell_config = config.visual_bell(); + VisualBell { + animation: visual_bell_config.animation(), + duration: visual_bell_config.duration(), + start_time: None, + } + } + + /// 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 currenty 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(&self) -> bool { + match self.start_time { + Some(earlier) => Instant::now().duration_since(earlier) > self.duration, + 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 + + elapsed.subsec_nanos() as f64 / 1e9f64; + let duration_f = self.duration.as_secs() as f64 + + self.duration.subsec_nanos() as f64 / 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 { + VisualBellAnimation::Ease => cubic_bezier(0.25, 0.1, 0.25, 1.0, time), + VisualBellAnimation::EaseOut => cubic_bezier(0.25, 0.1, 0.25, 1.0, time), + VisualBellAnimation::EaseOutSine => cubic_bezier(0.39, 0.575, 0.565, 1.0, time), + VisualBellAnimation::EaseOutQuad => cubic_bezier(0.25, 0.46, 0.45, 0.94, time), + VisualBellAnimation::EaseOutCubic => cubic_bezier(0.215, 0.61, 0.355, 1.0, time), + VisualBellAnimation::EaseOutQuart => cubic_bezier(0.165, 0.84, 0.44, 1.0, time), + VisualBellAnimation::EaseOutQuint => cubic_bezier(0.23, 1.0, 0.32, 1.0, time), + VisualBellAnimation::EaseOutExpo => cubic_bezier(0.19, 1.0, 0.22, 1.0, time), + VisualBellAnimation::EaseOutCirc => cubic_bezier(0.075, 0.82, 0.165, 1.0, time), + VisualBellAnimation::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, config: &Config) { + let visual_bell_config = config.visual_bell(); + self.animation = visual_bell_config.animation(); + self.duration = visual_bell_config.duration(); + } +} + pub struct Term { /// The grid grid: Grid<Cell>, @@ -345,6 +459,8 @@ pub struct Term { pub dirty: bool, + pub visual_bell: VisualBell, + custom_cursor_colors: bool, /// Saved cursor from main grid @@ -424,6 +540,7 @@ impl Term { Term { next_title: None, dirty: false, + visual_bell: VisualBell::new(config), input_needs_wrap: false, grid: grid, alt_grid: alt, @@ -445,6 +562,7 @@ impl Term { pub fn update_config(&mut self, config: &Config) { self.custom_cursor_colors = config.custom_cursor_colors(); self.semantic_escape_chars = config.selection().semantic_escape_chars.clone(); + self.visual_bell.update_config(config); } #[inline] @@ -1027,6 +1145,7 @@ impl ansi::Handler for Term { #[inline] fn bell(&mut self) { trace!("bell"); + self.visual_bell.ring(); } #[inline] |