Skip to content

Commit

Permalink
add visual bell
Browse files Browse the repository at this point in the history
refs: #3
  • Loading branch information
wez committed Sep 25, 2021
1 parent e287474 commit 0c1ad71
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 0 deletions.
52 changes: 52 additions & 0 deletions config/src/bell.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use crate::*;

/// <https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function>
#[derive(Debug, Deserialize, Serialize, Clone)]
pub enum EasingFunction {
Linear,
CubicBezier(f32, f32, f32, f32),
Ease,
EaseIn,
EaseInOut,
EaseOut,
}
impl_lua_conversion!(EasingFunction);

impl EasingFunction {
pub fn evaluate_at_position(&self, position: f32) -> f32 {
fn cubic_bezier(p0: f32, p1: f32, p2: f32, p3: f32, x: f32) -> f32 {
(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
}

match self {
Self::Linear => cubic_bezier(0., 0., 1.0, 1.0, position),
Self::CubicBezier(a, b, c, d) => cubic_bezier(*a, *b, *c, *d, position),
Self::Ease => cubic_bezier(0.25, 0.1, 0.25, 1.0, position),
Self::EaseIn => cubic_bezier(0.42, 0.0, 1.0, 1.0, position),
Self::EaseInOut => cubic_bezier(0.42, 0., 0.58, 1.0, position),
Self::EaseOut => cubic_bezier(0., 0., 0.58, 1.0, position),
}
}
}

impl Default for EasingFunction {
fn default() -> Self {
Self::Ease
}
}

#[derive(Default, Debug, Deserialize, Serialize, Clone)]
pub struct VisualBell {
#[serde(default)]
pub fade_in_duration_ms: u64,
#[serde(default)]
pub fade_in_function: EasingFunction,
#[serde(default)]
pub fade_out_duration_ms: u64,
#[serde(default)]
pub fade_out_function: EasingFunction,
}
impl_lua_conversion!(VisualBell);
3 changes: 3 additions & 0 deletions config/src/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ pub struct Palette {
pub scrollbar_thumb: Option<RgbColor>,
/// The color of the split line between panes
pub split: Option<RgbColor>,
/// The color of the visual bell. If unspecified, the foreground
/// color is used instead.
pub visual_bell: Option<RgbColor>,
}
impl_lua_conversion!(Palette);

Expand Down
5 changes: 5 additions & 0 deletions config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use toml;
use wezterm_input_types::{KeyCode, Modifiers, WindowDecorations};

mod background;
mod bell;
mod color;
mod daemon;
mod font;
Expand All @@ -42,6 +43,7 @@ mod unix;
mod version;

pub use background::*;
pub use bell::*;
pub use color::*;
pub use daemon::*;
pub use font::*;
Expand Down Expand Up @@ -1222,6 +1224,9 @@ pub struct Config {

#[serde(default = "default_max_fps")]
pub max_fps: u8,

#[serde(default)]
pub visual_bell: VisualBell,
}
impl_lua_conversion!(Config);

Expand Down
1 change: 1 addition & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ As features stabilize some brief notes about them will accumulate here.
* Fixed: italic fonts weren't always recognized as being italic, resulting in italic variants being used instead of the non-italic variants in some cases! [#1162](https://github.com/wez/wezterm/issues/1162)
* New: [bell](config/lua/window-events/bell.md) event allows you to trigger lua code when the bell is run. [#3](https://github.com/wez/wezterm/issues/3)
* Fixed: Ask freetype for cell metrics in bitmap-only fonts, rather than simply taking the bitmap width. [#1165](https://github.com/wez/wezterm/issues/1165)
* New: [visual_bell](config/lua/config/visual_bell.md) configuration option

### 20210814-124438-54e29167

Expand Down
44 changes: 44 additions & 0 deletions docs/config/lua/config/visual_bell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# visual_bell

*Since: nightly builds only*

When the BEL ascii sequence is sent to a pane, the bell is "rung" in that pane.

You may choose to configure the `visual_bell` option so show a visible representation of the bell event,
by having the background color of the pane briefly change color.

There are four fields to the visual_bell config option:

* `fade_in_duration_ms` - how long it should take for the bell color to fade in, in milliseconds. The default is 0.
* `fade_out_duration_ms` - how long it should take for the bell color to fade out, in milliseconds. The default is 0.
* `fade_in_function` - an easing function, similar to [CSS easing functions](https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function), that affects how the bell color is faded in.
* `fade_out_function` - an easing function that affects how the bell color is faded out.

If the total fade in and out durations are 0, then there will be no visual bell indication.

The bell color is itself specified in your color settings; if not specified, the text foreground color will be used.

The following easing functions are supported:

* `Linear` - the fade happens at a constant rate.
* `Ease` - The fade starts slowly, accelerates sharply, and then slows gradually towards the end. This is the default.
* `EaseIn` - The fade starts slowly, and then progressively speeds up until the end, at which point it stops abruptly.
* `EaseInOut` - The fade starts slowly, speeds up, and then slows down towards the end.
* `EaseOut` - The fade starts abruptly, and then progressively slows down towards the end.
* `{CubicBezier={0.0, 0.0, 0.58, 1.0}}` - an arbitrary cubic bezier with the specified parameters.

The following configuration enables a low intensity visual bell that takes a total of 300ms to "flash" the screen:

```lua
return {
visual_bell = {
fade_in_function = "EaseIn",
fade_in_duration_ms = 150,
fade_out_function = "EaseOut",
fade_out_duration_ms = 150,
},
colors = {
visual_bell = "#202020"
},
}
```
6 changes: 6 additions & 0 deletions wezterm-gui/src/termwindow/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ pub struct PaneState {
/// contents, we're overlaying a little internal application
/// tab. We'll also route input to it.
pub overlay: Option<Rc<dyn Pane>>,

bell_start: Option<Instant>,
}

/// Data used when synchronously formatting pane and window titles
Expand Down Expand Up @@ -808,6 +810,10 @@ impl TermWindow {
} => {
log::info!("Ding! (this is the bell) in pane {}", pane_id);
self.emit_window_event("bell", Some(pane_id));

let mut per_pane = self.pane_state(pane_id);
per_pane.bell_start.replace(Instant::now());
window.invalidate();
}
MuxNotification::PaneOutput(pane_id) => {
self.mux_pane_output_event(pane_id);
Expand Down
88 changes: 88 additions & 0 deletions wezterm-gui/src/termwindow/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,13 +340,15 @@ impl super::TermWindow {
},
config.window_background_opacity,
);

quad.set_texture(white_space);
quad.set_is_background();
quad.set_fg_color(background);
quad.set_hsv(None);
}
}
}

