mirror of
https://github.com/wez/wezterm.git
synced 2024-12-23 21:32:13 +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:
parent
3566b82458
commit
ca89181098
@ -48,9 +48,24 @@ pub struct VisualBell {
|
||||
pub fade_out_duration_ms: u64,
|
||||
#[serde(default)]
|
||||
pub fade_out_function: EasingFunction,
|
||||
#[serde(default)]
|
||||
pub target: VisualBellTarget,
|
||||
}
|
||||
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)]
|
||||
pub enum AudibleBell {
|
||||
SystemBeep,
|
||||
|
@ -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: 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)
|
||||
* 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) 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 connect` now also supports the `--class` parameter to override the window class
|
||||
|
||||
|
@ -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_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.
|
||||
* `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.
|
||||
|
||||
@ -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).
|
||||
|
@ -15,7 +15,7 @@ use ::window::glium::uniforms::{
|
||||
use ::window::glium::{uniform, BlendingFunction, LinearBlendingFactor, Surface};
|
||||
use ::window::WindowOps;
|
||||
use anyhow::anyhow;
|
||||
use config::{ConfigHandle, HsbTransform, TextStyle};
|
||||
use config::{ConfigHandle, HsbTransform, TextStyle, VisualBellTarget};
|
||||
use mux::pane::Pane;
|
||||
use mux::renderable::{RenderableDimensions, StableCursorPosition};
|
||||
use mux::tab::{PositionedPane, PositionedSplit, SplitDirection};
|
||||
@ -75,6 +75,7 @@ pub struct ComputeCellFgBgParams<'a> {
|
||||
pub selection_bg: LinearRgba,
|
||||
pub cursor_fg: LinearRgba,
|
||||
pub cursor_bg: LinearRgba,
|
||||
pub pane: &'a Rc<dyn Pane>,
|
||||
}
|
||||
|
||||
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(
|
||||
&mut self,
|
||||
pos: &PositionedPane,
|
||||
@ -386,101 +440,65 @@ 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();
|
||||
if let Some(intensity) = self.get_intensity_if_bell_target_ringing(
|
||||
&pos.pane,
|
||||
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 =
|
||||
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),
|
||||
)
|
||||
let background = if window_is_transparent {
|
||||
// for transparent windows, we fade in the target color
|
||||
// by adjusting its alpha
|
||||
LinearRgba::with_components(r, g, b, intensity)
|
||||
} 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),
|
||||
)
|
||||
}
|
||||
// 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 color is {:?}", background);
|
||||
|
||||
match intensity {
|
||||
None => {
|
||||
per_pane.bell_start.take();
|
||||
}
|
||||
Some(intensity) => {
|
||||
// target background color
|
||||
let (r, g, b, _) = config
|
||||
.resolved_palette
|
||||
.visual_bell
|
||||
.unwrap_or(palette.foreground)
|
||||
.to_linear_tuple_rgba();
|
||||
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;
|
||||
|
||||
let background = if window_is_transparent {
|
||||
// for transparent windows, we fade in the target color
|
||||
// by adjusting its alpha
|
||||
LinearRgba::with_components(r, g, b, intensity)
|
||||
} else {
|
||||
// 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);
|
||||
quad.set_position(
|
||||
pos_x,
|
||||
pos_y,
|
||||
pos_x + pos.width as f32 * cell_width,
|
||||
pos_y + pos.height as f32 * cell_height,
|
||||
);
|
||||
|
||||
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)
|
||||
});
|
||||
}
|
||||
}
|
||||
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 {
|
||||
@ -1137,6 +1155,7 @@ impl super::TermWindow {
|
||||
selection_bg: params.selection_bg,
|
||||
cursor_fg: params.cursor_fg,
|
||||
cursor_bg: params.cursor_bg,
|
||||
pane: ¶ms.pos.pane,
|
||||
});
|
||||
|
||||
let pos_x = (self.dimensions.pixel_width as f32 / -2.)
|
||||
@ -1370,6 +1389,7 @@ impl super::TermWindow {
|
||||
selection_bg: params.selection_bg,
|
||||
cursor_fg: params.cursor_fg,
|
||||
cursor_bg: params.cursor_bg,
|
||||
pane: ¶ms.pos.pane,
|
||||
});
|
||||
|
||||
let pos_x = (self.dimensions.pixel_width as f32 / -2.)
|
||||
@ -1543,6 +1563,43 @@ impl super::TermWindow {
|
||||
let is_cursor =
|
||||
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) =
|
||||
if is_cursor && params.cursor.visibility == CursorVisibility::Visible {
|
||||
// This logic figures out whether the cursor is visible or not.
|
||||
|
Loading…
Reference in New Issue
Block a user