aboutsummaryrefslogtreecommitdiff
path: root/src
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
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')
-rw-r--r--src/cli.rs2
-rw-r--r--src/display.rs246
-rw-r--r--src/event.rs1
-rw-r--r--src/event_loop.rs23
-rw-r--r--src/lib.rs23
-rw-r--r--src/main.rs280
-rw-r--r--src/term/mod.rs14
-rw-r--r--src/tty.rs33
-rw-r--r--src/window.rs79
9 files changed, 421 insertions, 280 deletions
diff --git a/src/cli.rs b/src/cli.rs
index b61d1054..15bd57c5 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::env;
-use alacritty::index::{Line, Column};
+use index::{Line, Column};
/// Options specified on the command line
pub struct Options {
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();
+ }
+}
diff --git a/src/event.rs b/src/event.rs
index 900a0fb1..2a27dd61 100644
--- a/src/event.rs
+++ b/src/event.rs
@@ -5,6 +5,7 @@ use std::sync::{Arc, mpsc};
use serde_json as json;
use glutin;
+
use window::Window;
use input;
diff --git a/src/event_loop.rs b/src/event_loop.rs
index c2c5ce69..70aa4acb 100644
--- a/src/event_loop.rs
+++ b/src/event_loop.rs
@@ -10,14 +10,11 @@ use mio::{self, Events, PollOpt, Ready};
use mio::unix::EventedFd;
use ansi;
+use display;
use term::Term;
use util::thread;
use sync::FairMutex;
-use window;
-
-use super::Flag;
-
/// Messages that may be sent to the `EventLoop`
#[derive(Debug)]
pub enum Msg {
@@ -35,8 +32,7 @@ pub struct EventLoop<Io> {
rx: mio::channel::Receiver<Msg>,
tx: mio::channel::Sender<Msg>,
terminal: Arc<FairMutex<Term>>,
- proxy: window::Proxy,
- signal_flag: Flag,
+ display: display::Notifier,
ref_test: bool,
}
@@ -131,8 +127,7 @@ impl<Io> EventLoop<Io>
/// Create a new event loop
pub fn new(
terminal: Arc<FairMutex<Term>>,
- proxy: window::Proxy,
- signal_flag: Flag,
+ display: display::Notifier,
pty: Io,
ref_test: bool,
) -> EventLoop<Io> {
@@ -143,8 +138,7 @@ impl<Io> EventLoop<Io>
tx: tx,
rx: rx,
terminal: terminal,
- proxy: proxy,
- signal_flag: signal_flag,
+ display: display,
ref_test: ref_test,
}
}
@@ -196,14 +190,7 @@ impl<Io> EventLoop<Io>
terminal.dirty = true;
- // Only wake up the event loop if it hasn't already been
- // signaled. This is a really important optimization because
- // waking up the event loop redundantly burns *a lot* of
- // cycles.
- if !self.signal_flag.get() {
- self.proxy.wakeup_event_loop();
- self.signal_flag.set(true);
- }
+ self.display.notify();
},
Err(err) => {
match err.kind() {
diff --git a/src/lib.rs b/src/lib.rs
index cd4f382e..9903f591 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -49,7 +49,9 @@ extern crate bitflags;
pub mod macros;
pub mod ansi;
+pub mod cli;
pub mod config;
+pub mod display;
pub mod event;
pub mod event_loop;
pub mod grid;
@@ -63,9 +65,6 @@ pub mod tty;
pub mod util;
pub mod window;
-use std::sync::Arc;
-use std::sync::atomic::{AtomicBool, Ordering};
-
pub use grid::Grid;
pub use term::Term;
@@ -80,21 +79,3 @@ pub mod gl {
#![allow(non_upper_case_globals)]
include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
}
-
-#[derive(Clone)]
-pub struct Flag(pub Arc<AtomicBool>);
-impl Flag {
- pub fn new(initial_value: bool) -> Flag {
- Flag(Arc::new(AtomicBool::new(initial_value)))
- }
-
- #[inline]
- pub fn get(&self) -> bool {
- self.0.load(Ordering::Acquire)
- }
-
- #[inline]
- pub fn set(&self, value: bool) {
- self.0.store(value, Ordering::Release)
- }
-}
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<Error>> {
+ // 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<config::Config>,
- 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<Term>, 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();
- }
-}
diff --git a/src/term/mod.rs b/src/term/mod.rs
index 979999a9..da5683e0 100644
--- a/src/term/mod.rs
+++ b/src/term/mod.rs
@@ -23,6 +23,20 @@ use grid::{Grid, ClearRegion};
use index::{Cursor, Column, Line};
use ansi::{Color, NamedColor};
+use tty::ToWinsize;
+use libc::{self, winsize};
+
+impl<'a> ToWinsize for &'a SizeInfo {
+ fn to_winsize(&self) -> winsize {
+ winsize {
+ ws_row: self.lines().0 as libc::c_ushort,
+ ws_col: self.cols().0 as libc::c_ushort,
+ ws_xpixel: self.width as libc::c_ushort,
+ ws_ypixel: self.height as libc::c_ushort,
+ }
+ }
+}
+
pub mod cell;
pub use self::cell::Cell;
diff --git a/src/tty.rs b/src/tty.rs
index fddb4d99..7071bad3 100644
--- a/src/tty.rs
+++ b/src/tty.rs
@@ -23,8 +23,6 @@ use std::ptr;
use libc::{self, winsize, c_int, pid_t, WNOHANG, WIFEXITED, WEXITSTATUS, SIGCHLD};
-use index::{Line, Column};
-
/// Process ID of child process
///
/// Necessary to put this in static storage for `sigchld` to have access
@@ -238,8 +236,10 @@ fn execsh() -> ! {
}
/// Create a new tty and return a handle to interact with it.
-pub fn new(lines: Line, cols: Column) -> Pty {
- let (master, slave) = openpty(lines.0 as _, cols.0 as _);
+pub fn new<T: ToWinsize>(size: T) -> Pty {
+ let win = size.to_winsize();
+
+ let (master, slave) = openpty(win.ws_row as _, win.ws_col as _);
match fork() {
Relation::Child => {
@@ -282,7 +282,9 @@ pub fn new(lines: Line, cols: Column) -> Pty {
set_nonblocking(master);
}
- Pty { fd: master }
+ let pty = Pty { fd: master };
+ pty.resize(size);
+ pty
}
}
}
@@ -301,15 +303,12 @@ impl Pty {
}
}
- pub fn resize(&self, lines: Line, cols: Column, px_x: usize, px_y: usize) {
- let lines = lines.0;
- let cols = cols.0;
- let win = winsize {
- ws_row: lines as libc::c_ushort,
- ws_col: cols as libc::c_ushort,
- ws_xpixel: px_x as libc::c_ushort,
- ws_ypixel: px_y as libc::c_ushort,
- };
+ /// Resize the pty
+ ///
+ /// Tells the kernel that the window size changed with the new pixel
+ /// dimensions and line/column counts.
+ pub fn resize<T: ToWinsize>(&self, size: T) {
+ let win = size.to_winsize();
let res = unsafe {
libc::ioctl(self.fd, libc::TIOCSWINSZ, &win as *const _)
@@ -321,6 +320,12 @@ impl Pty {
}
}
+/// Types that can produce a `libc::winsize`
+pub trait ToWinsize {
+ /// Get a `libc::winsize`
+ fn to_winsize(&self) -> winsize;
+}
+
unsafe fn set_nonblocking(fd: c_int) {
use libc::{fcntl, F_SETFL, F_GETFL, O_NONBLOCK};
diff --git a/src/window.rs b/src/window.rs
index 4d8a0c02..f9f2050c 100644
--- a/src/window.rs
+++ b/src/window.rs
@@ -11,6 +11,8 @@
// 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.
+use std::sync::Arc;
+use std::sync::atomic::{AtomicBool, Ordering};
use std::convert::From;
use std::fmt::{self, Display};
use std::ops::Deref;
@@ -55,10 +57,17 @@ type Result<T> = ::std::result::Result<T, Error>;
/// Wraps the underlying windowing library to provide a stable API in Alacritty
pub struct Window {
glutin_window: glutin::Window,
+
+ /// This flag allows calls to wakeup_event_loop to be coalesced. Waking the
+ /// event loop is potentially very slow (and indeed is on X11).
+ flag: Flag
}
/// Threadsafe APIs for the window
-pub struct Proxy(glutin::WindowProxy);
+pub struct Proxy {
+ inner: glutin::WindowProxy,
+ flag: Flag,
+}
/// Information about where the window is being displayed
///
@@ -89,6 +98,46 @@ pub struct Pixels<T>(pub T);
#[derive(Debug, Copy, Clone)]
pub struct Points<T>(pub T);
+/// A wrapper around glutin's WaitEventsIterator that clears the wakeup
+/// optimization flag on drop.
+pub struct WaitEventsIterator<'a> {
+ inner: glutin::WaitEventsIterator<'a>,
+ flag: Flag
+}
+
+impl<'a> Iterator for WaitEventsIterator<'a> {
+ type Item = glutin::Event;
+
+ #[inline]
+ fn next(&mut self) -> Option<Self::Item> {
+ self.inner.next()
+ }
+}
+
+impl<'a> Drop for WaitEventsIterator<'a> {
+ fn drop(&mut self) {
+ self.flag.set(false);
+ }
+}
+
+#[derive(Clone)]
+pub struct Flag(pub Arc<AtomicBool>);
+impl Flag {
+ pub fn new(initial_value: bool) -> Flag {
+ Flag(Arc::new(AtomicBool::new(initial_value)))
+ }
+
+ #[inline]
+ pub fn get(&self) -> bool {
+ self.0.load(Ordering::Acquire)
+ }
+
+ #[inline]
+ pub fn set(&self, value: bool) {
+ self.0.store(value, Ordering::Release)
+ }
+}
+
pub trait ToPoints {
fn to_points(&self, scale: f32) -> Size<Points<u32>>;
}
@@ -216,6 +265,7 @@ impl Window {
Ok(Window {
glutin_window: window,
+ flag: Flag::new(false),
})
}
@@ -256,7 +306,10 @@ impl Window {
#[inline]
pub fn create_window_proxy(&self) -> Proxy {
- Proxy(self.glutin_window.create_window_proxy())
+ Proxy {
+ inner: self.glutin_window.create_window_proxy(),
+ flag: self.flag.clone(),
+ }
}
#[inline]
@@ -267,11 +320,17 @@ impl Window {
}
/// Block waiting for events
- ///
- /// FIXME should return our own type
#[inline]
- pub fn wait_events(&self) -> glutin::WaitEventsIterator {
- self.glutin_window.wait_events()
+ pub fn wait_events(&self) -> WaitEventsIterator {
+ WaitEventsIterator {
+ inner: self.glutin_window.wait_events(),
+ flag: self.flag.clone()
+ }
+ }
+
+ /// Clear the wakeup optimization flag
+ pub fn clear_wakeup_flag(&self) {
+ self.flag.set(false);
}
/// Block waiting for events
@@ -289,7 +348,13 @@ impl Proxy {
/// This is useful for triggering a draw when the renderer would otherwise
/// be waiting on user input.
pub fn wakeup_event_loop(&self) {
- self.0.wakeup_event_loop();
+ // Only wake up the window event loop if it hasn't already been
+ // signaled. This is a really important optimization because waking up
+ // the event loop redundantly burns *a lot* of cycles.
+ if !self.flag.get() {
+ self.inner.wakeup_event_loop();
+ self.flag.set(true);
+ }
}
}