if num_panes > 1 && self.window_background.is_none() {
// Per-pane, palette-specified background
let mut quad = layers[0].allocate()?;
Expand Down Expand Up @@ -381,6 +383,92 @@ impl super::TermWindow {
});
}

{
// If the bell is ringing, we draw another background layer over the
// top of this in the configured bell color
let mut per_pane = self.pane_state(pos.pane.pane_id());
if let Some(ringing) = per_pane.bell_start {
let elapsed = ringing.elapsed().as_secs_f32();

let in_duration =
Duration::from_millis(config.visual_bell.fade_in_duration_ms).as_secs_f32();
let out_duration =
Duration::from_millis(config.visual_bell.fade_out_duration_ms).as_secs_f32();

let intensity = if elapsed < in_duration {
Some(
config
.visual_bell
.fade_in_function
.evaluate_at_position(elapsed / in_duration),
)
} else {
let completion = (elapsed - in_duration) / out_duration;
if completion >= 1.0 {
None
} else {
Some(
1.0 - config
.visual_bell
.fade_out_function
.evaluate_at_position(completion),
)
}
};

match intensity {
None => {
per_pane.bell_start.take();
}
Some(intensity) => {
let (r, g, b, _) = config
.resolved_palette
.visual_bell
.unwrap_or(palette.foreground)
.to_linear_tuple_rgba();

let background = LinearRgba::with_components(
r * intensity,
g * intensity,
b * intensity,
1.0,
);
log::trace!("bell bg is {:?}", background);

self.update_next_frame_time(Some(
Instant::now() + Duration::from_millis(1000 / config.max_fps as u64),
));

let mut quad = layers[0].allocate()?;
let cell_width = self.render_metrics.cell_size.width as f32;
let cell_height = self.render_metrics.cell_size.height as f32;
let pos_x = (self.dimensions.pixel_width as f32 / -2.)
+ (pos.left as f32 * cell_width)
+ self.config.window_padding.left as f32;
let pos_y = (self.dimensions.pixel_height as f32 / -2.)
+ ((first_line_offset + pos.top) as f32 * cell_height)
+ self.config.window_padding.top as f32;

quad.set_position(
pos_x,
pos_y,
pos_x + pos.width as f32 * cell_width,
pos_y + pos.height as f32 * cell_height,
);

quad.set_texture_adjust(0., 0., 0., 0.);
quad.set_texture(white_space);
quad.set_is_background();
quad.set_fg_color(background);
quad.set_hsv(if pos.is_active {
None
} else {
Some(config.inactive_pane_hsb)
});
}
}
}
}
if self.show_tab_bar && pos.index == 0 {
let tab_dims = RenderableDimensions {
cols: self.terminal_size.cols as _,
Expand Down

0 comments on commit 0c1ad71

Please sign in to comment.