1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-20 03:41:36 +03:00
wezterm/wezterm-gui/src/renderstate.rs

354 lines
12 KiB
Rust
Raw Normal View History

use super::glyphcache::GlyphCache;
use super::quad::*;
2019-10-27 02:59:53 +03:00
use super::utilsprites::{RenderMetrics, UtilSprites};
use ::window::bitmaps::atlas::OutOfTextureSpace;
2019-10-27 02:59:53 +03:00
use ::window::glium::backend::Context as GliumContext;
use ::window::glium::texture::SrgbTexture2d;
use ::window::glium::{IndexBuffer, VertexBuffer};
use ::window::*;
add `window-resized` event This is to support <https://github.com/wez/wezterm/issues/291>. The window resized event happens asynchronously wrt. processing a window resize, triggering at the end of the normal window resize handling. This commit introduces the notion of whether we are in full screen mode or not in the underlying event callback, which is useful to gate the desired feature, which is: when in full screen mode, increase the padding for the window to center its content. While poking around at this, I noticed that we weren't passing the per-window config down to the code that computes the quad locations for the window. This commit also changes the font size increase/decrease behavior so that in full screen mode it doesn't try to resize the window. ```lua local wezterm = require 'wezterm'; wezterm.on("window-resized", function(window, pane) local window_dims = window:get_dimensions(); local pane_dims = pane:get_dimensions(); local overrides = window:get_config_overrides() or {} if not window_dims.is_full_screen then if not overrides.window_padding then -- not changing anything return; end overrides.window_padding = nil; else -- Use only the middle 33% local third = math.floor(window_dims.pixel_width / 3) local new_padding = { left = third, right = third, top = 0, bottom = 0 }; if overrides.window_padding and new_padding.left == overrides.window_padding.left then -- padding is same, avoid triggering further changes return end overrides.window_padding = new_padding end window:set_config_overrides(overrides) end); return { } ```
2021-03-07 22:54:15 +03:00
use config::ConfigHandle;
2019-10-27 02:59:53 +03:00
use std::cell::RefCell;
use std::rc::Rc;
use wezterm_font::FontConfiguration;
2019-10-27 02:59:53 +03:00
pub struct RenderState {
2019-10-27 02:59:53 +03:00
pub context: Rc<GliumContext>,
pub glyph_cache: RefCell<GlyphCache<SrgbTexture2d>>,
pub util_sprites: UtilSprites<SrgbTexture2d>,
pub background_prog: glium::Program,
pub line_prog: glium::Program,
pub glyph_prog: glium::Program,
2019-10-27 02:59:53 +03:00
pub glyph_vertex_buffer: RefCell<VertexBuffer<Vertex>>,
pub glyph_index_buffer: IndexBuffer<u32>,
pub quads: Quads,
2019-10-27 02:59:53 +03:00
}
impl RenderState {
2019-10-27 02:59:53 +03:00
pub fn new(
add `window-resized` event This is to support <https://github.com/wez/wezterm/issues/291>. The window resized event happens asynchronously wrt. processing a window resize, triggering at the end of the normal window resize handling. This commit introduces the notion of whether we are in full screen mode or not in the underlying event callback, which is useful to gate the desired feature, which is: when in full screen mode, increase the padding for the window to center its content. While poking around at this, I noticed that we weren't passing the per-window config down to the code that computes the quad locations for the window. This commit also changes the font size increase/decrease behavior so that in full screen mode it doesn't try to resize the window. ```lua local wezterm = require 'wezterm'; wezterm.on("window-resized", function(window, pane) local window_dims = window:get_dimensions(); local pane_dims = pane:get_dimensions(); local overrides = window:get_config_overrides() or {} if not window_dims.is_full_screen then if not overrides.window_padding then -- not changing anything return; end overrides.window_padding = nil; else -- Use only the middle 33% local third = math.floor(window_dims.pixel_width / 3) local new_padding = { left = third, right = third, top = 0, bottom = 0 }; if overrides.window_padding and new_padding.left == overrides.window_padding.left then -- padding is same, avoid triggering further changes return end overrides.window_padding = new_padding end window:set_config_overrides(overrides) end); return { } ```
2021-03-07 22:54:15 +03:00
config: &ConfigHandle,
2019-10-27 02:59:53 +03:00
context: Rc<GliumContext>,
fonts: &Rc<FontConfiguration>,
metrics: &RenderMetrics,
mut atlas_size: usize,
2019-10-27 02:59:53 +03:00
pixel_width: usize,
pixel_height: usize,
2019-12-15 08:43:05 +03:00
) -> anyhow::Result<Self> {
loop {
let glyph_cache =
RefCell::new(GlyphCache::new_gl(&context, fonts, atlas_size, metrics)?);
let result = UtilSprites::new(&mut *glyph_cache.borrow_mut(), metrics);
match result {
Ok(util_sprites) => {
let background_prog = Self::compile_prog(
&context,
cfg!(target_os = "macos"),
Self::background_shader,
)?;
let line_prog =
Self::compile_prog(&context, cfg!(target_os = "macos"), Self::line_shader)?;
2019-10-27 02:59:53 +03:00
// Last prog outputs srgb for gamma correction
let glyph_prog = Self::compile_prog(&context, true, Self::glyph_shader)?;
let (glyph_vertex_buffer, glyph_index_buffer, quads) = Self::compute_vertices(
add `window-resized` event This is to support <https://github.com/wez/wezterm/issues/291>. The window resized event happens asynchronously wrt. processing a window resize, triggering at the end of the normal window resize handling. This commit introduces the notion of whether we are in full screen mode or not in the underlying event callback, which is useful to gate the desired feature, which is: when in full screen mode, increase the padding for the window to center its content. While poking around at this, I noticed that we weren't passing the per-window config down to the code that computes the quad locations for the window. This commit also changes the font size increase/decrease behavior so that in full screen mode it doesn't try to resize the window. ```lua local wezterm = require 'wezterm'; wezterm.on("window-resized", function(window, pane) local window_dims = window:get_dimensions(); local pane_dims = pane:get_dimensions(); local overrides = window:get_config_overrides() or {} if not window_dims.is_full_screen then if not overrides.window_padding then -- not changing anything return; end overrides.window_padding = nil; else -- Use only the middle 33% local third = math.floor(window_dims.pixel_width / 3) local new_padding = { left = third, right = third, top = 0, bottom = 0 }; if overrides.window_padding and new_padding.left == overrides.window_padding.left then -- padding is same, avoid triggering further changes return end overrides.window_padding = new_padding end window:set_config_overrides(overrides) end); return { } ```
2021-03-07 22:54:15 +03:00
config,
&context,
metrics,
pixel_width as f32,
pixel_height as f32,
)?;
return Ok(Self {
context,
glyph_cache,
util_sprites,
background_prog,
line_prog,
glyph_prog,
glyph_vertex_buffer: RefCell::new(glyph_vertex_buffer),
glyph_index_buffer,
quads,
});
}
Err(OutOfTextureSpace { size: Some(size) }) => {
atlas_size = size;
}
Err(OutOfTextureSpace { size: None }) => {
anyhow::bail!("requested texture size is impossible!?")
2019-10-27 02:59:53 +03:00
}
};
}
}
fn compile_prog(
context: &Rc<GliumContext>,
outputs_srgb: bool,
fragment_shader: fn(&str) -> (String, String),
) -> anyhow::Result<glium::Program> {
let mut errors = vec![];
for version in &["330", "300 es"] {
let (vertex_shader, fragment_shader) = fragment_shader(version);
let source = glium::program::ProgramCreationInput::SourceCode {
vertex_shader: &vertex_shader,
fragment_shader: &fragment_shader,
outputs_srgb,
tessellation_control_shader: None,
tessellation_evaluation_shader: None,
transform_feedback_varyings: None,
uses_point_size: false,
geometry_shader: None,
};
log::trace!("compiling a prog with version {}", version);
match glium::Program::new(context, source) {
Ok(prog) => {
return Ok(prog);
}
Err(err) => errors.push(err.to_string()),
};
}
anyhow::bail!("Failed to compile shaders: {}", errors.join("\n"))
}
2019-10-27 02:59:53 +03:00
pub fn advise_of_window_size_change(
&mut self,
add `window-resized` event This is to support <https://github.com/wez/wezterm/issues/291>. The window resized event happens asynchronously wrt. processing a window resize, triggering at the end of the normal window resize handling. This commit introduces the notion of whether we are in full screen mode or not in the underlying event callback, which is useful to gate the desired feature, which is: when in full screen mode, increase the padding for the window to center its content. While poking around at this, I noticed that we weren't passing the per-window config down to the code that computes the quad locations for the window. This commit also changes the font size increase/decrease behavior so that in full screen mode it doesn't try to resize the window. ```lua local wezterm = require 'wezterm'; wezterm.on("window-resized", function(window, pane) local window_dims = window:get_dimensions(); local pane_dims = pane:get_dimensions(); local overrides = window:get_config_overrides() or {} if not window_dims.is_full_screen then if not overrides.window_padding then -- not changing anything return; end overrides.window_padding = nil; else -- Use only the middle 33% local third = math.floor(window_dims.pixel_width / 3) local new_padding = { left = third, right = third, top = 0, bottom = 0 }; if overrides.window_padding and new_padding.left == overrides.window_padding.left then -- padding is same, avoid triggering further changes return end overrides.window_padding = new_padding end window:set_config_overrides(overrides) end); return { } ```
2021-03-07 22:54:15 +03:00
config: &ConfigHandle,
2019-10-27 02:59:53 +03:00
metrics: &RenderMetrics,
pixel_width: usize,
pixel_height: usize,
2019-12-15 08:43:05 +03:00
) -> anyhow::Result<()> {
let (glyph_vertex_buffer, glyph_index_buffer, quads) = Self::compute_vertices(
add `window-resized` event This is to support <https://github.com/wez/wezterm/issues/291>. The window resized event happens asynchronously wrt. processing a window resize, triggering at the end of the normal window resize handling. This commit introduces the notion of whether we are in full screen mode or not in the underlying event callback, which is useful to gate the desired feature, which is: when in full screen mode, increase the padding for the window to center its content. While poking around at this, I noticed that we weren't passing the per-window config down to the code that computes the quad locations for the window. This commit also changes the font size increase/decrease behavior so that in full screen mode it doesn't try to resize the window. ```lua local wezterm = require 'wezterm'; wezterm.on("window-resized", function(window, pane) local window_dims = window:get_dimensions(); local pane_dims = pane:get_dimensions(); local overrides = window:get_config_overrides() or {} if not window_dims.is_full_screen then if not overrides.window_padding then -- not changing anything return; end overrides.window_padding = nil; else -- Use only the middle 33% local third = math.floor(window_dims.pixel_width / 3) local new_padding = { left = third, right = third, top = 0, bottom = 0 }; if overrides.window_padding and new_padding.left == overrides.window_padding.left then -- padding is same, avoid triggering further changes return end overrides.window_padding = new_padding end window:set_config_overrides(overrides) end); return { } ```
2021-03-07 22:54:15 +03:00
config,
2019-10-27 02:59:53 +03:00
&self.context,
metrics,
pixel_width as f32,
pixel_height as f32,
)?;
*self.glyph_vertex_buffer.borrow_mut() = glyph_vertex_buffer;
self.glyph_index_buffer = glyph_index_buffer;
self.quads = quads;
2019-10-27 02:59:53 +03:00
Ok(())
}
fn glyph_shader(version: &str) -> (String, String) {
(
format!(
"#version {}\n{}\n{}",
version,
include_str!("vertex-common.glsl"),
include_str!("glyph-vertex.glsl")
),
format!(
"#version {}\n{}\n{}",
version,
include_str!("fragment-common.glsl"),
include_str!("glyph-frag.glsl")
),
)
}
fn line_shader(version: &str) -> (String, String) {
(
format!(
"#version {}\n{}\n{}",
version,
include_str!("vertex-common.glsl"),
include_str!("line-vertex.glsl")
),
format!(
"#version {}\n{}\n{}",
version,
include_str!("fragment-common.glsl"),
include_str!("line-frag.glsl")
),
)
2019-10-27 02:59:53 +03:00
}
fn background_shader(version: &str) -> (String, String) {
(
format!(
"#version {}\n{}\n{}",
version,
include_str!("vertex-common.glsl"),
include_str!("background-vertex.glsl")
),
format!(
"#version {}\n{}\n{}",
version,
include_str!("fragment-common.glsl"),
include_str!("background-frag.glsl")
),
)
2019-10-27 02:59:53 +03:00
}
/// Compute a vertex buffer to hold the quads that comprise the visible
/// portion of the screen. We recreate this when the screen is resized.
/// The idea is that we want to minimize any heavy lifting and computation
2019-10-27 02:59:53 +03:00
/// and instead just poke some attributes into the offset that corresponds
/// to a changed cell when we need to repaint the screen, and then just
/// let the GPU figure out the rest.
fn compute_vertices(
add `window-resized` event This is to support <https://github.com/wez/wezterm/issues/291>. The window resized event happens asynchronously wrt. processing a window resize, triggering at the end of the normal window resize handling. This commit introduces the notion of whether we are in full screen mode or not in the underlying event callback, which is useful to gate the desired feature, which is: when in full screen mode, increase the padding for the window to center its content. While poking around at this, I noticed that we weren't passing the per-window config down to the code that computes the quad locations for the window. This commit also changes the font size increase/decrease behavior so that in full screen mode it doesn't try to resize the window. ```lua local wezterm = require 'wezterm'; wezterm.on("window-resized", function(window, pane) local window_dims = window:get_dimensions(); local pane_dims = pane:get_dimensions(); local overrides = window:get_config_overrides() or {} if not window_dims.is_full_screen then if not overrides.window_padding then -- not changing anything return; end overrides.window_padding = nil; else -- Use only the middle 33% local third = math.floor(window_dims.pixel_width / 3) local new_padding = { left = third, right = third, top = 0, bottom = 0 }; if overrides.window_padding and new_padding.left == overrides.window_padding.left then -- padding is same, avoid triggering further changes return end overrides.window_padding = new_padding end window:set_config_overrides(overrides) end); return { } ```
2021-03-07 22:54:15 +03:00
config: &ConfigHandle,
2019-10-27 02:59:53 +03:00
context: &Rc<GliumContext>,
metrics: &RenderMetrics,
width: f32,
height: f32,
) -> anyhow::Result<(VertexBuffer<Vertex>, IndexBuffer<u32>, Quads)> {
2019-10-27 02:59:53 +03:00
let cell_width = metrics.cell_size.width as f32;
let cell_height = metrics.cell_size.height as f32;
let mut verts = Vec::new();
let mut indices = Vec::new();
2021-02-28 05:40:04 +03:00
let padding_right = super::termwindow::resize::effective_right_padding(&config, metrics);
let avail_width =
(width as usize).saturating_sub((config.window_padding.left + padding_right) as usize);
let avail_height = (height as usize)
.saturating_sub((config.window_padding.top + config.window_padding.bottom) as usize);
let num_cols = avail_width as usize / cell_width as usize;
let num_rows = avail_height as usize / cell_height as usize;
let padding_left = config.window_padding.left as f32;
let padding_top = config.window_padding.top as f32;
2019-10-27 02:59:53 +03:00
log::debug!(
"compute_vertices {}x{} {}x{} padding={} {}",
num_cols,
num_rows,
width,
height,
padding_left,
padding_top
);
let mut quads = Quads::default();
quads.cols = num_cols;
let mut define_quad = |left, top, right, bottom| -> u32 {
// Remember starting index for this position
let idx = verts.len() as u32;
verts.push(Vertex {
// Top left
position: (left, top),
..Default::default()
});
verts.push(Vertex {
// Top Right
position: (right, top),
..Default::default()
});
verts.push(Vertex {
// Bottom Left
position: (left, bottom),
..Default::default()
});
verts.push(Vertex {
// Bottom Right
position: (right, bottom),
..Default::default()
});
// Emit two triangles to form the glyph quad
indices.push(idx + V_TOP_LEFT as u32);
indices.push(idx + V_TOP_RIGHT as u32);
indices.push(idx + V_BOT_LEFT as u32);
indices.push(idx + V_TOP_RIGHT as u32);
indices.push(idx + V_BOT_LEFT as u32);
indices.push(idx + V_BOT_RIGHT as u32);
idx
};
wezterm: add window background image and opacity options I thought it would be cute to add an option to add a background image to the window. While playing around with the parameters, I accidentally implemented window transparency on X11/Wayland, provided that you have a compositing window manager. I don't know of the transparency also works on macOS or Windows as of yet; will try that out once I push this commit. This commit introduces three new configuration options explained below. In the future I'd like to allow specifying equivalent settings in a color scheme, and then that would allow setting per-pane background images. ```lua return { --[[ Specifies the path to a background image attachment file. The file can be any image format that the rust `image` crate is able to identify and load. A window background image is rendered into the background of the window before any other content. The image will be scaled to fit the window. If the path is not absolute, then it will taken as being relative to the directory containing wezterm.lua. ]] window_background_image = "/some/file", --[[ Specifies the alpha value to use when rendering the background of the window. The background is taken either from the window_background_image, or if there is none, the background color of the cell in the current position. The default is 1.0 which is 100% opaque. Setting it to a number between 0.0 and 1.0 will allow for the screen behind the window to "shine through" to varying degrees. This only works on systems with a compositing window manager. Setting opacity to a value other than 1.0 can impact render performance. ]] window_background_opacity = 1.0, --[[ 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 or window_background_opacity are in used. It can be a number between 0.0 and 1.0. The default is 0.0 Larger numbers increase the amount of the color scheme's background color that is applied over the background image. This can be useful to increase effective contrast for text that is rendered over the top. ]] window_background_tint = 0.0, } ``` refs: https://github.com/wez/wezterm/issues/141
2020-10-18 19:47:55 +03:00
// Background image fills the entire window background
quads.background_image =
define_quad(width / -2.0, height / -2.0, width / 2.0, height / 2.0) as usize;
for y in 0..=num_rows {
let y_pos = (height / -2.0) + (y as f32 * cell_height) + padding_top;
for x in 0..num_cols {
let x_pos = (width / -2.0) + (x as f32 * cell_width) + padding_left;
let idx = define_quad(x_pos, y_pos, x_pos + cell_width, y_pos + cell_height);
if x == 0 {
// build row -> vertex mapping
quads.row_starts.push(idx as usize);
}
}
}
// And a quad for the scrollbar thumb
quads.scroll_thumb = define_quad(0.0, 0.0, 0.0, 0.0) as usize;
2019-10-27 02:59:53 +03:00
Ok((
VertexBuffer::dynamic(context, &verts)?,
IndexBuffer::new(
context,
glium::index::PrimitiveType::TrianglesList,
&indices,
)?,
quads,
2019-10-27 02:59:53 +03:00
))
}
pub fn clear_texture_atlas(&mut self, metrics: &RenderMetrics) -> anyhow::Result<()> {
let mut glyph_cache = self.glyph_cache.borrow_mut();
glyph_cache.clear();
self.util_sprites = UtilSprites::new(&mut glyph_cache, metrics)?;
Ok(())
}
2019-10-27 02:59:53 +03:00
pub fn recreate_texture_atlas(
&mut self,
fonts: &Rc<FontConfiguration>,
metrics: &RenderMetrics,
size: Option<usize>,
) -> anyhow::Result<()> {
// We make a a couple of passes at resizing; if the user has selected a large
// font size (or a large scaling factor) then the `size==None` case will not
// be able to fit the initial utility glyphs and apply_scale_change won't
// be able to deal with that error situation. Rather than make every
// caller know how to deal with OutOfTextureSpace we try to absorb
// and accomodate that here.
let mut size = size;
let mut attempt = 10;
loop {
match self.recreate_texture_atlas_impl(fonts, metrics, size) {
Ok(_) => return Ok(()),
Err(err) => {
attempt -= 1;
if attempt == 0 {
return Err(err);
}
if let Some(&OutOfTextureSpace {
size: Some(needed_size),
}) = err.downcast_ref::<OutOfTextureSpace>()
{
size.replace(needed_size);
continue;
}
return Err(err);
}
}
}
}
fn recreate_texture_atlas_impl(
&mut self,
fonts: &Rc<FontConfiguration>,
metrics: &RenderMetrics,
size: Option<usize>,
2019-12-15 08:43:05 +03:00
) -> anyhow::Result<()> {
let size = size.unwrap_or_else(|| self.glyph_cache.borrow().atlas.size());
let mut glyph_cache = GlyphCache::new_gl(&self.context, fonts, size, metrics)?;
self.util_sprites = UtilSprites::new(&mut glyph_cache, metrics)?;
*self.glyph_cache.borrow_mut() = glyph_cache;
2019-10-27 02:59:53 +03:00
Ok(())
}
}