mirror of
				https://github.com/alacritty/alacritty.git
				synced 2025-10-30 23:36:53 -04: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'
 | 
			
		||||
#     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
 | 
			
		||||
#
 | 
			
		||||
# Each binding is defined as an object with some properties. Most of the
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,7 @@
 | 
			
		|||
in vec2 TexCoords;
 | 
			
		||||
in vec3 fg;
 | 
			
		||||
in vec3 bg;
 | 
			
		||||
flat in float vb;
 | 
			
		||||
flat in int background;
 | 
			
		||||
 | 
			
		||||
layout(location = 0, index = 0) out vec4 color;
 | 
			
		||||
| 
						 | 
				
			
			@ -26,7 +27,7 @@ void main()
 | 
			
		|||
{
 | 
			
		||||
    if (background != 0) {
 | 
			
		||||
        alphaMask = vec4(1.0, 1.0, 1.0, 1.0);
 | 
			
		||||
        color = vec4(bg, 1.0);
 | 
			
		||||
        color = vec4(bg + vb, 1.0);
 | 
			
		||||
    } else {
 | 
			
		||||
        alphaMask = vec4(texture(mask, TexCoords).rgb, 1.0);
 | 
			
		||||
        color = vec4(fg, 1.0);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,10 +36,12 @@ out vec3 bg;
 | 
			
		|||
uniform vec2 termDim;
 | 
			
		||||
uniform vec2 cellDim;
 | 
			
		||||
 | 
			
		||||
uniform float visualBell;
 | 
			
		||||
uniform int backgroundPass;
 | 
			
		||||
 | 
			
		||||
// Orthographic projection
 | 
			
		||||
uniform mat4 projection;
 | 
			
		||||
flat out float vb;
 | 
			
		||||
flat out int background;
 | 
			
		||||
 | 
			
		||||
void main()
 | 
			
		||||
| 
						 | 
				
			
			@ -72,6 +74,7 @@ void main()
 | 
			
		|||
        TexCoords = uvOffset + vec2(position.x, 1 - position.y) * uvSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vb = visualBell;
 | 
			
		||||
    background = backgroundPass;
 | 
			
		||||
    bg = backgroundColor / 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,
 | 
			
		||||
//! the config file will also hold user and platform specific keybindings.
 | 
			
		||||
use std::borrow::Cow;
 | 
			
		||||
use std::env;
 | 
			
		||||
use std::fmt;
 | 
			
		||||
use std::fs::File;
 | 
			
		||||
use std::fs;
 | 
			
		||||
use std::{env, fmt};
 | 
			
		||||
use std::fs::{self, File};
 | 
			
		||||
use std::io::{self, Read, Write};
 | 
			
		||||
use std::ops::{Index, IndexMut};
 | 
			
		||||
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)]
 | 
			
		||||
pub struct Shell<'a> {
 | 
			
		||||
    program: Cow<'a, str>,
 | 
			
		||||
| 
						 | 
				
			
			@ -307,6 +363,10 @@ pub struct Config {
 | 
			
		|||
 | 
			
		||||
    /// Path where config was loaded from
 | 
			
		||||
    config_path: Option<PathBuf>,
 | 
			
		||||
 | 
			
		||||
    /// Visual bell configuration
 | 
			
		||||
    #[serde(default)]
 | 
			
		||||
    visual_bell: VisualBellConfig,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(not(target_os="macos"))]
 | 
			
		||||
