diff options
-rw-r--r-- | src/ansi.rs | 2 | ||||
-rw-r--r-- | src/event.rs | 3 | ||||
-rw-r--r-- | src/event_loop.rs | 6 | ||||
-rw-r--r-- | src/lib.rs | 7 | ||||
-rw-r--r-- | src/macros.rs | 9 | ||||
-rw-r--r-- | src/main.rs | 83 | ||||
-rw-r--r-- | src/renderer/mod.rs | 27 | ||||
-rw-r--r-- | src/window.rs | 299 |
8 files changed, 362 insertions, 74 deletions
diff --git a/src/ansi.rs b/src/ansi.rs index 50dec9ac..f876cb54 100644 --- a/src/ansi.rs +++ b/src/ansi.rs @@ -927,7 +927,6 @@ pub mod C1 { #[cfg(test)] mod tests { use std::io; - use index::{Line, Column}; use super::{Processor, Handler, Attr, TermInfo, Color}; use ::Rgb; @@ -945,7 +944,6 @@ mod tests { } } - #[derive(Default)] struct AttrHandler { attr: Option<Attr>, diff --git a/src/event.rs b/src/event.rs index 2656db72..900a0fb1 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; use sync::FairMutex; @@ -114,7 +115,7 @@ impl<N: input::Notify> Processor<N> { } /// Process at least one event and handle any additional queued events. - pub fn process_events(&mut self, window: &glutin::Window) { + pub fn process_events(&mut self, window: &Window) { for event in window.wait_events() { self.handle_event(event); break; diff --git a/src/event_loop.rs b/src/event_loop.rs index 72b98425..c2c5ce69 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -14,6 +14,8 @@ use term::Term; use util::thread; use sync::FairMutex; +use window; + use super::Flag; /// Messages that may be sent to the `EventLoop` @@ -33,7 +35,7 @@ pub struct EventLoop<Io> { rx: mio::channel::Receiver<Msg>, tx: mio::channel::Sender<Msg>, terminal: Arc<FairMutex<Term>>, - proxy: ::glutin::WindowProxy, + proxy: window::Proxy, signal_flag: Flag, ref_test: bool, } @@ -129,7 +131,7 @@ impl<Io> EventLoop<Io> /// Create a new event loop pub fn new( terminal: Arc<FairMutex<Term>>, - proxy: ::glutin::WindowProxy, + proxy: window::Proxy, signal_flag: Flag, pty: Io, ref_test: bool, @@ -48,8 +48,11 @@ extern crate bitflags; #[macro_use] pub mod macros; +pub mod ansi; +pub mod config; pub mod event; pub mod event_loop; +pub mod grid; pub mod index; pub mod input; pub mod meter; @@ -58,9 +61,7 @@ pub mod sync; pub mod term; pub mod tty; pub mod util; -pub mod ansi; -pub mod config; -pub mod grid; +pub mod window; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; diff --git a/src/macros.rs b/src/macros.rs index eb37d4db..740e5ed9 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -48,3 +48,12 @@ macro_rules! debug_print { } } +#[macro_export] +macro_rules! maybe { + ($option:expr) => { + match $option { + Some(value) => value, + None => return None, + } + } +} diff --git a/src/main.rs b/src/main.rs index 71edea67..9c9c50d7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,24 +51,14 @@ use alacritty::Flag; use alacritty::Rgb; use alacritty::config::{self, Config}; use alacritty::event; -use alacritty::gl; +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::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)); - } -} +use alacritty::window::{self, Window, SetInnerSize, Pixels, Size}; mod cli; @@ -100,35 +90,22 @@ fn main() { let dpi = config.dpi(); let render_timer = config.render_timer(); - // Create glutin window - let mut window = glutin::WindowBuilder::new() - .with_vsync() - .with_title("Alacritty") - .build().unwrap(); - - // Set the glutin window resize callback for this one window. - window.set_window_resize_callback(Some(window_resize_handler as fn(u32, u32))); + // Create the window where Alacritty will be displayed + let mut window = match Window::new() { + Ok(window) => window, + Err(err) => die!("{}", err) + }; - // load gl symbols - gl::load_with(|symbol| window.get_proc_address(symbol) as *const _); // get window properties for initializing the other subsytems - let (width, height) = window.get_inner_size_pixels().unwrap(); + let size = window.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); + let mut renderer = QuadRenderer::new(&config, size); // Initialize glyph cache let glyph_cache = { @@ -156,19 +133,17 @@ fn main() { // Resize window to specified dimensions let width = cell_width * options.columns_u32() + 4; let height = cell_height * options.lines_u32() + 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 _); + 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: width as f32, - height: height as f32, + width: *size.width as f32, + height: *size.height as f32, cell_width: cell_width as f32, cell_height: cell_height as f32 }; @@ -189,17 +164,15 @@ fn main() { 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(); - } - })); - } + 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(); + } + }); let event_loop = EventLoop::new( terminal.clone(), @@ -279,7 +252,7 @@ fn main() { struct ConfigHandler { tx: mpsc::Sender<config::Config>, - window: ::glutin::WindowProxy, + window: window::Proxy, } impl config::OnConfigReload for ConfigHandler { @@ -294,7 +267,7 @@ impl config::OnConfigReload for ConfigHandler { } struct Display<'a> { - window: &'a glutin::Window, + window: &'a Window, renderer: QuadRenderer, glyph_cache: GlyphCache, render_timer: bool, @@ -310,7 +283,7 @@ impl<'a> Display<'a> { } pub fn new( - window: &glutin::Window, + window: &Window, renderer: QuadRenderer, glyph_cache: GlyphCache, render_timer: bool, diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 2728ad5f..b1fb27ed 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -26,6 +26,8 @@ use gl; use notify::{Watcher as WatcherApi, RecommendedWatcher as Watcher, op}; use index::{Line, Column}; +use window::{Size, Pixels}; + use ansi::{Color, NamedColor}; use config::{Config, ColorList}; @@ -385,8 +387,8 @@ const ATLAS_SIZE: i32 = 1024; impl QuadRenderer { // TODO should probably hand this a transform instead of width/height - pub fn new(config: &Config, width: u32, height: u32) -> QuadRenderer { - let program = ShaderProgram::new(width, height).unwrap(); + pub fn new(config: &Config, size: Size<Pixels<u32>>) -> QuadRenderer { + let program = ShaderProgram::new(size).unwrap(); let mut vao: GLuint = 0; let mut vbo: GLuint = 0; @@ -394,8 +396,11 @@ impl QuadRenderer { let mut vbo_instance: GLuint = 0; - unsafe { + gl::Enable(gl::BLEND); + gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR); + gl::Enable(gl::MULTISAMPLE); + gl::GenVertexArrays(1, &mut vao); gl::GenBuffers(1, &mut vbo); gl::GenBuffers(1, &mut ebo); @@ -552,7 +557,10 @@ impl QuadRenderer { while let Ok(msg) = self.rx.try_recv() { match msg { Msg::ShaderReload => { - self.reload_shaders(props.width as u32, props.height as u32); + self.reload_shaders(Size { + width: Pixels(props.width as u32), + height: Pixels(props.height as u32) + }); } } } @@ -599,8 +607,8 @@ impl QuadRenderer { }) } - pub fn reload_shaders(&mut self, width: u32, height: u32) { - let program = match ShaderProgram::new(width, height) { + pub fn reload_shaders(&mut self, size: Size<Pixels<u32>>) { + let program = match ShaderProgram::new(size) { Ok(program) => program, Err(err) => { match err { @@ -809,10 +817,7 @@ impl ShaderProgram { } } - pub fn new( - width: u32, - height: u32 - ) -> Result<ShaderProgram, ShaderCreationError> { + pub fn new(size: Size<Pixels<u32>>) -> Result<ShaderProgram, ShaderCreationError> { let vertex_source = if cfg!(feature = "live-shader-reload") { None } else { @@ -885,7 +890,7 @@ impl ShaderProgram { u_background: background, }; - shader.update_projection(width as f32, height as f32); + shader.update_projection(*size.width as f32, *size.height as f32); shader.deactivate(); diff --git a/src/window.rs b/src/window.rs new file mode 100644 index 00000000..c1790bb6 --- /dev/null +++ b/src/window.rs @@ -0,0 +1,299 @@ +// 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. +use std::convert::From; +use std::fmt::{self, Display}; +use std::ops::Deref; + +use gl; +use glutin; + +/// Default title for the window +const DEFAULT_TITLE: &'static str = "Alacritty"; + +/// Resize handling for Mac and maybe other platforms +/// +/// This delegates to a statically referenced closure for convenience. The +/// C-style callback doesn't receive a pointer or anything, so we are forced to +/// use static storage. +/// +/// This will fail horribly if more than one window is created. Don't do that :) +fn window_resize_handler(width: u32, height: u32) { + unsafe { + RESIZE_CALLBACK.as_ref().map(|func| func(width, height)); + } +} + +/// The resize callback invoked by `window_resize_handler` +static mut RESIZE_CALLBACK: Option<Box<Fn(u32, u32)>> = None; + +/// Window errors +#[derive(Debug)] +pub enum Error { + /// Error creating the window + Creation(glutin::CreationError), + + /// Error manipulating the rendering context + Context(glutin::ContextError), +} + +/// Result of fallible operations concerning a Window. +type Result<T> = ::std::result::Result<T, Error>; + +/// A window which can be used for displaying the terminal +/// +/// Wraps the underlying windowing library to provide a stable API in Alacritty +pub struct Window { + glutin_window: glutin::Window, +} + +/// Threadsafe APIs for the window +pub struct Proxy(glutin::WindowProxy); + +/// Information about where the window is being displayed +/// +/// Useful for subsystems like the font rasterized which depend on DPI and scale +/// factor. +pub struct DeviceProperties { + /// Scale factor for pixels <-> points. + /// + /// This will be 1. on standard displays and may have a different value on + /// hidpi displays. + pub scale_factor: f32, +} + +/// Size of the window +#[derive(Debug, Copy, Clone)] +pub struct Size<T> { + pub width: T, + pub height: T, +} + +/// Strongly typed Pixels unit +#[derive(Debug, Copy, Clone)] +pub struct Pixels<T>(pub T); + +/// Strongly typed Points unit +/// +/// Points are like pixels but adjusted for DPI. +#[derive(Debug, Copy, Clone)] +pub struct Points<T>(pub T); + +pub trait ToPoints { + fn to_points(&self, scale: f32) -> Size<Points<u32>>; +} + +impl ToPoints for Size<Points<u32>> { + #[inline] + fn to_points(&self, _scale: f32) -> Size<Points<u32>> { + *self + } +} + +impl ToPoints for Size<Pixels<u32>> { + fn to_points(&self, scale: f32) -> Size<Points<u32>> { + let width_pts = (*self.width as f32 / scale) as u32; + let height_pts = (*self.height as f32 / scale) as u32; + + Size { + width: Points(width_pts), + height: Points(height_pts) + } + } +} + +impl<T: Display> Display for Size<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} × {}", self.width, self.height) + } +} + +macro_rules! deref_newtype { + ($($src:ty),+) => { + $( + impl<T> Deref for $src { + type Target = T; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } + } + )+ + } +} + +deref_newtype! { Points<T>, Pixels<T> } + + +impl<T: Display> Display for Pixels<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}px", self.0) + } +} + +impl ::std::error::Error for Error { + fn cause(&self) -> Option<&::std::error::Error> { + match *self { + Error::Creation(ref err) => Some(err), + Error::Context(ref err) => Some(err), + } + } + + fn description(&self) -> &str { + match *self { + Error::Creation(ref _err) => "Error creating glutin Window", + Error::Context(ref _err) => "Error operating on render context", + } + } +} + +impl Display for Error { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match *self { + Error::Creation(ref err) => { + write!(f, "Error creating glutin::Window; {}", err) + }, + Error::Context(ref err) => { + write!(f, "Error operating on render context; {}", err) + }, + } + } +} + +impl From<glutin::CreationError> for Error { + fn from(val: glutin::CreationError) -> Error { + Error::Creation(val) + } +} + +impl From<glutin::ContextError> for Error { + fn from(val: glutin::ContextError) -> Error { + Error::Context(val) + } +} + +impl Window { + /// Create a new window + /// + /// This creates a window and fully initializes a window. + pub fn new() -> Result<Window> { + /// Create a glutin::Window + let mut window = glutin::WindowBuilder::new() + .with_vsync() + .with_title(DEFAULT_TITLE) + .build()?; + + /// Set the glutin window resize callback for *this* window. The + /// function pointer must be a C-style callback. This sets such a + /// callback which simply delegates to a statically referenced Rust + /// closure. + window.set_window_resize_callback(Some(window_resize_handler as fn(u32, u32))); + + /// Set OpenGL symbol loader + gl::load_with(|symbol| window.get_proc_address(symbol) as *const _); + + /// Make the window's context current so OpenGL operations can run + unsafe { + window.make_current()?; + } + + Ok(Window { + glutin_window: window, + }) + } + + /// Get some properties about the device + /// + /// Some window properties are provided since subsystems like font + /// rasterization depend on DPI and scale factor. + pub fn device_properties(&self) -> DeviceProperties { + DeviceProperties { + scale_factor: self.glutin_window.hidpi_factor(), + } + } + + /// Set the window resize callback + /// + /// Pass a `move` closure which will be called with the new width and height + /// when the window is resized. According to the glutin docs, this can be + /// used to draw during resizing. + /// + /// This method takes self mutably to ensure there's no race condition + /// setting the callback. + pub fn set_resize_callback<F: Fn(u32, u32) + 'static>(&mut self, func: F) { + unsafe { + RESIZE_CALLBACK = Some(Box::new(func)); + } + } + + pub fn inner_size_pixels(&self) -> Option<Size<Pixels<u32>>> { + self.glutin_window + .get_inner_size_pixels() + .map(|(w, h)| Size { width: Pixels(w), height: Pixels(h) }) + } + + #[inline] + pub fn hidpi_factor(&self) -> f32 { + self.glutin_window.hidpi_factor() + } + + #[inline] + pub fn create_window_proxy(&self) -> Proxy { + Proxy(self.glutin_window.create_window_proxy()) + } + + #[inline] + pub fn swap_buffers(&self) -> Result<()> { + self.glutin_window + .swap_buffers() + .map_err(From::from) + } + + /// Block waiting for events + /// + /// FIXME should return our own type + #[inline] + pub fn wait_events(&self) -> glutin::WaitEventsIterator { + self.glutin_window.wait_events() + } + + /// Block waiting for events + /// + /// FIXME should return our own type + #[inline] + pub fn poll_events(&self) -> glutin::PollEventsIterator { + self.glutin_window.poll_events() + } +} + +impl Proxy { + /// Wakes up the event loop of the window + /// + /// 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(); + } +} + +pub trait SetInnerSize<T> { + fn set_inner_size<S: ToPoints>(&mut self, size: S); +} + +impl SetInnerSize<Pixels<u32>> for Window { + fn set_inner_size<T: ToPoints>(&mut self, size: T) { + let size = size.to_points(self.hidpi_factor()); + self.glutin_window.set_inner_size(*size.width as _, *size.height as _); + } +} |