1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-24 13:52:55 +03:00

bell: allow changing the cursor color when the bell is rung

I find this less distracting than both the audible and the default
background color flashing.
This commit is contained in:
Wez Furlong 2021-09-29 08:02:34 -07:00
parent 3566b82458
commit ca89181098
4 changed files with 176 additions and 92 deletions

View File

@ -48,9 +48,24 @@ pub struct VisualBell {
pub fade_out_duration_ms: u64, pub fade_out_duration_ms: u64,
#[serde(default)] #[serde(default)]
pub fade_out_function: EasingFunction, pub fade_out_function: EasingFunction,
#[serde(default)]
pub target: VisualBellTarget,
} }
impl_lua_conversion!(VisualBell); impl_lua_conversion!(VisualBell);
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub enum VisualBellTarget {
BackgroundColor,
CursorColor,
}
impl_lua_conversion!(VisualBellTarget);
impl Default for VisualBellTarget {
fn default() -> VisualBellTarget {
Self::BackgroundColor
}
}
#[derive(Debug, Deserialize, Serialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone)]
pub enum AudibleBell { pub enum AudibleBell {
SystemBeep, SystemBeep,

View File

@ -48,9 +48,8 @@ As features stabilize some brief notes about them will accumulate here.
* Fixed: Wayland: panic with most recent wlroots. Thanks to [@unrelentingtech](https://github.com/unrelentingtech)! [#1144](https://github.com/wez/wezterm/issues/1144) * Fixed: Wayland: panic with most recent wlroots. Thanks to [@unrelentingtech](https://github.com/unrelentingtech)! [#1144](https://github.com/wez/wezterm/issues/1144)
* Fixed: incorrect spacing for IDEOGRAPHIC SPACE. [#1161](https://github.com/wez/wezterm/issues/1161) * Fixed: incorrect spacing for IDEOGRAPHIC SPACE. [#1161](https://github.com/wez/wezterm/issues/1161)
* 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) * 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) * 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) and [audible_bell](config/lua/config/audible_bell.md) configuration options * New: [visual_bell](config/lua/config/visual_bell.md) and [audible_bell](config/lua/config/audible_bell.md) configuration options, as well as a [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)
* New: [wezterm.action_callback](config/lua/wezterm/action_callback.md) function to make it easier to use custom events. Thanks to [@bew](https://github.com/bew)! [#1151](https://github.com/wez/wezterm/pull/1151) * New: [wezterm.action_callback](config/lua/wezterm/action_callback.md) function to make it easier to use custom events. Thanks to [@bew](https://github.com/bew)! [#1151](https://github.com/wez/wezterm/pull/1151)
* New: `wezterm connect` now also supports the `--class` parameter to override the window class * New: `wezterm connect` now also supports the `--class` parameter to override the window class

View File

@ -13,6 +13,7 @@ There are four fields to the visual_bell config option:
* `fade_out_duration_ms` - how long it should take for the bell color to fade out, 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_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. * `fade_out_function` - an easing function that affects how the bell color is faded out.
* `target` - can be `"BackgroundColor"` (the default) to have the background color of the terminal change when the bell is rung, or `"CursorColor"` to have the cursor color change when the bell is rung.
If the total fade in and out durations are 0, then there will be no visual bell indication. If the total fade in and out durations are 0, then there will be no visual bell indication.
@ -43,4 +44,16 @@ return {
} }
``` ```
The follow configuration make the cursor briefly flare when the bell is run:
```lua
return {
visual_bell = {
fade_in_duration_ms = 75,
fade_out_duration_ms = 75,
target = "CursorColor",
},
}
```
See also [audible_bell](audible_bell.md) and [bell event](../window-events/bell.md). See also [audible_bell](audible_bell.md) and [bell event](../window-events/bell.md).

View File

@ -15,7 +15,7 @@ use ::window::glium::uniforms::{
use ::window::glium::{uniform, BlendingFunction, LinearBlendingFactor, Surface}; use ::window::glium::{uniform, BlendingFunction, LinearBlendingFactor, Surface};
use ::window::WindowOps; use ::window::WindowOps;
use anyhow::anyhow; use anyhow::anyhow;
use config::{ConfigHandle, HsbTransform, TextStyle}; use config::{ConfigHandle, HsbTransform, TextStyle, VisualBellTarget};
use mux::pane::Pane; use mux::pane::Pane;
use mux::renderable::{RenderableDimensions, StableCursorPosition}; use mux::renderable::{RenderableDimensions, StableCursorPosition};
use mux::tab::{PositionedPane, PositionedSplit, SplitDirection}; use mux::tab::{PositionedPane, PositionedSplit, SplitDirection};
@ -75,6 +75,7 @@ pub struct ComputeCellFgBgParams<'a> {
pub selection_bg: LinearRgba, pub selection_bg: LinearRgba,
pub cursor_fg: LinearRgba, pub cursor_fg: LinearRgba,
pub cursor_bg: LinearRgba, pub cursor_bg: LinearRgba,
pub pane: &'a Rc<dyn Pane>,
} }
pub struct ComputeCellFgBgResult { pub struct ComputeCellFgBgResult {
@ -209,6 +210,59 @@ impl super::TermWindow {
} }
} }
fn get_intensity_if_bell_target_ringing(
&self,
pane: &Rc<dyn Pane>,
config: &ConfigHandle,
target: VisualBellTarget,
) -> Option<f32> {
let mut per_pane = self.pane_state(pane.pane_id());
if let Some(ringing) = per_pane.bell_start {
if config.visual_bell.target == target {
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) => {
self.update_next_frame_time(Some(
Instant::now() + Duration::from_millis(1000 / config.max_fps as u64),
));
return Some(intensity);
}
}
}
}
None
}
pub fn paint_pane_opengl( pub fn paint_pane_opengl(
&mut self, &mut self,
pos: &PositionedPane, pos: &PositionedPane,
@ -386,101 +440,65 @@ impl super::TermWindow {
{ {
// If the bell is ringing, we draw another background layer over the // If the bell is ringing, we draw another background layer over the
// top of this in the configured bell color // top of this in the configured bell color
let mut per_pane = self.pane_state(pos.pane.pane_id()); if let Some(intensity) = self.get_intensity_if_bell_target_ringing(
if let Some(ringing) = per_pane.bell_start { &pos.pane,
let elapsed = ringing.elapsed().as_secs_f32(); config,
VisualBellTarget::BackgroundColor,
) {
// target background color
let (r, g, b, _) = config
.resolved_palette
.visual_bell
.unwrap_or(palette.foreground)
.to_linear_tuple_rgba();
let in_duration = let background = if window_is_transparent {
Duration::from_millis(config.visual_bell.fade_in_duration_ms).as_secs_f32(); // for transparent windows, we fade in the target color
let out_duration = // by adjusting its alpha
Duration::from_millis(config.visual_bell.fade_out_duration_ms).as_secs_f32(); LinearRgba::with_components(r, g, b, intensity)
let intensity = if elapsed < in_duration {
Some(
config
.visual_bell
.fade_in_function
.evaluate_at_position(elapsed / in_duration),
)
} else { } else {
let completion = (elapsed - in_duration) / out_duration; // otherwise We'll interpolate between the background color
if completion >= 1.0 { // and the the target color
None let (r1, g1, b1, a) = rgbcolor_alpha_to_window_color(
} else { palette.background,
Some( config.window_background_opacity,
1.0 - config )
.visual_bell .tuple();
.fade_out_function LinearRgba::with_components(
.evaluate_at_position(completion), r1 + (r - r1) * intensity,
) g1 + (g - g1) * intensity,
} b1 + (b - b1) * intensity,
a,
)
}; };
log::trace!("bell color is {:?}", background);
match intensity { let mut quad = layers[0].allocate()?;
None => { let cell_width = self.render_metrics.cell_size.width as f32;
per_pane.bell_start.take(); let cell_height = self.render_metrics.cell_size.height as f32;
} let pos_x = (self.dimensions.pixel_width as f32 / -2.)
Some(intensity) => { + (pos.left as f32 * cell_width)
// target background color + self.config.window_padding.left as f32;
let (r, g, b, _) = config let pos_y = (self.dimensions.pixel_height as f32 / -2.)
.resolved_palette + ((first_line_offset + pos.top) as f32 * cell_height)
.visual_bell + self.config.window_padding.top as f32;
.unwrap_or(palette.foreground)
.to_linear_tuple_rgba();
let background = if window_is_transparent { quad.set_position(
// for transparent windows, we fade in the target color pos_x,
// by adjusting its alpha pos_y,
LinearRgba::with_components(r, g, b, intensity) pos_x + pos.width as f32 * cell_width,
} else { pos_y + pos.height as f32 * cell_height,
// otherwise We'll interpolate between the background color );
// and the the target color
let (r1, g1, b1, a) = rgbcolor_alpha_to_window_color(
palette.background,
config.window_background_opacity,
)
.tuple();
LinearRgba::with_components(
r1 + (r - r1) * intensity,
g1 + (g - g1) * intensity,
b1 + (b - b1) * intensity,
a,
)
};
log::trace!("bell bg is {:?}", background);
self.update_next_frame_time(Some( quad.set_texture_adjust(0., 0., 0., 0.);
Instant::now() + Duration::from_millis(1000 / config.max_fps as u64), quad.set_texture(white_space);
)); quad.set_is_background();
quad.set_fg_color(background);
let mut quad = layers[0].allocate()?; quad.set_hsv(if pos.is_active {
let cell_width = self.render_metrics.cell_size.width as f32; None
let cell_height = self.render_metrics.cell_size.height as f32; } else {
let pos_x = (self.dimensions.pixel_width as f32 / -2.) Some(config.inactive_pane_hsb)
+ (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 { if self.show_tab_bar && pos.index == 0 {
@ -1137,6 +1155,7 @@ impl super::TermWindow {
selection_bg: params.selection_bg, selection_bg: params.selection_bg,
cursor_fg: params.cursor_fg, cursor_fg: params.cursor_fg,
cursor_bg: params.cursor_bg, cursor_bg: params.cursor_bg,
pane: &params.pos.pane,
}); });
let pos_x = (self.dimensions.pixel_width as f32 / -2.) let pos_x = (self.dimensions.pixel_width as f32 / -2.)
@ -1370,6 +1389,7 @@ impl super::TermWindow {
selection_bg: params.selection_bg, selection_bg: params.selection_bg,
cursor_fg: params.cursor_fg, cursor_fg: params.cursor_fg,
cursor_bg: params.cursor_bg, cursor_bg: params.cursor_bg,
pane: &params.pos.pane,
}); });
let pos_x = (self.dimensions.pixel_width as f32 / -2.) let pos_x = (self.dimensions.pixel_width as f32 / -2.)
@ -1543,6 +1563,43 @@ impl super::TermWindow {
let is_cursor = let is_cursor =
params.stable_line_idx == Some(params.cursor.y) && params.cursor.x == params.cell_idx; params.stable_line_idx == Some(params.cursor.y) && params.cursor.x == params.cell_idx;
if is_cursor {
if let Some(intensity) = self.get_intensity_if_bell_target_ringing(
params.pane,
params.config,
VisualBellTarget::CursorColor,
) {
let (fg_color, bg_color) = if self.config.force_reverse_video_cursor {
(params.bg_color, params.fg_color)
} else {
(params.cursor_fg, params.cursor_bg)
};
// interpolate between the background color
// and the the target color
let (r1, g1, b1, a) = bg_color.tuple();
let (r, g, b, _) = params
.config
.resolved_palette
.visual_bell
.map(|c| c.to_linear_tuple_rgba())
.unwrap_or_else(|| fg_color.tuple());
let bg_color = LinearRgba::with_components(
r1 + (r - r1) * intensity,
g1 + (g - g1) * intensity,
b1 + (b - b1) * intensity,
a,
);
return ComputeCellFgBgResult {
fg_color,
bg_color,
cursor_shape: Some(CursorShape::SteadyBlock),
};
}
}
let (cursor_shape, visibility) = let (cursor_shape, visibility) =
if is_cursor && params.cursor.visibility == CursorVisibility::Visible { if is_cursor && params.cursor.visibility == CursorVisibility::Visible {
// This logic figures out whether the cursor is visible or not. // This logic figures out whether the cursor is visible or not.