aboutsummaryrefslogtreecommitdiff
path: root/src/display.rs
diff options
context:
space:
mode:
authorJoe Wilm <joe@jwilm.com>2016-12-10 22:44:13 -0800
committerJoe Wilm <joe@jwilm.com>2016-12-11 20:23:41 -0800
commited0b1cfff04903fe26f586340e036c38bbf30b33 (patch)
treeeb4eb3545bee57ed401cb7727c9dc3f106fabe3b /src/display.rs
parentbbd8ddbfc055e85f8810285e71fd227cdd418221 (diff)
downloadalacritty-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.rs246
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();
+ }
+}