mirror of
https://github.com/alacritty/alacritty.git
synced 2024-11-18 13:55:23 -05:00
Add visual bell support
This commit adds support for a visual bell. Although the Handler in src/ansi.rs warns "Hopefully this is never implemented", I wanted to give it a try. A new config option is added, `visual_bell`, which sets the `duration` and `animation` function of the visual bell. The default `duration` is 150 ms, and the default `animation` is `EaseOutExpo`. To disable the visual bell, set its duration to 0. The visual bell is modeled by VisualBell in src/term/mod.rs. It has a method to ring the bell, `ring`, and another method, `intensity`. Both return the "intensity" of the bell, which ramps down from 1.0 to 0.0 at a rate set by `duration` and `animation`. Whether or not the Processor waits for events is now configurable in order to allow for smooth drawing of the visual bell.
This commit is contained in:
parent
92e1cec088
commit
fbc7b72271
8 changed files with 276 additions and 33 deletions
|
@ -118,6 +118,32 @@ colors:
|
||||||
# cyan: '0x93a1a1'
|
# cyan: '0x93a1a1'
|
||||||
# white: '0xfdf6e3'
|
# white: '0xfdf6e3'
|
||||||
|
|
||||||
|
# Visual Bell
|
||||||
|
#
|
||||||
|
# Any time the BEL code is received, Alacritty "rings" the visual bell. Once
|
||||||
|
# rung, the terminal background will be set to white and transition back to the
|
||||||
|
# default background color. You can control the rate of this transition by
|
||||||
|
# setting the `duration` property (represented in milliseconds). You can also
|
||||||
|
# configure the transition function by setting the `animation` property.
|
||||||
|
#
|
||||||
|
# Possible values for `animation`
|
||||||
|
# `Ease`
|
||||||
|
# `EaseOut`
|
||||||
|
# `EaseOutSine`
|
||||||
|
# `EaseOutQuad`
|
||||||
|
# `EaseOutCubic`
|
||||||
|
# `EaseOutQuart`
|
||||||
|
# `EaseOutQuint`
|
||||||
|
# `EaseOutExpo`
|
||||||
|
# `EaseOutCirc`
|
||||||
|
# `Linear`
|
||||||
|
#
|
||||||
|
# To completely disable the visual bell, set its duration to 0.
|
||||||
|
#
|
||||||
|
#visual_bell:
|
||||||
|
# animation: EaseOutExpo
|
||||||
|
# duration: 150
|
||||||
|
|
||||||
# Key bindings
|
# Key bindings
|
||||||
#
|
#
|
||||||
# Each binding is defined as an object with some properties. Most of the
|
# Each binding is defined as an object with some properties. Most of the
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
in vec2 TexCoords;
|
in vec2 TexCoords;
|
||||||
in vec3 fg;
|
in vec3 fg;
|
||||||
in vec3 bg;
|
in vec3 bg;
|
||||||
|
flat in float vb;
|
||||||
flat in int background;
|
flat in int background;
|
||||||
|
|
||||||
layout(location = 0, index = 0) out vec4 color;
|
layout(location = 0, index = 0) out vec4 color;
|
||||||
|
@ -26,7 +27,7 @@ void main()
|
||||||
{
|
{
|
||||||
if (background != 0) {
|
if (background != 0) {
|
||||||
alphaMask = vec4(1.0, 1.0, 1.0, 1.0);
|
alphaMask = vec4(1.0, 1.0, 1.0, 1.0);
|
||||||
color = vec4(bg, 1.0);
|
color = vec4(bg + vb, 1.0);
|
||||||
} else {
|
} else {
|
||||||
alphaMask = vec4(texture(mask, TexCoords).rgb, 1.0);
|
alphaMask = vec4(texture(mask, TexCoords).rgb, 1.0);
|
||||||
color = vec4(fg, 1.0);
|
color = vec4(fg, 1.0);
|
||||||
|
|
|
@ -36,10 +36,12 @@ out vec3 bg;
|
||||||
uniform vec2 termDim;
|
uniform vec2 termDim;
|
||||||
uniform vec2 cellDim;
|
uniform vec2 cellDim;
|
||||||
|
|
||||||
|
uniform float visualBell;
|
||||||
uniform int backgroundPass;
|
uniform int backgroundPass;
|
||||||
|
|
||||||
// Orthographic projection
|
// Orthographic projection
|
||||||
uniform mat4 projection;
|
uniform mat4 projection;
|
||||||
|
flat out float vb;
|
||||||
flat out int background;
|
flat out int background;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
|
@ -72,6 +74,7 @@ void main()
|
||||||
TexCoords = uvOffset + vec2(position.x, 1 - position.y) * uvSize;
|
TexCoords = uvOffset + vec2(position.x, 1 - position.y) * uvSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vb = visualBell;
|
||||||
background = backgroundPass;
|
background = backgroundPass;
|
||||||
bg = backgroundColor / vec3(255.0, 255.0, 255.0);
|
bg = backgroundColor / vec3(255.0, 255.0, 255.0);
|
||||||
fg = textColor / vec3(255.0, 255.0, 255.0);
|
fg = textColor / vec3(255.0, 255.0, 255.0);
|
||||||
|
|
|
@ -4,10 +4,8 @@
|
||||||
//! parameters including font family and style, font size, etc. In the future,
|
//! parameters including font family and style, font size, etc. In the future,
|
||||||
//! the config file will also hold user and platform specific keybindings.
|
//! the config file will also hold user and platform specific keybindings.
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::env;
|
use std::{env, fmt};
|
||||||
use std::fmt;
|
use std::fs::{self, File};
|
||||||
use std::fs::File;
|
|
||||||
use std::fs;
|
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
@ -221,6 +219,64 @@ impl IndexMut<usize> for ColorList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// VisulBellAnimations are modeled after a subset of CSS transitions and Robert
|
||||||
|
/// Penner's Easing Functions.
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize)]
|
||||||
|
pub enum VisualBellAnimation {
|
||||||
|
Ease, // CSS
|
||||||
|
EaseOut, // CSS
|
||||||
|
EaseOutSine, // Penner
|
||||||
|
EaseOutQuad, // Penner
|
||||||
|
EaseOutCubic, // Penner
|
||||||
|
EaseOutQuart, // Penner
|
||||||
|
EaseOutQuint, // Penner
|
||||||
|
EaseOutExpo, // Penner
|
||||||
|
EaseOutCirc, // Penner
|
||||||
|
Linear,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct VisualBellConfig {
|
||||||
|
/// Visual bell animation function
|
||||||
|
#[serde(default="default_visual_bell_animation")]
|
||||||
|
animation: VisualBellAnimation,
|
||||||
|
|
||||||
|
/// Visual bell duration in milliseconds
|
||||||
|
#[serde(default="default_visual_bell_duration")]
|
||||||
|
duration: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_visual_bell_animation() -> VisualBellAnimation {
|
||||||
|
VisualBellAnimation::EaseOutExpo
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_visual_bell_duration() -> u16 {
|
||||||
|
150
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VisualBellConfig {
|
||||||
|
/// Visual bell animation
|
||||||
|
#[inline]
|
||||||
|
pub fn animation(&self) -> VisualBellAnimation {
|
||||||
|
self.animation
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Visual bell duration in milliseconds
|
||||||
|
#[inline]
|
||||||
|
pub fn duration(&self) -> Duration {
|
||||||
|
Duration::from_millis(self.duration as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for VisualBellConfig {
|
||||||
|
fn default() -> VisualBellConfig {
|
||||||
|
VisualBellConfig {
|
||||||
|
animation: default_visual_bell_animation(),
|
||||||
|
duration: default_visual_bell_duration(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Shell<'a> {
|
pub struct Shell<'a> {
|
||||||
program: Cow<'a, str>,
|
program: Cow<'a, str>,
|
||||||
|
@ -307,6 +363,10 @@ pub struct Config {
|
||||||
|
|
||||||
/// Path where config was loaded from
|
/// Path where config was loaded from
|
||||||
config_path: Option<PathBuf>,
|
config_path: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Visual bell configuration
|
||||||
|
#[serde(default)]
|
||||||
|
visual_bell: VisualBellConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os="macos"))]
|
#[cfg(not(target_os="macos"))]
|
||||||
|
@ -351,6 +411,7 @@ impl Default for Config {
|
||||||
mouse: Default::default(),
|
mouse: Default::default(),
|
||||||
shell: None,
|
shell: None,
|
||||||
config_path: None,
|
config_path: None,
|
||||||
|
visual_bell: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1058,6 +1119,12 @@ impl Config {
|
||||||
&self.dpi
|
&self.dpi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get visual bell config
|
||||||
|
#[inline]
|
||||||
|
pub fn visual_bell(&self) -> &VisualBellConfig {
|
||||||
|
&self.visual_bell
|
||||||
|
}
|
||||||
|
|
||||||
/// Should show render timer
|
/// Should show render timer
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn render_timer(&self) -> bool {
|
pub fn render_timer(&self) -> bool {
|
||||||
|
|
|
@ -272,7 +272,7 @@ impl Display {
|
||||||
/// This call may block if vsync is enabled
|
/// This call may block if vsync is enabled
|
||||||
pub fn draw(&mut self, mut terminal: MutexGuard<Term>, config: &Config, selection: &Selection) {
|
pub fn draw(&mut self, mut terminal: MutexGuard<Term>, config: &Config, selection: &Selection) {
|
||||||
// Clear dirty flag
|
// Clear dirty flag
|
||||||
terminal.dirty = false;
|
terminal.dirty = !terminal.visual_bell.completed();
|
||||||
|
|
||||||
if let Some(title) = terminal.get_next_title() {
|
if let Some(title) = terminal.get_next_title() {
|
||||||
self.window.set_title(&title);
|
self.window.set_title(&title);
|
||||||
|
@ -291,6 +291,8 @@ impl Display {
|
||||||
// mutable borrow
|
// mutable borrow
|
||||||
let size_info = *terminal.size_info();
|
let size_info = *terminal.size_info();
|
||||||
self.renderer.with_api(config, &size_info, |mut api| {
|
self.renderer.with_api(config, &size_info, |mut api| {
|
||||||
|
api.set_visual_bell(terminal.visual_bell.intensity() as f32);
|
||||||
|
|
||||||
api.clear();
|
api.clear();
|
||||||
|
|
||||||
// Draw the grid
|
// Draw the grid
|
||||||
|
|
50
src/event.rs
50
src/event.rs
|
@ -135,6 +135,7 @@ pub struct Processor<N> {
|
||||||
mouse_bindings: Vec<MouseBinding>,
|
mouse_bindings: Vec<MouseBinding>,
|
||||||
mouse_config: config::Mouse,
|
mouse_config: config::Mouse,
|
||||||
print_events: bool,
|
print_events: bool,
|
||||||
|
wait_for_event: bool,
|
||||||
notifier: N,
|
notifier: N,
|
||||||
mouse: Mouse,
|
mouse: Mouse,
|
||||||
resize_tx: mpsc::Sender<(u32, u32)>,
|
resize_tx: mpsc::Sender<(u32, u32)>,
|
||||||
|
@ -170,6 +171,7 @@ impl<N: Notify> Processor<N> {
|
||||||
mouse_bindings: config.mouse_bindings().to_vec(),
|
mouse_bindings: config.mouse_bindings().to_vec(),
|
||||||
mouse_config: config.mouse().to_owned(),
|
mouse_config: config.mouse().to_owned(),
|
||||||
print_events: options.print_events,
|
print_events: options.print_events,
|
||||||
|
wait_for_event: true,
|
||||||
notifier: notifier,
|
notifier: notifier,
|
||||||
resize_tx: resize_tx,
|
resize_tx: resize_tx,
|
||||||
ref_test: ref_test,
|
ref_test: ref_test,
|
||||||
|
@ -245,7 +247,8 @@ impl<N: Notify> Processor<N> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process at least one event and handle any additional queued events.
|
/// Process events. When `wait_for_event` is set, this method is guaranteed
|
||||||
|
/// to process at least one event.
|
||||||
pub fn process_events<'a>(
|
pub fn process_events<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
term: &'a FairMutex<Term>,
|
term: &'a FairMutex<Term>,
|
||||||
|
@ -276,28 +279,31 @@ impl<N: Notify> Processor<N> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match window.wait_events().next() {
|
let event = if self.wait_for_event {
|
||||||
Some(event) => {
|
window.wait_events().next()
|
||||||
terminal = term.lock();
|
} else {
|
||||||
context = ActionContext {
|
None
|
||||||
terminal: &mut terminal,
|
};
|
||||||
notifier: &mut self.notifier,
|
|
||||||
selection: &mut self.selection,
|
|
||||||
mouse: &mut self.mouse,
|
|
||||||
size_info: &self.size_info,
|
|
||||||
};
|
|
||||||
|
|
||||||
processor = input::Processor {
|
terminal = term.lock();
|
||||||
ctx: context,
|
|
||||||
mouse_config: &self.mouse_config,
|
|
||||||
key_bindings: &self.key_bindings[..],
|
|
||||||
mouse_bindings: &self.mouse_bindings[..],
|
|
||||||
};
|
|
||||||
|
|
||||||
process!(event);
|
context = ActionContext {
|
||||||
},
|
terminal: &mut terminal,
|
||||||
// Glutin guarantees the WaitEventsIterator never returns None.
|
notifier: &mut self.notifier,
|
||||||
None => unreachable!(),
|
selection: &mut self.selection,
|
||||||
|
mouse: &mut self.mouse,
|
||||||
|
size_info: &self.size_info,
|
||||||
|
};
|
||||||
|
|
||||||
|
processor = input::Processor {
|
||||||
|
ctx: context,
|
||||||
|
mouse_config: &self.mouse_config,
|
||||||
|
key_bindings: &self.key_bindings[..],
|
||||||
|
mouse_bindings: &self.mouse_bindings[..]
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(event) = event {
|
||||||
|
process!(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
for event in window.poll_events() {
|
for event in window.poll_events() {
|
||||||
|
@ -305,6 +311,8 @@ impl<N: Notify> Processor<N> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.wait_for_event = !terminal.dirty;
|
||||||
|
|
||||||
terminal
|
terminal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -110,6 +110,9 @@ pub struct ShaderProgram {
|
||||||
/// Cell dimensions (pixels)
|
/// Cell dimensions (pixels)
|
||||||
u_cell_dim: GLint,
|
u_cell_dim: GLint,
|
||||||
|
|
||||||
|
/// Visual bell
|
||||||
|
u_visual_bell: GLint,
|
||||||
|
|
||||||
/// Background pass flag
|
/// Background pass flag
|
||||||
///
|
///
|
||||||
/// Rendering is split into two passes; 1 for backgrounds, and one for text
|
/// Rendering is split into two passes; 1 for backgrounds, and one for text
|
||||||
|
@ -321,6 +324,7 @@ pub struct RenderApi<'a> {
|
||||||
atlas: &'a mut Vec<Atlas>,
|
atlas: &'a mut Vec<Atlas>,
|
||||||
program: &'a mut ShaderProgram,
|
program: &'a mut ShaderProgram,
|
||||||
colors: &'a ColorList,
|
colors: &'a ColorList,
|
||||||
|
visual_bell: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -646,6 +650,7 @@ impl QuadRenderer {
|
||||||
atlas: &mut self.atlas,
|
atlas: &mut self.atlas,
|
||||||
program: &mut self.program,
|
program: &mut self.program,
|
||||||
colors: config.color_list(),
|
colors: config.color_list(),
|
||||||
|
visual_bell: 0.0,
|
||||||
});
|
});
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -708,13 +713,18 @@ impl QuadRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RenderApi<'a> {
|
impl<'a> RenderApi<'a> {
|
||||||
|
pub fn set_visual_bell(&mut self, visual_bell: f32) {
|
||||||
|
self.visual_bell = visual_bell;
|
||||||
|
self.program.set_visual_bell(visual_bell);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn clear(&self) {
|
pub fn clear(&self) {
|
||||||
let color = self.colors[NamedColor::Background];
|
let color = self.colors[NamedColor::Background];
|
||||||
unsafe {
|
unsafe {
|
||||||
gl::ClearColor(
|
gl::ClearColor(
|
||||||
color.r as f32 / 255.0,
|
(self.visual_bell + color.r as f32 / 255.0).min(1.0),
|
||||||
color.g as f32 / 255.0,
|
(self.visual_bell + color.g as f32 / 255.0).min(1.0),
|
||||||
color.b as f32 / 255.0,
|
(self.visual_bell + color.b as f32 / 255.0).min(1.0),
|
||||||
1.0
|
1.0
|
||||||
);
|
);
|
||||||
gl::Clear(gl::COLOR_BUFFER_BIT);
|
gl::Clear(gl::COLOR_BUFFER_BIT);
|
||||||
|
@ -736,7 +746,6 @@ impl<'a> RenderApi<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
||||||
self.program.set_background_pass(true);
|
self.program.set_background_pass(true);
|
||||||
gl::DrawElementsInstanced(gl::TRIANGLES,
|
gl::DrawElementsInstanced(gl::TRIANGLES,
|
||||||
6, gl::UNSIGNED_INT, ptr::null(),
|
6, gl::UNSIGNED_INT, ptr::null(),
|
||||||
|
@ -941,11 +950,12 @@ impl ShaderProgram {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get uniform locations
|
// get uniform locations
|
||||||
let (projection, term_dim, cell_dim, background) = unsafe {
|
let (projection, term_dim, cell_dim, visual_bell, background) = unsafe {
|
||||||
(
|
(
|
||||||
gl::GetUniformLocation(program, cptr!(b"projection\0")),
|
gl::GetUniformLocation(program, cptr!(b"projection\0")),
|
||||||
gl::GetUniformLocation(program, cptr!(b"termDim\0")),
|
gl::GetUniformLocation(program, cptr!(b"termDim\0")),
|
||||||
gl::GetUniformLocation(program, cptr!(b"cellDim\0")),
|
gl::GetUniformLocation(program, cptr!(b"cellDim\0")),
|
||||||
|
gl::GetUniformLocation(program, cptr!(b"visualBell\0")),
|
||||||
gl::GetUniformLocation(program, cptr!(b"backgroundPass\0")),
|
gl::GetUniformLocation(program, cptr!(b"backgroundPass\0")),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@ -957,6 +967,7 @@ impl ShaderProgram {
|
||||||
u_projection: projection,
|
u_projection: projection,
|
||||||
u_term_dim: term_dim,
|
u_term_dim: term_dim,
|
||||||
u_cell_dim: cell_dim,
|
u_cell_dim: cell_dim,
|
||||||
|
u_visual_bell: visual_bell,
|
||||||
u_background: background,
|
u_background: background,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -988,6 +999,12 @@ impl ShaderProgram {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_visual_bell(&self, visual_bell: f32) {
|
||||||
|
unsafe {
|
||||||
|
gl::Uniform1f(self.u_visual_bell, visual_bell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn set_background_pass(&self, background_pass: bool) {
|
fn set_background_pass(&self, background_pass: bool) {
|
||||||
let value = if background_pass {
|
let value = if background_pass {
|
||||||
1
|
1
|
||||||
|
|
121
src/term/mod.rs
121
src/term/mod.rs
|
@ -18,12 +18,13 @@ use std::ops::{Deref, Range, Index, IndexMut};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use ansi::{self, Color, NamedColor, Attr, Handler, CharsetIndex, StandardCharset};
|
use ansi::{self, Color, NamedColor, Attr, Handler, CharsetIndex, StandardCharset};
|
||||||
use grid::{BidirectionalIterator, Grid, ClearRegion, ToRange};
|
use grid::{BidirectionalIterator, Grid, ClearRegion, ToRange};
|
||||||
use index::{self, Point, Column, Line, Linear, IndexRange, Contains, RangeInclusive, Side};
|
use index::{self, Point, Column, Line, Linear, IndexRange, Contains, RangeInclusive, Side};
|
||||||
use selection::{Span, Selection};
|
use selection::{Span, Selection};
|
||||||
use config::{Config};
|
use config::{Config, VisualBellAnimation};
|
||||||
|
|
||||||
pub mod cell;
|
pub mod cell;
|
||||||
pub use self::cell::Cell;
|
pub use self::cell::Cell;
|
||||||
|
@ -299,6 +300,119 @@ pub struct Cursor {
|
||||||
charsets: Charsets,
|
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 {
|
pub struct Term {
|
||||||
/// The grid
|
/// The grid
|
||||||
grid: Grid<Cell>,
|
grid: Grid<Cell>,
|
||||||
|
@ -345,6 +459,8 @@ pub struct Term {
|
||||||
|
|
||||||
pub dirty: bool,
|
pub dirty: bool,
|
||||||
|
|
||||||
|
pub visual_bell: VisualBell,
|
||||||
|
|
||||||
custom_cursor_colors: bool,
|
custom_cursor_colors: bool,
|
||||||
|
|
||||||
/// Saved cursor from main grid
|
/// Saved cursor from main grid
|
||||||
|
@ -424,6 +540,7 @@ impl Term {
|
||||||
Term {
|
Term {
|
||||||
next_title: None,
|
next_title: None,
|
||||||
dirty: false,
|
dirty: false,
|
||||||
|
visual_bell: VisualBell::new(config),
|
||||||
input_needs_wrap: false,
|
input_needs_wrap: false,
|
||||||
grid: grid,
|
grid: grid,
|
||||||
alt_grid: alt,
|
alt_grid: alt,
|
||||||
|
@ -445,6 +562,7 @@ impl Term {
|
||||||
pub fn update_config(&mut self, config: &Config) {
|
pub fn update_config(&mut self, config: &Config) {
|
||||||
self.custom_cursor_colors = config.custom_cursor_colors();
|
self.custom_cursor_colors = config.custom_cursor_colors();
|
||||||
self.semantic_escape_chars = config.selection().semantic_escape_chars.clone();
|
self.semantic_escape_chars = config.selection().semantic_escape_chars.clone();
|
||||||
|
self.visual_bell.update_config(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -1027,6 +1145,7 @@ impl ansi::Handler for Term {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bell(&mut self) {
|
fn bell(&mut self) {
|
||||||
trace!("bell");
|
trace!("bell");
|
||||||
|
self.visual_bell.ring();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
Loading…
Reference in a new issue