1
0
Fork 0
mirror of https://github.com/alacritty/alacritty.git synced 2024-11-11 13:51:01 -05:00
alacritty/src/main.rs
2016-12-11 20:23:41 -08:00

389 lines
11 KiB
Rust

// 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.
//
//! Alacritty - The GPU Enhanced Terminal
#![feature(question_mark)]
#![feature(inclusive_range_syntax)]
#![feature(drop_types_in_const)]
#![allow(stable_features)] // lying about question_mark because 1.14.0 isn't released!
#![feature(proc_macro)]
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate alacritty;
extern crate cgmath;
extern crate copypasta;
extern crate errno;
extern crate font;
extern crate glutin;
extern crate libc;
extern crate mio;
extern crate notify;
extern crate parking_lot;
extern crate serde;
extern crate serde_json;
extern crate serde_yaml;
extern crate vte;
#[macro_use]
extern crate bitflags;
use std::sync::{mpsc, Arc};
use std::sync::atomic::Ordering;
use parking_lot::{MutexGuard};
use alacritty::Flag;
use alacritty::Rgb;
use alacritty::config::{self, Config};
use alacritty::event;
use alacritty::gl;
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::event_loop::EventLoop;
/// Channel used by resize handling on mac
static mut RESIZE_CALLBACK: Option<Box<Fn(u32, u32)>> = None;
/// Resize handling for Mac
fn window_resize_handler(width: u32, height: u32) {
unsafe {
RESIZE_CALLBACK.as_ref().map(|func| func(width, height));
}
}
fn main() {
// Load configuration
let (config, config_path) = match Config::load() {
Err(err) => match err {
// Use default config when not found
config::Error::NotFound => (Config::default(), None),
// Exit when there's a problem with it
_ => die!("{}", err),
},
Ok((config, path)) => (config, Some(path)),
};
let mut ref_test = false;
let mut columns = 80;
let mut lines = 24;
let mut args_iter = ::std::env::args();
while let Some(arg) = args_iter.next() {
match &arg[..] {
// Generate ref test
"--ref-test" => ref_test = true,
// Set dimensions
"-d" | "--dimensions" => {
args_iter.next()
.map(|w| w.parse().map(|w| columns = w));
args_iter.next()
.map(|h| h.parse().map(|h| lines = h));
},
// ignore unexpected
_ => (),
}
}
let font = config.font();
let dpi = config.dpi();
let render_timer = config.render_timer();
let mut window = glutin::WindowBuilder::new()
.with_vsync()
.with_title("Alacritty")
.build().unwrap();
window.set_window_resize_callback(Some(window_resize_handler as fn(u32, u32)));
gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
let (width, height) = window.get_inner_size_pixels().unwrap();
let dpr = window.hidpi_factor();
println!("device_pixel_ratio: {}", dpr);
let _ = unsafe { window.make_current() };
unsafe {
// gl::Viewport(0, 0, width as i32, height as i32);
gl::Enable(gl::BLEND);
gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR);
gl::Enable(gl::MULTISAMPLE);
}
let rasterizer = font::Rasterizer::new(dpi.x(), dpi.y(), dpr);
// Create renderer
let mut renderer = QuadRenderer::new(&config, width, height);
// 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
};
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 be 80 col x 24 lines
let width = cell_width * columns + 4;
let height = cell_height * lines + 4;
println!("set_inner_size: {} x {}", width, height);
// Is this in points?
let width_pts = (width as f32 / dpr) as u32;
let height_pts = (height as f32 / dpr) as u32;
println!("set_inner_size: {} x {}; pts: {} x {}", width, height, width_pts, height_pts);
window.set_inner_size(width_pts, height_pts);
renderer.resize(width as _, height as _);
println!("Cell Size: ({} x {})", cell_width, cell_height);
let size = term::SizeInfo {
width: width as f32,
height: 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();
unsafe {
RESIZE_CALLBACK = Some(Box::new(move |width: u32, height: u32| {
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();
}
}));
}
let event_loop = EventLoop::new(
terminal.clone(),
window.create_window_proxy(),
signal_flag.clone(),
pty_io,
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 mut processor = event::Processor::new(
input::LoopNotifier(loop_tx),
terminal.clone(),
tx,
&config,
ref_test,
);
let (config_tx, config_rx) = mpsc::channel();
// create a config watcher when config is loaded from disk
let _config_reloader = config_path.map(|config_path| {
config::Watcher::new(config_path, ConfigHandler {
tx: config_tx,
window: window.create_window_proxy(),
})
});
// Main loop
let mut force_draw;
loop {
force_draw = false;
// Wait for something to happen
processor.process_events(&window);
// Handle config reloads
if let Ok(config) = config_rx.try_recv() {
force_draw = 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 {
display.draw(terminal, &config);
}
// Begin shutdown if the flag was raised.
if process_should_exit() {
break;
}
}
// FIXME need file watcher to work with custom delegates before
// joining config reloader is possible
// config_reloader.join().ok();
// shutdown
event_loop_handle.join().ok();
println!("Goodbye");
}
struct ConfigHandler {
tx: mpsc::Sender<config::Config>,
window: ::glutin::WindowProxy,
}
// TODO FIXME
impl config::OnConfigReload for ConfigHandler {
fn on_config_reload(&mut self, config: Config) {
if let Err(..) = self.tx.send(config) {
err_println!("Failed to notify of new config");
return;
}
self.window.wakeup_event_loop();
}
}
struct Display<'a> {
window: &'a glutin::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: &glutin::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();
}
}