diff --git a/config/src/lib.rs b/config/src/lib.rs index df510f5e9..5d089274c 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -714,6 +714,42 @@ pub struct Config { #[serde(default = "default_one_point_oh")] pub window_background_opacity: f32, + /// inactive_pane_hue, inactive_pane_saturation and + /// inactive_pane_brightness allow for transforming the color + /// of inactive panes. + /// The pane colors are converted to HSV values and multiplied + /// by these values before being converted back to RGB to + /// use in the display. + /// + /// The default is 1.0 which leaves the values as-is. + /// + /// Modifying the hue changes the hue of the color by rotating + /// it through the color wheel. It is not as useful as the + /// other components, but is available "for free" as part of + /// the colorspace conversion. + /// + /// Modifying the saturation can add or reduce the amount of + /// "colorfulness". Making the value smaller can make it appear + /// more washed out. + /// + /// Modifying the brightness can be used to dim or increase + /// the perceived amount of light. + /// + /// The range of these values is 0.0 and up; they are used to + /// multiply the existing values, so the default of 1.0 + /// preserves the existing component, whilst 0.5 will reduce + /// it by half, and 2.0 will double the value. + /// + /// A subtle dimming effect can be achieved by setting: + /// inactive_pane_saturation = 0.9 + /// inactive_pane_brightness = 0.8 + #[serde(default = "default_one_point_oh")] + pub inactive_pane_hue: f32, + #[serde(default = "default_one_point_oh")] + pub inactive_pane_saturation: f32, + #[serde(default = "default_one_point_oh")] + pub inactive_pane_brightness: f32, + /// Specifies the alpha value to use when applying the default /// background color in a cell. This is useful to apply a kind /// of "tint" to the background image if either window_background_image diff --git a/wezterm/src/gui/fragment.glsl b/wezterm/src/gui/fragment.glsl index 922d0d71e..cf020e3ea 100644 --- a/wezterm/src/gui/fragment.glsl +++ b/wezterm/src/gui/fragment.glsl @@ -4,6 +4,7 @@ in vec2 o_tex; in vec4 o_fg_color; in vec4 o_bg_color; in float o_has_color; +in vec3 o_hsv; in vec2 o_underline; in vec2 o_cursor; in vec4 o_cursor_color; @@ -34,6 +35,30 @@ vec4 multiply(vec4 src, vec4 dst) { dst.a); } +vec3 rgb2hsv(vec3 c) +{ + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +vec3 hsv2rgb(vec3 c) +{ + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +vec4 apply_hsv(vec4 c) +{ + vec3 hsv = rgb2hsv(c.rgb) * o_hsv; + return vec4(hsv2rgb(hsv).rgb, c.a); +} + void main() { if (window_bg_layer) { if (o_has_color == 2.0) { @@ -87,4 +112,6 @@ void main() { } } } + + color = apply_hsv(color); } diff --git a/wezterm/src/gui/quad.rs b/wezterm/src/gui/quad.rs index a627b0367..dc69c93f7 100644 --- a/wezterm/src/gui/quad.rs +++ b/wezterm/src/gui/quad.rs @@ -30,6 +30,7 @@ pub struct Vertex { pub cursor_color: (f32, f32, f32, f32), pub bg_color: (f32, f32, f32, f32), pub fg_color: (f32, f32, f32, f32), + pub hsv: (f32, f32, f32), // We use a float for this because I can't get // bool or integer values to work: // "bool can't be an in in the vertex shader" @@ -52,6 +53,7 @@ pub struct Vertex { cursor_color, bg_color, fg_color, + hsv, has_color ); @@ -190,6 +192,13 @@ impl<'a> Quad<'a> { } } + pub fn set_hsv(&mut self, hsv: Option<(f32, f32, f32)>) { + let s = hsv.unwrap_or((1., 1., 1.)); + for v in self.vert.iter_mut() { + v.hsv = s; + } + } + #[allow(unused)] pub fn get_position(&self) -> (f32, f32, f32, f32) { let top_left = self.vert[V_TOP_LEFT].position; diff --git a/wezterm/src/gui/renderstate.rs b/wezterm/src/gui/renderstate.rs index 621e9df3e..d45059243 100644 --- a/wezterm/src/gui/renderstate.rs +++ b/wezterm/src/gui/renderstate.rs @@ -206,16 +206,8 @@ impl OpenGLRenderState { } } - { - // And a quad for the scrollbar thumb - let x_pos = (width / 2.0) - cell_width; - let y_pos = (height / -2.0) + padding_top; - let thumb_width = cell_width; - let thumb_height = height; - - quads.scroll_thumb = - define_quad(x_pos, y_pos, x_pos + thumb_width, y_pos + thumb_height) as usize; - } + // And a quad for the scrollbar thumb + quads.scroll_thumb = define_quad(0.0, 0.0, 0.0, 0.0) as usize; Ok(( VertexBuffer::dynamic(context, &verts)?, diff --git a/wezterm/src/gui/termwindow.rs b/wezterm/src/gui/termwindow.rs index a33af037f..239900e2b 100644 --- a/wezterm/src/gui/termwindow.rs +++ b/wezterm/src/gui/termwindow.rs @@ -79,6 +79,7 @@ struct RenderScreenLineOpenGLParams<'a> { cursor_border_color: Color, foreground: Color, + is_active: bool, } struct ComputeCellFgBgParams<'a> { @@ -2339,6 +2340,7 @@ impl TermWindow { quad.set_fg_color(foreground); quad.set_bg_color(background); + quad.set_hsv(None); quad.set_texture(texture_rect); quad.set_texture_adjust(left, top, right, bottom); quad.set_underline(underline_tex_rect); @@ -2406,12 +2408,18 @@ impl TermWindow { cursor_border_color, foreground, pos, + is_active: true, }, &mut quads, )?; } - { + // TODO: we only have a single scrollbar in a single position. + // We only update it for the active pane, but we should probably + // do a per-pane scrollbar. That will require more extensive + // changes to ScrollHit, mouse positioning, PositionedPane + // and tab size calculation. + if pos.is_active { let (thumb_top, thumb_size, color) = if self.show_scroll_bar { let info = ScrollHit::thumb( &*term, @@ -2447,6 +2455,7 @@ impl TermWindow { quad.set_position(left, top, right, bottom); quad.set_texture(white_space); quad.set_texture_adjust(0., 0., 0., 0.); + quad.set_hsv(None); quad.set_underline(white_space); quad.set_has_color(false); quad.set_cursor(white_space); @@ -2470,6 +2479,7 @@ impl TermWindow { quad.set_is_background_image(); } } + quad.set_hsv(None); quad.set_cursor_color(color); quad.set_fg_color(color); quad.set_bg_color(background); @@ -2497,6 +2507,7 @@ impl TermWindow { cursor_border_color, foreground, pos, + is_active: pos.is_active, }, &mut quads, )?; @@ -2630,6 +2641,16 @@ impl TermWindow { let num_cols = params.dims.cols; + let hsv = if params.is_active { + None + } else { + Some(( + params.config.inactive_pane_hue, + params.config.inactive_pane_saturation, + params.config.inactive_pane_brightness, + )) + }; + // Break the line into clusters of cells with the same attributes let cell_clusters = params.line.cluster(); let mut last_cell_idx = 0; @@ -2806,6 +2827,7 @@ impl TermWindow { Err(_) => break, }; + quad.set_hsv(hsv); quad.set_fg_color(glyph_color); quad.set_bg_color(bg_color); quad.set_texture(texture_rect); @@ -2858,6 +2880,7 @@ impl TermWindow { quad.set_texture(texture_rect); quad.set_texture_adjust(left, top, right, bottom); quad.set_underline(underline_tex_rect); + quad.set_hsv(hsv); quad.set_has_color(glyph.has_color); quad.set_cursor( gl_state @@ -2915,6 +2938,7 @@ impl TermWindow { quad.set_texture_adjust(0., 0., 0., 0.); quad.set_underline(white_space); quad.set_has_color(false); + quad.set_hsv(hsv); quad.set_cursor( gl_state .util_sprites diff --git a/wezterm/src/gui/vertex.glsl b/wezterm/src/gui/vertex.glsl index 9d01117af..6a448fd15 100644 --- a/wezterm/src/gui/vertex.glsl +++ b/wezterm/src/gui/vertex.glsl @@ -8,6 +8,7 @@ in vec4 fg_color; in float has_color; in vec2 cursor; in vec4 cursor_color; +in vec3 hsv; uniform mat4 projection; uniform bool window_bg_layer; @@ -21,6 +22,7 @@ out float o_has_color; out vec2 o_underline; out vec2 o_cursor; out vec4 o_cursor_color; +out vec3 o_hsv; // Returns a position that is outside of the viewport, // such that this vertex effectively won't contribute @@ -38,6 +40,7 @@ void main() { o_underline = underline; o_cursor = cursor; o_cursor_color = cursor_color; + o_hsv = hsv; if (window_bg_layer) { if (o_has_color == 2.0) {