Fix duplicate resize events

If a resize event is identical to the current size, it is no longer
propagated but the resize is discarded immediately.

To further prevent resizes when not necessary, the list of monitors is
enumerated and the DPR of the first display is assumed to be the target
DPR.

This allows spawning a window with dimensions when the config has
columns and lines specified and the window only needs to be resized if
the estimated DPR is not correct.

Fixes #1825.
Fixes #204.
This commit is contained in:
Christian Duerr 2019-04-14 15:37:58 +00:00 committed by GitHub
parent aac62ce5ac
commit 5174f9b274
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 142 additions and 61 deletions

View File

@ -18,6 +18,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Reset scrolling region when the RIS escape sequence is received
- Subprocess spawning on macos
- Unnecessary resize at startup
- Text getting blurry after live-reloading shaders with padding active
## Version 0.3.0

View File

@ -77,7 +77,7 @@ impl crate::Rasterize for RustTypeRasterizer {
FontCollection::from_bytes(
system_fonts::get(&fp.build()).ok_or_else(|| Error::MissingFont(desc.clone()))?.0,
)
.and_then(|fc| fc.into_font())
.and_then(FontCollection::into_font)
.map_err(|_| Error::UnsupportedFont)?,
);
Ok(FontKey { token: (self.fonts.len() - 1) as u16 })

View File

