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

305 lines
10 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::*;
use anyhow::anyhow;
use config::configuration;
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 SoftwareRenderState {}
2019-10-27 02:59:53 +03:00
impl SoftwareRenderState {
pub fn new() -> anyhow::Result<Self> {
Ok(Self {})
2019-10-27 02:59:53 +03:00
}
}
pub struct OpenGLRenderState {
pub context: Rc<GliumContext>,
pub glyph_cache: RefCell<GlyphCache<SrgbTexture2d>>,
pub util_sprites: UtilSprites<SrgbTexture2d>,
pub program: glium::Program,
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 OpenGLRenderState {
pub fn new(
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 mut errors = vec![];
let mut program = None;
for version in &["330", "300 es"] {
let source = glium::program::ProgramCreationInput::SourceCode {
vertex_shader: &Self::vertex_shader(version),
fragment_shader: &Self::fragment_shader(version),
outputs_srgb: true,
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) => {
program = Some(prog);
break;
}
Err(err) => errors.push(err.to_string()),
};
}
2019-10-27 02:59:53 +03:00
let program = program.ok_or_else(|| {
anyhow!("Failed to compile shaders: {}", errors.join("\n"))
})?;
let (glyph_vertex_buffer, glyph_index_buffer, quads) = Self::compute_vertices(
&context,
metrics,
pixel_width as f32,
pixel_height as f32,
)?;
return Ok(Self {
context,
glyph_cache,
util_sprites,
program,
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
}
};
}
}
pub fn advise_of_window_size_change(
&mut self,
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(
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 vertex_shader(version: &str) -> String {
format!("#version {}\n{}", version, include_str!("vertex.glsl"))
}
fn fragment_shader(version: &str) -> String {
format!("#version {}\n{}", version, include_str!("fragment.glsl"))
}
/// 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(
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();
let config = configuration();
let padding_right = super::termwindow::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
))
}
}
#[allow(clippy::large_enum_variant)]
pub enum RenderState {
Software(SoftwareRenderState),
GL(OpenGLRenderState),
}
impl RenderState {
pub fn clear_texture_atlas(&mut self, metrics: &RenderMetrics) -> anyhow::Result<()> {
match self {
RenderState::Software(_) => {}
RenderState::GL(gl) => {
let mut glyph_cache = gl.glyph_cache.borrow_mut();
glyph_cache.clear();
gl.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>,
2019-12-15 08:43:05 +03:00
) -> anyhow::Result<()> {
2019-10-27 02:59:53 +03:00
match self {
RenderState::Software(_) => {}
2019-10-27 02:59:53 +03:00
RenderState::GL(gl) => {
let size = size.unwrap_or_else(|| gl.glyph_cache.borrow().atlas.size());
let mut glyph_cache = GlyphCache::new_gl(&gl.context, fonts, size, metrics)?;
2019-10-27 02:59:53 +03:00
gl.util_sprites = UtilSprites::new(&mut glyph_cache, metrics)?;
*gl.glyph_cache.borrow_mut() = glyph_cache;
}
};
Ok(())
}
pub fn advise_of_window_size_change(
&mut self,
metrics: &RenderMetrics,
pixel_width: usize,
pixel_height: usize,
2019-12-15 08:43:05 +03:00
) -> anyhow::Result<()> {
2019-10-27 02:59:53 +03:00
if let RenderState::GL(gl) = self {
gl.advise_of_window_size_change(metrics, pixel_width, pixel_height)?;
}
Ok(())
}
pub fn has_opengl(&self) -> bool {
match self {
RenderState::GL(_) => true,
_ => false,
}
}
2019-10-27 02:59:53 +03:00
pub fn opengl(&self) -> &OpenGLRenderState {
match self {
RenderState::GL(gl) => gl,
_ => panic!("only valid for opengl render mode"),
}
}
}