| 
						 | 
				
			
			@ -351,6 +411,7 @@ impl Default for Config {
 | 
			
		|||
            mouse: Default::default(),
 | 
			
		||||
            shell: None,
 | 
			
		||||
            config_path: None,
 | 
			
		||||
            visual_bell: Default::default(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1058,6 +1119,12 @@ impl Config {
 | 
			
		|||
        &self.dpi
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get visual bell config
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn visual_bell(&self) -> &VisualBellConfig {
 | 
			
		||||
        &self.visual_bell
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Should show render timer
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn render_timer(&self) -> bool {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -272,7 +272,7 @@ impl Display {
 | 
			
		|||
    /// This call may block if vsync is enabled
 | 
			
		||||
    pub fn draw(&mut self, mut terminal: MutexGuard<Term>, config: &Config, selection: &Selection) {
 | 
			
		||||
        // Clear dirty flag
 | 
			
		||||
        terminal.dirty = false;
 | 
			
		||||
        terminal.dirty = !terminal.visual_bell.completed();
 | 
			
		||||
 | 
			
		||||
        if let Some(title) = terminal.get_next_title() {
 | 
			
		||||
            self.window.set_title(&title);
 | 
			
		||||
| 
						 | 
				
			
			@ -291,6 +291,8 @@ impl Display {
 | 
			
		|||
                // mutable borrow
 | 
			
		||||
                let size_info = *terminal.size_info();
 | 
			
		||||
                self.renderer.with_api(config, &size_info, |mut api| {
 | 
			
		||||
                    api.set_visual_bell(terminal.visual_bell.intensity() as f32);
 | 
			
		||||
 | 
			
		||||
                    api.clear();
 | 
			
		||||
 | 
			
		||||
                    // Draw the grid
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										50
									
								
								src/event.rs
									
										
									
									
									
								
							
							
						
						
									
										50
									
								
								src/event.rs
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -135,6 +135,7 @@ pub struct Processor<N> {
 | 
			
		|||
    mouse_bindings: Vec<MouseBinding>,
 | 
			
		||||
    mouse_config: config::Mouse,
 | 
			
		||||
    print_events: bool,
 | 
			
		||||
    wait_for_event: bool,
 | 
			
		||||
    notifier: N,
 | 
			
		||||
    mouse: Mouse,
 | 
			
		||||
    resize_tx: mpsc::Sender<(u32, u32)>,
 | 
			
		||||
| 
						 | 
				
			
			@ -170,6 +171,7 @@ impl<N: Notify> Processor<N> {
 | 
			
		|||
            mouse_bindings: config.mouse_bindings().to_vec(),
 | 
			
		||||
            mouse_config: config.mouse().to_owned(),
 | 
			
		||||
            print_events: options.print_events,
 | 
			
		||||
            wait_for_event: true,
 | 
			
		||||
            notifier: notifier,
 | 
			
		||||
            resize_tx: resize_tx,
 | 
			
		||||
            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>(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        term: &'a FairMutex<Term>,
 | 
			
		||||
| 
						 | 
				
			
			@ -276,28 +279,31 @@ impl<N: Notify> Processor<N> {
 | 
			
		|||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            match window.wait_events().next() {
 | 
			
		||||
                Some(event) => {
 | 
			
		||||
                    terminal = term.lock();
 | 
			
		||||
                    context = ActionContext {
 | 
			
		||||
                        terminal: &mut terminal,
 | 
			
		||||
                        notifier: &mut self.notifier,
 | 
			
		||||
                        selection: &mut self.selection,
 | 
			
		||||
                        mouse: &mut self.mouse,
 | 
			
		||||
                        size_info: &self.size_info,
 | 
			
		||||
                    };
 | 
			
		||||
            let event = if self.wait_for_event {
 | 
			
		||||
                window.wait_events().next()
 | 
			
		||||
            } else {
 | 
			
		||||
                None
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
                    processor = input::Processor {
 | 
			
		||||
                        ctx: context,
 | 
			
		||||
                        mouse_config: &self.mouse_config,
 | 
			
		||||
                        key_bindings: &self.key_bindings[..],
 | 
			
		||||
                        mouse_bindings: &self.mouse_bindings[..],
 | 
			
		||||
                    };
 | 
			
		||||
            terminal = term.lock();
 | 
			
		||||
 | 
			
		||||
                    process!(event);
 | 
			
		||||
                },
 | 
			
		||||
                // Glutin guarantees the WaitEventsIterator never returns None.
 | 
			
		||||
                None => unreachable!(),
 | 
			
		||||
            context = ActionContext {
 | 
			
		||||
                terminal: &mut terminal,
 | 
			
		||||
                notifier: &mut self.notifier,
 | 
			
		||||
                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() {
 | 
			
		||||
| 
						 | 
				
			
			@ -305,6 +311,8 @@ impl<N: Notify> Processor<N> {
 | 
			
		|||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.wait_for_event = !terminal.dirty;
 | 
			
		||||
 | 
			
		||||
        terminal
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -110,6 +110,9 @@ pub struct ShaderProgram {
 | 
			
		|||
    /// Cell dimensions (pixels)
 | 
			
		||||
    u_cell_dim: GLint,
 | 
			
		||||
 | 
			
		||||
    /// Visual bell
 | 
			
		||||
    u_visual_bell: GLint,
 | 
			
		||||
 | 
			
		||||
    /// Background pass flag
 | 
			
		||||
    ///
 | 
			
		||||
    /// 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>,
 | 
			
		||||
    program: &'a mut ShaderProgram,
 | 
			
		||||
    colors: &'a ColorList,
 | 
			
		||||
    visual_bell: f32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
| 
						 | 
				
			
			@ -646,6 +650,7 @@ impl QuadRenderer {
 | 
			
		|||
            atlas: &mut self.atlas,
 | 
			
		||||
            program: &mut self.program,
 | 
			
		||||
            colors: config.color_list(),
 | 
			
		||||
            visual_bell: 0.0,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        unsafe {
 | 
			
		||||
| 
						 | 
				
			
			@ -708,13 +713,18 @@ impl QuadRenderer {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
        let color = self.colors[NamedColor::Background];
 | 
			
		||||
        unsafe {
 | 
			
		||||
            gl::ClearColor(
 | 
			
		||||
                color.r as f32 / 255.0,
 | 
			
		||||
                color.g as f32 / 255.0,
 | 
			
		||||
                color.b as f32 / 255.0,
 | 
			
		||||
                (self.visual_bell + color.r as f32 / 255.0).min(1.0),
 | 
			
		||||
                (self.visual_bell + color.g as f32 / 255.0).min(1.0),
 | 
			
		||||
                (self.visual_bell + color.b as f32 / 255.0).min(1.0),
 | 
			
		||||
                1.0
 | 
			
		||||
                );
 | 
			
		||||
            gl::Clear(gl::COLOR_BUFFER_BIT);
 | 
			
		||||
| 
						 | 
				
			
			@ -736,7 +746,6 @@ impl<'a> RenderApi<'a> {
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        unsafe {
 | 
			
		||||
 | 
			
		||||
            self.program.set_background_pass(true);
 | 
			
		||||
            gl::DrawElementsInstanced(gl::TRIANGLES,
 | 
			
		||||
                                      6, gl::UNSIGNED_INT, ptr::null(),
 | 
			
		||||
| 
						 | 
				
			
			@ -941,11 +950,12 @@ impl ShaderProgram {
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        // 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"termDim\0")),
 | 
			
		||||
                gl::GetUniformLocation(program, cptr!(b"cellDim\0")),
 | 
			
		||||
                gl::GetUniformLocation(program, cptr!(b"visualBell\0")),
 | 
			
		||||
                gl::GetUniformLocation(program, cptr!(b"backgroundPass\0")),
 | 
			
		||||
            )
 | 
			
		||||
        };
 | 
			
		||||
| 
						 | 
				
			
			@ -957,6 +967,7 @@ impl ShaderProgram {
 | 
			
		|||
            u_projection: projection,
 | 
			
		||||
            u_term_dim: term_dim,
 | 
			
		||||
            u_cell_dim: cell_dim,
 | 
			
		||||
            u_visual_bell: visual_bell,
 | 
			
		||||
            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) {
 | 
			
		||||
        let value = if background_pass {
 | 
			
		||||
            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::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]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue