From ed0b1cfff04903fe26f586340e036c38bbf30b33 Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Sat, 10 Dec 2016 22:44:13 -0800 Subject: 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. --- src/main.rs | 280 +++++++++++++----------------------------------------------- 1 file changed, 61 insertions(+), 219 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 1c1397d0..0fba05a6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -42,25 +42,20 @@ extern crate vte; #[macro_use] extern crate bitflags; +use std::error::Error; use std::sync::{mpsc, Arc}; -use std::sync::atomic::Ordering; +use std::rc::Rc; -use parking_lot::{MutexGuard}; - -use alacritty::Flag; -use alacritty::Rgb; +use alacritty::cli; use alacritty::config::{self, Config}; +use alacritty::display::{self, Display}; use alacritty::event; use alacritty::event_loop::EventLoop; use alacritty::input; -use alacritty::meter::Meter; -use alacritty::renderer::{QuadRenderer, GlyphCache}; use alacritty::sync::FairMutex; -use alacritty::term::{self, Term}; -use alacritty::tty::{self, Pty, process_should_exit}; -use alacritty::window::{self, Window, SetInnerSize, Pixels, Size}; +use alacritty::term::{Term}; +use alacritty::tty::{self, process_should_exit}; -mod cli; fn main() { // Load configuration @@ -86,7 +81,11 @@ fn main() { let options = cli::Options::load(); // Run alacritty - run(config, options); + if let Err(err) = run(config, options) { + die!("{}", err); + } + + println!("Goodbye"); } /// Run Alacritty @@ -156,124 +155,59 @@ fn main() { /// /// instead of the 200 line monster it currently is. /// -fn run(config: Config, options: cli::Options) { - - - // 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 = term::SizeInfo { - width: *size.width as f32, - height: *size.height as f32, - cell_width: cell_width as f32, - cell_height: cell_height as f32 - }; - - let terminal = Term::new(size); - let pty = tty::new(size.lines(), size.cols()); - pty.resize(size.lines(), size.cols(), size.width as usize, size.height as usize); - let pty_io = pty.reader(); - - let (tx, rx) = mpsc::channel(); - - let signal_flag = Flag::new(false); - - let terminal = Arc::new(FairMutex::new(terminal)); - - // Setup the rsize callback for osx - let terminal_ref = terminal.clone(); - let signal_flag_ref = signal_flag.clone(); - let proxy = window.create_window_proxy(); - let tx2 = tx.clone(); - window.set_resize_callback(move |width, height| { - let _ = tx2.send((width, height)); - if !signal_flag_ref.0.swap(true, Ordering::AcqRel) { - // We raised the signal flag - let mut terminal = terminal_ref.lock(); - terminal.dirty = true; - proxy.wakeup_event_loop(); - } +fn run(config: Config, options: cli::Options) -> Result<(), Box> { + // Create a display. + // + // The display manages a window and can draw the terminal + let mut display = Display::new(&config, &options)?; + + // Create the terminal + // + // This object contains all of the state about what's being displayed. It's + // wrapped in a clonable mutex since both the I/O loop and display need to + // access it. + let terminal = Arc::new(FairMutex::new(Term::new(display.size().to_owned()))); + + // Create the pty + // + // The pty forks a process to run the shell on the slave side of the + // pseudoterminal. A file descriptor for the master side is retained for + // reading/writing to the shell. + let pty = Rc::new(tty::new(display.size())); + + // When the display is resized, inform the kernel of changes to pty + // dimensions. + // + // TODO: The Rc on pty is needed due to a borrowck error here. The borrow + // checker says that `pty` is still borrowed when it is dropped at the end + // of the `run` function. + let pty_ref = pty.clone(); + display.set_resize_callback(move |size| { + pty_ref.resize(size); }); + // Create the pseudoterminal I/O loop + // + // pty I/O is ran on another thread as to not occupy cycles used by the + // renderer and input processing. Note that access to the terminal state is + // synchronized since the I/O loop updates the state, and the display + // consumes it periodically. let event_loop = EventLoop::new( terminal.clone(), - window.create_window_proxy(), - signal_flag.clone(), - pty_io, + display.notifier(), + pty.reader(), options.ref_test, ); let loop_tx = event_loop.channel(); let event_loop_handle = event_loop.spawn(None); - // Wraps a renderer and gives simple draw() api. - let mut display = Display::new( - &window, - renderer, - glyph_cache, - render_timer, - rx, - pty - ); - // Event processor + let resize_tx = display.resize_channel(); let mut processor = event::Processor::new( input::LoopNotifier(loop_tx), terminal.clone(), - tx, + resize_tx, &config, options.ref_test, ); @@ -284,28 +218,26 @@ fn run(config: Config, options: cli::Options) { let _config_reloader = config.path().map(|path| { config::Watcher::new(path, ConfigHandler { tx: config_tx, - window: window.create_window_proxy(), + loop_kicker: display.notifier(), }) }); // Main loop - let mut force_draw; + let mut config_updated = false; loop { - force_draw = false; // Wait for something to happen - processor.process_events(&window); + processor.process_events(display.window()); // Handle config reloads if let Ok(config) = config_rx.try_recv() { - force_draw = true; + config_updated = true; display.update_config(&config); processor.update_config(&config); } // Maybe draw the terminal let terminal = terminal.lock(); - signal_flag.set(false); - if force_draw || terminal.dirty { + if terminal.dirty || config_updated { display.draw(terminal, &config); } @@ -317,16 +249,19 @@ fn run(config: Config, options: cli::Options) { // FIXME need file watcher to work with custom delegates before // joining config reloader is possible + // + // HELP I don't know what I meant in the above fixme // config_reloader.join().ok(); // shutdown event_loop_handle.join().ok(); - println!("Goodbye"); + + Ok(()) } struct ConfigHandler { tx: mpsc::Sender, - window: window::Proxy, + loop_kicker: display::Notifier, } impl config::OnConfigReload for ConfigHandler { @@ -336,100 +271,7 @@ impl config::OnConfigReload for ConfigHandler { return; } - self.window.wakeup_event_loop(); + self.loop_kicker.notify(); } } -struct Display<'a> { - window: &'a Window, - renderer: QuadRenderer, - glyph_cache: GlyphCache, - render_timer: bool, - rx: mpsc::Receiver<(u32, u32)>, - meter: Meter, - pty: Pty, -} - -impl<'a> Display<'a> { - pub fn update_config(&mut self, config: &Config) { - self.renderer.update_config(config); - self.render_timer = config.render_timer(); - } - - pub fn new( - window: &Window, - renderer: QuadRenderer, - glyph_cache: GlyphCache, - render_timer: bool, - rx: mpsc::Receiver<(u32, u32)>, - pty: Pty - ) -> Display { - Display { - window: window, - renderer: renderer, - glyph_cache: glyph_cache, - render_timer: render_timer, - rx: rx, - meter: Meter::new(), - pty: pty, - } - } - - /// 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, config: &Config) { - 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.pty.resize(size.lines(), size.cols(), w as _, h as _); - 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 = alacritty::ansi::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(); - } -} -- cgit v1.2.3-54-g00ecf