diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/grid.rs | 6 | ||||
-rw-r--r-- | src/list_fonts.rs | 186 | ||||
-rw-r--r-- | src/main.rs | 193 | ||||
-rw-r--r-- | src/renderer/mod.rs | 12 | ||||
-rw-r--r-- | src/text.rs | 140 | ||||
-rw-r--r-- | src/tty.rs | 40 |
6 files changed, 132 insertions, 445 deletions
diff --git a/src/grid.rs b/src/grid.rs index 7a1fedb8..9ce16c81 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -9,10 +9,8 @@ use term::{Cursor, DEFAULT_FG, DEFAULT_BG}; use ::Rgb; /// Calculate the number of cells for an axis -pub fn num_cells_axis(cell_width: u32, cell_sep: i32, screen_width: u32) -> u32 { - println!("num_cells_axis(cell_width: {}, cell_sep: {}, screen_width: {}", - cell_width, cell_sep, screen_width); - ((screen_width as i32 - cell_sep) as f64 / (cell_width as i32 + cell_sep) as f64) as u32 +pub fn num_cells_axis(cell_width: u32, screen_width: u32) -> u32 { + (screen_width as f64 / cell_width as f64) as u32 } #[derive(Clone, Debug)] diff --git a/src/list_fonts.rs b/src/list_fonts.rs deleted file mode 100644 index f171f57e..00000000 --- a/src/list_fonts.rs +++ /dev/null @@ -1,186 +0,0 @@ -use std::collections::HashMap; -use std::ffi::{CStr, CString}; -use std::fmt; -use std::path::PathBuf; -use std::ptr; -use std::str::from_utf8; - -use libc::{c_char, c_int}; - -use fontconfig::fontconfig::{FcConfigGetCurrent, FcConfigGetFonts, FcSetSystem}; -use fontconfig::fontconfig::{FcPatternGetString, FcPatternCreate, FcPatternAddString}; -use fontconfig::fontconfig::{FcPatternGetInteger}; -use fontconfig::fontconfig::{FcObjectSetCreate, FcObjectSetAdd}; -use fontconfig::fontconfig::{FcResultMatch, FcFontSetList}; -use fontconfig::fontconfig::{FcChar8}; -use fontconfig::fontconfig::{FcFontSetDestroy, FcPatternDestroy, FcObjectSetDestroy}; - -unsafe fn fc_char8_to_string(fc_str: *mut FcChar8) -> String { - from_utf8(CStr::from_ptr(fc_str as *const c_char).to_bytes()).unwrap().to_owned() -} - -fn list_families() -> Vec<String> { - let mut families = Vec::new(); - unsafe { - // https://www.freedesktop.org/software/fontconfig/fontconfig-devel/fcconfiggetcurrent.html - let config = FcConfigGetCurrent(); // *mut FcConfig - - // https://www.freedesktop.org/software/fontconfig/fontconfig-devel/fcconfiggetfonts.html - let font_set = FcConfigGetFonts(config, FcSetSystem); // *mut FcFontSet - - let nfont = (*font_set).nfont as isize; - for i in 0..nfont { - let font = (*font_set).fonts.offset(i); // *mut FcPattern - let id = 0 as c_int; - let mut family: *mut FcChar8 = ptr::null_mut(); - let mut format: *mut FcChar8 = ptr::null_mut(); - - let result = FcPatternGetString(*font, - b"fontformat\0".as_ptr() as *mut c_char, - id, - &mut format); - - if result != FcResultMatch { - continue; - } - - let format = fc_char8_to_string(format); - - if format != "TrueType" && format != "CFF" { - continue - } - - let mut id = 0; - while FcPatternGetString(*font, b"family\0".as_ptr() as *mut c_char, id, &mut family) == FcResultMatch { - let safe_family = fc_char8_to_string(family); - id += 1; - families.push(safe_family); - } - } - } - - families.sort(); - families.dedup(); - families -} - -#[derive(Debug)] -pub struct Variant { - style: String, - file: PathBuf, - index: isize, -} - -impl Variant { - #[inline] - pub fn path(&self) -> &::std::path::Path { - self.file.as_path() - } - - #[inline] - pub fn index(&self) -> isize { - self.index - } -} - -#[derive(Debug)] -pub struct Family { - name: String, - variants: HashMap<String, Variant>, -} - -impl fmt::Display for Family { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "{}: ", self.name)); - for (k, _v) in &self.variants { - try!(write!(f, "{}, ", k)); - } - - Ok(()) - } -} - -impl Family { - #[inline] - pub fn variants(&self) -> &HashMap<String, Variant> { - &self.variants - } -} - -static FILE: &'static [u8] = b"file\0"; -static FAMILY: &'static [u8] = b"family\0"; -static INDEX: &'static [u8] = b"index\0"; -static STYLE: &'static [u8] = b"style\0"; - -pub fn get_family_info(family: String) -> Family { - - let mut members = Vec::new(); - - unsafe { - let config = FcConfigGetCurrent(); // *mut FcConfig - let mut font_set = FcConfigGetFonts(config, FcSetSystem); // *mut FcFontSet - - let pattern = FcPatternCreate(); - let family_name = CString::new(&family[..]).unwrap(); - let family_name = family_name.as_ptr(); - - // Add family name to pattern. Use this for searching. - FcPatternAddString(pattern, FAMILY.as_ptr() as *mut c_char, family_name as *mut FcChar8); - - // Request filename, style, and index for each variant in family - let object_set = FcObjectSetCreate(); // *mut FcObjectSet - FcObjectSetAdd(object_set, FILE.as_ptr() as *mut c_char); - FcObjectSetAdd(object_set, INDEX.as_ptr() as *mut c_char); - FcObjectSetAdd(object_set, STYLE.as_ptr() as *mut c_char); - - let variants = FcFontSetList(config, &mut font_set, 1 /* nsets */, pattern, object_set); - let num_variant = (*variants).nfont as isize; - - for i in 0..num_variant { - let font = (*variants).fonts.offset(i); - let mut file: *mut FcChar8 = ptr::null_mut(); - assert_eq!(FcPatternGetString(*font, FILE.as_ptr() as *mut c_char, 0, &mut file), - FcResultMatch); - let file = fc_char8_to_string(file); - - let mut style: *mut FcChar8 = ptr::null_mut(); - assert_eq!(FcPatternGetString(*font, STYLE.as_ptr() as *mut c_char, 0, &mut style), - FcResultMatch); - let style = fc_char8_to_string(style); - - let mut index = 0 as c_int; - assert_eq!(FcPatternGetInteger(*font, INDEX.as_ptr() as *mut c_char, 0, &mut index), - FcResultMatch); - - members.push(Variant { - style: style, - file: PathBuf::from(file), - index: index as isize, - }); - } - - FcFontSetDestroy(variants); - FcPatternDestroy(pattern); - FcObjectSetDestroy(object_set); - } - - Family { - name: family, - variants: members.into_iter().map(|v| (v.style.clone(), v)).collect() - } -} - -pub fn get_font_families() -> HashMap<String, Family> { - list_families().into_iter() - .map(|family| (family.clone(), get_family_info(family))) - .collect() -} - -#[cfg(test)] -mod tests { - #[test] - fn get_font_families() { - let families = super::get_font_families(); - assert!(!families.is_empty()); - } -} diff --git a/src/main.rs b/src/main.rs index f3808e0e..48f2df74 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,12 +5,12 @@ #![feature(io)] #![feature(unicode)] -extern crate fontconfig; -extern crate freetype; +extern crate font; extern crate libc; extern crate glutin; extern crate cgmath; extern crate notify; +extern crate errno; #[macro_use] extern crate bitflags; @@ -18,8 +18,6 @@ extern crate bitflags; #[macro_use] mod macros; -mod list_fonts; -mod text; mod renderer; pub mod grid; mod meter; @@ -36,7 +34,7 @@ use grid::Grid; use meter::Meter; use renderer::{QuadRenderer, GlyphCache}; use term::Term; -use text::FontDesc; +use font::FontDesc; use tty::process_should_exit; use util::thread; @@ -115,41 +113,47 @@ pub struct TermProps { height: f32, cell_width: f32, cell_height: f32, - sep_x: f32, - sep_y: f32, } -fn main() { - let window = glutin::WindowBuilder::new() - .with_title("alacritty".into()) - .build() - .unwrap(); +#[cfg(target_os = "linux")] +static FONT: &'static str = "DejaVu Sans Mono"; +#[cfg(target_os = "linux")] +static FONT_STYLE: &'static str = "Book"; - let (width, height) = window.get_inner_size_pixels().unwrap(); - unsafe { - window.make_current().unwrap(); - } +#[cfg(target_os = "macos")] +static FONT: &'static str = "Menlo"; +#[cfg(target_os = "macos")] +static FONT_STYLE: &'static str = "Regular"; - unsafe { - gl::load_with(|symbol| window.get_proc_address(symbol) as *const _); - gl::Viewport(0, 0, width as i32, height as i32); - } - let (dpi_x, dpi_y) = window.get_dpi().unwrap(); +fn main() { + + let window = glutin::WindowBuilder::new().build().unwrap(); + window.set_title("Alacritty"); + // window.set_window_resize_callback(Some(resize_callback 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 font_size = 11.; - let sep_x = 2; - let sep_y = 5; + let sep_x = 2.0; + let sep_y = -7.0; - let desc = FontDesc::new("DejaVu Sans Mono", "Book"); - let mut rasterizer = text::Rasterizer::new(dpi_x, dpi_y, dpr); + let desc = FontDesc::new(FONT, FONT_STYLE); + let mut rasterizer = font::Rasterizer::new(96., 96., dpr); - let (cell_width, cell_height) = rasterizer.box_size_for_font(&desc, font_size); + let metrics = rasterizer.metrics(&desc, font_size); + let cell_width = (metrics.average_advance + sep_x) as u32; + let cell_height = (metrics.line_height + sep_y) as u32; - let num_cols = grid::num_cells_axis(cell_width, sep_x, width); - let num_rows = grid::num_cells_axis(cell_height, sep_y, height); + println!("Cell Size: ({} x {})", cell_width, cell_height); + + let num_cols = grid::num_cells_axis(cell_width, width); + let num_rows = grid::num_cells_axis(cell_height, height); let tty = tty::new(num_rows as u8, num_cols as u8); tty.resize(num_rows as usize, num_cols as usize, width as usize, height as usize); @@ -162,25 +166,13 @@ fn main() { let props = TermProps { cell_width: cell_width as f32, - sep_x: sep_x as f32, cell_height: cell_height as f32, - sep_y: sep_y as f32, height: height as f32, width: width as f32, }; - let mut renderer = QuadRenderer::new(width, height); - let mut glyph_cache = GlyphCache::new(rasterizer, desc, font_size); - renderer.with_api(&props, |mut api| { - glyph_cache.init(&mut api); - }); - unsafe { - gl::Enable(gl::BLEND); - gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR); - gl::Enable(gl::MULTISAMPLE); - } let (tx, rx) = mpsc::channel(); let reader_tx = tx.clone(); @@ -198,83 +190,98 @@ fn main() { let window = Arc::new(window); let window_ref = window.clone(); - let input_thread = thread::spawn_named("Input Thread", move || { - for event in window_ref.wait_events() { - tx.send(Event::Glutin(event)).unwrap(); - if process_should_exit() { - break; - } + let render_thread = thread::spawn_named("Galaxy", move || { + 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); } - }); - 'main_loop: loop { - { - let mut writer = BufWriter::new(&writer); + let mut renderer = QuadRenderer::new(width, height); + renderer.with_api(&props, |mut api| { + glyph_cache.init(&mut api); + }); - // Block waiting for next event - match rx.recv() { - Ok(e) => { - let res = handle_event(e, &mut writer, &mut terminal, &mut pty_parser); - if res == ShouldExit::Yes { - break; - } - }, - Err(mpsc::RecvError) => break, - } + 'main_loop: loop { + { + let mut writer = BufWriter::new(&writer); - // Handle Any events that have been queued - loop { - match rx.try_recv() { + // Block waiting for next event + match rx.recv() { Ok(e) => { let res = handle_event(e, &mut writer, &mut terminal, &mut pty_parser); - if res == ShouldExit::Yes { break; } }, - Err(mpsc::TryRecvError::Disconnected) => break 'main_loop, - Err(mpsc::TryRecvError::Empty) => break, + Err(mpsc::RecvError) => break, } - // TODO make sure this doesn't block renders + // Handle Any events that have been queued + loop { + match rx.try_recv() { + Ok(e) => { + let res = handle_event(e, &mut writer, &mut terminal, &mut pty_parser); + + if res == ShouldExit::Yes { + break; + } + }, + Err(mpsc::TryRecvError::Disconnected) => break 'main_loop, + Err(mpsc::TryRecvError::Empty) => break, + } + + // TODO make sure this doesn't block renders + } } - } - unsafe { - gl::ClearColor(0.0, 0.0, 0.00, 1.0); - gl::Clear(gl::COLOR_BUFFER_BIT); - } + unsafe { + gl::ClearColor(0.0, 0.0, 0.00, 1.0); + gl::Clear(gl::COLOR_BUFFER_BIT); + } - { - let _sampler = meter.sampler(); + { + let _sampler = meter.sampler(); - renderer.with_api(&props, |mut api| { - // Draw the grid - api.render_grid(terminal.grid(), &mut glyph_cache); + renderer.with_api(&props, |mut api| { + // Draw the grid + api.render_grid(terminal.grid(), &mut glyph_cache); - // Also draw the cursor - if !terminal.mode().contains(term::mode::TEXT_CURSOR) { - api.render_cursor(terminal.cursor(), &mut glyph_cache); - } - }) - } + // Also draw the cursor + if !terminal.mode().contains(term::mode::TEXT_CURSOR) { + api.render_cursor(terminal.cursor(), &mut glyph_cache); + } + }) + } - // Draw render timer - let timing = format!("{:.3} usec", meter.average()); - let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 }; - renderer.with_api(&props, |mut api| { - api.render_string(&timing[..], &mut glyph_cache, &color); - }); + // Draw render timer + let timing = format!("{:.3} usec", meter.average()); + let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 }; + renderer.with_api(&props, |mut api| { + api.render_string(&timing[..], &mut glyph_cache, &color); + }); - window.swap_buffers().unwrap(); + window.swap_buffers().unwrap(); + + if process_should_exit() { + break 'main_loop; + } + } + }); - if process_should_exit() { - break; + 'event_processing: loop { + for event in window_ref.wait_events() { + tx.send(Event::Glutin(event)).unwrap(); + if process_should_exit() { + break 'event_processing; + } } } reader_thread.join().ok(); - input_thread.join().ok(); + render_thread.join().ok(); println!("Goodbye"); } diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index a0051aaa..dff20070 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -13,7 +13,7 @@ use gl::types::*; use gl; use notify::{Watcher as WatcherApi, RecommendedWatcher as Watcher, op}; -use text::{Rasterizer, RasterizedGlyph, FontDesc}; +use font::{Rasterizer, RasterizedGlyph, FontDesc}; use grid::{self, Grid, Cell, CellFlags}; use term; @@ -45,9 +45,6 @@ pub struct ShaderProgram { /// Cell dimensions (pixels) u_cell_dim: GLint, - /// Cell separation (pixels) - u_cell_sep: GLint, - /// Background pass flag /// /// Rendering is split into two passes; 1 for backgrounds, and one for text @@ -649,24 +646,22 @@ impl ShaderProgram { } // get uniform locations - let (projection, term_dim, cell_dim, cell_sep, background) = unsafe { + let (projection, term_dim, cell_dim, background) = unsafe { ( gl::GetUniformLocation(program, cptr!(b"projection\0")), gl::GetUniformLocation(program, cptr!(b"termDim\0")), gl::GetUniformLocation(program, cptr!(b"cellDim\0")), - gl::GetUniformLocation(program, cptr!(b"cellSep\0")), gl::GetUniformLocation(program, cptr!(b"backgroundPass\0")), ) }; - assert_uniform_valid!(projection, term_dim, cell_dim, cell_sep); + assert_uniform_valid!(projection, term_dim, cell_dim); let shader = ShaderProgram { id: program, u_projection: projection, u_term_dim: term_dim, u_cell_dim: cell_dim, - u_cell_sep: cell_sep, u_background: background, }; @@ -690,7 +685,6 @@ impl ShaderProgram { unsafe { gl::Uniform2f(self.u_term_dim, props.width, props.height); gl::Uniform2f(self.u_cell_dim, props.cell_width, props.cell_height); - gl::Uniform2f(self.u_cell_sep, props.sep_x, props.sep_y); } } diff --git a/src/text.rs b/src/text.rs deleted file mode 100644 index 5086a3a4..00000000 --- a/src/text.rs +++ /dev/null @@ -1,140 +0,0 @@ -use std::collections::HashMap; -use list_fonts::get_font_families; - -use freetype::Library; -use freetype::Face; -use freetype; - -/// Rasterizes glyphs for a single font face. -pub struct Rasterizer { - faces: HashMap<FontDesc, Face<'static>>, - library: Library, - system_fonts: HashMap<String, ::list_fonts::Family>, - dpi_x: u32, - dpi_y: u32, - dpr: f32, -} - -#[inline] -fn to_freetype_26_6(f: f32) -> isize { - ((1i32 << 6) as f32 * f) as isize -} - -#[derive(Clone, PartialEq, Eq, Hash)] -pub struct FontDesc { - name: String, - style: String, -} - -impl FontDesc { - pub fn new<S>(name: S, style: S) -> FontDesc - where S: Into<String> - { - FontDesc { - name: name.into(), - style: style.into() - } - } -} - -impl Rasterizer { - pub fn new(dpi_x: f32, dpi_y: f32, device_pixel_ratio: f32) -> Rasterizer { - let library = Library::init().unwrap(); - - Rasterizer { - system_fonts: get_font_families(), - faces: HashMap::new(), - library: library, - dpi_x: dpi_x as u32, - dpi_y: dpi_y as u32, - dpr: device_pixel_ratio, - } - } - - pub fn box_size_for_font(&mut self, desc: &FontDesc, size: f32) -> (u32, u32) { - let face = self.get_face(&desc).unwrap(); - - let scale_size = self.dpr * size; - - let em_size = face.em_size() as f32; - let w = face.max_advance_width() as f32; - let h = face.height() as f32; - - let w_scale = w / em_size; - let h_scale = h / em_size; - - ((w_scale * scale_size) as u32, (h_scale * scale_size) as u32) - } - - pub fn get_face(&mut self, desc: &FontDesc) -> Option<Face<'static>> { - if let Some(face) = self.faces.get(desc) { - return Some(face.clone()); - } - - if let Some(font) = self.system_fonts.get(&desc.name[..]) { - if let Some(variant) = font.variants().get(&desc.style[..]) { - let face = self.library.new_face(variant.path(), variant.index()) - .expect("TODO handle new_face error"); - - self.faces.insert(desc.to_owned(), face); - return Some(self.faces.get(desc).unwrap().clone()); - } - } - - None - } - - pub fn get_glyph(&mut self, desc: &FontDesc, size: f32, c: char) -> RasterizedGlyph { - let face = self.get_face(desc).expect("TODO handle get_face error"); - face.set_char_size(to_freetype_26_6(size * self.dpr), 0, self.dpi_x, self.dpi_y).unwrap(); - face.load_char(c as usize, freetype::face::TARGET_LIGHT).unwrap(); - let glyph = face.glyph(); - glyph.render_glyph(freetype::render_mode::RenderMode::Lcd).unwrap(); - - unsafe { - let ft_lib = self.library.raw(); - freetype::ffi::FT_Library_SetLcdFilter(ft_lib, freetype::ffi::FT_LCD_FILTER_DEFAULT); - } - - let bitmap = glyph.bitmap(); - let buf = bitmap.buffer(); - let pitch = bitmap.pitch() as usize; - - let mut packed = Vec::with_capacity((bitmap.rows() * bitmap.width()) as usize); - for i in 0..bitmap.rows() { - let start = (i as usize) * pitch; - let stop = start + bitmap.width() as usize; - packed.extend_from_slice(&buf[start..stop]); - } - - RasterizedGlyph { - c: c, - top: glyph.bitmap_top(), - left: glyph.bitmap_left(), - width: glyph.bitmap().width() / 3, - height: glyph.bitmap().rows(), - buf: packed, - } - } -} - -#[derive(Debug)] -pub struct RasterizedGlyph { - pub c: char, - pub width: i32, - pub height: i32, - pub top: i32, - pub left: i32, - pub buf: Vec<u8>, -} - - -#[cfg(test)] -mod tests { - use super::{Rasterizer, FontDesc}; - - #[cfg(target_os = "linux")] - fn font_desc() -> FontDesc { - FontDesc::new("Ubuntu Mono", "Regular") - } -} @@ -47,9 +47,7 @@ pub fn process_should_exit() -> bool { /// Get the current value of errno fn errno() -> c_int { - unsafe { - ptr::read(libc::__errno_location() as *const _) - } + ::errno::errno().0 } enum Relation { @@ -74,6 +72,7 @@ fn fork() -> Relation { } /// Get raw fds for master/slave ends of a new pty +#[cfg(target_os = "linux")] fn openpty(rows: u8, cols: u8) -> (c_int, c_int) { let mut master: c_int = 0; let mut slave: c_int = 0; @@ -96,10 +95,33 @@ fn openpty(rows: u8, cols: u8) -> (c_int, c_int) { (master, slave) } +#[cfg(target_os = "macos")] +fn openpty(rows: u8, cols: u8) -> (c_int, c_int) { + let mut master: c_int = 0; + let mut slave: c_int = 0; + + let mut win = winsize { + ws_row: rows as libc::c_ushort, + ws_col: cols as libc::c_ushort, + ws_xpixel: 0, + ws_ypixel: 0, + }; + + let res = unsafe { + libc::openpty(&mut master, &mut slave, ptr::null_mut(), ptr::null_mut(), &mut win) + }; + + if res < 0 { + die!("openpty failed"); + } + + (master, slave) +} + /// Really only needed on BSD, but should be fine elsewhere fn set_controlling_terminal(fd: c_int) { let res = unsafe { - libc::ioctl(fd, libc::TIOCSCTTY, 0) + libc::ioctl(fd, libc::TIOCSCTTY as _, 0) }; if res < 0 { @@ -125,15 +147,7 @@ struct Passwd<'a> { /// If `buf` is changed while `Passwd` is alive, bad thing will almost certainly happen. fn get_pw_entry<'a>(buf: &'a mut [i8; 1024]) -> Passwd<'a> { // Create zeroed passwd struct - let mut entry = libc::passwd { - pw_name: ptr::null_mut(), - pw_passwd: ptr::null_mut(), - pw_uid: 0, - pw_gid: 0, - pw_gecos: ptr::null_mut(), - pw_dir: ptr::null_mut(), - pw_shell: ptr::null_mut(), - }; + let mut entry: libc::passwd = unsafe { ::std::mem::uninitialized() }; let mut res: *mut libc::passwd = ptr::null_mut(); |