diff options
author | Joe Wilm <joe@jwilm.com> | 2016-12-10 22:44:13 -0800 |
---|---|---|
committer | Joe Wilm <joe@jwilm.com> | 2016-12-11 20:23:41 -0800 |
commit | ed0b1cfff04903fe26f586340e036c38bbf30b33 (patch) | |
tree | eb4eb3545bee57ed401cb7727c9dc3f106fabe3b /src/display.rs | |
parent | bbd8ddbfc055e85f8810285e71fd227cdd418221 (diff) | |
download | alacritty-ed0b1cfff04903fe26f586340e036c38bbf30b33.tar.gz alacritty-ed0b1cfff04903fe26f586340e036c38bbf30b33.zip |
Display manages window, renderer, rasterizer
This is part of an ongoing decoupling effort across the codebase and
tidying effort in main.rs. Everything to do with showing the window with
a grid of characters is now managed by the `Display` type. It owns the
window, the font rasterizer, and the renderer. The only info needed from
it are dimensions of characters and the window itself for sizing the
terminal properly. Additionally, the I/O loop has access to wake it up
when new data arrives.
Diffstat (limited to 'src/display.rs')
-rw-r--r-- | src/display.rs | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/src/display.rs b/src/display.rs new file mode 100644 index 00000000..cfe684c0 --- /dev/null +++ b/src/display.rs @@ -0,0 +1,246 @@ +// Copyright 2016 Joe Wilm, The Alacritty Project Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The display subsystem including window management, font rasterization, and +//! GPU drawing. +use std::sync::mpsc; + +use parking_lot::{MutexGuard}; + +use font; +use Rgb; +use ansi::Color; +use cli; +use config::Config; +use meter::Meter; +use renderer::{GlyphCache, QuadRenderer}; +use term::{Term, SizeInfo}; + +use window::{self, Size, Pixels, Window, SetInnerSize}; + +/// The display wraps a window, font rasterizer, and GPU renderer +pub struct Display<F> { + window: Window, + renderer: QuadRenderer, + glyph_cache: GlyphCache, + render_timer: bool, + rx: mpsc::Receiver<(u32, u32)>, + tx: mpsc::Sender<(u32, u32)>, + meter: Meter, + resize_callback: Option<F>, + size_info: SizeInfo, +} + +/// Can wakeup the render loop from other threads +pub struct Notifier(window::Proxy); + +impl Notifier { + pub fn notify(&self) { + self.0.wakeup_event_loop(); + } +} + +impl<F> Display<F> + where F: Fn(&SizeInfo) +{ + pub fn notifier(&self) -> Notifier { + Notifier(self.window.create_window_proxy()) + } + + pub fn update_config(&mut self, config: &Config) { + self.renderer.update_config(config); + self.render_timer = config.render_timer(); + } + + /// Provide a callback to be invoked then the display changes size. + pub fn set_resize_callback(&mut self, callback: F) { + self.resize_callback = Some(callback); + } + + /// Get size info about the display + pub fn size(&self) -> &SizeInfo { + &self.size_info + } + + pub fn new( + config: &Config, + options: &cli::Options, + ) -> Result<Display<F>, window::Error> { + // Extract some properties from config + let font = config.font(); + let dpi = config.dpi(); + let render_timer = config.render_timer(); + + // Create the window where Alacritty will be displayed + let mut window = match Window::new() { + Ok(window) => window, + Err(err) => die!("{}", err) + }; + + // get window properties for initializing the other subsytems + let size = window.inner_size_pixels().unwrap(); + let dpr = window.hidpi_factor(); + + println!("device_pixel_ratio: {}", dpr); + + let rasterizer = font::Rasterizer::new(dpi.x(), dpi.y(), dpr); + + // Create renderer + let mut renderer = QuadRenderer::new(&config, size); + + // Initialize glyph cache + let glyph_cache = { + println!("Initializing glyph cache"); + let init_start = ::std::time::Instant::now(); + + let cache = renderer.with_loader(|mut api| { + GlyphCache::new(rasterizer, &config, &mut api) + }); + + let stop = init_start.elapsed(); + let stop_f = stop.as_secs() as f64 + stop.subsec_nanos() as f64 / 1_000_000_000f64; + println!("Finished initializing glyph cache in {}", stop_f); + + cache + }; + + // Need font metrics to resize the window properly. This suggests to me the + // font metrics should be computed before creating the window in the first + // place so that a resize is not needed. + let metrics = glyph_cache.font_metrics(); + let cell_width = (metrics.average_advance + font.offset().x() as f64) as u32; + let cell_height = (metrics.line_height + font.offset().y() as f64) as u32; + + // Resize window to specified dimensions + let width = cell_width * options.columns_u32() + 4; + let height = cell_height * options.lines_u32() + 4; + let size = Size { width: Pixels(width), height: Pixels(height) }; + println!("set_inner_size: {}", size); + + window.set_inner_size(size); + renderer.resize(*size.width as _, *size.height as _); + println!("Cell Size: ({} x {})", cell_width, cell_height); + + let size_info = SizeInfo { + width: *size.width as f32, + height: *size.height as f32, + cell_width: cell_width as f32, + cell_height: cell_height as f32 + }; + + // Channel for resize events + // + // macOS has a callback for getting resize events, the channel is used + // to queue resize events until the next draw call. Unfortunately, it + // seems that the event loop is blocked until the window is done + // resizing. If any drawing were to happen during a resize, it would + // need to be in the callback. + let (tx, rx) = mpsc::channel(); + + let mut display = Display { + window: window, + renderer: renderer, + glyph_cache: glyph_cache, + render_timer: render_timer, + tx: tx, + rx: rx, + meter: Meter::new(), + resize_callback: None, + size_info: size_info, + }; + + let resize_tx = display.resize_channel(); + let proxy = display.window.create_window_proxy(); + display.window.set_resize_callback(move |width, height| { + let _ = resize_tx.send((width, height)); + proxy.wakeup_event_loop(); + }); + + Ok(display) + } + + #[inline] + pub fn resize_channel(&self) -> mpsc::Sender<(u32, u32)> { + self.tx.clone() + } + + pub fn window(&self) -> &Window { + &self.window + } + + /// Draw the screen + /// + /// A reference to Term whose state is being drawn must be provided. + /// + /// This call may block if vsync is enabled + pub fn draw(&mut self, mut terminal: MutexGuard<Term>, config: &Config) { + // This is a hack since sometimes we get stuck waiting for events + // in the main loop otherwise. + // + // TODO figure out why this is necessary + self.window.clear_wakeup_flag(); + + // Clear dirty flag + terminal.dirty = false; + + // Resize events new_size and are handled outside the poll_events + // iterator. This has the effect of coalescing multiple resize + // events into one. + let mut new_size = None; + + // Check for any out-of-band resize events (mac only) + while let Ok(sz) = self.rx.try_recv() { + new_size = Some(sz); + } + + // Receive any resize events; only call gl::Viewport on last + // available + if let Some((w, h)) = new_size.take() { + terminal.resize(w as f32, h as f32); + let size = terminal.size_info(); + self.resize_callback.as_ref() + .map(|func| func(&size)); + self.renderer.resize(w as i32, h as i32); + } + + { + let glyph_cache = &mut self.glyph_cache; + // Draw grid + { + let _sampler = self.meter.sampler(); + + let size_info = terminal.size_info().clone(); + self.renderer.with_api(config, &size_info, |mut api| { + api.clear(); + + // Draw the grid + api.render_cells(terminal.renderable_cells(), glyph_cache); + }); + } + + // Draw render timer + if self.render_timer { + let timing = format!("{:.3} usec", self.meter.average()); + let color = Color::Spec(Rgb { r: 0xd5, g: 0x4e, b: 0x53 }); + self.renderer.with_api(config, terminal.size_info(), |mut api| { + api.render_string(&timing[..], glyph_cache, &color); + }); + } + } + + // Unlock the terminal mutex + drop(terminal); + self.window.swap_buffers().unwrap(); + } +} |