1
1
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:
Wez Furlong 2019-02-22 14:52:35 -08:00
parent 7e2e48dadc
commit 89da009dce
7 changed files with 185 additions and 91 deletions

View File

@ -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));
}
}

View File

@ -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,
)?);
}

View File

@ -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 }))

View File

@ -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();

View File

@ -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

View File

@ -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:

View File

@ -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(())
}