mirror of
https://github.com/wez/wezterm.git
synced 2024-11-23 23:21:08 +03:00
Adjust for high dpi displays with the glutin backend
This results in a less blurry display. As a bonus, this lays down some plumbing for changing the font size on the fly. Refs: https://github.com/wez/wezterm/issues/2
This commit is contained in:
parent
7e2e48dadc
commit
89da009dce
@ -42,7 +42,12 @@ struct NamedFontImpl {
|
||||
}
|
||||
|
||||
impl FontSystem for CoreTextSystem {
|
||||
fn load_font(&self, config: &Config, style: &TextStyle) -> Result<Box<NamedFont>, Error> {
|
||||
fn load_font(
|
||||
&self,
|
||||
config: &Config,
|
||||
style: &TextStyle,
|
||||
font_scale: f64,
|
||||
) -> Result<Box<NamedFont>, Error> {
|
||||
let mut fonts = Vec::new();
|
||||
for font_attr in style.font_with_fallback() {
|
||||
let col = match create_for_family(&font_attr.family) {
|
||||
@ -63,7 +68,8 @@ impl FontSystem for CoreTextSystem {
|
||||
let has_color = (traits & kCTFontTraitColorGlyphs) == kCTFontTraitColorGlyphs;
|
||||
|
||||
let d = d.clone();
|
||||
let ct_font = new_from_descriptor(&d, config.font_size * config.dpi / 72.0);
|
||||
let ct_font =
|
||||
new_from_descriptor(&d, font_scale * config.font_size * config.dpi / 72.0);
|
||||
fonts.push(CoreTextFontImpl::new(ct_font, has_color));
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,12 @@ impl FontLoaderAndFreeType {
|
||||
}
|
||||
|
||||
impl FontSystem for FontLoaderAndFreeType {
|
||||
fn load_font(&self, config: &Config, style: &TextStyle) -> Result<Box<NamedFont>, Error> {
|
||||
fn load_font(
|
||||
&self,
|
||||
config: &Config,
|
||||
style: &TextStyle,
|
||||
font_scale: f64,
|
||||
) -> Result<Box<NamedFont>, Error> {
|
||||
let mut lib = ftwrap::Library::new()?;
|
||||
// Some systems don't support this mode, so if it fails, we don't
|
||||
// care to abort the rest of what we're doing
|
||||
@ -47,7 +52,7 @@ impl FontSystem for FontLoaderAndFreeType {
|
||||
|
||||
fonts.push(FreeTypeFontImpl::with_face_size_and_dpi(
|
||||
face,
|
||||
config.font_size,
|
||||
config.font_size * font_scale,
|
||||
config.dpi as u32,
|
||||
)?);
|
||||
}
|
||||
|
@ -19,14 +19,19 @@ impl RustTypeFonts {
|
||||
}
|
||||
|
||||
impl FontSystem for RustTypeFonts {
|
||||
fn load_font(&self, config: &Config, style: &TextStyle) -> Result<Box<NamedFont>, Error> {
|
||||
fn load_font(
|
||||
&self,
|
||||
config: &Config,
|
||||
style: &TextStyle,
|
||||
font_scale: f64,
|
||||
) -> Result<Box<NamedFont>, Error> {
|
||||
let mut fonts = Vec::new();
|
||||
for (data, idx) in fontloader::load_system_fonts(config, style)? {
|
||||
eprintln!("want idx {} in bytes of len {}", idx, data.len());
|
||||
fonts.push(RustTypeFontImpl::from_bytes(
|
||||
data,
|
||||
idx,
|
||||
config.font_size as f32 * config.dpi as f32 / 72.0,
|
||||
font_scale as f32 * config.font_size as f32 * config.dpi as f32 / 72.0,
|
||||
)?);
|
||||
}
|
||||
Ok(Box::new(NamedFontImpl { fonts }))
|
||||
|
@ -38,6 +38,8 @@ pub struct FontConfiguration {
|
||||
fonts: RefCell<HashMap<TextStyle, FontPtr>>,
|
||||
system: Box<FontSystem>,
|
||||
metrics: RefCell<Option<FontMetrics>>,
|
||||
dpi_scale: RefCell<f64>,
|
||||
font_scale: RefCell<f64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Copy)]
|
||||
@ -122,6 +124,8 @@ impl FontConfiguration {
|
||||
fonts: RefCell::new(HashMap::new()),
|
||||
system: system.new_font_system(),
|
||||
metrics: RefCell::new(None),
|
||||
font_scale: RefCell::new(1.0),
|
||||
dpi_scale: RefCell::new(1.0),
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,16 +138,32 @@ impl FontConfiguration {
|
||||
return Ok(Rc::clone(entry));
|
||||
}
|
||||
|
||||
let font = Rc::new(RefCell::new(self.system.load_font(&self.config, style)?));
|
||||
let scale = *self.dpi_scale.borrow() * *self.font_scale.borrow();
|
||||
let font = Rc::new(RefCell::new(self.system.load_font(
|
||||
&self.config,
|
||||
style,
|
||||
scale,
|
||||
)?));
|
||||
fonts.insert(style.clone(), Rc::clone(&font));
|
||||
Ok(font)
|
||||
}
|
||||
|
||||
pub fn change_scaling(&self, font_scale: f64, dpi_scale: f64) {
|
||||
*self.dpi_scale.borrow_mut() = dpi_scale;
|
||||
*self.font_scale.borrow_mut() = font_scale;
|
||||
self.fonts.borrow_mut().clear();
|
||||
self.metrics.borrow_mut().take();
|
||||
}
|
||||
|
||||
/// Returns the baseline font specified in the configuration
|
||||
pub fn default_font(&self) -> Result<Rc<RefCell<Box<NamedFont>>>, Error> {
|
||||
self.cached_font(&self.config.font)
|
||||
}
|
||||
|
||||
pub fn get_font_scale(&self) -> f64 {
|
||||
*self.font_scale.borrow()
|
||||
}
|
||||
|
||||
pub fn default_font_metrics(&self) -> Result<FontMetrics, Error> {
|
||||
{
|
||||
let metrics = self.metrics.borrow();
|
||||
|
@ -85,7 +85,12 @@ pub trait NamedFont {
|
||||
pub trait FontSystem {
|
||||
/// Given a text style, load (without caching) the font that
|
||||
/// best matches according to the fontconfig pattern.
|
||||
fn load_font(&self, config: &Config, style: &TextStyle) -> Result<Box<NamedFont>, Error>;
|
||||
fn load_font(
|
||||
&self,
|
||||
config: &Config,
|
||||
style: &TextStyle,
|
||||
font_scale: f64,
|
||||
) -> Result<Box<NamedFont>, Error>;
|
||||
}
|
||||
|
||||
/// Describes the key font metrics that we use in rendering
|
||||
|
@ -11,7 +11,7 @@ use crate::Child;
|
||||
use crate::MasterPty;
|
||||
use clipboard::{ClipboardContext, ClipboardProvider};
|
||||
use glium;
|
||||
use glium::glutin::dpi::{LogicalPosition, LogicalSize};
|
||||
use glium::glutin::dpi::{LogicalPosition, LogicalSize, PhysicalPosition};
|
||||
use glium::glutin::{self, ElementState, MouseCursor};
|
||||
use std::io::Write;
|
||||
use std::rc::Rc;
|
||||
@ -115,6 +115,7 @@ pub struct TerminalWindow {
|
||||
host: Host,
|
||||
_event_loop: Rc<GuiEventLoop>,
|
||||
_config: Rc<Config>,
|
||||
fonts: Rc<FontConfiguration>,
|
||||
renderer: Renderer,
|
||||
width: u16,
|
||||
height: u16,
|
||||
@ -122,7 +123,7 @@ pub struct TerminalWindow {
|
||||
cell_width: usize,
|
||||
terminal: Terminal,
|
||||
process: Child,
|
||||
last_mouse_coords: LogicalPosition,
|
||||
last_mouse_coords: PhysicalPosition,
|
||||
last_modifiers: KeyModifiers,
|
||||
allow_received_character: bool,
|
||||
}
|
||||
@ -185,6 +186,7 @@ impl TerminalWindow {
|
||||
host,
|
||||
_event_loop: Rc::clone(event_loop),
|
||||
_config: Rc::clone(config),
|
||||
fonts: Rc::clone(fonts),
|
||||
renderer,
|
||||
width,
|
||||
height,
|
||||
@ -192,7 +194,7 @@ impl TerminalWindow {
|
||||
cell_width,
|
||||
terminal,
|
||||
process,
|
||||
last_mouse_coords: LogicalPosition::new(0.0, 0.0),
|
||||
last_mouse_coords: PhysicalPosition::new(0.0, 0.0),
|
||||
last_modifiers: Default::default(),
|
||||
allow_received_character: false,
|
||||
})
|
||||
@ -236,7 +238,8 @@ impl TerminalWindow {
|
||||
}
|
||||
|
||||
fn resize_surfaces(&mut self, size: LogicalSize) -> Result<bool, Error> {
|
||||
let (width, height): (u32, u32) = size.into();
|
||||
let dpi_scale = self.host.display.gl_window().get_hidpi_factor();
|
||||
let (width, height): (u32, u32) = size.to_physical(dpi_scale).into();
|
||||
let width = width as u16;
|
||||
let height = height as u16;
|
||||
|
||||
@ -282,7 +285,7 @@ impl TerminalWindow {
|
||||
|
||||
fn mouse_move(
|
||||
&mut self,
|
||||
position: LogicalPosition,
|
||||
position: PhysicalPosition,
|
||||
modifiers: glium::glutin::ModifiersState,
|
||||
) -> Result<(), Error> {
|
||||
self.last_mouse_coords = position;
|
||||
@ -615,6 +618,34 @@ impl TerminalWindow {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Called in response to either the screen dpi changing,
|
||||
/// or the font scaling factor changing
|
||||
fn scaling_changed(&mut self, font_scale: Option<f64>) -> Result<(), Error> {
|
||||
let dpi_scale = self.host.display.gl_window().get_hidpi_factor();
|
||||
let font_scale = font_scale.unwrap_or_else(|| self.fonts.get_font_scale());
|
||||
eprintln!(
|
||||
"TerminalWindow::scaling_changed dpi_scale={} font_scale={}",
|
||||
dpi_scale, font_scale
|
||||
);
|
||||
|
||||
self.fonts.change_scaling(font_scale, dpi_scale);
|
||||
|
||||
let metrics = self.fonts.default_font_metrics()?;
|
||||
let (cell_height, cell_width) = (metrics.cell_height, metrics.cell_width);
|
||||
self.cell_height = cell_height.ceil() as usize;
|
||||
self.cell_width = cell_width.ceil() as usize;
|
||||
|
||||
self.renderer.scaling_changed(&mut self.host.display)?;
|
||||
|
||||
let size = self.host.display.gl_window().get_inner_size();
|
||||
self.width = 0;
|
||||
self.height = 0;
|
||||
if let Some(size) = size {
|
||||
self.resize_surfaces(size)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn dispatch_event(&mut self, event: &glutin::Event) -> Result<(), Error> {
|
||||
use glium::glutin::{Event, WindowEvent};
|
||||
match *event {
|
||||
@ -630,10 +661,7 @@ impl TerminalWindow {
|
||||
} => {
|
||||
// Assuming that this is dragging a window between hidpi and
|
||||
// normal dpi displays. Treat this as a resize event of sorts
|
||||
let size = self.host.display.gl_window().get_inner_size();
|
||||
if let Some(size) = size {
|
||||
self.resize_surfaces(size)?;
|
||||
}
|
||||
self.scaling_changed(None)?;
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::Resized(size),
|
||||
@ -680,7 +708,8 @@ impl TerminalWindow {
|
||||
},
|
||||
..
|
||||
} => {
|
||||
self.mouse_move(position, modifiers)?;
|
||||
let dpi_scale = self.host.display.gl_window().get_hidpi_factor();
|
||||
self.mouse_move(position.to_physical(dpi_scale), modifiers)?;
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event:
|
||||
|
@ -413,78 +413,7 @@ impl Renderer {
|
||||
let cell_height = cell_height.ceil() as usize;
|
||||
let cell_width = cell_width.ceil() as usize;
|
||||
|
||||
// Create the texture atlas for the line decoration layer.
|
||||
// This is a bitmap with columns to accomodate the U_XXX
|
||||
// constants defined above.
|
||||
let underline_tex = {
|
||||
let width = 5 * cell_width;
|
||||
let mut underline_data = Vec::with_capacity(width * cell_height * 4);
|
||||
underline_data.resize(width * cell_height * 4, 0u8);
|
||||
|
||||
let descender_row = (cell_height as isize + descender) as usize;
|
||||
let descender_plus_one = (1 + descender_row).min(cell_height - 1);
|
||||
let descender_plus_two = (2 + descender_row).min(cell_height - 1);
|
||||
let strike_row = descender_row / 2;
|
||||
|
||||
// First, the single underline.
|
||||
// We place this just under the descender position.
|
||||
{
|
||||
let col = 0;
|
||||
let offset = ((width * 4) * descender_plus_one) + (col * 4 * cell_width);
|
||||
for i in 0..4 * cell_width {
|
||||
underline_data[offset + i] = 0xff;
|
||||
}
|
||||
}
|
||||
// Double underline,
|
||||
// We place this at and just below the descender
|
||||
{
|
||||
let col = 1;
|
||||
let offset_one = ((width * 4) * (descender_row)) + (col * 4 * cell_width);
|
||||
let offset_two = ((width * 4) * (descender_plus_two)) + (col * 4 * cell_width);
|
||||
for i in 0..4 * cell_width {
|
||||
underline_data[offset_one + i] = 0xff;
|
||||
underline_data[offset_two + i] = 0xff;
|
||||
}
|
||||
}
|
||||
// Strikethrough
|
||||
{
|
||||
let col = 2;
|
||||
let offset = (width * 4) * strike_row + (col * 4 * cell_width);
|
||||
for i in 0..4 * cell_width {
|
||||
underline_data[offset + i] = 0xff;
|
||||
}
|
||||
}
|
||||
// Strikethrough and single underline
|
||||
{
|
||||
let col = 3;
|
||||
let offset_one = ((width * 4) * descender_plus_one) + (col * 4 * cell_width);
|
||||
let offset_two = ((width * 4) * strike_row) + (col * 4 * cell_width);
|
||||
for i in 0..4 * cell_width {
|
||||
underline_data[offset_one + i] = 0xff;
|
||||
underline_data[offset_two + i] = 0xff;
|
||||
}
|
||||
}
|
||||
// Strikethrough and double underline
|
||||
{
|
||||
let col = 4;
|
||||
let offset_one = ((width * 4) * (descender_row)) + (col * 4 * cell_width);
|
||||
let offset_two = ((width * 4) * strike_row) + (col * 4 * cell_width);
|
||||
let offset_three = ((width * 4) * (descender_plus_two)) + (col * 4 * cell_width);
|
||||
for i in 0..4 * cell_width {
|
||||
underline_data[offset_one + i] = 0xff;
|
||||
underline_data[offset_two + i] = 0xff;
|
||||
underline_data[offset_three + i] = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
glium::texture::SrgbTexture2d::new(
|
||||
facade,
|
||||
glium::texture::RawImage2d::from_raw_rgba(
|
||||
underline_data,
|
||||
(width as u32, cell_height as u32),
|
||||
),
|
||||
)?
|
||||
};
|
||||
let underline_tex = Self::compute_underlines(facade, cell_width, cell_height, descender)?;
|
||||
|
||||
let (glyph_vertex_buffer, glyph_index_buffer) = Self::compute_vertices(
|
||||
facade,
|
||||
@ -526,10 +455,105 @@ impl Renderer {
|
||||
})
|
||||
}
|
||||
|
||||
/// Create the texture atlas for the line decoration layer.
|
||||
/// This is a bitmap with columns to accomodate the U_XXX
|
||||
/// constants defined above.
|
||||
fn compute_underlines<F: Facade>(
|
||||
facade: &F,
|
||||
cell_width: usize,
|
||||
cell_height: usize,
|
||||
descender: isize,
|
||||
) -> Result<SrgbTexture2d, glium::texture::TextureCreationError> {
|
||||
let width = 5 * cell_width;
|
||||
let mut underline_data = Vec::with_capacity(width * cell_height * 4);
|
||||
underline_data.resize(width * cell_height * 4, 0u8);
|
||||
|
||||
let descender_row = (cell_height as isize + descender) as usize;
|
||||
let descender_plus_one = (1 + descender_row).min(cell_height - 1);
|
||||
let descender_plus_two = (2 + descender_row).min(cell_height - 1);
|
||||
let strike_row = descender_row / 2;
|
||||
|
||||
// First, the single underline.
|
||||
// We place this just under the descender position.
|
||||
{
|
||||
let col = 0;
|
||||
let offset = ((width * 4) * descender_plus_one) + (col * 4 * cell_width);
|
||||
for i in 0..4 * cell_width {
|
||||
underline_data[offset + i] = 0xff;
|
||||
}
|
||||
}
|
||||
// Double underline,
|
||||
// We place this at and just below the descender
|
||||
{
|
||||
let col = 1;
|
||||
let offset_one = ((width * 4) * (descender_row)) + (col * 4 * cell_width);
|
||||
let offset_two = ((width * 4) * (descender_plus_two)) + (col * 4 * cell_width);
|
||||
for i in 0..4 * cell_width {
|
||||
underline_data[offset_one + i] = 0xff;
|
||||
underline_data[offset_two + i] = 0xff;
|
||||
}
|
||||
}
|
||||
// Strikethrough
|
||||
{
|
||||
let col = 2;
|
||||
let offset = (width * 4) * strike_row + (col * 4 * cell_width);
|
||||
for i in 0..4 * cell_width {
|
||||
underline_data[offset + i] = 0xff;
|
||||
}
|
||||
}
|
||||
// Strikethrough and single underline
|
||||
{
|
||||
let col = 3;
|
||||
let offset_one = ((width * 4) * descender_plus_one) + (col * 4 * cell_width);
|
||||
let offset_two = ((width * 4) * strike_row) + (col * 4 * cell_width);
|
||||
for i in 0..4 * cell_width {
|
||||
underline_data[offset_one + i] = 0xff;
|
||||
underline_data[offset_two + i] = 0xff;
|
||||
}
|
||||
}
|
||||
// Strikethrough and double underline
|
||||
{
|
||||
let col = 4;
|
||||
let offset_one = ((width * 4) * (descender_row)) + (col * 4 * cell_width);
|
||||
let offset_two = ((width * 4) * strike_row) + (col * 4 * cell_width);
|
||||
let offset_three = ((width * 4) * (descender_plus_two)) + (col * 4 * cell_width);
|
||||
for i in 0..4 * cell_width {
|
||||
underline_data[offset_one + i] = 0xff;
|
||||
underline_data[offset_two + i] = 0xff;
|
||||
underline_data[offset_three + i] = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
glium::texture::SrgbTexture2d::new(
|
||||
facade,
|
||||
glium::texture::RawImage2d::from_raw_rgba(
|
||||
underline_data,
|
||||
(width as u32, cell_height as u32),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn scaling_changed<F: Facade>(&mut self, facade: &F) -> Result<(), Error> {
|
||||
let metrics = self.fonts.default_font_metrics()?;
|
||||
self.cell_height = metrics.cell_height.ceil() as usize;
|
||||
self.cell_width = metrics.cell_width.ceil() as usize;
|
||||
self.descender = if metrics.descender.is_positive() {
|
||||
((f64::from(metrics.descender)) / 64.0).ceil() as isize
|
||||
} else {
|
||||
((f64::from(metrics.descender)) / 64.0).floor() as isize
|
||||
};
|
||||
|
||||
self.glyph_cache.borrow_mut().clear();
|
||||
self.atlas = RefCell::new(Atlas::new(facade, TEX_SIZE)?);
|
||||
self.underline_tex =
|
||||
Self::compute_underlines(facade, self.cell_width, self.cell_height, self.descender)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn recreate_atlas<F: Facade>(&mut self, facade: &F, size: u32) -> Result<(), Error> {
|
||||
let atlas = RefCell::new(Atlas::new(facade, size)?);
|
||||
self.atlas = atlas;
|
||||
self.glyph_cache = RefCell::new(HashMap::new());
|
||||
self.glyph_cache.borrow_mut().clear();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user