@ -485,6 +485,10 @@ impl WindowConfig {
pub fn start_maximized(&self) -> bool {
self.start_maximized
}
pub fn position(&self) -> Option<Delta<i32>> {
self.position
}
}
/// Top-level config type
@ -1794,11 +1798,6 @@ impl Config {
self.dimensions.unwrap_or(self.window.dimensions)
}
#[inline]
pub fn position(&self) -> Option<Delta<i32>> {
self.window.position
}
/// Get window config
#[inline]
pub fn window(&self) -> &WindowConfig {

View File

@ -18,6 +18,7 @@ use std::f64;
use std::sync::mpsc;
use glutin::dpi::{PhysicalPosition, PhysicalSize};
use glutin::EventsLoop;
use parking_lot::MutexGuard;
use crate::cli;
@ -137,16 +138,24 @@ impl Display {
// Extract some properties from config
let render_timer = config.render_timer();
// Create the window where Alacritty will be displayed
let mut window = Window::new(&options, config.window())?;
// Guess DPR based on first monitor
let event_loop = EventsLoop::new();
let estimated_dpr =
event_loop.get_available_monitors().next().map(|m| m.get_hidpi_factor()).unwrap_or(1.);
// TODO: replace `set_position` with `with_position` once available
// Upstream issue: https://github.com/tomaka/winit/issues/806
// Set window position early so it doesn't "teleport"
let position = options.position().or_else(|| config.position());
if let Some(position) = position {
window.set_position(position.x, position.y);
}
// Guess the target window dimensions
let metrics = GlyphCache::static_metrics(config, estimated_dpr as f32)?;
let (cell_width, cell_height) = Self::compute_cell_size(config, &metrics);
let dimensions =
Self::calculate_dimensions(config, options, estimated_dpr, cell_width, cell_height);
debug!("Estimated DPR: {}", estimated_dpr);
debug!("Estimated Cell Size: {} x {}", cell_width, cell_height);
debug!("Estimated Dimensions: {:?}", dimensions);
// Create the window where Alacritty will be displayed
let logical = dimensions.map(|d| PhysicalSize::new(d.0, d.1).to_logical(estimated_dpr));
let mut window = Window::new(event_loop, &options, config.window(), logical)?;
let dpr = window.hidpi_factor();
info!("Device pixel ratio: {}", dpr);
@ -156,43 +165,39 @@ impl Display {
window.inner_size_pixels().expect("glutin returns window size").to_physical(dpr);
// Create renderer
let mut renderer = QuadRenderer::new(viewport_size)?;
let mut renderer = QuadRenderer::new()?;
let (glyph_cache, cell_width, cell_height) =
Self::new_glyph_cache(dpr, &mut renderer, config)?;
let dimensions = options.dimensions().unwrap_or_else(|| config.dimensions());
let mut padding_x = f64::from(config.padding().x) * dpr;
let mut padding_y = f64::from(config.padding().y) * dpr;
if dimensions.columns_u32() > 0
&& dimensions.lines_u32() > 0
&& !config.window().start_maximized()
if let Some((width, height)) =
Self::calculate_dimensions(config, options, dpr, cell_width, cell_height)
{
// Calculate new size based on cols/lines specified in config
let width = cell_width as u32 * dimensions.columns_u32();
let height = cell_height as u32 * dimensions.lines_u32();
padding_x = padding_x.floor();
padding_y = padding_y.floor();
viewport_size = PhysicalSize::new(
f64::from(width) + 2. * padding_x,
f64::from(height) + 2. * padding_y,
);
if dimensions == Some((width, height)) {
info!("Estimated DPR correctly, skipping resize");
} else {
viewport_size = PhysicalSize::new(width, height);
window.set_inner_size(viewport_size.to_logical(dpr));
}
} else if config.window().dynamic_padding() {
// Make sure additional padding is spread evenly
let cw = f64::from(cell_width);
let ch = f64::from(cell_height);
padding_x = (padding_x + (viewport_size.width - 2. * padding_x) % cw / 2.).floor();
padding_y = (padding_y + (viewport_size.height - 2. * padding_y) % ch / 2.).floor();
padding_x = padding_x + (viewport_size.width - 2. * padding_x) % cw / 2.;
padding_y = padding_y + (viewport_size.height - 2. * padding_y) % ch / 2.;
}
window.set_inner_size(viewport_size.to_logical(dpr));
padding_x = padding_x.floor();
padding_y = padding_y.floor();
// Update OpenGL projection
renderer.resize(viewport_size, padding_x as f32, padding_y as f32);
info!("Cell Size: ({} x {})", cell_width, cell_height);
info!("Padding: ({} x {})", padding_x, padding_y);
info!("Cell Size: {} x {}", cell_width, cell_height);
info!("Padding: {} x {}", padding_x, padding_y);
let size_info = SizeInfo {
dpr,
@ -227,12 +232,41 @@ impl Display {
tx,
rx,
meter: Meter::new(),
font_size: font::Size::new(0.),
font_size: config.font().size(),
size_info,
last_message: None,
})
}
fn calculate_dimensions(
config: &Config,
options: &cli::Options,
dpr: f64,
cell_width: f32,
cell_height: f32,
) -> Option<(f64, f64)> {
let dimensions = options.dimensions().unwrap_or_else(|| config.dimensions());
if dimensions.columns_u32() == 0
|| dimensions.lines_u32() == 0
|| config.window().start_maximized()
{
return None;
}
let padding_x = f64::from(config.padding().x) * dpr;
let padding_y = f64::from(config.padding().y) * dpr;
// Calculate new size based on cols/lines specified in config
let grid_width = cell_width as u32 * dimensions.columns_u32();
let grid_height = cell_height as u32 * dimensions.lines_u32();
let width = (f64::from(grid_width) + 2. * padding_x).floor();
let height = (f64::from(grid_height) + 2. * padding_y).floor();
Some((width, height))
}
fn new_glyph_cache(
dpr: f64,
renderer: &mut QuadRenderer,
@ -321,6 +355,16 @@ impl Display {
let font_changed =
terminal.font_size != self.font_size || (dpr - self.size_info.dpr).abs() > f64::EPSILON;
// Skip resize if nothing changed
if let Some(new_size) = new_size {
if !font_changed
&& (new_size.width - f64::from(self.size_info.width)).abs() < f64::EPSILON
&& (new_size.height - f64::from(self.size_info.height)).abs() < f64::EPSILON
{
return;
}
}
if font_changed || self.last_message != terminal.message_buffer_mut().message() {
if new_size == None {
// Force a resize to refresh things

View File

@ -327,6 +327,21 @@ impl GlyphCache {
Ok(())
}
// Calculate font metrics without access to a glyph cache
//
// This should only be used *before* OpenGL is initialized and the glyph cache can be filled.
pub fn static_metrics(config: &Config, dpr: f32) -> Result<font::Metrics, font::Error> {
let font = config.font().clone();
let mut rasterizer = font::Rasterizer::new(dpr, config.use_thin_strokes())?;
let regular_desc =
GlyphCache::make_desc(&font.normal(), font::Slant::Normal, font::Weight::Normal);
let regular = rasterizer.load_font(&regular_desc, font.size())?;
rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size() })?;
rasterizer.metrics(regular, font.size())
}
}
#[derive(Debug)]
@ -475,8 +490,8 @@ const BATCH_MAX: usize = 0x1_0000;
const ATLAS_SIZE: i32 = 1024;
impl QuadRenderer {
pub fn new(size: PhysicalSize) -> Result<QuadRenderer, Error> {
let program = TextShaderProgram::new(size)?;
pub fn new() -> Result<QuadRenderer, Error> {
let program = TextShaderProgram::new()?;
let rect_program = RectShaderProgram::new()?;
let mut vao: GLuint = 0;
@ -723,8 +738,7 @@ impl QuadRenderer {
{
// Flush message queue
if let Ok(Msg::ShaderReload) = self.rx.try_recv() {
let size = PhysicalSize::new(f64::from(props.width), f64::from(props.height));
self.reload_shaders(size);
self.reload_shaders(props);
}
while let Ok(_) = self.rx.try_recv() {}
@ -773,11 +787,22 @@ impl QuadRenderer {
})
}
pub fn reload_shaders(&mut self, size: PhysicalSize) {
warn!("Reloading shaders...");
let result = (TextShaderProgram::new(size), RectShaderProgram::new());
pub fn reload_shaders(&mut self, props: &term::SizeInfo) {
info!("Reloading shaders...");
let result = (TextShaderProgram::new(), RectShaderProgram::new());
let (program, rect_program) = match result {
(Ok(program), Ok(rect_program)) => {
unsafe {
gl::UseProgram(program.id);
program.update_projection(
props.width,
props.height,
props.padding_x,
props.padding_y,
);
gl::UseProgram(0);
}
info!("... successfully reloaded shaders");
(program, rect_program)
},
@ -1077,7 +1102,7 @@ impl<'a> Drop for RenderApi<'a> {
}
impl TextShaderProgram {
pub fn new(size: PhysicalSize) -> Result<TextShaderProgram, ShaderCreationError> {
pub fn new() -> Result<TextShaderProgram, ShaderCreationError> {
let (vertex_src, fragment_src) = if cfg!(feature = "live-shader-reload") {
(None, None)
} else {
@ -1127,8 +1152,6 @@ impl TextShaderProgram {
u_background: background,
};
shader.update_projection(size.width as f32, size.height as f32, 0., 0.);
unsafe {
gl::UseProgram(0);
}

View File

@ -143,7 +143,7 @@ pub fn new<'a>(
let mut startup_info_ex: STARTUPINFOEXW = Default::default();
let title = options.title.as_ref().map(|w| w.as_str()).unwrap_or("Alacritty");
let title = options.title.as_ref().map(String::as_str).unwrap_or("Alacritty");
let title = U16CString::from_str(title).unwrap();
startup_info_ex.StartupInfo.lpTitle = title.as_ptr() as LPWSTR;

View File

@ -116,10 +116,15 @@ impl From<glutin::ContextError> for Error {
}
fn create_gl_window(
window: WindowBuilder,
mut window: WindowBuilder,
event_loop: &EventsLoop,
srgb: bool,
dimensions: Option<LogicalSize>,
) -> Result<glutin::WindowedContext<PossiblyCurrent>> {
if let Some(dimensions) = dimensions {
window = window.with_dimensions(dimensions);
}
let windowed_context = ContextBuilder::new()
.with_srgb(srgb)
.with_vsync(true)
@ -136,14 +141,18 @@ impl Window {
/// Create a new window
///
/// This creates a window and fully initializes a window.
pub fn new(options: &Options, window_config: &WindowConfig) -> Result<Window> {
let event_loop = EventsLoop::new();
pub fn new(
event_loop: EventsLoop,
options: &Options,
window_config: &WindowConfig,
dimensions: Option<LogicalSize>,
) -> Result<Window> {
let title = options.title.as_ref().map_or(DEFAULT_NAME, |t| t);
let class = options.class.as_ref().map_or(DEFAULT_NAME, |c| c);
let window_builder = Window::get_platform_window(title, class, window_config);
let windowed_context = create_gl_window(window_builder.clone(), &event_loop, false)
.or_else(|_| create_gl_window(window_builder, &event_loop, true))?;
let windowed_context =
create_gl_window(window_builder.clone(), &event_loop, false, dimensions)
.or_else(|_| create_gl_window(window_builder, &event_loop, true, dimensions))?;
let window = windowed_context.window();
window.show();
@ -155,6 +164,17 @@ impl Window {
}
}
// Set window position
//
// TODO: replace `set_position` with `with_position` once available
// Upstream issue: https://github.com/tomaka/winit/issues/806
let position = options.position().or_else(|| window_config.position());
if let Some(position) = position {
let physical = PhysicalPosition::from((position.x, position.y));
let logical = physical.to_logical(window.get_hidpi_factor());
window.set_position(logical);
}
// Text cursor
window.set_cursor(MouseCursor::Text);
@ -185,13 +205,6 @@ impl Window {
self.window().set_inner_size(size);
}
// TODO: use `with_position` once available
// Upstream issue: https://github.com/tomaka/winit/issues/806
pub fn set_position(&mut self, x: i32, y: i32) {
let logical = PhysicalPosition::from((x, y)).to_logical(self.window().get_hidpi_factor());
self.window().set_position(logical);
}
#[inline]
pub fn hidpi_factor(&self) -> f64 {
self.window().get_hidpi_factor()