aboutsummaryrefslogtreecommitdiff
path: root/src/term
diff options
context:
space:
mode:
Diffstat (limited to 'src/term')
-rw-r--r--src/term/mod.rs121
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]