Add rustfmt style guide

This commit is contained in:
Christian Duerr 2019-03-30 16:48:36 +00:00 committed by GitHub
parent 91aa683bcd
commit cfd025b528
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 1895 additions and 2412 deletions

View File

@ -35,6 +35,10 @@ matrix:
os: windows os: windows
env: CLIPPY=true env: CLIPPY=true
rust: stable rust: stable
- name: "Rustfmt"
os: linux
env: RUSTFMT=true
rust: nightly
allow_failures: allow_failures:
- rust: nightly - rust: nightly

View File

@ -89,7 +89,7 @@ Changes compared to the latest Alacritty release which have a direct effect on t
### Style ### Style
Alacritty currently does not have any automatically enforced style guidelines. As a result of that, it is not possible to run `rustfmt` on existing files. New code should however follow the default ruleset of `rustfmt` and for newly created files it is possible to run the `rustfmt` tool directly. All Alacritty changes are automatically verified by CI to conform to its rustfmt guidelines. If a CI build is failing because of formatting issues, you can install rustfmt using `rustup component add rustfmt` and then format all code using `cargo fmt`.
# Contact # Contact

View File

@ -14,10 +14,10 @@
#[cfg(windows)] #[cfg(windows)]
use embed_resource; use embed_resource;
#[cfg(windows)] #[cfg(windows)]
use tempfile;
#[cfg(windows)]
use reqwest; use reqwest;
#[cfg(windows)] #[cfg(windows)]
use tempfile;
#[cfg(windows)]
use zip; use zip;
use gl_generator::{Api, Fallbacks, GlobalGenerator, Profile, Registry}; use gl_generator::{Api, Fallbacks, GlobalGenerator, Profile, Registry};
@ -26,25 +26,21 @@ use std::env;
use std::fs::File; use std::fs::File;
use std::path::Path; use std::path::Path;
#[cfg(windows)]
use std::io;
#[cfg(windows)] #[cfg(windows)]
use std::fs::OpenOptions; use std::fs::OpenOptions;
#[cfg(windows)]
use std::io;
#[cfg(windows)] #[cfg(windows)]
const WINPTY_PACKAGE_URL: &str = "https://github.com/rprichard/winpty/releases/download/0.4.3/winpty-0.4.3-msvc2015.zip"; const WINPTY_PACKAGE_URL: &str =
"https://github.com/rprichard/winpty/releases/download/0.4.3/winpty-0.4.3-msvc2015.zip";
fn main() { fn main() {
let dest = env::var("OUT_DIR").unwrap(); let dest = env::var("OUT_DIR").unwrap();
let mut file = File::create(&Path::new(&dest).join("gl_bindings.rs")).unwrap(); let mut file = File::create(&Path::new(&dest).join("gl_bindings.rs")).unwrap();
Registry::new( Registry::new(Api::Gl, (4, 5), Profile::Core, Fallbacks::All, ["GL_ARB_blend_func_extended"])
Api::Gl, .write_bindings(GlobalGenerator, &mut file)
(4, 5),
Profile::Core,
Fallbacks::All,
["GL_ARB_blend_func_extended"],
).write_bindings(GlobalGenerator, &mut file)
.unwrap(); .unwrap();
#[cfg(windows)] #[cfg(windows)]
@ -68,7 +64,8 @@ fn aquire_winpty_agent(out_path: &Path) {
.read(true) .read(true)
.write(true) .write(true)
.create(true) .create(true)
.open(tmp_dir.path().join("winpty_package.zip")).unwrap(); .open(tmp_dir.path().join("winpty_package.zip"))
.unwrap();
io::copy(&mut response, &mut file).unwrap(); io::copy(&mut response, &mut file).unwrap();
@ -77,7 +74,7 @@ fn aquire_winpty_agent(out_path: &Path) {
let target = match env::var("TARGET").unwrap().split("-").next().unwrap() { let target = match env::var("TARGET").unwrap().split("-").next().unwrap() {
"x86_64" => "x64", "x86_64" => "x64",
"i386" => "ia32", "i386" => "ia32",
_ => panic!("architecture has no winpty binary") _ => panic!("architecture has no winpty binary"),
}; };
let mut winpty_agent = archive.by_name(&format!("{}/bin/winpty-agent.exe", target)).unwrap(); let mut winpty_agent = archive.by_name(&format!("{}/bin/winpty-agent.exe", target)).unwrap();

View File

@ -1,6 +1,11 @@
#!/bin/bash #!/bin/bash
# Add clippy for linting with nightly builds # Add clippy for lint validation
if [ "$CLIPPY" == "true" ]; then if [ "$CLIPPY" == "true" ]; then
rustup component add clippy rustup component add clippy
fi fi
# Add rustfmt for format validation
if [ "$RUSTFMT" == "true" ]; then
rustup component add rustfmt
fi

View File

@ -9,6 +9,12 @@ if [ "$CLIPPY" == "true" ]; then
exit exit
fi fi
# Run clippy rustfmt
if [ "$RUSTFMT" == "true" ]; then
cargo fmt -- --check
exit
fi
# Run test in release mode if a tag is present, to produce an optimized binary # Run test in release mode if a tag is present, to produce an optimized binary
if [ -n "$TRAVIS_TAG" ]; then if [ -n "$TRAVIS_TAG" ]; then
cargo test --release || error=true cargo test --release || error=true

View File

@ -4,18 +4,18 @@
//! https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/PasteboardGuide106/Articles/pbReading.html#//apple_ref/doc/uid/TP40008123-SW1 //! https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/PasteboardGuide106/Articles/pbReading.html#//apple_ref/doc/uid/TP40008123-SW1
mod ns { mod ns {
extern crate objc_id;
extern crate objc_foundation; extern crate objc_foundation;
extern crate objc_id;
#[link(name = "AppKit", kind = "framework")] #[link(name = "AppKit", kind = "framework")]
extern {} extern "C" {}
use std::mem; use std::mem;
use objc::runtime::{Class, Object}; use self::objc_foundation::{INSArray, INSObject, INSString};
use self::objc_foundation::{NSArray, NSDictionary, NSObject, NSString};
use self::objc_id::{Id, Owned}; use self::objc_id::{Id, Owned};
use self::objc_foundation::{NSArray, NSObject, NSDictionary, NSString}; use objc::runtime::{Class, Object};
use self::objc_foundation::{INSString, INSArray, INSObject};
/// Rust API for NSPasteboard /// Rust API for NSPasteboard
pub struct Pasteboard(Id<Object>); pub struct Pasteboard(Id<Object>);
@ -55,6 +55,7 @@ mod ns {
impl PasteboardReadObject<String> for Pasteboard { impl PasteboardReadObject<String> for Pasteboard {
type Err = ReadStringError; type Err = ReadStringError;
fn read_object(&self) -> Result<String, ReadStringError> { fn read_object(&self) -> Result<String, ReadStringError> {
// Get string class; need this for passing to readObjectsForClasses // Get string class; need this for passing to readObjectsForClasses
let ns_string_class = match Class::get("NSString") { let ns_string_class = match Class::get("NSString") {
@ -133,9 +134,7 @@ mod ns {
// The writeObjects method returns true in case of success, and // The writeObjects method returns true in case of success, and
// false otherwise. // false otherwise.
let ok: bool = unsafe { let ok: bool = unsafe { msg_send![self.0, writeObjects: objects] };
msg_send![self.0, writeObjects:objects]
};
if ok { if ok {
Ok(()) Ok(())
@ -175,9 +174,7 @@ mod ns {
impl ::std::error::Error for NewPasteboardError { impl ::std::error::Error for NewPasteboardError {
fn description(&self) -> &str { fn description(&self) -> &str {
match *self { match *self {
NewPasteboardError::GetPasteboardClass => { NewPasteboardError::GetPasteboardClass => "NSPasteboard class not found",
"NSPasteboard class not found"
},
NewPasteboardError::LoadGeneralPasteboard => { NewPasteboardError::LoadGeneralPasteboard => {
"[NSPasteboard generalPasteboard] failed" "[NSPasteboard generalPasteboard] failed"
}, },
@ -209,9 +206,7 @@ mod ns {
} }
}; };
let id = unsafe { let id = unsafe { Id::from_ptr(ptr) };
Id::from_ptr(ptr)
};
Ok(Pasteboard(id)) Ok(Pasteboard(id))
} }
@ -222,9 +217,7 @@ mod ns {
/// This is the first step in providing data on the pasteboard. The /// This is the first step in providing data on the pasteboard. The
/// return value is the change count of the pasteboard /// return value is the change count of the pasteboard
pub fn clear_contents(&mut self) -> usize { pub fn clear_contents(&mut self) -> usize {
unsafe { unsafe { msg_send![self.0, clearContents] }
msg_send![self.0, clearContents]
}
} }
} }
} }
@ -236,7 +229,6 @@ pub enum Error {
WriteString(ns::WriteStringError), WriteString(ns::WriteStringError),
} }
impl ::std::error::Error for Error { impl ::std::error::Error for Error {
fn cause(&self) -> Option<&::std::error::Error> { fn cause(&self) -> Option<&::std::error::Error> {
match *self { match *self {
@ -258,9 +250,7 @@ impl ::std::error::Error for Error {
impl ::std::fmt::Display for Error { impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match *self { match *self {
Error::CreatePasteboard(ref err) => { Error::CreatePasteboard(ref err) => write!(f, "Failed to create pasteboard: {}", err),
write!(f, "Failed to create pasteboard: {}", err)
},
Error::ReadString(ref err) => { Error::ReadString(ref err) => {
write!(f, "Failed to read string from pasteboard: {}", err) write!(f, "Failed to read string from pasteboard: {}", err)
}, },
@ -301,23 +291,23 @@ impl super::Load for Clipboard {
fn load_primary(&self) -> Result<String, Self::Err> { fn load_primary(&self) -> Result<String, Self::Err> {
use self::ns::PasteboardReadObject; use self::ns::PasteboardReadObject;
self.0.read_object() self.0.read_object().map_err(::std::convert::From::from)
.map_err(::std::convert::From::from)
} }
} }
impl super::Store for Clipboard { impl super::Store for Clipboard {
fn store_primary<S>(&mut self, contents: S) -> Result<(), Self::Err> fn store_primary<S>(&mut self, contents: S) -> Result<(), Self::Err>
where S: Into<String> where
S: Into<String>,
{ {
use self::ns::PasteboardWriteObject; use self::ns::PasteboardWriteObject;
self.0.write_object(contents.into()) self.0.write_object(contents.into()).map_err(::std::convert::From::from)
.map_err(::std::convert::From::from)
} }
fn store_selection<S>(&mut self, _contents: S) -> Result<(), Self::Err> fn store_selection<S>(&mut self, _contents: S) -> Result<(), Self::Err>
where S: Into<String> where
S: Into<String>,
{ {
// No such thing on macOS // No such thing on macOS
Ok(()) Ok(())
@ -327,7 +317,7 @@ impl super::Store for Clipboard {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::Clipboard; use super::Clipboard;
use ::{Load, Store}; use {Load, Store};
#[test] #[test]
fn create_clipboard_save_load_contents() { fn create_clipboard_save_load_contents() {

View File

@ -33,9 +33,7 @@ impl Load for Clipboard {
type Err = Error; type Err = Error;
fn new() -> Result<Self, Error> { fn new() -> Result<Self, Error> {
ClipboardContext::new() ClipboardContext::new().map(Clipboard).map_err(Error::Clipboard)
.map(Clipboard)
.map_err(Error::Clipboard)
} }
fn load_primary(&self) -> Result<String, Self::Err> { fn load_primary(&self) -> Result<String, Self::Err> {
@ -56,9 +54,7 @@ impl Store for Clipboard {
where where
S: Into<String>, S: Into<String>,
{ {
self.0 self.0.set_contents(contents.into()).map_err(Error::Clipboard)
.set_contents(contents.into())
.map_err(Error::Clipboard)
} }
/// Sets the secondary clipboard contents /// Sets the secondary clipboard contents
@ -67,8 +63,6 @@ impl Store for Clipboard {
where where
S: Into<String>, S: Into<String>,
{ {
self.0 self.0.set_contents(contents.into()).map_err(Error::Clipboard)
.set_contents(contents.into())
.map_err(Error::Clipboard)
} }
} }

View File

@ -7,10 +7,10 @@
//! //!
//! FIXME: Implement actual X11 clipboard API using the ICCCM reference //! FIXME: Implement actual X11 clipboard API using the ICCCM reference
//! https://tronche.com/gui/x/icccm/ //! https://tronche.com/gui/x/icccm/
use std::io;
use std::process::{Output, Command};
use std::string::FromUtf8Error;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::io;
use std::process::{Command, Output};
use std::string::FromUtf8Error;
use super::{Load, Store}; use super::{Load, Store};
@ -45,13 +45,11 @@ impl ::std::error::Error for Error {
impl ::std::fmt::Display for Error { impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match *self { match *self {
Error::Io(ref err) => { Error::Io(ref err) => match err.kind() {
match err.kind() { io::ErrorKind::NotFound => {
io::ErrorKind::NotFound => { write!(f, "Please install `xclip` to enable clipboard support")
write!(f, "Please install `xclip` to enable clipboard support") },
}, _ => write!(f, "Error calling xclip: {}", err),
_ => write!(f, "Error calling xclip: {}", err),
}
}, },
Error::Xclip(ref s) => write!(f, "Error from xclip: {}", s), Error::Xclip(ref s) => write!(f, "Error from xclip: {}", s),
Error::Utf8(ref err) => write!(f, "Error parsing xclip output: {}", err), Error::Utf8(ref err) => write!(f, "Error parsing xclip output: {}", err),
@ -79,17 +77,13 @@ impl Load for Clipboard {
} }
fn load_primary(&self) -> Result<String, Self::Err> { fn load_primary(&self) -> Result<String, Self::Err> {
let output = Command::new("xclip") let output = Command::new("xclip").args(&["-o", "-selection", "clipboard"]).output()?;
.args(&["-o", "-selection", "clipboard"])
.output()?;
Clipboard::process_xclip_output(output) Clipboard::process_xclip_output(output)
} }
fn load_selection(&self) -> Result<String, Self::Err> { fn load_selection(&self) -> Result<String, Self::Err> {
let output = Command::new("xclip") let output = Command::new("xclip").args(&["-o"]).output()?;
.args(&["-o"])
.output()?;
Clipboard::process_xclip_output(output) Clipboard::process_xclip_output(output)
} }
@ -99,7 +93,8 @@ impl Store for Clipboard {
/// Sets the primary clipboard contents /// Sets the primary clipboard contents
#[inline] #[inline]
fn store_primary<S>(&mut self, contents: S) -> Result<(), Self::Err> fn store_primary<S>(&mut self, contents: S) -> Result<(), Self::Err>
where S: Into<String> where
S: Into<String>,
{ {
self.store(contents, &["-i", "-selection", "clipboard"]) self.store(contents, &["-i", "-selection", "clipboard"])
} }
@ -107,7 +102,8 @@ impl Store for Clipboard {
/// Sets the secondary clipboard contents /// Sets the secondary clipboard contents
#[inline] #[inline]
fn store_selection<S>(&mut self, contents: S) -> Result<(), Self::Err> fn store_selection<S>(&mut self, contents: S) -> Result<(), Self::Err>
where S: Into<String> where
S: Into<String>,
{ {
self.store(contents, &["-i"]) self.store(contents, &["-i"])
} }
@ -116,26 +112,22 @@ impl Store for Clipboard {
impl Clipboard { impl Clipboard {
fn process_xclip_output(output: Output) -> Result<String, Error> { fn process_xclip_output(output: Output) -> Result<String, Error> {
if output.status.success() { if output.status.success() {
String::from_utf8(output.stdout) String::from_utf8(output.stdout).map_err(::std::convert::From::from)
.map_err(::std::convert::From::from)
} else { } else {
String::from_utf8(output.stderr) String::from_utf8(output.stderr).map_err(::std::convert::From::from)
.map_err(::std::convert::From::from)
} }
} }
fn store<C, S>(&mut self, contents: C, args: &[S]) -> Result<(), Error> fn store<C, S>(&mut self, contents: C, args: &[S]) -> Result<(), Error>
where C: Into<String>, where
S: AsRef<OsStr>, C: Into<String>,
S: AsRef<OsStr>,
{ {
use std::io::Write; use std::io::Write;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
let contents = contents.into(); let contents = contents.into();
let mut child = Command::new("xclip") let mut child = Command::new("xclip").args(args).stdin(Stdio::piped()).spawn()?;
.args(args)
.stdin(Stdio::piped())
.spawn()?;
if let Some(stdin) = child.stdin.as_mut() { if let Some(stdin) = child.stdin.as_mut() {
stdin.write_all(contents.as_bytes())?; stdin.write_all(contents.as_bytes())?;
@ -154,7 +146,7 @@ impl Clipboard {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::Clipboard; use super::Clipboard;
use ::{Load, Store}; use {Load, Store};
#[test] #[test]
fn clipboard_works() { fn clipboard_works() {

View File

@ -40,9 +40,10 @@ pub fn extract_rgb(bytes: &[u8]) -> Vec<u8> {
#[cfg(target_endian = "big")] #[cfg(target_endian = "big")]
pub fn extract_rgb(bytes: Vec<u8>) -> Vec<u8> { pub fn extract_rgb(bytes: Vec<u8>) -> Vec<u8> {
bytes.into_iter() bytes
.enumerate() .into_iter()
.filter(|&(index, _)| ((index) % 4) != 0) .enumerate()
.map(|(_, val)| val) .filter(|&(index, _)| ((index) % 4) != 0)
.collect::<Vec<_>>() .map(|(_, val)| val)
.collect::<Vec<_>>()
} }

View File

@ -17,34 +17,37 @@
//! TODO error handling... just search for unwrap. //! TODO error handling... just search for unwrap.
#![allow(improper_ctypes)] #![allow(improper_ctypes)]
use std::collections::HashMap; use std::collections::HashMap;
use std::ptr;
use std::path::PathBuf; use std::path::PathBuf;
use std::ptr;
use ::{Slant, Weight, Style}; use {Slant, Style, Weight};
use core_foundation::string::{CFString}; use core_foundation::array::{CFArray, CFIndex};
use core_foundation::array::{CFIndex, CFArray}; use core_foundation::string::CFString;
use core_graphics::base::kCGImageAlphaPremultipliedFirst; use core_graphics::base::kCGImageAlphaPremultipliedFirst;
use core_graphics::color_space::CGColorSpace; use core_graphics::color_space::CGColorSpace;
use core_graphics::context::{CGContext}; use core_graphics::context::CGContext;
use core_graphics::font::{CGFont, CGGlyph}; use core_graphics::font::{CGFont, CGGlyph};
use core_graphics::geometry::{CGPoint, CGRect, CGSize}; use core_graphics::geometry::{CGPoint, CGRect, CGSize};
use core_text::font::{CTFont, new_from_descriptor as ct_new_from_descriptor, cascade_list_for_languages as ct_cascade_list_for_languages}; use core_text::font::{
cascade_list_for_languages as ct_cascade_list_for_languages,
new_from_descriptor as ct_new_from_descriptor, CTFont,
};
use core_text::font_collection::create_for_family; use core_text::font_collection::create_for_family;
use core_text::font_collection::get_family_names as ct_get_family_names; use core_text::font_collection::get_family_names as ct_get_family_names;
use core_text::font_descriptor::kCTFontDefaultOrientation; use core_text::font_descriptor::kCTFontDefaultOrientation;
use core_text::font_descriptor::kCTFontHorizontalOrientation; use core_text::font_descriptor::kCTFontHorizontalOrientation;
use core_text::font_descriptor::kCTFontVerticalOrientation; use core_text::font_descriptor::kCTFontVerticalOrientation;
use core_text::font_descriptor::{CTFontDescriptor, CTFontOrientation};
use core_text::font_descriptor::SymbolicTraitAccessors; use core_text::font_descriptor::SymbolicTraitAccessors;
use core_text::font_descriptor::{CTFontDescriptor, CTFontOrientation};
use euclid::{Point2D, Rect, Size2D}; use euclid::{Point2D, Rect, Size2D};
use super::{FontDesc, RasterizedGlyph, Metrics, FontKey, GlyphKey}; use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph};
pub mod byte_order; pub mod byte_order;
use self::byte_order::kCGBitmapByteOrder32Host;
use self::byte_order::extract_rgb; use self::byte_order::extract_rgb;
use self::byte_order::kCGBitmapByteOrder32Host;
use super::Size; use super::Size;
@ -59,11 +62,11 @@ pub struct Descriptor {
display_name: String, display_name: String,
font_path: PathBuf, font_path: PathBuf,
ct_descriptor: CTFontDescriptor ct_descriptor: CTFontDescriptor,
} }
impl Descriptor { impl Descriptor {
fn new(desc:CTFontDescriptor) -> Descriptor { fn new(desc: CTFontDescriptor) -> Descriptor {
Descriptor { Descriptor {
family_name: desc.family_name(), family_name: desc.family_name(),
font_name: desc.font_name(), font_name: desc.font_name(),
@ -111,16 +114,14 @@ impl ::std::error::Error for Error {
impl ::std::fmt::Display for Error { impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match *self { match *self {
Error::MissingGlyph(ref c) => { Error::MissingGlyph(ref c) => write!(f, "Glyph not found for char {:?}", c),
write!(f, "Glyph not found for char {:?}", c) Error::MissingFont(ref desc) => write!(
}, f,
Error::MissingFont(ref desc) => { "Couldn't find a font with {}\n\tPlease check the font config in your \
write!(f, "Couldn't find a font with {}\ alacritty.yml.",
\n\tPlease check the font config in your alacritty.yml.", desc) desc
}, ),
Error::FontNotLoaded => { Error::FontNotLoaded => f.write_str("Tried to use a font that hasn't been loaded"),
f.write_str("Tried to use a font that hasn't been loaded")
}
} }
} }
} }
@ -139,50 +140,41 @@ impl ::Rasterize for Rasterizer {
/// Get metrics for font specified by FontKey /// Get metrics for font specified by FontKey
fn metrics(&self, key: FontKey, _size: Size) -> Result<Metrics, Error> { fn metrics(&self, key: FontKey, _size: Size) -> Result<Metrics, Error> {
let font = self.fonts let font = self.fonts.get(&key).ok_or(Error::FontNotLoaded)?;
.get(&key)
.ok_or(Error::FontNotLoaded)?;
Ok(font.metrics()) Ok(font.metrics())
} }
fn load_font(&mut self, desc: &FontDesc, size: Size) -> Result<FontKey, Error> { fn load_font(&mut self, desc: &FontDesc, size: Size) -> Result<FontKey, Error> {
let scaled_size = Size::new(size.as_f32_pts() * self.device_pixel_ratio); let scaled_size = Size::new(size.as_f32_pts() * self.device_pixel_ratio);
self.keys self.keys.get(&(desc.to_owned(), scaled_size)).map(|k| Ok(*k)).unwrap_or_else(|| {
.get(&(desc.to_owned(), scaled_size)) let font = self.get_font(desc, size)?;
.map(|k| Ok(*k)) let key = FontKey::next();
.unwrap_or_else(|| {
let font = self.get_font(desc, size)?;
let key = FontKey::next();
self.fonts.insert(key, font); self.fonts.insert(key, font);
self.keys.insert((desc.clone(), scaled_size), key); self.keys.insert((desc.clone(), scaled_size), key);
Ok(key) Ok(key)
}) })
} }
/// Get rasterized glyph for given glyph key /// Get rasterized glyph for given glyph key
fn get_glyph(&mut self, glyph: GlyphKey) -> Result<RasterizedGlyph, Error> { fn get_glyph(&mut self, glyph: GlyphKey) -> Result<RasterizedGlyph, Error> {
// get loaded font // get loaded font
let font = self.fonts let font = self.fonts.get(&glyph.font_key).ok_or(Error::FontNotLoaded)?;
.get(&glyph.font_key)
.ok_or(Error::FontNotLoaded)?;
// first try the font itself as a direct hit // first try the font itself as a direct hit
self.maybe_get_glyph(glyph, font) self.maybe_get_glyph(glyph, font).unwrap_or_else(|| {
.unwrap_or_else(|| { // then try fallbacks
// then try fallbacks for fallback in &font.fallbacks {
for fallback in &font.fallbacks { if let Some(result) = self.maybe_get_glyph(glyph, &fallback) {
if let Some(result) = self.maybe_get_glyph(glyph, &fallback) { // found a fallback
// found a fallback return result;
return result;
}
} }
// no fallback, give up. }
Err(Error::MissingGlyph(glyph.c)) // no fallback, give up.
}) Err(Error::MissingGlyph(glyph.c))
})
} }
fn update_dpr(&mut self, device_pixel_ratio: f32) { fn update_dpr(&mut self, device_pixel_ratio: f32) {
@ -195,7 +187,7 @@ impl Rasterizer {
&mut self, &mut self,
desc: &FontDesc, desc: &FontDesc,
style: &str, style: &str,
size: Size size: Size,
) -> Result<Font, Error> { ) -> Result<Font, Error> {
let descriptors = descriptors_for_family(&desc.name[..]); let descriptors = descriptors_for_family(&desc.name[..]);
for descriptor in descriptors { for descriptor in descriptors {
@ -215,11 +207,11 @@ impl Rasterizer {
desc: &FontDesc, desc: &FontDesc,
slant: Slant, slant: Slant,
weight: Weight, weight: Weight,
size: Size size: Size,
) -> Result<Font, Error> { ) -> Result<Font, Error> {
let bold = match weight { let bold = match weight {
Weight::Bold => true, Weight::Bold => true,
_ => false _ => false,
}; };
let italic = match slant { let italic = match slant {
Slant::Normal => false, Slant::Normal => false,
@ -262,7 +254,6 @@ impl Rasterizer {
_ => Some(Err(e)), _ => Some(Err(e)),
}) })
} }
} }
/// Specifies the intended rendering orientation of the font for obtaining glyph metrics /// Specifies the intended rendering orientation of the font for obtaining glyph metrics
@ -302,18 +293,12 @@ pub fn get_family_names() -> Vec<String> {
owned_names owned_names
} }
/// Return fallback descriptors for font/language list /// Return fallback descriptors for font/language list
fn cascade_list_for_languages( fn cascade_list_for_languages(ct_font: &CTFont, languages: &[String]) -> Vec<Descriptor> {
ct_font: &CTFont,
languages: &[String]
) -> Vec<Descriptor> {
// convert language type &Vec<String> -> CFArray // convert language type &Vec<String> -> CFArray
let langarr:CFArray<CFString> = { let langarr: CFArray<CFString> = {
let tmp:Vec<CFString> = languages.iter() let tmp: Vec<CFString> =
.map(|language| CFString::new(&language)) languages.iter().map(|language| CFString::new(&language)).collect();
.collect();
CFArray::from_CFTypes(&tmp) CFArray::from_CFTypes(&tmp)
}; };
@ -321,12 +306,9 @@ fn cascade_list_for_languages(
let list = ct_cascade_list_for_languages(ct_font, &langarr); let list = ct_cascade_list_for_languages(ct_font, &langarr);
// convert CFArray to Vec<Descriptor> // convert CFArray to Vec<Descriptor>
list.into_iter() list.into_iter().map(|fontdesc| Descriptor::new(fontdesc.clone())).collect()
.map(|fontdesc| Descriptor::new(fontdesc.clone()))
.collect()
} }
/// Get descriptors for family name /// Get descriptors for family name
pub fn descriptors_for_family(family: &str) -> Vec<Descriptor> { pub fn descriptors_for_family(family: &str) -> Vec<Descriptor> {
let mut out = Vec::new(); let mut out = Vec::new();
@ -350,7 +332,7 @@ pub fn descriptors_for_family(family: &str) -> Vec<Descriptor> {
impl Descriptor { impl Descriptor {
/// Create a Font from this descriptor /// Create a Font from this descriptor
pub fn to_font(&self, size: f64, load_fallbacks:bool) -> Font { pub fn to_font(&self, size: f64, load_fallbacks: bool) -> Font {
let ct_font = ct_new_from_descriptor(&self.ct_descriptor, size); let ct_font = ct_new_from_descriptor(&self.ct_descriptor, size);
let cg_font = ct_font.copy_to_CGFont(); let cg_font = ct_font.copy_to_CGFont();
@ -385,7 +367,7 @@ impl Descriptor {
fallbacks.insert(0, Font { fallbacks.insert(0, Font {
cg_font: menlo.copy_to_CGFont(), cg_font: menlo.copy_to_CGFont(),
ct_font: menlo, ct_font: menlo,
fallbacks: Vec::new() fallbacks: Vec::new(),
}); });
fallbacks fallbacks
@ -395,25 +377,16 @@ impl Descriptor {
Vec::new() Vec::new()
}; };
Font { Font { ct_font, cg_font, fallbacks }
ct_font,
cg_font,
fallbacks,
}
} }
} }
impl Font { impl Font {
/// The the bounding rect of a glyph /// The the bounding rect of a glyph
pub fn bounding_rect_for_glyph( pub fn bounding_rect_for_glyph(&self, orientation: FontOrientation, index: u32) -> Rect<f64> {
&self, let cg_rect = self
orientation: FontOrientation, .ct_font
index: u32 .get_bounding_rects_for_glyphs(orientation as CTFontOrientation, &[index as CGGlyph]);
) -> Rect<f64> {
let cg_rect = self.ct_font.get_bounding_rects_for_glyphs(
orientation as CTFontOrientation,
&[index as CGGlyph]
);
Rect::new( Rect::new(
Point2D::new(cg_rect.origin.x, cg_rect.origin.y), Point2D::new(cg_rect.origin.x, cg_rect.origin.y),
@ -465,12 +438,17 @@ impl Font {
FontOrientation::Default as _, FontOrientation::Default as _,
&indices[0], &indices[0],
ptr::null_mut(), ptr::null_mut(),
1 1,
) )
} }
} }
pub fn get_glyph(&self, character: char, _size: f64, use_thin_strokes: bool) -> Result<RasterizedGlyph, Error> { pub fn get_glyph(
&self,
character: char,
_size: f64,
use_thin_strokes: bool,
) -> Result<RasterizedGlyph, Error> {
// Render custom symbols for underline and beam cursor // Render custom symbols for underline and beam cursor
match character { match character {
super::UNDERLINE_CURSOR_CHAR => { super::UNDERLINE_CURSOR_CHAR => {
@ -480,7 +458,7 @@ impl Font {
let width = self.glyph_advance('0') as i32; let width = self.glyph_advance('0') as i32;
// Return the new custom glyph // Return the new custom glyph
return super::get_underline_cursor_glyph(descent, width); return super::get_underline_cursor_glyph(descent, width);
} },
super::BEAM_CURSOR_CHAR | super::BOX_CURSOR_CHAR => { super::BEAM_CURSOR_CHAR | super::BOX_CURSOR_CHAR => {
// Get the top of the bounding box // Get the top of the bounding box
let metrics = self.metrics(); let metrics = self.metrics();
@ -496,12 +474,12 @@ impl Font {
} else { } else {
return super::get_box_cursor_glyph(ascent as i32, height as i32, width); return super::get_box_cursor_glyph(ascent as i32, height as i32, width);
} }
} },
_ => () _ => (),
} }
let glyph_index = self.glyph_index(character) let glyph_index =
.ok_or_else(|| Error::MissingGlyph(character))?; self.glyph_index(character).ok_or_else(|| Error::MissingGlyph(character))?;
let bounds = self.bounding_rect_for_glyph(Default::default(), glyph_index); let bounds = self.bounding_rect_for_glyph(Default::default(), glyph_index);
@ -519,7 +497,7 @@ impl Font {
height: 0, height: 0,
top: 0, top: 0,
left: 0, left: 0,
buf: Vec::new() buf: Vec::new(),
}); });
} }
@ -530,17 +508,14 @@ impl Font {
8, // bits per component 8, // bits per component
rasterized_width as usize * 4, rasterized_width as usize * 4,
&CGColorSpace::create_device_rgb(), &CGColorSpace::create_device_rgb(),
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
); );
// Give the context an opaque, black background // Give the context an opaque, black background
cg_context.set_rgb_fill_color(0.0, 0.0, 0.0, 1.0); cg_context.set_rgb_fill_color(0.0, 0.0, 0.0, 1.0);
let context_rect = CGRect::new( let context_rect = CGRect::new(
&CGPoint::new(0.0, 0.0), &CGPoint::new(0.0, 0.0),
&CGSize::new( &CGSize::new(f64::from(rasterized_width), f64::from(rasterized_height)),
f64::from(rasterized_width),
f64::from(rasterized_height)
)
); );
cg_context.fill_rect(context_rect); cg_context.fill_rect(context_rect);
@ -560,14 +535,14 @@ impl Font {
// Set fill color to white for drawing the glyph // Set fill color to white for drawing the glyph
cg_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0); cg_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0);
let rasterization_origin = CGPoint { let rasterization_origin =
x: f64::from(-rasterized_left), CGPoint { x: f64::from(-rasterized_left), y: f64::from(rasterized_descent) };
y: f64::from(rasterized_descent),
};
self.ct_font.draw_glyphs(&[glyph_index as CGGlyph], self.ct_font.draw_glyphs(
&[rasterization_origin], &[glyph_index as CGGlyph],
cg_context.clone()); &[rasterization_origin],
cg_context.clone(),
);
let rasterized_pixels = cg_context.data().to_vec(); let rasterized_pixels = cg_context.data().to_vec();
@ -586,22 +561,22 @@ impl Font {
fn glyph_index(&self, character: char) -> Option<u32> { fn glyph_index(&self, character: char) -> Option<u32> {
// encode this char as utf-16 // encode this char as utf-16
let mut buf = [0; 2]; let mut buf = [0; 2];
let encoded:&[u16] = character.encode_utf16(&mut buf); let encoded: &[u16] = character.encode_utf16(&mut buf);
// and use the utf-16 buffer to get the index // and use the utf-16 buffer to get the index
self.glyph_index_utf16(encoded) self.glyph_index_utf16(encoded)
} }
fn glyph_index_utf16(&self, encoded: &[u16]) -> Option<u32> {
fn glyph_index_utf16(&self, encoded: &[u16]) -> Option<u32> {
// output buffer for the glyph. for non-BMP glyphs, like // output buffer for the glyph. for non-BMP glyphs, like
// emojis, this will be filled with two chars the second // emojis, this will be filled with two chars the second
// always being a 0. // always being a 0.
let mut glyphs:[CGGlyph; 2] = [0; 2]; let mut glyphs: [CGGlyph; 2] = [0; 2];
let res = unsafe { let res = unsafe {
self.ct_font.get_glyphs_for_characters( self.ct_font.get_glyphs_for_characters(
encoded.as_ptr(), encoded.as_ptr(),
glyphs.as_mut_ptr(), glyphs.as_mut_ptr(),
encoded.len() as CFIndex encoded.len() as CFIndex,
) )
}; };
@ -629,9 +604,7 @@ mod tests {
println!("{:?}", list); println!("{:?}", list);
// Check to_font // Check to_font
let fonts = list.iter() let fonts = list.iter().map(|desc| desc.to_font(72., false)).collect::<Vec<_>>();
.map(|desc| desc.to_font(72., false))
.collect::<Vec<_>>();
for font in fonts { for font in fonts {
// Get a glyph // Get a glyph
@ -649,7 +622,7 @@ mod tests {
101...150 => '~', 101...150 => '~',
151...200 => '*', 151...200 => '*',
201...255 => '#', 201...255 => '#',
_ => unreachable!() _ => unreachable!(),
}; };
print!("{}", c); print!("{}", c);
} }

View File

@ -11,10 +11,10 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use foreign_types::{ForeignTypeRef}; use foreign_types::ForeignTypeRef;
use super::ffi::{FcCharSet, FcCharSetDestroy, FcCharSetAddChar}; use super::ffi::FcCharSetCreate;
use super::ffi::{FcCharSetCreate}; use super::ffi::{FcCharSet, FcCharSetAddChar, FcCharSetDestroy};
foreign_type! { foreign_type! {
type CType = FcCharSet; type CType = FcCharSet;
@ -37,12 +37,6 @@ impl Default for CharSet {
impl CharSetRef { impl CharSetRef {
pub fn add(&mut self, glyph: char) -> bool { pub fn add(&mut self, glyph: char) -> bool {
unsafe { unsafe { FcCharSetAddChar(self.as_ptr(), glyph as _) == 1 }
FcCharSetAddChar(
self.as_ptr(),
glyph as _
) == 1
}
} }
} }

View File

@ -11,10 +11,10 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use foreign_types::{ForeignTypeRef}; use foreign_types::ForeignTypeRef;
use super::{SetName, FontSetRef}; use super::ffi::{FcConfig, FcConfigDestroy, FcConfigGetCurrent, FcConfigGetFonts};
use super::ffi::{FcConfigGetCurrent, FcConfigGetFonts, FcConfig, FcConfigDestroy}; use super::{FontSetRef, SetName};
foreign_type! { foreign_type! {
type CType = FcConfig; type CType = FcConfig;
@ -25,13 +25,10 @@ foreign_type! {
pub struct ConfigRef; pub struct ConfigRef;
} }
impl Config { impl Config {
/// Get the current configuration /// Get the current configuration
pub fn get_current() -> &'static ConfigRef { pub fn get_current() -> &'static ConfigRef {
unsafe { unsafe { ConfigRef::from_ptr(FcConfigGetCurrent()) }
ConfigRef::from_ptr(FcConfigGetCurrent())
}
} }
} }

View File

@ -15,9 +15,9 @@ use std::ops::Deref;
use foreign_types::{ForeignType, ForeignTypeRef}; use foreign_types::{ForeignType, ForeignTypeRef};
use super::{ConfigRef, PatternRef, ObjectSetRef}; use super::{ConfigRef, ObjectSetRef, PatternRef};
use super::ffi::{FcFontSetList, FcFontSetDestroy, FcFontSet}; use super::ffi::{FcFontSet, FcFontSetDestroy, FcFontSetList};
foreign_type! { foreign_type! {
type CType = FcFontSet; type CType = FcFontSet;
@ -33,13 +33,13 @@ impl FontSet {
config: &ConfigRef, config: &ConfigRef,
source: &mut FontSetRef, source: &mut FontSetRef,
pattern: &PatternRef, pattern: &PatternRef,
objects: &ObjectSetRef objects: &ObjectSetRef,
) -> FontSet { ) -> FontSet {
let raw = unsafe { let raw = unsafe {
FcFontSetList( FcFontSetList(
config.as_ptr(), config.as_ptr(),
&mut source.as_ptr(), &mut source.as_ptr(),
1 /* nsets */, 1, // nsets
pattern.as_ptr(), pattern.as_ptr(),
objects.as_ptr(), objects.as_ptr(),
) )
@ -56,38 +56,28 @@ pub struct Iter<'a> {
} }
impl<'a> IntoIterator for &'a FontSet { impl<'a> IntoIterator for &'a FontSet {
type Item = &'a PatternRef;
type IntoIter = Iter<'a>; type IntoIter = Iter<'a>;
type Item = &'a PatternRef;
fn into_iter(self) -> Iter<'a> { fn into_iter(self) -> Iter<'a> {
let num_fonts = unsafe { let num_fonts = unsafe { (*self.as_ptr()).nfont as isize };
(*self.as_ptr()).nfont as isize
};
trace!("Number of fonts is {}", num_fonts); trace!("Number of fonts is {}", num_fonts);
Iter { Iter { font_set: self.deref(), num_fonts: num_fonts as _, current: 0 }
font_set: self.deref(),
num_fonts: num_fonts as _,
current: 0,
}
} }
} }
impl<'a> IntoIterator for &'a FontSetRef { impl<'a> IntoIterator for &'a FontSetRef {
type Item = &'a PatternRef;
type IntoIter = Iter<'a>; type IntoIter = Iter<'a>;
type Item = &'a PatternRef;
fn into_iter(self) -> Iter<'a> { fn into_iter(self) -> Iter<'a> {
let num_fonts = unsafe { let num_fonts = unsafe { (*self.as_ptr()).nfont as isize };
(*self.as_ptr()).nfont as isize
};
trace!("Number of fonts is {}", num_fonts); trace!("Number of fonts is {}", num_fonts);
Iter { Iter { font_set: self, num_fonts: num_fonts as _, current: 0 }
font_set: self,
num_fonts: num_fonts as _,
current: 0,
}
} }
} }

View File

@ -12,21 +12,21 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// //
use std::ptr;
use std::fmt; use std::fmt;
use std::ptr;
use foreign_types::{ForeignType, ForeignTypeRef}; use foreign_types::{ForeignType, ForeignTypeRef};
use fontconfig::fontconfig as ffi; use fontconfig::fontconfig as ffi;
use self::ffi::{FcSetSystem, FcSetApplication};
use self::ffi::FcResultNoMatch; use self::ffi::FcResultNoMatch;
use self::ffi::{FcFontMatch, FcFontList, FcFontSort}; use self::ffi::{FcFontList, FcFontMatch, FcFontSort};
use self::ffi::{FcMatchFont, FcMatchPattern, FcMatchScan}; use self::ffi::{FcMatchFont, FcMatchPattern, FcMatchScan};
use self::ffi::{FC_SLANT_OBLIQUE, FC_SLANT_ITALIC, FC_SLANT_ROMAN}; use self::ffi::{FcSetApplication, FcSetSystem};
use self::ffi::{FC_WEIGHT_THIN, FC_WEIGHT_EXTRALIGHT, FC_WEIGHT_LIGHT}; use self::ffi::{FC_SLANT_ITALIC, FC_SLANT_OBLIQUE, FC_SLANT_ROMAN};
use self::ffi::{FC_WEIGHT_BOOK, FC_WEIGHT_REGULAR, FC_WEIGHT_MEDIUM, FC_WEIGHT_SEMIBOLD}; use self::ffi::{FC_WEIGHT_BLACK, FC_WEIGHT_BOLD, FC_WEIGHT_EXTRABLACK, FC_WEIGHT_EXTRABOLD};
use self::ffi::{FC_WEIGHT_BOLD, FC_WEIGHT_EXTRABOLD, FC_WEIGHT_BLACK, FC_WEIGHT_EXTRABLACK}; use self::ffi::{FC_WEIGHT_BOOK, FC_WEIGHT_MEDIUM, FC_WEIGHT_REGULAR, FC_WEIGHT_SEMIBOLD};
use self::ffi::{FC_WEIGHT_EXTRALIGHT, FC_WEIGHT_LIGHT, FC_WEIGHT_THIN};
pub mod config; pub mod config;
pub use self::config::{Config, ConfigRef}; pub use self::config::{Config, ConfigRef};
@ -46,10 +46,7 @@ pub use self::pattern::{Pattern, PatternRef};
/// Find the font closest matching the provided pattern. /// Find the font closest matching the provided pattern.
/// ///
/// The returned pattern is the result of Pattern::render_prepare. /// The returned pattern is the result of Pattern::render_prepare.
pub fn font_match( pub fn font_match(config: &ConfigRef, pattern: &mut PatternRef) -> Option<Pattern> {
config: &ConfigRef,
pattern: &mut PatternRef,
) -> Option<Pattern> {
pattern.config_substitute(config, MatchKind::Pattern); pattern.config_substitute(config, MatchKind::Pattern);
pattern.default_substitute(); pattern.default_substitute();
@ -57,11 +54,7 @@ pub fn font_match(
// What is this result actually used for? Seems redundant with // What is this result actually used for? Seems redundant with
// return type. // return type.
let mut result = FcResultNoMatch; let mut result = FcResultNoMatch;
let ptr = FcFontMatch( let ptr = FcFontMatch(config.as_ptr(), pattern.as_ptr(), &mut result);
config.as_ptr(),
pattern.as_ptr(),
&mut result,
);
if ptr.is_null() { if ptr.is_null() {
None None
@ -72,10 +65,7 @@ pub fn font_match(
} }
/// list fonts by closeness to the pattern /// list fonts by closeness to the pattern
pub fn font_sort( pub fn font_sort(config: &ConfigRef, pattern: &mut PatternRef) -> Option<FontSet> {
config: &ConfigRef,
pattern: &mut PatternRef,
) -> Option<FontSet> {
pattern.config_substitute(config, MatchKind::Pattern); pattern.config_substitute(config, MatchKind::Pattern);
pattern.default_substitute(); pattern.default_substitute();
@ -112,11 +102,7 @@ pub fn font_list(
pattern.default_substitute(); pattern.default_substitute();
unsafe { unsafe {
let ptr = FcFontList( let ptr = FcFontList(config.as_ptr(), pattern.as_ptr(), objects.as_ptr());
config.as_ptr(),
pattern.as_ptr(),
objects.as_ptr(),
);
if ptr.is_null() { if ptr.is_null() {
None None
@ -174,7 +160,7 @@ pub enum Width {
Expanded, Expanded,
Extraexpanded, Extraexpanded,
Ultraexpanded, Ultraexpanded,
Other(i32) Other(i32),
} }
impl Width { impl Width {
@ -190,7 +176,7 @@ impl Width {
Expanded => 125, Expanded => 125,
Extraexpanded => 150, Extraexpanded => 150,
Ultraexpanded => 200, Ultraexpanded => 200,
Other(value) => value as isize Other(value) => value as isize,
} }
} }
} }
@ -207,7 +193,7 @@ impl From<isize> for Width {
125 => Width::Expanded, 125 => Width::Expanded,
150 => Width::Extraexpanded, 150 => Width::Extraexpanded,
200 => Width::Ultraexpanded, 200 => Width::Ultraexpanded,
_ => Width::Other(value as _) _ => Width::Other(value as _),
} }
} }
} }
@ -219,7 +205,7 @@ pub enum Rgba {
Bgr, Bgr,
Vrgb, Vrgb,
Vbgr, Vbgr,
None None,
} }
impl Rgba { impl Rgba {
@ -230,7 +216,7 @@ impl Rgba {
Rgba::Bgr => 2, Rgba::Bgr => 2,
Rgba::Vrgb => 3, Rgba::Vrgb => 3,
Rgba::Vbgr => 4, Rgba::Vbgr => 4,
Rgba::None => 5 Rgba::None => 5,
} }
} }
} }
@ -268,7 +254,7 @@ pub enum HintStyle {
None, None,
Slight, Slight,
Medium, Medium,
Full Full,
} }
impl fmt::Display for HintStyle { impl fmt::Display for HintStyle {
@ -287,7 +273,7 @@ pub enum LcdFilter {
None, None,
Default, Default,
Light, Light,
Legacy Legacy,
} }
impl fmt::Display for LcdFilter { impl fmt::Display for LcdFilter {
@ -334,8 +320,7 @@ mod tests {
pattern.set_slant(Slant::Italic); pattern.set_slant(Slant::Italic);
let config = Config::get_current(); let config = Config::get_current();
let fonts = super::font_sort(config, &mut pattern) let fonts = super::font_sort(config, &mut pattern).expect("sort font monospace");
.expect("sort font monospace");
for font in fonts.into_iter().take(10) { for font in fonts.into_iter().take(10) {
let font = font.render_prepare(&config, &pattern); let font = font.render_prepare(&config, &pattern);

View File

@ -13,8 +13,8 @@
// limitations under the License. // limitations under the License.
use libc::c_char; use libc::c_char;
use super::ffi::{FcObjectSet, FcObjectSetAdd, FcObjectSetCreate, FcObjectSetDestroy};
use foreign_types::ForeignTypeRef; use foreign_types::ForeignTypeRef;
use super::ffi::{FcObjectSetCreate, FcObjectSetAdd, FcObjectSet, FcObjectSetDestroy};
foreign_type! { foreign_type! {
type CType = FcObjectSet; type CType = FcObjectSet;
@ -31,9 +31,7 @@ impl ObjectSet {
impl Default for ObjectSet { impl Default for ObjectSet {
fn default() -> Self { fn default() -> Self {
ObjectSet(unsafe { ObjectSet(unsafe { FcObjectSetCreate() })
FcObjectSetCreate()
})
} }
} }

View File

@ -11,38 +11,34 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use std::ptr;
use std::fmt;
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
use std::path::PathBuf; use std::fmt;
use std::str;
use std::mem; use std::mem;
use std::path::PathBuf;
use std::ptr;
use std::str;
use libc::{c_char, c_int, c_double};
use foreign_types::{ForeignType, ForeignTypeRef}; use foreign_types::{ForeignType, ForeignTypeRef};
use libc::{c_char, c_double, c_int};
use super::ffi::FcResultMatch; use super::ffi::FcResultMatch;
use super::ffi::{FcPatternDestroy, FcPatternAddCharSet}; use super::ffi::{FcBool, FcFontRenderPrepare, FcPatternGetBool, FcPatternGetDouble};
use super::ffi::{FcPatternGetString, FcPatternCreate, FcPatternAddString, FcPatternAddDouble}; use super::ffi::{FcChar8, FcConfigSubstitute, FcDefaultSubstitute, FcPattern};
use super::ffi::{FcPatternGetInteger, FcPatternAddInteger, FcPatternPrint}; use super::ffi::{FcPatternAddCharSet, FcPatternDestroy};
use super::ffi::{FcChar8, FcPattern, FcDefaultSubstitute, FcConfigSubstitute}; use super::ffi::{FcPatternAddDouble, FcPatternAddString, FcPatternCreate, FcPatternGetString};
use super::ffi::{FcFontRenderPrepare, FcPatternGetBool, FcBool, FcPatternGetDouble}; use super::ffi::{FcPatternAddInteger, FcPatternGetInteger, FcPatternPrint};
use super::{MatchKind, ConfigRef, CharSetRef, Weight, Slant, Width, Rgba, HintStyle, LcdFilter}; use super::{CharSetRef, ConfigRef, HintStyle, LcdFilter, MatchKind, Rgba, Slant, Weight, Width};
pub struct StringPropertyIter<'a> { pub struct StringPropertyIter<'a> {
pattern: &'a PatternRef, pattern: &'a PatternRef,
object: &'a [u8], object: &'a [u8],
index: usize index: usize,
} }
impl<'a> StringPropertyIter<'a> { impl<'a> StringPropertyIter<'a> {
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> StringPropertyIter<'b> { fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> StringPropertyIter<'b> {
StringPropertyIter { StringPropertyIter { pattern, object, index: 0 }
pattern,
object,
index: 0
}
} }
fn get_value(&self, index: usize) -> Option<&'a str> { fn get_value(&self, index: usize) -> Option<&'a str> {
@ -53,7 +49,7 @@ impl<'a> StringPropertyIter<'a> {
self.pattern.as_ptr(), self.pattern.as_ptr(),
self.object.as_ptr() as *mut c_char, self.object.as_ptr() as *mut c_char,
index as c_int, index as c_int,
&mut value &mut value,
) )
}; };
@ -75,17 +71,12 @@ impl<'a> StringPropertyIter<'a> {
pub struct BooleanPropertyIter<'a> { pub struct BooleanPropertyIter<'a> {
pattern: &'a PatternRef, pattern: &'a PatternRef,
object: &'a [u8], object: &'a [u8],
index: usize index: usize,
} }
impl<'a> BooleanPropertyIter<'a> { impl<'a> BooleanPropertyIter<'a> {
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> BooleanPropertyIter<'b> { fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> BooleanPropertyIter<'b> {
BooleanPropertyIter { BooleanPropertyIter { pattern, object, index: 0 }
pattern,
object,
index: 0
}
} }
fn get_value(&self, index: usize) -> Option<bool> { fn get_value(&self, index: usize) -> Option<bool> {
@ -96,7 +87,7 @@ impl<'a> BooleanPropertyIter<'a> {
self.pattern.as_ptr(), self.pattern.as_ptr(),
self.object.as_ptr() as *mut c_char, self.object.as_ptr() as *mut c_char,
index as c_int, index as c_int,
&mut value &mut value,
) )
}; };
@ -112,16 +103,12 @@ impl<'a> BooleanPropertyIter<'a> {
pub struct IntPropertyIter<'a> { pub struct IntPropertyIter<'a> {
pattern: &'a PatternRef, pattern: &'a PatternRef,
object: &'a [u8], object: &'a [u8],
index: usize index: usize,
} }
impl<'a> IntPropertyIter<'a> { impl<'a> IntPropertyIter<'a> {
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> IntPropertyIter<'b> { fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> IntPropertyIter<'b> {
IntPropertyIter { IntPropertyIter { pattern, object, index: 0 }
pattern,
object,
index: 0
}
} }
fn get_value(&self, index: usize) -> Option<isize> { fn get_value(&self, index: usize) -> Option<isize> {
@ -132,7 +119,7 @@ impl<'a> IntPropertyIter<'a> {
self.pattern.as_ptr(), self.pattern.as_ptr(),
self.object.as_ptr() as *mut c_char, self.object.as_ptr() as *mut c_char,
index as c_int, index as c_int,
&mut value &mut value,
) )
}; };
@ -150,9 +137,7 @@ pub struct RgbaPropertyIter<'a> {
impl<'a> RgbaPropertyIter<'a> { impl<'a> RgbaPropertyIter<'a> {
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> RgbaPropertyIter<'b> { fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> RgbaPropertyIter<'b> {
RgbaPropertyIter { RgbaPropertyIter { inner: IntPropertyIter::new(pattern, object) }
inner: IntPropertyIter::new(pattern, object)
}
} }
#[inline] #[inline]
@ -161,8 +146,7 @@ impl<'a> RgbaPropertyIter<'a> {
} }
fn get_value(&self, index: usize) -> Option<Rgba> { fn get_value(&self, index: usize) -> Option<Rgba> {
self.inner.get_value(index) self.inner.get_value(index).map(Rgba::from)
.map(Rgba::from)
} }
} }
@ -172,9 +156,7 @@ pub struct HintStylePropertyIter<'a> {
impl<'a> HintStylePropertyIter<'a> { impl<'a> HintStylePropertyIter<'a> {
fn new(pattern: &PatternRef) -> HintStylePropertyIter { fn new(pattern: &PatternRef) -> HintStylePropertyIter {
HintStylePropertyIter { HintStylePropertyIter { inner: IntPropertyIter::new(pattern, b"hintstyle\0") }
inner: IntPropertyIter::new(pattern, b"hintstyle\0")
}
} }
#[inline] #[inline]
@ -183,16 +165,15 @@ impl<'a> HintStylePropertyIter<'a> {
} }
fn get_value(&self, index: usize) -> Option<HintStyle> { fn get_value(&self, index: usize) -> Option<HintStyle> {
self.inner.get_value(index) self.inner.get_value(index).and_then(|hint_style| {
.and_then(|hint_style| { Some(match hint_style {
Some(match hint_style { 0 => HintStyle::None,
0 => HintStyle::None, 1 => HintStyle::Slight,
1 => HintStyle::Slight, 2 => HintStyle::Medium,
2 => HintStyle::Medium, 3 => HintStyle::Full,
3 => HintStyle::Full, _ => return None,
_ => return None
})
}) })
})
} }
} }
@ -202,9 +183,7 @@ pub struct LcdFilterPropertyIter<'a> {
impl<'a> LcdFilterPropertyIter<'a> { impl<'a> LcdFilterPropertyIter<'a> {
fn new(pattern: &PatternRef) -> LcdFilterPropertyIter { fn new(pattern: &PatternRef) -> LcdFilterPropertyIter {
LcdFilterPropertyIter { LcdFilterPropertyIter { inner: IntPropertyIter::new(pattern, b"lcdfilter\0") }
inner: IntPropertyIter::new(pattern, b"lcdfilter\0")
}
} }
#[inline] #[inline]
@ -213,16 +192,15 @@ impl<'a> LcdFilterPropertyIter<'a> {
} }
fn get_value(&self, index: usize) -> Option<LcdFilter> { fn get_value(&self, index: usize) -> Option<LcdFilter> {
self.inner.get_value(index) self.inner.get_value(index).and_then(|hint_style| {
.and_then(|hint_style| { Some(match hint_style {
Some(match hint_style { 0 => LcdFilter::None,
0 => LcdFilter::None, 1 => LcdFilter::Default,
1 => LcdFilter::Default, 2 => LcdFilter::Light,
2 => LcdFilter::Light, 3 => LcdFilter::Legacy,
3 => LcdFilter::Legacy, _ => return None,
_ => return None
})
}) })
})
} }
} }
@ -230,16 +208,12 @@ impl<'a> LcdFilterPropertyIter<'a> {
pub struct DoublePropertyIter<'a> { pub struct DoublePropertyIter<'a> {
pattern: &'a PatternRef, pattern: &'a PatternRef,
object: &'a [u8], object: &'a [u8],
index: usize index: usize,
} }
impl<'a> DoublePropertyIter<'a> { impl<'a> DoublePropertyIter<'a> {
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> DoublePropertyIter<'b> { fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> DoublePropertyIter<'b> {
DoublePropertyIter { DoublePropertyIter { pattern, object, index: 0 }
pattern,
object,
index: 0
}
} }
fn get_value(&self, index: usize) -> Option<f64> { fn get_value(&self, index: usize) -> Option<f64> {
@ -250,7 +224,7 @@ impl<'a> DoublePropertyIter<'a> {
self.pattern.as_ptr(), self.pattern.as_ptr(),
self.object.as_ptr() as *mut c_char, self.object.as_ptr() as *mut c_char,
index as c_int, index as c_int,
&mut value &mut value,
) )
}; };
@ -277,13 +251,13 @@ macro_rules! impl_property_iter_debug {
write!(f, "{}", val)?; write!(f, "{}", val)?;
} }
}, },
_ => break _ => break,
} }
} }
write!(f, "]") write!(f, "]")
} }
} }
} };
} }
/// Implement Iterator and Debug for a property iterator /// Implement Iterator and Debug for a property iterator
@ -427,76 +401,6 @@ macro_rules! double_getter {
} }
impl PatternRef { impl PatternRef {
// Prints the pattern to stdout
//
// FontConfig doesn't expose a way to iterate over all members of a pattern;
// instead, we just defer to FcPatternPrint. Otherwise, this could have been
// a `fmt::Debug` impl.
pub fn print(&self) {
unsafe {
FcPatternPrint(self.as_ptr())
}
}
/// Add a string value to the pattern
///
/// If the returned value is `true`, the value is added at the end of
/// any existing list, otherwise it is inserted at the beginning.
///
/// # Unsafety
///
/// `object` is not checked to be a valid null-terminated string
unsafe fn add_string(&mut self, object: &[u8], value: &str) -> bool {
let value = CString::new(&value[..]).unwrap();
let value = value.as_ptr();
FcPatternAddString(
self.as_ptr(),
object.as_ptr() as *mut c_char,
value as *mut FcChar8
) == 1
}
unsafe fn add_integer(&self, object: &[u8], int: isize) -> bool {
FcPatternAddInteger(
self.as_ptr(),
object.as_ptr() as *mut c_char,
int as c_int
) == 1
}
unsafe fn add_double(&self, object: &[u8], value: f64) -> bool {
FcPatternAddDouble(
self.as_ptr(),
object.as_ptr() as *mut c_char,
value as c_double
) == 1
}
unsafe fn get_string<'a>(&'a self, object: &'a [u8]) -> StringPropertyIter<'a> {
StringPropertyIter::new(self, object)
}
unsafe fn get_integer<'a>(&'a self, object: &'a [u8]) -> IntPropertyIter<'a> {
IntPropertyIter::new(self, object)
}
unsafe fn get_double<'a>(&'a self, object: &'a [u8]) -> DoublePropertyIter<'a> {
DoublePropertyIter::new(self, object)
}
unsafe fn get_boolean<'a>(&'a self, object: &'a [u8]) -> BooleanPropertyIter<'a> {
BooleanPropertyIter::new(self, object)
}
pub fn hintstyle(&self) -> HintStylePropertyIter {
HintStylePropertyIter::new(self)
}
pub fn lcdfilter(&self) -> LcdFilterPropertyIter {
LcdFilterPropertyIter::new(self)
}
boolean_getter! { boolean_getter! {
antialias() => b"antialias\0", antialias() => b"antialias\0",
hinting() => b"hinting\0", hinting() => b"hinting\0",
@ -535,36 +439,85 @@ impl PatternRef {
[postscriptname, add_postscriptname] => b"postscriptname\0" [postscriptname, add_postscriptname] => b"postscriptname\0"
} }
pattern_get_integer! {
index() => b"index\0"
}
// Prints the pattern to stdout
//
// FontConfig doesn't expose a way to iterate over all members of a pattern;
// instead, we just defer to FcPatternPrint. Otherwise, this could have been
// a `fmt::Debug` impl.
pub fn print(&self) {
unsafe { FcPatternPrint(self.as_ptr()) }
}
/// Add a string value to the pattern
///
/// If the returned value is `true`, the value is added at the end of
/// any existing list, otherwise it is inserted at the beginning.
///
/// # Unsafety
///
/// `object` is not checked to be a valid null-terminated string
unsafe fn add_string(&mut self, object: &[u8], value: &str) -> bool {
let value = CString::new(&value[..]).unwrap();
let value = value.as_ptr();
FcPatternAddString(self.as_ptr(), object.as_ptr() as *mut c_char, value as *mut FcChar8)
== 1
}
unsafe fn add_integer(&self, object: &[u8], int: isize) -> bool {
FcPatternAddInteger(self.as_ptr(), object.as_ptr() as *mut c_char, int as c_int) == 1
}
unsafe fn add_double(&self, object: &[u8], value: f64) -> bool {
FcPatternAddDouble(self.as_ptr(), object.as_ptr() as *mut c_char, value as c_double) == 1
}
unsafe fn get_string<'a>(&'a self, object: &'a [u8]) -> StringPropertyIter<'a> {
StringPropertyIter::new(self, object)
}
unsafe fn get_integer<'a>(&'a self, object: &'a [u8]) -> IntPropertyIter<'a> {
IntPropertyIter::new(self, object)
}
unsafe fn get_double<'a>(&'a self, object: &'a [u8]) -> DoublePropertyIter<'a> {
DoublePropertyIter::new(self, object)
}
unsafe fn get_boolean<'a>(&'a self, object: &'a [u8]) -> BooleanPropertyIter<'a> {
BooleanPropertyIter::new(self, object)
}
pub fn hintstyle(&self) -> HintStylePropertyIter {
HintStylePropertyIter::new(self)
}
pub fn lcdfilter(&self) -> LcdFilterPropertyIter {
LcdFilterPropertyIter::new(self)
}
pub fn set_slant(&mut self, slant: Slant) -> bool { pub fn set_slant(&mut self, slant: Slant) -> bool {
unsafe { unsafe { self.add_integer(b"slant\0", slant as isize) }
self.add_integer(b"slant\0", slant as isize)
}
} }
pub fn add_pixelsize(&mut self, size: f64) -> bool { pub fn add_pixelsize(&mut self, size: f64) -> bool {
unsafe { unsafe { self.add_double(b"pixelsize\0", size) }
self.add_double(b"pixelsize\0", size)
}
} }
pub fn set_weight(&mut self, weight: Weight) -> bool { pub fn set_weight(&mut self, weight: Weight) -> bool {
unsafe { unsafe { self.add_integer(b"weight\0", weight as isize) }
self.add_integer(b"weight\0", weight as isize)
}
} }
pub fn set_width(&mut self, width: Width) -> bool { pub fn set_width(&mut self, width: Width) -> bool {
unsafe { unsafe { self.add_integer(b"width\0", width.to_isize()) }
self.add_integer(b"width\0", width.to_isize())
}
} }
pub fn get_width(&self) -> Option<Width> { pub fn get_width(&self) -> Option<Width> {
unsafe { unsafe { self.get_integer(b"width\0").nth(0).map(Width::from) }
self.get_integer(b"width\0")
.nth(0)
.map(Width::from)
}
} }
pub fn rgba(&self) -> RgbaPropertyIter { pub fn rgba(&self) -> RgbaPropertyIter {
@ -572,9 +525,7 @@ impl PatternRef {
} }
pub fn set_rgba(&self, rgba: &Rgba) -> bool { pub fn set_rgba(&self, rgba: &Rgba) -> bool {
unsafe { unsafe { self.add_integer(b"rgba\0", rgba.to_isize()) }
self.add_integer(b"rgba\0", rgba.to_isize())
}
} }
pub fn render_prepare(&self, config: &ConfigRef, request: &PatternRef) -> Pattern { pub fn render_prepare(&self, config: &ConfigRef, request: &PatternRef) -> Pattern {
@ -595,19 +546,13 @@ impl PatternRef {
FcPatternAddCharSet( FcPatternAddCharSet(
self.as_ptr(), self.as_ptr(),
b"charset\0".as_ptr() as *mut c_char, b"charset\0".as_ptr() as *mut c_char,
charset.as_ptr() charset.as_ptr(),
) == 1 ) == 1
} }
} }
pub fn file(&self, index: usize) -> Option<PathBuf> { pub fn file(&self, index: usize) -> Option<PathBuf> {
unsafe { unsafe { self.get_string(b"file\0").nth(index) }.map(From::from)
self.get_string(b"file\0").nth(index)
}.map(From::from)
}
pattern_get_integer! {
index() => b"index\0"
} }
pub fn config_substitute(&mut self, config: &ConfigRef, kind: MatchKind) { pub fn config_substitute(&mut self, config: &ConfigRef, kind: MatchKind) {
@ -622,4 +567,3 @@ impl PatternRef {
} }
} }
} }

View File

@ -13,19 +13,18 @@
// limitations under the License. // limitations under the License.
// //
//! Rasterization powered by FreeType and FontConfig //! Rasterization powered by FreeType and FontConfig
use std::collections::HashMap;
use std::cmp::min; use std::cmp::min;
use std::path::PathBuf; use std::collections::HashMap;
use std::fmt; use std::fmt;
use std::path::PathBuf;
use freetype::tt_os2::TrueTypeOS2Table; use freetype::tt_os2::TrueTypeOS2Table;
use freetype::{self, Library}; use freetype::{self, Library};
use libc::c_uint; use libc::c_uint;
pub mod fc; pub mod fc;
use super::{FontDesc, RasterizedGlyph, Metrics, Size, FontKey, GlyphKey, Weight, Slant, Style}; use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight};
struct FixedSize { struct FixedSize {
pixelsize: f64, pixelsize: f64,
@ -37,7 +36,7 @@ struct Face {
load_flags: freetype::face::LoadFlag, load_flags: freetype::face::LoadFlag,
render_mode: freetype::RenderMode, render_mode: freetype::RenderMode,
lcd_filter: c_uint, lcd_filter: c_uint,
non_scalable: Option<FixedSize> non_scalable: Option<FixedSize>,
} }
impl fmt::Debug for Face { impl fmt::Debug for Face {
@ -46,14 +45,17 @@ impl fmt::Debug for Face {
.field("ft_face", &self.ft_face) .field("ft_face", &self.ft_face)
.field("key", &self.key) .field("key", &self.key)
.field("load_flags", &self.load_flags) .field("load_flags", &self.load_flags)
.field("render_mode", &match self.render_mode { .field(
freetype::RenderMode::Normal => "Normal", "render_mode",
freetype::RenderMode::Light => "Light", &match self.render_mode {
freetype::RenderMode::Mono => "Mono", freetype::RenderMode::Normal => "Normal",
freetype::RenderMode::Lcd => "Lcd", freetype::RenderMode::Light => "Light",
freetype::RenderMode::LcdV => "LcdV", freetype::RenderMode::Mono => "Mono",
freetype::RenderMode::Max => "Max", freetype::RenderMode::Lcd => "Lcd",
}) freetype::RenderMode::LcdV => "LcdV",
freetype::RenderMode::Max => "Max",
},
)
.field("lcd_filter", &self.lcd_filter) .field("lcd_filter", &self.lcd_filter)
.finish() .finish()
} }
@ -87,9 +89,7 @@ impl ::Rasterize for FreeTypeRasterizer {
} }
fn metrics(&self, key: FontKey, _size: Size) -> Result<Metrics, Error> { fn metrics(&self, key: FontKey, _size: Size) -> Result<Metrics, Error> {
let face = self.faces let face = self.faces.get(&key).ok_or(Error::FontNotLoaded)?;
.get(&key)
.ok_or(Error::FontNotLoaded)?;
let full = self.full_metrics(key)?; let full = self.full_metrics(key)?;
let height = (full.size_metrics.height / 64) as f64; let height = (full.size_metrics.height / 64) as f64;
@ -108,20 +108,19 @@ impl ::Rasterize for FreeTypeRasterizer {
// Get strikeout position and thickness in device pixels // Get strikeout position and thickness in device pixels
let (strikeout_position, strikeout_thickness) = let (strikeout_position, strikeout_thickness) =
match TrueTypeOS2Table::from_face(&mut face.ft_face.clone()) match TrueTypeOS2Table::from_face(&mut face.ft_face.clone()) {
{ Some(os2) => {
Some(os2) => { let strikeout_position = f32::from(os2.y_strikeout_position()) * x_scale / 64.;
let strikeout_position = f32::from(os2.y_strikeout_position()) * x_scale / 64.; let strikeout_thickness = f32::from(os2.y_strikeout_size()) * x_scale / 64.;
let strikeout_thickness = f32::from(os2.y_strikeout_size()) * x_scale / 64.; (strikeout_position, strikeout_thickness)
(strikeout_position, strikeout_thickness) },
}, _ => {
_ => { // Fallback if font doesn't provide info about strikeout
// Fallback if font doesn't provide info about strikeout trace!("Using fallback strikeout metrics");
trace!("Using fallback strikeout metrics"); let strikeout_position = height as f32 / 2. + descent;
let strikeout_position = height as f32 / 2. + descent; (strikeout_position, underline_thickness)
(strikeout_position, underline_thickness) },
}, };
};
Ok(Metrics { Ok(Metrics {
average_advance: full.cell_width, average_advance: full.cell_width,
@ -154,6 +153,7 @@ pub trait IntoFontconfigType {
impl IntoFontconfigType for Slant { impl IntoFontconfigType for Slant {
type FcType = fc::Slant; type FcType = fc::Slant;
fn into_fontconfig_type(&self) -> Self::FcType { fn into_fontconfig_type(&self) -> Self::FcType {
match *self { match *self {
Slant::Normal => fc::Slant::Roman, Slant::Normal => fc::Slant::Roman,
@ -176,7 +176,7 @@ impl IntoFontconfigType for Weight {
struct FullMetrics { struct FullMetrics {
size_metrics: freetype::ffi::FT_Size_Metrics, size_metrics: freetype::ffi::FT_Size_Metrics,
cell_width: f64 cell_width: f64,
} }
impl FreeTypeRasterizer { impl FreeTypeRasterizer {
@ -189,31 +189,25 @@ impl FreeTypeRasterizer {
Style::Description { slant, weight } => { Style::Description { slant, weight } => {
// Match nearest font // Match nearest font
self.get_matching_face(&desc, slant, weight, size) self.get_matching_face(&desc, slant, weight, size)
} },
Style::Specific(ref style) => { Style::Specific(ref style) => {
// If a name was specified, try and load specifically that font. // If a name was specified, try and load specifically that font.
self.get_specific_face(&desc, &style, size) self.get_specific_face(&desc, &style, size)
} },
} }
} }
fn full_metrics(&self, key: FontKey) -> Result<FullMetrics, Error> { fn full_metrics(&self, key: FontKey) -> Result<FullMetrics, Error> {
let face = self.faces let face = self.faces.get(&key).ok_or(Error::FontNotLoaded)?;
.get(&key)
.ok_or(Error::FontNotLoaded)?;
let size_metrics = face.ft_face.size_metrics() let size_metrics = face.ft_face.size_metrics().ok_or(Error::MissingSizeMetrics)?;
.ok_or(Error::MissingSizeMetrics)?;
let width = match face.ft_face.load_char('0' as usize, face.load_flags) { let width = match face.ft_face.load_char('0' as usize, face.load_flags) {
Ok(_) => face.ft_face.glyph().metrics().horiAdvance / 64, Ok(_) => face.ft_face.glyph().metrics().horiAdvance / 64,
Err(_) => size_metrics.max_advance / 64 Err(_) => size_metrics.max_advance / 64,
} as f64; } as f64;
Ok(FullMetrics { Ok(FullMetrics { size_metrics, cell_width: width })
size_metrics,
cell_width: width
})
} }
fn get_matching_face( fn get_matching_face(
@ -232,12 +226,9 @@ impl FreeTypeRasterizer {
let font = fc::font_match(fc::Config::get_current(), &mut pattern) let font = fc::font_match(fc::Config::get_current(), &mut pattern)
.ok_or_else(|| Error::MissingFont(desc.to_owned()))?; .ok_or_else(|| Error::MissingFont(desc.to_owned()))?;
self.face_from_pattern(&font) self.face_from_pattern(&font).and_then(|pattern| {
.and_then(|pattern| { pattern.map(Ok).unwrap_or_else(|| Err(Error::MissingFont(desc.to_owned())))
pattern })
.map(Ok)
.unwrap_or_else(|| Err(Error::MissingFont(desc.to_owned())))
})
} }
fn get_specific_face( fn get_specific_face(
@ -253,12 +244,9 @@ impl FreeTypeRasterizer {
let font = fc::font_match(fc::Config::get_current(), &mut pattern) let font = fc::font_match(fc::Config::get_current(), &mut pattern)
.ok_or_else(|| Error::MissingFont(desc.to_owned()))?; .ok_or_else(|| Error::MissingFont(desc.to_owned()))?;
self.face_from_pattern(&font) self.face_from_pattern(&font).and_then(|pattern| {
.and_then(|pattern| { pattern.map(Ok).unwrap_or_else(|| Err(Error::MissingFont(desc.to_owned())))
pattern })
.map(Ok)
.unwrap_or_else(|| Err(Error::MissingFont(desc.to_owned())))
})
} }
fn face_from_pattern(&mut self, pattern: &fc::Pattern) -> Result<Option<FontKey>, Error> { fn face_from_pattern(&mut self, pattern: &fc::Pattern) -> Result<Option<FontKey>, Error> {
@ -277,9 +265,7 @@ impl FreeTypeRasterizer {
let mut pixelsize = pattern.pixelsize(); let mut pixelsize = pattern.pixelsize();
debug!("pixelsizes: {:?}", pixelsize); debug!("pixelsizes: {:?}", pixelsize);
Some(FixedSize { Some(FixedSize { pixelsize: pixelsize.next().expect("has 1+ pixelsize") })
pixelsize: pixelsize.next().expect("has 1+ pixelsize"),
})
}; };
let face = Face { let face = Face {
@ -303,7 +289,11 @@ impl FreeTypeRasterizer {
} }
} }
fn face_for_glyph(&mut self, glyph_key: GlyphKey, have_recursed: bool) -> Result<FontKey, Error> { fn face_for_glyph(
&mut self,
glyph_key: GlyphKey,
have_recursed: bool,
) -> Result<FontKey, Error> {
let c = glyph_key.c; let c = glyph_key.c;
let use_initial_face = if let Some(face) = self.faces.get(&glyph_key.font_key) { let use_initial_face = if let Some(face) = self.faces.get(&glyph_key.font_key) {
@ -322,8 +312,7 @@ impl FreeTypeRasterizer {
} }
} }
fn get_rendered_glyph(&mut self, glyph_key: GlyphKey) fn get_rendered_glyph(&mut self, glyph_key: GlyphKey) -> Result<RasterizedGlyph, Error> {
-> Result<RasterizedGlyph, Error> {
// Render a custom symbol for the underline and beam cursor // Render a custom symbol for the underline and beam cursor
match glyph_key.c { match glyph_key.c {
super::UNDERLINE_CURSOR_CHAR => { super::UNDERLINE_CURSOR_CHAR => {
@ -370,9 +359,10 @@ impl FreeTypeRasterizer {
let face = &self.faces[&font_key]; let face = &self.faces[&font_key];
let index = face.ft_face.get_char_index(glyph_key.c as usize); let index = face.ft_face.get_char_index(glyph_key.c as usize);
let size = face.non_scalable.as_ref() let size =
.map(|v| v.pixelsize as f32) face.non_scalable.as_ref().map(|v| v.pixelsize as f32).unwrap_or_else(|| {
.unwrap_or_else(|| glyph_key.size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.); glyph_key.size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.
});
face.ft_face.set_char_size(to_freetype_26_6(size), 0, 0, 0)?; face.ft_face.set_char_size(to_freetype_26_6(size), 0, 0, 0)?;
@ -405,7 +395,7 @@ impl FreeTypeRasterizer {
use freetype::face::LoadFlag; use freetype::face::LoadFlag;
match (antialias, hinting, rgba) { match (antialias, hinting, rgba) {
(false, fc::HintStyle::None, _) => LoadFlag::NO_HINTING | LoadFlag::MONOCHROME, (false, fc::HintStyle::None, _) => LoadFlag::NO_HINTING | LoadFlag::MONOCHROME,
(false, _, _) => LoadFlag::TARGET_MONO | LoadFlag::MONOCHROME, (false, ..) => LoadFlag::TARGET_MONO | LoadFlag::MONOCHROME,
(true, fc::HintStyle::None, _) => LoadFlag::NO_HINTING | LoadFlag::TARGET_NORMAL, (true, fc::HintStyle::None, _) => LoadFlag::NO_HINTING | LoadFlag::TARGET_NORMAL,
// hintslight does *not* use LCD hinting even when a subpixel mode // hintslight does *not* use LCD hinting even when a subpixel mode
// is selected. // is selected.
@ -459,7 +449,9 @@ impl FreeTypeRasterizer {
/// Given a FreeType `Bitmap`, returns packed buffer with 1 byte per LCD channel. /// Given a FreeType `Bitmap`, returns packed buffer with 1 byte per LCD channel.
/// ///
/// The i32 value in the return type is the number of pixels per row. /// The i32 value in the return type is the number of pixels per row.
fn normalize_buffer(bitmap: &freetype::bitmap::Bitmap) -> freetype::FtResult<(i32, i32, Vec<u8>)> { fn normalize_buffer(
bitmap: &freetype::bitmap::Bitmap,
) -> freetype::FtResult<(i32, i32, Vec<u8>)> {
use freetype::bitmap::PixelMode; use freetype::bitmap::PixelMode;
let buf = bitmap.buffer(); let buf = bitmap.buffer();
@ -475,7 +467,7 @@ impl FreeTypeRasterizer {
Ok((bitmap.rows(), bitmap.width() / 3, packed)) Ok((bitmap.rows(), bitmap.width() / 3, packed))
}, },
PixelMode::LcdV => { PixelMode::LcdV => {
for i in 0..bitmap.rows()/3 { for i in 0..bitmap.rows() / 3 {
for j in 0..bitmap.width() { for j in 0..bitmap.width() {
for k in 0..3 { for k in 0..3 {
let offset = ((i as usize) * 3 + k) * pitch + (j as usize); let offset = ((i as usize) * 3 + k) * pitch + (j as usize);
@ -529,14 +521,11 @@ impl FreeTypeRasterizer {
} }
Ok((bitmap.rows(), bitmap.width(), packed)) Ok((bitmap.rows(), bitmap.width(), packed))
}, },
mode => panic!("unhandled pixel mode: {:?}", mode) mode => panic!("unhandled pixel mode: {:?}", mode),
} }
} }
fn load_face_with_glyph( fn load_face_with_glyph(&mut self, glyph: char) -> Result<FontKey, Error> {
&mut self,
glyph: char,
) -> Result<FontKey, Error> {
let mut charset = fc::CharSet::new(); let mut charset = fc::CharSet::new();
charset.add(glyph); charset.add(glyph);
let mut pattern = fc::Pattern::new(); let mut pattern = fc::Pattern::new();
@ -560,19 +549,19 @@ impl FreeTypeRasterizer {
// and index above. // and index above.
let key = self.face_from_pattern(&pattern)?.unwrap(); let key = self.face_from_pattern(&pattern)?.unwrap();
Ok(key) Ok(key)
} },
} }
} } else {
else { Err(Error::MissingFont(FontDesc::new(
Err(Error::MissingFont( "fallback-without-path",
FontDesc::new("fallback-without-path", Style::Specific(glyph.to_string())))) Style::Specific(glyph.to_string()),
)))
} }
}, },
None => { None => Err(Error::MissingFont(FontDesc::new(
Err(Error::MissingFont( "no-fallback-for",
FontDesc::new("no-fallback-for", Style::Specific(glyph.to_string())) Style::Specific(glyph.to_string()),
)) ))),
}
} }
} }
} }
@ -614,19 +603,17 @@ impl ::std::error::Error for Error {
impl ::std::fmt::Display for Error { impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match *self { match *self {
Error::FreeType(ref err) => { Error::FreeType(ref err) => err.fmt(f),
err.fmt(f) Error::MissingFont(ref desc) => write!(
}, f,
Error::MissingFont(ref desc) => { "Couldn't find a font with {}\n\tPlease check the font config in your \
write!(f, "Couldn't find a font with {}\ alacritty.yml.",
\n\tPlease check the font config in your alacritty.yml.", desc) desc
}, ),
Error::FontNotLoaded => { Error::FontNotLoaded => f.write_str("Tried to use a font that hasn't been loaded"),
f.write_str("Tried to use a font that hasn't been loaded")
},
Error::MissingSizeMetrics => { Error::MissingSizeMetrics => {
f.write_str("Tried to get size metrics from a face without a size") f.write_str("Tried to get size metrics from a face without a size")
} },
} }
} }
} }

View File

@ -46,8 +46,8 @@ extern crate foreign_types;
extern crate log; extern crate log;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::{fmt, cmp};
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::{cmp, fmt};
// If target isn't macos or windows, reexport everything from ft // If target isn't macos or windows, reexport everything from ft
#[cfg(not(any(target_os = "macos", windows)))] #[cfg(not(any(target_os = "macos", windows)))]
@ -113,7 +113,7 @@ impl fmt::Display for Style {
Style::Specific(ref s) => f.write_str(&s), Style::Specific(ref s) => f.write_str(&s),
Style::Description { slant, weight } => { Style::Description { slant, weight } => {
write!(f, "slant={:?}, weight={:?}", slant, weight) write!(f, "slant={:?}, weight={:?}", slant, weight)
} },
} }
} }
} }
@ -123,10 +123,7 @@ impl FontDesc {
where where
S: Into<String>, S: Into<String>,
{ {
FontDesc { FontDesc { name: name.into(), style }
name: name.into(),
style,
}
} }
} }
@ -149,9 +146,7 @@ impl FontKey {
pub fn next() -> FontKey { pub fn next() -> FontKey {
static TOKEN: AtomicUsize = AtomicUsize::new(0); static TOKEN: AtomicUsize = AtomicUsize::new(0);
FontKey { FontKey { token: TOKEN.fetch_add(1, Ordering::SeqCst) as _ }
token: TOKEN.fetch_add(1, Ordering::SeqCst) as _,
}
} }
} }
@ -170,7 +165,8 @@ impl Hash for GlyphKey {
// - If GlyphKey ever becomes a different size, this will fail to compile // - If GlyphKey ever becomes a different size, this will fail to compile
// - Result is being used for hashing and has no fields (it's a u64) // - Result is being used for hashing and has no fields (it's a u64)
::std::mem::transmute::<GlyphKey, u64>(*self) ::std::mem::transmute::<GlyphKey, u64>(*self)
}.hash(state); }
.hash(state);
} }
} }
@ -228,14 +224,7 @@ pub struct RasterizedGlyph {
impl Default for RasterizedGlyph { impl Default for RasterizedGlyph {
fn default() -> RasterizedGlyph { fn default() -> RasterizedGlyph {
RasterizedGlyph { RasterizedGlyph { c: ' ', width: 0, height: 0, top: 0, left: 0, buf: Vec::new() }
c: ' ',
width: 0,
height: 0,
top: 0,
left: 0,
buf: Vec::new(),
}
} }
} }
@ -288,8 +277,11 @@ pub fn get_box_cursor_glyph(
let mut buf = Vec::with_capacity((width * height * 3) as usize); let mut buf = Vec::with_capacity((width * height * 3) as usize);
for y in 0..height { for y in 0..height {
for x in 0..width { for x in 0..width {
if y < border_width || y >= height - border_width || if y < border_width
x < border_width || x >= width - border_width { || y >= height - border_width
|| x < border_width
|| x >= width - border_width
{
buf.append(&mut vec![255u8; 3]); buf.append(&mut vec![255u8; 3]);
} else { } else {
buf.append(&mut vec![0u8; 3]); buf.append(&mut vec![0u8; 3]);
@ -298,24 +290,14 @@ pub fn get_box_cursor_glyph(
} }
// Create a custom glyph with the rectangle data attached to it // Create a custom glyph with the rectangle data attached to it
Ok(RasterizedGlyph { Ok(RasterizedGlyph { c: BOX_CURSOR_CHAR, top: ascent, left: 0, height, width, buf })
c: BOX_CURSOR_CHAR,
top: ascent,
left: 0,
height,
width,
buf,
})
} }
struct BufDebugger<'a>(&'a [u8]); struct BufDebugger<'a>(&'a [u8]);
impl<'a> fmt::Debug for BufDebugger<'a> { impl<'a> fmt::Debug for BufDebugger<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("GlyphBuffer") f.debug_struct("GlyphBuffer").field("len", &self.0.len()).field("bytes", &self.0).finish()
.field("len", &self.0.len())
.field("bytes", &self.0)
.finish()
} }
} }

View File

@ -15,10 +15,7 @@ impl crate::Rasterize for RustTypeRasterizer {
type Err = Error; type Err = Error;
fn new(device_pixel_ratio: f32, _: bool) -> Result<RustTypeRasterizer, Error> { fn new(device_pixel_ratio: f32, _: bool) -> Result<RustTypeRasterizer, Error> {
Ok(RustTypeRasterizer { Ok(RustTypeRasterizer { fonts: Vec::new(), dpi_ratio: device_pixel_ratio })
fonts: Vec::new(),
dpi_ratio: device_pixel_ratio,
})
} }
fn metrics(&self, key: FontKey, size: Size) -> Result<Metrics, Error> { fn metrics(&self, key: FontKey, size: Size) -> Result<Metrics, Error> {
@ -56,17 +53,13 @@ impl crate::Rasterize for RustTypeRasterizer {
} }
fn load_font(&mut self, desc: &FontDesc, _size: Size) -> Result<FontKey, Error> { fn load_font(&mut self, desc: &FontDesc, _size: Size) -> Result<FontKey, Error> {
let fp = system_fonts::FontPropertyBuilder::new() let fp = system_fonts::FontPropertyBuilder::new().family(&desc.name).monospace();
.family(&desc.name)
.monospace();
let fp = match desc.style { let fp = match desc.style {
Style::Specific(ref style) => { Style::Specific(ref style) => match style.to_lowercase().as_str() {
match style.to_lowercase().as_str() { "italic" => fp.italic(),
"italic" => fp.italic(), "bold" => fp.bold(),
"bold" => fp.bold(), _ => fp,
_ => fp,
}
}, },
Style::Description { slant, weight } => { Style::Description { slant, weight } => {
let fp = match slant { let fp = match slant {
@ -79,52 +72,52 @@ impl crate::Rasterize for RustTypeRasterizer {
Weight::Bold => fp.bold(), Weight::Bold => fp.bold(),
Weight::Normal => fp, Weight::Normal => fp,
} }
} },
}; };
self.fonts.push(FontCollection::from_bytes( self.fonts.push(
system_fonts::get(&fp.build()) FontCollection::from_bytes(
.ok_or_else(|| Error::MissingFont(desc.clone()))? system_fonts::get(&fp.build()).ok_or_else(|| Error::MissingFont(desc.clone()))?.0,
.0, )
).into_font() .into_font()
.ok_or(Error::UnsupportedFont)?); .ok_or(Error::UnsupportedFont)?,
Ok(FontKey { );
token: (self.fonts.len() - 1) as u16, Ok(FontKey { token: (self.fonts.len() - 1) as u16 })
})
} }
fn get_glyph(&mut self, glyph_key: GlyphKey) -> Result<RasterizedGlyph, Error> { fn get_glyph(&mut self, glyph_key: GlyphKey) -> Result<RasterizedGlyph, Error> {
match glyph_key.c { match glyph_key.c {
super::UNDERLINE_CURSOR_CHAR => { super::UNDERLINE_CURSOR_CHAR => {
let metrics = self.metrics(glyph_key.font_key, glyph_key.size)?; let metrics = self.metrics(glyph_key.font_key, glyph_key.size)?;
return super::get_underline_cursor_glyph(metrics.descent as i32, metrics.average_advance as i32); return super::get_underline_cursor_glyph(
} metrics.descent as i32,
metrics.average_advance as i32,
);
},
super::BEAM_CURSOR_CHAR => { super::BEAM_CURSOR_CHAR => {
let metrics = self.metrics(glyph_key.font_key, glyph_key.size)?; let metrics = self.metrics(glyph_key.font_key, glyph_key.size)?;
return super::get_beam_cursor_glyph( return super::get_beam_cursor_glyph(
(metrics.line_height + f64::from(metrics.descent)).round() as i32, (metrics.line_height + f64::from(metrics.descent)).round() as i32,
metrics.line_height.round() as i32, metrics.line_height.round() as i32,
metrics.average_advance.round() as i32 metrics.average_advance.round() as i32,
); );
} },
super::BOX_CURSOR_CHAR => { super::BOX_CURSOR_CHAR => {
let metrics = self.metrics(glyph_key.font_key, glyph_key.size)?; let metrics = self.metrics(glyph_key.font_key, glyph_key.size)?;
return super::get_box_cursor_glyph( return super::get_box_cursor_glyph(
(metrics.line_height + f64::from(metrics.descent)).round() as i32, (metrics.line_height + f64::from(metrics.descent)).round() as i32,
metrics.line_height.round() as i32, metrics.line_height.round() as i32,
metrics.average_advance.round() as i32 metrics.average_advance.round() as i32,
); );
} },
_ => () _ => (),
} }
let scaled_glyph = self.fonts[glyph_key.font_key.token as usize] let scaled_glyph = self.fonts[glyph_key.font_key.token as usize]
.glyph(glyph_key.c) .glyph(glyph_key.c)
.ok_or(Error::MissingGlyph)? .ok_or(Error::MissingGlyph)?
.scaled(Scale::uniform( .scaled(Scale::uniform(glyph_key.size.as_f32_pts() * self.dpi_ratio * 96. / 72.));
glyph_key.size.as_f32_pts() * self.dpi_ratio * 96. / 72.,
));
let glyph = scaled_glyph.positioned(point(0.0, 0.0)); let glyph = scaled_glyph.positioned(point(0.0, 0.0));
@ -132,10 +125,7 @@ impl crate::Rasterize for RustTypeRasterizer {
let bb = match glyph.pixel_bounding_box() { let bb = match glyph.pixel_bounding_box() {
Some(bb) => bb, Some(bb) => bb,
// Bounding box calculation fails for spaces so we provide a placeholder bounding box // Bounding box calculation fails for spaces so we provide a placeholder bounding box
None => rusttype::Rect { None => rusttype::Rect { min: point(0, 0), max: point(0, 0) },
min: point(0, 0),
max: point(0, 0),
},
}; };
let mut buf = Vec::with_capacity((bb.width() * bb.height()) as usize); let mut buf = Vec::with_capacity((bb.width() * bb.height()) as usize);
@ -185,8 +175,8 @@ impl ::std::fmt::Display for Error {
match *self { match *self {
Error::MissingFont(ref desc) => write!( Error::MissingFont(ref desc) => write!(
f, f,
"Couldn't find a font with {}\ "Couldn't find a font with {}\n\tPlease check the font config in your \
\n\tPlease check the font config in your alacritty.yml.", alacritty.yml.",
desc desc
), ),
Error::UnsupportedFont => write!( Error::UnsupportedFont => write!(
@ -195,7 +185,7 @@ impl ::std::fmt::Display for Error {
), ),
Error::UnsupportedStyle => { Error::UnsupportedStyle => {
write!(f, "The selected font style is not supported by rusttype.") write!(f, "The selected font style is not supported by rusttype.")
} },
Error::MissingGlyph => write!(f, "The selected font did not have the requested glyph."), Error::MissingGlyph => write!(f, "The selected font did not have the requested glyph."),
} }
} }

13
rustfmt.toml Normal file
View File

@ -0,0 +1,13 @@
match_block_trailing_comma = true
condense_wildcard_suffixes = true
use_field_init_shorthand = true
overflow_delimited_expr = true
use_small_heuristics = "Max"
format_doc_comments = true
normalize_comments = true
reorder_impl_items = true
use_try_shorthand = true
newline_style = "Unix"
format_strings = true
wrap_comments = true
comment_width = 100

View File

@ -17,10 +17,10 @@ use std::io;
use std::ops::Range; use std::ops::Range;
use std::str; use std::str;
use vte; use crate::index::{Column, Contains, Line};
use base64; use base64;
use glutin::MouseCursor; use glutin::MouseCursor;
use crate::index::{Column, Line, Contains}; use vte;
use crate::term::color::Rgb; use crate::term::color::Rgb;
@ -33,7 +33,7 @@ fn parse_rgb_color(color: &[u8]) -> Option<Rgb> {
macro_rules! next { macro_rules! next {
() => { () => {
iter.next().map(|v| *v as char) iter.next().map(|v| *v as char)
} };
} }
macro_rules! parse_hex { macro_rules! parse_hex {
@ -50,32 +50,36 @@ fn parse_rgb_color(color: &[u8]) -> Option<Rgb> {
digit += value as u8; digit += value as u8;
} }
digit digit
}} }};
} }
match next!() { match next!() {
Some('r') => { Some('r') => {
if next!() != Some('g') { return None; } if next!() != Some('g') {
if next!() != Some('b') { return None; } return None;
if next!() != Some(':') { return None; } }
if next!() != Some('b') {
return None;
}
if next!() != Some(':') {
return None;
}
let r = parse_hex!(); let r = parse_hex!();
let val = next!(); let val = next!();
if val != Some('/') { return None; } if val != Some('/') {
return None;
}
let g = parse_hex!(); let g = parse_hex!();
if next!() != Some('/') { return None; } if next!() != Some('/') {
return None;
}
let b = parse_hex!(); let b = parse_hex!();
Some(Rgb { r, g, b }) Some(Rgb { r, g, b })
} },
Some('#') => { Some('#') => Some(Rgb { r: parse_hex!(), g: parse_hex!(), b: parse_hex!() }),
Some(Rgb { _ => None,
r: parse_hex!(),
g: parse_hex!(),
b: parse_hex!(),
})
}
_ => None
} }
} }
@ -106,7 +110,7 @@ pub struct Processor {
/// Internal state for VTE processor /// Internal state for VTE processor
struct ProcessorState { struct ProcessorState {
preceding_char: Option<char> preceding_char: Option<char>,
} }
/// Helper type that implements `vte::Perform`. /// Helper type that implements `vte::Perform`.
@ -116,7 +120,7 @@ struct ProcessorState {
struct Performer<'a, H: Handler + TermInfo, W: io::Write> { struct Performer<'a, H: Handler + TermInfo, W: io::Write> {
_state: &'a mut ProcessorState, _state: &'a mut ProcessorState,
handler: &'a mut H, handler: &'a mut H,
writer: &'a mut W writer: &'a mut W,
} }
impl<'a, H: Handler + TermInfo + 'a, W: io::Write> Performer<'a, H, W> { impl<'a, H: Handler + TermInfo + 'a, W: io::Write> Performer<'a, H, W> {
@ -127,20 +131,13 @@ impl<'a, H: Handler + TermInfo + 'a, W: io::Write> Performer<'a, H, W> {
handler: &'b mut H, handler: &'b mut H,
writer: &'b mut W, writer: &'b mut W,
) -> Performer<'b, H, W> { ) -> Performer<'b, H, W> {
Performer { Performer { _state: state, handler, writer }
_state: state,
handler,
writer,
}
} }
} }
impl Default for Processor { impl Default for Processor {
fn default() -> Processor { fn default() -> Processor {
Processor { Processor { state: ProcessorState { preceding_char: None }, parser: vte::Parser::new() }
state: ProcessorState { preceding_char: None },
parser: vte::Parser::new(),
}
} }
} }
@ -150,21 +147,16 @@ impl Processor {
} }
#[inline] #[inline]
pub fn advance<H, W>( pub fn advance<H, W>(&mut self, handler: &mut H, byte: u8, writer: &mut W)
&mut self, where
handler: &mut H, H: Handler + TermInfo,
byte: u8, W: io::Write,
writer: &mut W
)
where H: Handler + TermInfo,
W: io::Write
{ {
let mut performer = Performer::new(&mut self.state, handler, writer); let mut performer = Performer::new(&mut self.state, handler, writer);
self.parser.advance(&mut performer, byte); self.parser.advance(&mut performer, byte);
} }
} }
/// Trait that provides properties of terminal /// Trait that provides properties of terminal
pub trait TermInfo { pub trait TermInfo {
fn lines(&self) -> Line; fn lines(&self) -> Line;
@ -447,14 +439,14 @@ impl Mode {
2004 => Mode::BracketedPaste, 2004 => Mode::BracketedPaste,
_ => { _ => {
trace!("[unimplemented] primitive mode: {}", num); trace!("[unimplemented] primitive mode: {}", num);
return None return None;
} },
}) })
} else { } else {
Some(match num { Some(match num {
4 => Mode::Insert, 4 => Mode::Insert,
20 => Mode::LineFeedNewLine, 20 => Mode::LineFeedNewLine,
_ => return None _ => return None,
}) })
} }
} }
@ -485,7 +477,7 @@ pub enum ClearMode {
/// Clear entire terminal /// Clear entire terminal
All, All,
/// Clear 'saved' lines (scrollback) /// Clear 'saved' lines (scrollback)
Saved Saved,
} }
/// Mode for clearing tab stops /// Mode for clearing tab stops
@ -586,7 +578,7 @@ impl NamedColor {
NamedColor::DimMagenta => NamedColor::Magenta, NamedColor::DimMagenta => NamedColor::Magenta,
NamedColor::DimCyan => NamedColor::Cyan, NamedColor::DimCyan => NamedColor::Cyan,
NamedColor::DimWhite => NamedColor::White, NamedColor::DimWhite => NamedColor::White,
val => val val => val,
} }
} }
@ -610,7 +602,7 @@ impl NamedColor {
NamedColor::BrightCyan => NamedColor::Cyan, NamedColor::BrightCyan => NamedColor::Cyan,
NamedColor::BrightWhite => NamedColor::White, NamedColor::BrightWhite => NamedColor::White,
NamedColor::BrightForeground => NamedColor::Foreground, NamedColor::BrightForeground => NamedColor::Foreground,
val => val val => val,
} }
} }
} }
@ -697,8 +689,9 @@ impl Default for StandardCharset {
} }
impl<'a, H, W> vte::Perform for Performer<'a, H, W> impl<'a, H, W> vte::Perform for Performer<'a, H, W>
where H: Handler + TermInfo + 'a, where
W: io::Write + 'a H: Handler + TermInfo + 'a,
W: io::Write + 'a,
{ {
#[inline] #[inline]
fn print(&mut self, c: char) { fn print(&mut self, c: char) {
@ -720,14 +713,16 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
C1::NEL => self.handler.newline(), C1::NEL => self.handler.newline(),
C1::HTS => self.handler.set_horizontal_tabstop(), C1::HTS => self.handler.set_horizontal_tabstop(),
C1::DECID => self.handler.identify_terminal(self.writer), C1::DECID => self.handler.identify_terminal(self.writer),
_ => debug!("[unhandled] execute byte={:02x}", byte) _ => debug!("[unhandled] execute byte={:02x}", byte),
} }
} }
#[inline] #[inline]
fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool) { fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool) {
debug!("[unhandled hook] params={:?}, ints: {:?}, ignore: {:?}", debug!(
params, intermediates, ignore); "[unhandled hook] params={:?}, ints: {:?}, ignore: {:?}",
params, intermediates, ignore
);
} }
#[inline] #[inline]
@ -788,7 +783,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
} }
} }
unhandled(params); unhandled(params);
} },
// Set foreground color // Set foreground color
b"10" => { b"10" => {
@ -799,7 +794,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
} }
} }
unhandled(params); unhandled(params);
} },
// Set background color // Set background color
b"11" => { b"11" => {
@ -810,7 +805,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
} }
} }
unhandled(params); unhandled(params);
} },
// Set text cursor color // Set text cursor color
b"12" => { b"12" => {
@ -821,11 +816,14 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
} }
} }
unhandled(params); unhandled(params);
} },
// Set cursor style // Set cursor style
b"50" => { b"50" => {
if params.len() >= 2 && params[1].len() >= 13 && params[1][0..12] == *b"CursorShape=" { if params.len() >= 2
&& params[1].len() >= 13
&& params[1][0..12] == *b"CursorShape="
{
let style = match params[1][12] as char { let style = match params[1][12] as char {
'0' => CursorStyle::Block, '0' => CursorStyle::Block,
'1' => CursorStyle::Beam, '1' => CursorStyle::Beam,
@ -836,7 +834,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
return; return;
} }
unhandled(params); unhandled(params);
} },
// Set clipboard // Set clipboard
b"52" => { b"52" => {
@ -852,9 +850,9 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
self.handler.set_clipboard(utf8_string); self.handler.set_clipboard(utf8_string);
} }
} }
} },
} }
} },
// Reset color index // Reset color index
b"104" => { b"104" => {
@ -873,7 +871,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
None => unhandled(params), None => unhandled(params),
} }
} }
} },
// Reset foreground color // Reset foreground color
b"110" => self.handler.reset_color(NamedColor::Foreground as usize), b"110" => self.handler.reset_color(NamedColor::Foreground as usize),
@ -889,35 +887,27 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
} }
#[inline] #[inline]
fn csi_dispatch( fn csi_dispatch(&mut self, args: &[i64], intermediates: &[u8], _ignore: bool, action: char) {
&mut self,
args: &[i64],
intermediates: &[u8],
_ignore: bool,
action: char
) {
let private = intermediates.get(0).map(|b| *b == b'?').unwrap_or(false); let private = intermediates.get(0).map(|b| *b == b'?').unwrap_or(false);
let handler = &mut self.handler; let handler = &mut self.handler;
let writer = &mut self.writer; let writer = &mut self.writer;
macro_rules! unhandled { macro_rules! unhandled {
() => {{ () => {{
debug!("[Unhandled CSI] action={:?}, args={:?}, intermediates={:?}", debug!(
action, args, intermediates); "[Unhandled CSI] action={:?}, args={:?}, intermediates={:?}",
action, args, intermediates
);
return; return;
}} }};
} }
macro_rules! arg_or_default { macro_rules! arg_or_default {
(idx: $idx:expr, default: $default:expr) => { (idx: $idx:expr, default: $default:expr) => {
args.get($idx).and_then(|v| { args.get($idx)
if *v == 0 { .and_then(|v| if *v == 0 { None } else { Some(*v) })
None .unwrap_or($default)
} else { };
Some(*v)
}
}).unwrap_or($default)
}
} }
match action { match action {
@ -930,8 +920,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
for _ in 0..arg_or_default!(idx: 0, default: 1) { for _ in 0..arg_or_default!(idx: 0, default: 1) {
handler.input(c); handler.input(c);
} }
} } else {
else {
debug!("tried to repeat with no preceding char"); debug!("tried to repeat with no preceding char");
} }
}, },
@ -1012,7 +1001,8 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
return; return;
} }
loop { loop {
if i >= args.len() { // C-for condition if i >= args.len() {
// C-for condition
break; break;
} }
@ -1061,7 +1051,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
45 => Attr::Background(Color::Named(NamedColor::Magenta)), 45 => Attr::Background(Color::Named(NamedColor::Magenta)),
46 => Attr::Background(Color::Named(NamedColor::Cyan)), 46 => Attr::Background(Color::Named(NamedColor::Cyan)),
47 => Attr::Background(Color::Named(NamedColor::White)), 47 => Attr::Background(Color::Named(NamedColor::White)),
48 => { 48 => {
let mut start = 0; let mut start = 0;
if let Some(color) = parse_color(&args[i..], &mut start) { if let Some(color) = parse_color(&args[i..], &mut start) {
i += start; i += start;
@ -1094,7 +1084,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
i += 1; // C-for expr i += 1; // C-for expr
} }
} },
'n' => handler.device_status(writer, arg_or_default!(idx: 0, default: 0) as usize), 'n' => handler.device_status(writer, arg_or_default!(idx: 0, default: 0) as usize),
'r' => { 'r' => {
if private { if private {
@ -1119,29 +1109,25 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
1 | 2 => Some(CursorStyle::Block), 1 | 2 => Some(CursorStyle::Block),
3 | 4 => Some(CursorStyle::Underline), 3 | 4 => Some(CursorStyle::Underline),
5 | 6 => Some(CursorStyle::Beam), 5 | 6 => Some(CursorStyle::Beam),
_ => unhandled!() _ => unhandled!(),
}; };
handler.set_cursor_style(style); handler.set_cursor_style(style);
} },
_ => unhandled!(), _ => unhandled!(),
} }
} }
#[inline] #[inline]
fn esc_dispatch( fn esc_dispatch(&mut self, params: &[i64], intermediates: &[u8], _ignore: bool, byte: u8) {
&mut self,
params: &[i64],
intermediates: &[u8],
_ignore: bool,
byte: u8
) {
macro_rules! unhandled { macro_rules! unhandled {
() => {{ () => {{
debug!("[unhandled] esc_dispatch params={:?}, ints={:?}, byte={:?} ({:02x})", debug!(
params, intermediates, byte as char, byte); "[unhandled] esc_dispatch params={:?}, ints={:?}, byte={:?} ({:02x})",
params, intermediates, byte as char, byte
);
return; return;
}} }};
} }
macro_rules! configure_charset { macro_rules! configure_charset {
@ -1154,7 +1140,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
_ => unhandled!(), _ => unhandled!(),
}; };
self.handler.configure_charset(index, $charset) self.handler.configure_charset(index, $charset)
}} }};
} }
match byte { match byte {
@ -1163,7 +1149,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
b'E' => { b'E' => {
self.handler.linefeed(); self.handler.linefeed();
self.handler.carriage_return(); self.handler.carriage_return();
} },
b'H' => self.handler.set_horizontal_tabstop(), b'H' => self.handler.set_horizontal_tabstop(),
b'M' => self.handler.reverse_index(), b'M' => self.handler.reverse_index(),
b'Z' => self.handler.identify_terminal(self.writer), b'Z' => self.handler.identify_terminal(self.writer),
@ -1176,7 +1162,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
} else { } else {
self.handler.restore_cursor_position(); self.handler.restore_cursor_position();
} }
} },
b'=' => self.handler.set_keypad_application_mode(), b'=' => self.handler.set_keypad_application_mode(),
b'>' => self.handler.unset_keypad_application_mode(), b'>' => self.handler.unset_keypad_application_mode(),
b'\\' => (), // String terminator, do nothing (parser handles as string terminator) b'\\' => (), // String terminator, do nothing (parser handles as string terminator)
@ -1185,14 +1171,13 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
} }
} }
/// Parse a color specifier from list of attributes /// Parse a color specifier from list of attributes
fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Color> { fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Color> {
if attrs.len() < 2 { if attrs.len() < 2 {
return None; return None;
} }
match attrs[*i+1] { match attrs[*i + 1] {
2 => { 2 => {
// RGB color spec // RGB color spec
if attrs.len() < 5 { if attrs.len() < 5 {
@ -1200,9 +1185,9 @@ fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Color> {
return None; return None;
} }
let r = attrs[*i+2]; let r = attrs[*i + 2];
let g = attrs[*i+3]; let g = attrs[*i + 3];
let b = attrs[*i+4]; let b = attrs[*i + 4];
*i += 4; *i += 4;
@ -1212,11 +1197,7 @@ fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Color> {
return None; return None;
} }
Some(Color::Spec(Rgb { Some(Color::Spec(Rgb { r: r as u8, g: g as u8, b: b as u8 }))
r: r as u8,
g: g as u8,
b: b as u8
}))
}, },
5 => { 5 => {
if attrs.len() < 3 { if attrs.len() < 3 {
@ -1226,20 +1207,18 @@ fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Color> {
*i += 2; *i += 2;
let idx = attrs[*i]; let idx = attrs[*i];
match idx { match idx {
0 ..= 255 => { 0..=255 => Some(Color::Indexed(idx as u8)),
Some(Color::Indexed(idx as u8))
},
_ => { _ => {
debug!("Invalid color index: {}", idx); debug!("Invalid color index: {}", idx);
None None
} },
} }
} }
}, },
_ => { _ => {
debug!("Unexpected color attr: {}", attrs[*i+1]); debug!("Unexpected color attr: {}", attrs[*i + 1]);
None None
} },
} }
} }
@ -1314,7 +1293,6 @@ pub mod C0 {
pub const DEL: u8 = 0x7f; pub const DEL: u8 = 0x7f;
} }
/// C1 set of 8-bit control characters (from ANSI X3.64-1979) /// C1 set of 8-bit control characters (from ANSI X3.64-1979)
/// ///
/// 0x80 (@), 0x81 (A), 0x82 (B), 0x83 (C) are reserved /// 0x80 (@), 0x81 (A), 0x82 (B), 0x83 (C) are reserved
@ -1393,10 +1371,13 @@ pub mod C1 {
// Byte sequences used in these tests are recording of pty stdout. // Byte sequences used in these tests are recording of pty stdout.
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::io; use super::{
use crate::index::{Line, Column}; parse_number, parse_rgb_color, Attr, CharsetIndex, Color, Handler, Processor,
use super::{Processor, Handler, Attr, TermInfo, Color, StandardCharset, CharsetIndex, parse_rgb_color, parse_number}; StandardCharset, TermInfo,
};
use crate::index::{Column, Line};
use crate::term::color::Rgb; use crate::term::color::Rgb;
use std::io;
/// The /dev/null of `io::Write` /// The /dev/null of `io::Write`
struct Void; struct Void;
@ -1434,9 +1415,7 @@ mod tests {
#[test] #[test]
fn parse_control_attribute() { fn parse_control_attribute() {
static BYTES: &'static [u8] = &[ static BYTES: &'static [u8] = &[0x1b, 0x5b, 0x31, 0x6d];
0x1b, 0x5b, 0x31, 0x6d
];
let mut parser = Processor::new(); let mut parser = Processor::new();
let mut handler = AttrHandler::default(); let mut handler = AttrHandler::default();
@ -1451,8 +1430,8 @@ mod tests {
#[test] #[test]
fn parse_truecolor_attr() { fn parse_truecolor_attr() {
static BYTES: &'static [u8] = &[ static BYTES: &'static [u8] = &[
0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x32, 0x3b, 0x31, 0x32, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x32, 0x3b, 0x31, 0x32, 0x38, 0x3b, 0x36, 0x36, 0x3b,
0x38, 0x3b, 0x36, 0x36, 0x3b, 0x32, 0x35, 0x35, 0x6d 0x32, 0x35, 0x35, 0x6d,
]; ];
let mut parser = Processor::new(); let mut parser = Processor::new();
@ -1462,11 +1441,7 @@ mod tests {
parser.advance(&mut handler, *byte, &mut Void); parser.advance(&mut handler, *byte, &mut Void);
} }
let spec = Rgb { let spec = Rgb { r: 128, g: 66, b: 255 };
r: 128,
g: 66,
b: 255
};
assert_eq!(handler.attr, Some(Attr::Foreground(Color::Spec(spec)))); assert_eq!(handler.attr, Some(Attr::Foreground(Color::Spec(spec))));
} }
@ -1475,22 +1450,19 @@ mod tests {
#[test] #[test]
fn parse_zsh_startup() { fn parse_zsh_startup() {
static BYTES: &'static [u8] = &[ static BYTES: &'static [u8] = &[
0x1b, 0x5b, 0x31, 0x6d, 0x1b, 0x5b, 0x37, 0x6d, 0x25, 0x1b, 0x5b, 0x1b, 0x5b, 0x31, 0x6d, 0x1b, 0x5b, 0x37, 0x6d, 0x25, 0x1b, 0x5b, 0x32, 0x37, 0x6d,
0x32, 0x37, 0x6d, 0x1b, 0x5b, 0x31, 0x6d, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x31, 0x6d, 0x1b, 0x5b, 0x30, 0x6d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0d, 0x20, 0x0d, 0x0d, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x32,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x37, 0x6d, 0x1b, 0x5b, 0x32, 0x34, 0x6d, 0x1b, 0x5b, 0x4a, 0x6a, 0x77, 0x69, 0x6c,
0x20, 0x20, 0x0d, 0x20, 0x0d, 0x0d, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x6d, 0x40, 0x6a, 0x77, 0x69, 0x6c, 0x6d, 0x2d, 0x64, 0x65, 0x73, 0x6b, 0x20, 0x1b,
0x5b, 0x32, 0x37, 0x6d, 0x1b, 0x5b, 0x32, 0x34, 0x6d, 0x1b, 0x5b, 0x5b, 0x30, 0x31, 0x3b, 0x33, 0x32, 0x6d, 0xe2, 0x9e, 0x9c, 0x20, 0x1b, 0x5b, 0x30,
0x4a, 0x6a, 0x77, 0x69, 0x6c, 0x6d, 0x40, 0x6a, 0x77, 0x69, 0x6c, 0x31, 0x3b, 0x33, 0x32, 0x6d, 0x20, 0x1b, 0x5b, 0x33, 0x36, 0x6d, 0x7e, 0x2f, 0x63,
0x6d, 0x2d, 0x64, 0x65, 0x73, 0x6b, 0x20, 0x1b, 0x5b, 0x30, 0x31, 0x6f, 0x64, 0x65,
0x3b, 0x33, 0x32, 0x6d, 0xe2, 0x9e, 0x9c, 0x20, 0x1b, 0x5b, 0x30,
0x31, 0x3b, 0x33, 0x32, 0x6d, 0x20, 0x1b, 0x5b, 0x33, 0x36, 0x6d,
0x7e, 0x2f, 0x63, 0x6f, 0x64, 0x65
]; ];
let mut handler = AttrHandler::default(); let mut handler = AttrHandler::default();
@ -1508,10 +1480,7 @@ mod tests {
impl Default for CharsetHandler { impl Default for CharsetHandler {
fn default() -> CharsetHandler { fn default() -> CharsetHandler {
CharsetHandler { CharsetHandler { index: CharsetIndex::G0, charset: StandardCharset::Ascii }
index: CharsetIndex::G0,
charset: StandardCharset::Ascii,
}
} }
} }
@ -1527,8 +1496,13 @@ mod tests {
} }
impl TermInfo for CharsetHandler { impl TermInfo for CharsetHandler {
fn lines(&self) -> Line { Line(200) } fn lines(&self) -> Line {
fn cols(&self) -> Column { Column(90) } Line(200)
}
fn cols(&self) -> Column {
Column(90)
}
} }
#[test] #[test]

View File

@ -11,14 +11,14 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use ::log; use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg};
use clap::{Arg, App, crate_name, crate_version, crate_authors, crate_description}; use log;
use crate::index::{Line, Column}; use crate::config::{Delta, Dimensions, Shell};
use crate::config::{Dimensions, Delta, Shell}; use crate::index::{Column, Line};
use crate::window::{DEFAULT_NAME}; use crate::window::DEFAULT_NAME;
use std::path::{Path, PathBuf};
use std::borrow::Cow; use std::borrow::Cow;
use std::path::{Path, PathBuf};
/// Options specified on the command line /// Options specified on the command line
pub struct Options { pub struct Options {
@ -64,70 +64,95 @@ impl Options {
.version(crate_version!()) .version(crate_version!())
.author(crate_authors!("\n")) .author(crate_authors!("\n"))
.about(crate_description!()) .about(crate_description!())
.arg(Arg::with_name("ref-test") .arg(Arg::with_name("ref-test").long("ref-test").help("Generates ref test"))
.long("ref-test") .arg(
.help("Generates ref test")) Arg::with_name("live-config-reload")
.arg(Arg::with_name("live-config-reload") .long("live-config-reload")
.long("live-config-reload") .help("Enable automatic config reloading"),
.help("Enable automatic config reloading")) )
.arg(Arg::with_name("no-live-config-reload") .arg(
.long("no-live-config-reload") Arg::with_name("no-live-config-reload")
.help("Disable automatic config reloading") .long("no-live-config-reload")
.conflicts_with("live-config-reload")) .help("Disable automatic config reloading")
.arg(Arg::with_name("print-events") .conflicts_with("live-config-reload"),
.long("print-events") )
.help("Print all events to stdout")) .arg(
.arg(Arg::with_name("persistent-logging") Arg::with_name("print-events")
.long("persistent-logging") .long("print-events")
.help("Keep the log file after quitting Alacritty")) .help("Print all events to stdout"),
.arg(Arg::with_name("dimensions") )
.long("dimensions") .arg(
.short("d") Arg::with_name("persistent-logging")
.value_names(&["columns", "lines"]) .long("persistent-logging")
.help("Defines the window dimensions. Falls back to size specified by \ .help("Keep the log file after quitting Alacritty"),
window manager if set to 0x0 [default: 0x0]")) )
.arg(Arg::with_name("position") .arg(
.long("position") Arg::with_name("dimensions")
.allow_hyphen_values(true) .long("dimensions")
.value_names(&["x-pos", "y-pos"]) .short("d")
.help("Defines the window position. Falls back to position specified by \ .value_names(&["columns", "lines"])
window manager if unset [default: unset]")) .help(
.arg(Arg::with_name("title") "Defines the window dimensions. Falls back to size specified by window \
.long("title") manager if set to 0x0 [default: 0x0]",
.short("t") ),
.takes_value(true) )
.help(&format!("Defines the window title [default: {}]", DEFAULT_NAME))) .arg(
.arg(Arg::with_name("class") Arg::with_name("position")
.long("class") .long("position")
.takes_value(true) .allow_hyphen_values(true)
.help(&format!("Defines window class on Linux [default: {}]", DEFAULT_NAME))) .value_names(&["x-pos", "y-pos"])
.arg(Arg::with_name("q") .help(
.short("q") "Defines the window position. Falls back to position specified by window \
.multiple(true) manager if unset [default: unset]",
.conflicts_with("v") ),
.help("Reduces the level of verbosity (the min level is -qq)")) )
.arg(Arg::with_name("v") .arg(
.short("v") Arg::with_name("title")
.multiple(true) .long("title")
.conflicts_with("q") .short("t")
.help("Increases the level of verbosity (the max level is -vvv)")) .takes_value(true)
.arg(Arg::with_name("working-directory") .help(&format!("Defines the window title [default: {}]", DEFAULT_NAME)),
.long("working-directory") )
.takes_value(true) .arg(
.help("Start the shell in the specified working directory")) Arg::with_name("class")
.arg(Arg::with_name("config-file") .long("class")
.long("config-file") .takes_value(true)
.takes_value(true) .help(&format!("Defines window class on Linux [default: {}]", DEFAULT_NAME)),
.help("Specify alternative configuration file \ )
[default: $XDG_CONFIG_HOME/alacritty/alacritty.yml]")) .arg(
.arg(Arg::with_name("command") Arg::with_name("q")
.long("command") .short("q")
.short("e") .multiple(true)
.multiple(true) .conflicts_with("v")
.takes_value(true) .help("Reduces the level of verbosity (the min level is -qq)"),
.min_values(1) )
.allow_hyphen_values(true) .arg(
.help("Command and args to execute (must be last argument)")) Arg::with_name("v")
.short("v")
.multiple(true)
.conflicts_with("q")
.help("Increases the level of verbosity (the max level is -vvv)"),
)
.arg(
Arg::with_name("working-directory")
.long("working-directory")
.takes_value(true)
.help("Start the shell in the specified working directory"),
)
.arg(Arg::with_name("config-file").long("config-file").takes_value(true).help(
"Specify alternative configuration file [default: \
$XDG_CONFIG_HOME/alacritty/alacritty.yml]",
))
.arg(
Arg::with_name("command")
.long("command")
.short("e")
.multiple(true)
.takes_value(true)
.min_values(1)
.allow_hyphen_values(true)
.help("Command and args to execute (must be last argument)"),
)
.get_matches(); .get_matches();
if matches.is_present("ref-test") { if matches.is_present("ref-test") {
@ -170,14 +195,14 @@ impl Options {
match matches.occurrences_of("q") { match matches.occurrences_of("q") {
0 => {}, 0 => {},
1 => options.log_level = log::LevelFilter::Error, 1 => options.log_level = log::LevelFilter::Error,
2 | _ => options.log_level = log::LevelFilter::Off 2 | _ => options.log_level = log::LevelFilter::Off,
} }
match matches.occurrences_of("v") { match matches.occurrences_of("v") {
0 if !options.print_events => {}, 0 if !options.print_events => {},
0 | 1 => options.log_level = log::LevelFilter::Info, 0 | 1 => options.log_level = log::LevelFilter::Info,
2 => options.log_level = log::LevelFilter::Debug, 2 => options.log_level = log::LevelFilter::Debug,
3 | _ => options.log_level = log::LevelFilter::Trace 3 | _ => options.log_level = log::LevelFilter::Trace,
} }
if let Some(dir) = matches.value_of("working-directory") { if let Some(dir) = matches.value_of("working-directory") {

View File

@ -11,11 +11,11 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use glutin::{MouseButton, ModifiersState}; use glutin::{ModifiersState, MouseButton};
use crate::input::{MouseBinding, KeyBinding, Action};
use crate::term::TermMode;
use super::Key; use super::Key;
use crate::input::{Action, KeyBinding, MouseBinding};
use crate::term::TermMode;
macro_rules! bindings { macro_rules! bindings {
( (

File diff suppressed because it is too large Load Diff

View File

@ -14,24 +14,24 @@
//! The display subsystem including window management, font rasterization, and //! The display subsystem including window management, font rasterization, and
//! GPU drawing. //! GPU drawing.
use std::sync::mpsc;
use std::f64; use std::f64;
use std::sync::mpsc;
use parking_lot::MutexGuard;
use glutin::dpi::{PhysicalPosition, PhysicalSize}; use glutin::dpi::{PhysicalPosition, PhysicalSize};
use parking_lot::MutexGuard;
use crate::cli; use crate::cli;
use crate::config::Config; use crate::config::Config;
use font::{self, Rasterize};
use crate::meter::Meter;
use crate::renderer::{self, GlyphCache, QuadRenderer};
use crate::renderer::rects::{Rects, Rect};
use crate::term::{Term, SizeInfo, RenderableCell};
use crate::sync::FairMutex;
use crate::window::{self, Window};
use crate::term::color::Rgb;
use crate::index::Line; use crate::index::Line;
use crate::message_bar::Message; use crate::message_bar::Message;
use crate::meter::Meter;
use crate::renderer::rects::{Rect, Rects};
use crate::renderer::{self, GlyphCache, QuadRenderer};
use crate::sync::FairMutex;
use crate::term::color::Rgb;
use crate::term::{RenderableCell, SizeInfo, Term};
use crate::window::{self, Window};
use font::{self, Rasterize};
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
@ -152,8 +152,8 @@ impl Display {
info!("Device pixel ratio: {}", dpr); info!("Device pixel ratio: {}", dpr);
// get window properties for initializing the other subsystems // get window properties for initializing the other subsystems
let mut viewport_size = window.inner_size_pixels() let mut viewport_size =
.expect("glutin returns window size").to_physical(dpr); window.inner_size_pixels().expect("glutin returns window size").to_physical(dpr);
// Create renderer // Create renderer
let mut renderer = QuadRenderer::new(viewport_size)?; let mut renderer = QuadRenderer::new(viewport_size)?;
@ -161,8 +161,7 @@ impl Display {
let (glyph_cache, cell_width, cell_height) = let (glyph_cache, cell_width, cell_height) =
Self::new_glyph_cache(dpr, &mut renderer, config)?; Self::new_glyph_cache(dpr, &mut renderer, config)?;
let dimensions = options.dimensions() let dimensions = options.dimensions().unwrap_or_else(|| config.dimensions());
.unwrap_or_else(|| config.dimensions());
let mut padding_x = f64::from(config.padding().x) * dpr; let mut padding_x = f64::from(config.padding().x) * dpr;
let mut padding_y = f64::from(config.padding().y) * dpr; let mut padding_y = f64::from(config.padding().y) * dpr;
@ -216,13 +215,9 @@ impl Display {
// Clear screen // Clear screen
let background_color = config.colors().primary.background; let background_color = config.colors().primary.background;
renderer.with_api( renderer.with_api(config, &size_info, |api| {
config, api.clear(background_color);
&size_info, });
|api| {
api.clear(background_color);
},
);
Ok(Display { Ok(Display {
window, window,
@ -238,9 +233,11 @@ impl Display {
}) })
} }
fn new_glyph_cache(dpr: f64, renderer: &mut QuadRenderer, config: &Config) fn new_glyph_cache(
-> Result<(GlyphCache, f32, f32), Error> dpr: f64,
{ renderer: &mut QuadRenderer,
config: &Config,
) -> Result<(GlyphCache, f32, f32), Error> {
let font = config.font().clone(); let font = config.font().clone();
let rasterizer = font::Rasterizer::new(dpr as f32, config.use_thin_strokes())?; let rasterizer = font::Rasterizer::new(dpr as f32, config.use_thin_strokes())?;
@ -253,8 +250,7 @@ impl Display {
renderer.with_loader(|mut api| GlyphCache::new(rasterizer, &font, &mut api))?; renderer.with_loader(|mut api| GlyphCache::new(rasterizer, &font, &mut api))?;
let stop = init_start.elapsed(); let stop = init_start.elapsed();
let stop_f = stop.as_secs() as f64 + let stop_f = stop.as_secs() as f64 + f64::from(stop.subsec_nanos()) / 1_000_000_000f64;
f64::from(stop.subsec_nanos()) / 1_000_000_000f64;
info!("... finished initializing glyph cache in {}s", stop_f); info!("... finished initializing glyph cache in {}s", stop_f);
cache cache
@ -322,8 +318,8 @@ impl Display {
let dpr = self.window.hidpi_factor(); let dpr = self.window.hidpi_factor();
// Font size/DPI factor modification detected // Font size/DPI factor modification detected
let font_changed = terminal.font_size != self.font_size let font_changed =
|| (dpr - self.size_info.dpr).abs() > f64::EPSILON; terminal.font_size != self.font_size || (dpr - self.size_info.dpr).abs() > f64::EPSILON;
if font_changed || self.last_message != terminal.message_buffer_mut().message() { if font_changed || self.last_message != terminal.message_buffer_mut().message() {
if new_size == None { if new_size == None {
@ -391,9 +387,8 @@ impl Display {
let background_color = terminal.background_color(); let background_color = terminal.background_color();
let window_focused = self.window.is_focused; let window_focused = self.window.is_focused;
let grid_cells: Vec<RenderableCell> = terminal let grid_cells: Vec<RenderableCell> =
.renderable_cells(config, window_focused) terminal.renderable_cells(config, window_focused).collect();
.collect();
// Get message from terminal to ignore modifications after lock is dropped // Get message from terminal to ignore modifications after lock is dropped
let message_buffer = terminal.message_buffer_mut().message(); let message_buffer = terminal.message_buffer_mut().message();
@ -486,20 +481,14 @@ impl Display {
// Draw render timer // Draw render timer
if self.render_timer { if self.render_timer {
let timing = format!("{:.3} usec", self.meter.average()); let timing = format!("{:.3} usec", self.meter.average());
let color = Rgb { let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 };
r: 0xd5,
g: 0x4e,
b: 0x53,
};
self.renderer.with_api(config, &size_info, |mut api| { self.renderer.with_api(config, &size_info, |mut api| {
api.render_string(&timing[..], size_info.lines() - 2, glyph_cache, Some(color)); api.render_string(&timing[..], size_info.lines() - 2, glyph_cache, Some(color));
}); });
} }
} }
self.window self.window.swap_buffers().expect("swap buffers");
.swap_buffers()
.expect("swap buffers");
} }
pub fn get_window_id(&self) -> Option<usize> { pub fn get_window_id(&self) -> Option<usize> {
@ -509,13 +498,8 @@ impl Display {
/// Adjust the IME editor position according to the new location of the cursor /// Adjust the IME editor position according to the new location of the cursor
pub fn update_ime_position(&mut self, terminal: &Term) { pub fn update_ime_position(&mut self, terminal: &Term) {
let point = terminal.cursor().point; let point = terminal.cursor().point;
let SizeInfo { let SizeInfo { cell_width: cw, cell_height: ch, padding_x: px, padding_y: py, .. } =
cell_width: cw, *terminal.size_info();
cell_height: ch,
padding_x: px,
padding_y: py,
..
} = *terminal.size_info();
let dpr = self.window().hidpi_factor(); let dpr = self.window().hidpi_factor();
let nspot_x = f64::from(px + point.col.0 as f32 * cw); let nspot_x = f64::from(px + point.col.0 as f32 * cw);

View File

@ -1,33 +1,33 @@
//! Process window events //! Process window events
use std::borrow::Cow;
use std::env;
#[cfg(unix)] #[cfg(unix)]
use std::fs; use std::fs;
use std::borrow::Cow;
use std::fs::File; use std::fs::File;
use std::io::Write; use std::io::Write;
use std::sync::mpsc; use std::sync::mpsc;
use std::time::{Instant}; use std::time::Instant;
use std::env;
use serde_json as json; use copypasta::{Buffer as ClipboardBuffer, Clipboard, Load, Store};
use parking_lot::MutexGuard;
use glutin::{self, ModifiersState, Event, ElementState, MouseButton};
use copypasta::{Clipboard, Load, Store, Buffer as ClipboardBuffer};
use glutin::dpi::PhysicalSize; use glutin::dpi::PhysicalSize;
use glutin::{self, ElementState, Event, ModifiersState, MouseButton};
use parking_lot::MutexGuard;
use serde_json as json;
#[cfg(unix)]
use crate::tty;
use crate::grid::Scroll;
use crate::config::{self, Config};
use crate::cli::Options; use crate::cli::Options;
use crate::config::{self, Config};
use crate::display::OnResize; use crate::display::OnResize;
use crate::index::{Line, Column, Side, Point}; use crate::grid::Scroll;
use crate::input::{self, MouseBinding, KeyBinding}; use crate::index::{Column, Line, Point, Side};
use crate::input::{self, KeyBinding, MouseBinding};
use crate::selection::Selection; use crate::selection::Selection;
use crate::sync::FairMutex; use crate::sync::FairMutex;
use crate::term::{Term, SizeInfo};
use crate::term::cell::Cell; use crate::term::cell::Cell;
use crate::util::{limit, start_daemon}; use crate::term::{SizeInfo, Term};
#[cfg(unix)]
use crate::tty;
use crate::util::fmt::Red; use crate::util::fmt::Red;
use crate::util::{limit, start_daemon};
use crate::window::Window; use crate::window::Window;
/// Byte sequences are sent to a `Notify` in response to some events /// Byte sequences are sent to a `Notify` in response to some events
@ -66,10 +66,7 @@ impl<'a, N: Notify + 'a> input::ActionContext for ActionContext<'a, N> {
let size_info = self.size_info(); let size_info = self.size_info();
let point = size_info.pixels_to_coords(x, y); let point = size_info.pixels_to_coords(x, y);
let cell_side = self.mouse().cell_side; let cell_side = self.mouse().cell_side;
self.update_selection(Point { self.update_selection(Point { line: point.line, col: point.col }, cell_side);
line: point.line,
col: point.col
}, cell_side);
} }
} }
@ -209,9 +206,7 @@ impl WindowChanges {
impl Default for WindowChanges { impl Default for WindowChanges {
fn default() -> WindowChanges { fn default() -> WindowChanges {
WindowChanges { WindowChanges { hide: false }
hide: false,
}
} }
} }
@ -358,16 +353,14 @@ impl<N: Notify> Processor<N> {
grid.initialize_all(&Cell::default()); grid.initialize_all(&Cell::default());
grid.truncate(); grid.truncate();
let serialized_grid = json::to_string(&grid) let serialized_grid = json::to_string(&grid).expect("serialize grid");
.expect("serialize grid");
let serialized_size = json::to_string(processor.ctx.terminal.size_info()) let serialized_size =
.expect("serialize size"); json::to_string(processor.ctx.terminal.size_info())
.expect("serialize size");
let serialized_config = format!( let serialized_config =
"{{\"history_size\":{}}}", format!("{{\"history_size\":{}}}", grid.history_size());
grid.history_size()
);
File::create("./grid.json") File::create("./grid.json")
.and_then(|mut f| f.write_all(serialized_grid.as_bytes())) .and_then(|mut f| f.write_all(serialized_grid.as_bytes()))
@ -453,7 +446,7 @@ impl<N: Notify> Processor<N> {
}, },
Event::Awakened => { Event::Awakened => {
processor.ctx.terminal.dirty = true; processor.ctx.terminal.dirty = true;
} },
} }
} }
@ -462,7 +455,7 @@ impl<N: Notify> Processor<N> {
pub fn process_events<'a>( pub fn process_events<'a>(
&mut self, &mut self,
term: &'a FairMutex<Term>, term: &'a FairMutex<Term>,
window: &mut Window window: &mut Window,
) -> MutexGuard<'a, Term> { ) -> MutexGuard<'a, Term> {
// Terminal is lazily initialized the first time an event is returned // Terminal is lazily initialized the first time an event is returned
// from the blocking WaitEventsIterator. Otherwise, the pty reader would // from the blocking WaitEventsIterator. Otherwise, the pty reader would

View File

@ -1,10 +1,10 @@
//! The main event loop which performs I/O on the pseudoterminal //! The main event loop which performs I/O on the pseudoterminal
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::io::{self, ErrorKind, Read, Write};
use std::fs::File; use std::fs::File;
use std::sync::Arc; use std::io::{self, ErrorKind, Read, Write};
use std::marker::Send; use std::marker::Send;
use std::sync::Arc;
use mio::{self, Events, PollOpt, Ready}; use mio::{self, Events, PollOpt, Ready};
use mio_extras::channel::{self, Receiver, Sender}; use mio_extras::channel::{self, Receiver, Sender};
@ -15,10 +15,10 @@ use mio::unix::UnixReady;
use crate::ansi; use crate::ansi;
use crate::display; use crate::display;
use crate::event; use crate::event;
use crate::tty;
use crate::term::Term;
use crate::util::thread;
use crate::sync::FairMutex; use crate::sync::FairMutex;
use crate::term::Term;
use crate::tty;
use crate::util::thread;
/// Messages that may be sent to the `EventLoop` /// Messages that may be sent to the `EventLoop`
#[derive(Debug)] #[derive(Debug)]
@ -58,14 +58,14 @@ enum DrainResult {
/// Nothing was available to receive /// Nothing was available to receive
Empty, Empty,
/// A shutdown message was received /// A shutdown message was received
Shutdown Shutdown,
} }
impl DrainResult { impl DrainResult {
pub fn is_shutdown(&self) -> bool { pub fn is_shutdown(&self) -> bool {
match *self { match *self {
DrainResult::Shutdown => true, DrainResult::Shutdown => true,
_ => false _ => false,
} }
} }
} }
@ -84,12 +84,13 @@ pub struct Notifier(pub Sender<Msg>);
impl event::Notify for Notifier { impl event::Notify for Notifier {
fn notify<B>(&mut self, bytes: B) fn notify<B>(&mut self, bytes: B)
where B: Into<Cow<'static, [u8]>>, where
B: Into<Cow<'static, [u8]>>,
{ {
let bytes = bytes.into(); let bytes = bytes.into();
// terminal hangs if we send 0 bytes through. // terminal hangs if we send 0 bytes through.
if bytes.len() == 0 { if bytes.len() == 0 {
return return;
} }
if self.0.send(Msg::Input(bytes)).is_err() { if self.0.send(Msg::Input(bytes)).is_err() {
panic!("expected send event loop msg"); panic!("expected send event loop msg");
@ -99,11 +100,7 @@ impl event::Notify for Notifier {
impl Default for State { impl Default for State {
fn default() -> State { fn default() -> State {
State { State { write_list: VecDeque::new(), parser: ansi::Processor::new(), writing: None }
write_list: VecDeque::new(),
parser: ansi::Processor::new(),
writing: None,
}
} }
} }
@ -117,9 +114,7 @@ impl State {
#[inline] #[inline]
fn goto_next(&mut self) { fn goto_next(&mut self) {
self.writing = self.write_list self.writing = self.write_list.pop_front().map(Writing::new);
.pop_front()
.map(Writing::new);
} }
#[inline] #[inline]
@ -161,8 +156,8 @@ impl Writing {
} }
impl<T> EventLoop<T> impl<T> EventLoop<T>
where where
T: tty::EventedPty + Send + 'static, T: tty::EventedPty + Send + 'static,
{ {
/// Create a new event loop /// Create a new event loop
pub fn new( pub fn new(
@ -198,10 +193,10 @@ impl<T> EventLoop<T>
match msg { match msg {
Msg::Input(input) => { Msg::Input(input) => {
state.write_list.push_back(input); state.write_list.push_back(input);
} },
Msg::Shutdown => { Msg::Shutdown => {
return DrainResult::Shutdown; return DrainResult::Shutdown;
} },
} }
} }
@ -233,8 +228,8 @@ impl<T> EventLoop<T>
buf: &mut [u8], buf: &mut [u8],
mut writer: Option<&mut X>, mut writer: Option<&mut X>,
) -> io::Result<()> ) -> io::Result<()>
where where
X: Write, X: Write,
{ {
const MAX_READ: usize = 0x1_0000; const MAX_READ: usize = 0x1_0000;
let mut processed = 0; let mut processed = 0;
@ -271,20 +266,18 @@ impl<T> EventLoop<T>
// Run the parser // Run the parser
for byte in &buf[..got] { for byte in &buf[..got] {
state state.parser.advance(&mut **terminal, *byte, &mut self.pty.writer());
.parser
.advance(&mut **terminal, *byte, &mut self.pty.writer());
} }
// Exit if we've processed enough bytes // Exit if we've processed enough bytes
if processed > MAX_READ { if processed > MAX_READ {
break; break;
} }
} },
Err(err) => match err.kind() { Err(err) => match err.kind() {
ErrorKind::Interrupted | ErrorKind::WouldBlock => { ErrorKind::Interrupted | ErrorKind::WouldBlock => {
break; break;
} },
_ => return Err(err), _ => return Err(err),
}, },
} }
@ -311,21 +304,21 @@ impl<T> EventLoop<T>
Ok(0) => { Ok(0) => {
state.set_current(Some(current)); state.set_current(Some(current));
break 'write_many; break 'write_many;
} },
Ok(n) => { Ok(n) => {
current.advance(n); current.advance(n);
if current.finished() { if current.finished() {
state.goto_next(); state.goto_next();
break 'write_one; break 'write_one;
} }
} },
Err(err) => { Err(err) => {
state.set_current(Some(current)); state.set_current(Some(current));
match err.kind() { match err.kind() {
ErrorKind::Interrupted | ErrorKind::WouldBlock => break 'write_many, ErrorKind::Interrupted | ErrorKind::WouldBlock => break 'write_many,
_ => return Err(err), _ => return Err(err),
} }
} },
} }
} }
} }
@ -343,14 +336,10 @@ impl<T> EventLoop<T>
let poll_opts = PollOpt::edge() | PollOpt::oneshot(); let poll_opts = PollOpt::edge() | PollOpt::oneshot();
let channel_token = tokens.next().unwrap(); let channel_token = tokens.next().unwrap();
self.poll self.poll.register(&self.rx, channel_token, Ready::readable(), poll_opts).unwrap();
.register(&self.rx, channel_token, Ready::readable(), poll_opts)
.unwrap();
// Register TTY through EventedRW interface // Register TTY through EventedRW interface
self.pty self.pty.register(&self.poll, &mut tokens, Ready::readable(), poll_opts).unwrap();
.register(&self.poll, &mut tokens, Ready::readable(), poll_opts)
.unwrap();
let mut events = Events::with_capacity(1024); let mut events = Events::with_capacity(1024);
@ -385,8 +374,12 @@ impl<T> EventLoop<T>
} }
}, },
token if token == self.pty.read_token() || token == self.pty.write_token() => { token
#[cfg(unix)] { if token == self.pty.read_token()
|| token == self.pty.write_token() =>
{
#[cfg(unix)]
{
if UnixReady::from(event.readiness()).is_hup() { if UnixReady::from(event.readiness()).is_hup() {
// don't try to do I/O on a dead PTY // don't try to do I/O on a dead PTY
continue; continue;
@ -395,7 +388,8 @@ impl<T> EventLoop<T>
if event.readiness().is_readable() { if event.readiness().is_readable() {
if let Err(e) = self.pty_read(&mut state, &mut buf, pipe.as_mut()) { if let Err(e) = self.pty_read(&mut state, &mut buf, pipe.as_mut()) {
#[cfg(target_os = "linux")] { #[cfg(target_os = "linux")]
{
// On Linux, a `read` on the master side of a PTY can fail // On Linux, a `read` on the master side of a PTY can fail
// with `EIO` if the client side hangs up. In that case, // with `EIO` if the client side hangs up. In that case,
// just loop back round for the inevitable `Exited` event. // just loop back round for the inevitable `Exited` event.

View File

@ -14,10 +14,10 @@
//! A specialized 2d grid implementation optimized for use in a terminal. //! A specialized 2d grid implementation optimized for use in a terminal.
use std::cmp::{min, max, Ordering}; use std::cmp::{max, min, Ordering};
use std::ops::{Deref, Range, Index, IndexMut, RangeTo, RangeFrom, RangeFull, RangeInclusive}; use std::ops::{Deref, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo};
use crate::index::{self, Point, Line, Column, IndexRange}; use crate::index::{self, Column, IndexRange, Line, Point};
use crate::selection::Selection; use crate::selection::Selection;
mod row; mod row;
@ -55,13 +55,13 @@ impl<T> Deref for Indexed<T> {
impl<T: PartialEq> ::std::cmp::PartialEq for Grid<T> { impl<T: PartialEq> ::std::cmp::PartialEq for Grid<T> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
// Compare struct fields and check result of grid comparison // Compare struct fields and check result of grid comparison
self.raw.eq(&other.raw) && self.raw.eq(&other.raw)
self.cols.eq(&other.cols) && && self.cols.eq(&other.cols)
self.lines.eq(&other.lines) && && self.lines.eq(&other.lines)
self.display_offset.eq(&other.display_offset) && && self.display_offset.eq(&other.display_offset)
self.scroll_limit.eq(&other.scroll_limit) && && self.scroll_limit.eq(&other.scroll_limit)
self.selection.eq(&other.selection) && && self.selection.eq(&other.selection)
self.url_highlight.eq(&other.url_highlight) && self.url_highlight.eq(&other.url_highlight)
} }
} }
@ -142,10 +142,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
} }
pub fn visible_to_buffer(&self, point: Point) -> Point<usize> { pub fn visible_to_buffer(&self, point: Point) -> Point<usize> {
Point { Point { line: self.visible_line_to_buffer(point.line), col: point.col }
line: self.visible_line_to_buffer(point.line),
col: point.col
}
} }
pub fn buffer_line_to_visible(&self, line: usize) -> ViewportPosition { pub fn buffer_line_to_visible(&self, line: usize) -> ViewportPosition {
@ -164,8 +161,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
} }
/// Update the size of the scrollback history /// Update the size of the scrollback history
pub fn update_history(&mut self, history_size: usize, template: &T) pub fn update_history(&mut self, history_size: usize, template: &T) {
{
self.raw.update_history(history_size, Row::new(self.cols, &template)); self.raw.update_history(history_size, Row::new(self.cols, &template));
self.max_scroll_limit = history_size; self.max_scroll_limit = history_size;
self.scroll_limit = min(self.scroll_limit, history_size); self.scroll_limit = min(self.scroll_limit, history_size);
@ -177,20 +173,14 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
Scroll::Lines(count) => { Scroll::Lines(count) => {
self.display_offset = min( self.display_offset = min(
max((self.display_offset as isize) + count, 0isize) as usize, max((self.display_offset as isize) + count, 0isize) as usize,
self.scroll_limit self.scroll_limit,
); );
}, },
Scroll::PageUp => { Scroll::PageUp => {
self.display_offset = min( self.display_offset = min(self.display_offset + self.lines.0, self.scroll_limit);
self.display_offset + self.lines.0,
self.scroll_limit
);
}, },
Scroll::PageDown => { Scroll::PageDown => {
self.display_offset -= min( self.display_offset -= min(self.display_offset, self.lines.0);
self.display_offset,
self.lines.0
);
}, },
Scroll::Top => self.display_offset = self.scroll_limit, Scroll::Top => self.display_offset = self.scroll_limit,
Scroll::Bottom => self.display_offset = 0, Scroll::Bottom => self.display_offset = 0,
@ -222,8 +212,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
} }
} }
fn increase_scroll_limit(&mut self, count: usize, template: &T) fn increase_scroll_limit(&mut self, count: usize, template: &T) {
{
self.scroll_limit = min(self.scroll_limit + count, self.max_scroll_limit); self.scroll_limit = min(self.scroll_limit + count, self.max_scroll_limit);
// Initialize new lines when the history buffer is smaller than the scroll limit // Initialize new lines when the history buffer is smaller than the scroll limit
@ -246,11 +235,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
/// Alacritty keeps the cursor at the bottom of the terminal as long as there /// Alacritty keeps the cursor at the bottom of the terminal as long as there
/// is scrollback available. Once scrollback is exhausted, new lines are /// is scrollback available. Once scrollback is exhausted, new lines are
/// simply added to the bottom of the screen. /// simply added to the bottom of the screen.
fn grow_lines( fn grow_lines(&mut self, new_line_count: index::Line, template: &T) {
&mut self,
new_line_count: index::Line,
template: &T,
) {
let lines_added = new_line_count - self.lines; let lines_added = new_line_count - self.lines;
// Need to "resize" before updating buffer // Need to "resize" before updating buffer
@ -435,7 +420,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
// Now, restore any scroll region lines // Now, restore any scroll region lines
let lines = self.lines; let lines = self.lines;
for i in IndexRange(region.end .. lines) { for i in IndexRange(region.end..lines) {
self.raw.swap_lines(i, i + positions); self.raw.swap_lines(i, i + positions);
} }
@ -449,7 +434,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
self.raw.swap_lines(line, line - positions); self.raw.swap_lines(line, line - positions);
} }
for line in IndexRange(region.start .. (region.start + positions)) { for line in IndexRange(region.start..(region.start + positions)) {
self.raw[line].reset(&template); self.raw[line].reset(&template);
} }
} }
@ -458,19 +443,12 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
/// scroll_up moves lines at the bottom towards the top /// scroll_up moves lines at the bottom towards the top
/// ///
/// This is the performance-sensitive part of scrolling. /// This is the performance-sensitive part of scrolling.
pub fn scroll_up( pub fn scroll_up(&mut self, region: &Range<index::Line>, positions: index::Line, template: &T) {
&mut self,
region: &Range<index::Line>,
positions: index::Line,
template: &T
) {
if region.start == Line(0) { if region.start == Line(0) {
// Update display offset when not pinned to active area // Update display offset when not pinned to active area
if self.display_offset != 0 { if self.display_offset != 0 {
self.display_offset = min( self.display_offset =
self.display_offset + *positions, min(self.display_offset + *positions, self.len() - self.num_lines().0);
self.len() - self.num_lines().0,
);
} }
self.increase_scroll_limit(*positions, template); self.increase_scroll_limit(*positions, template);
@ -506,7 +484,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
} }
// Clear reused lines // Clear reused lines
for line in IndexRange((region.end - positions) .. region.end) { for line in IndexRange((region.end - positions)..region.end) {
self.raw[line].reset(&template); self.raw[line].reset(&template);
} }
} }
@ -569,7 +547,7 @@ impl<T> Grid<T> {
/// This is used only for initializing after loading ref-tests /// This is used only for initializing after loading ref-tests
pub fn initialize_all(&mut self, template: &T) pub fn initialize_all(&mut self, template: &T)
where where
T: Copy T: Copy,
{ {
let history_size = self.raw.len().saturating_sub(*self.lines); let history_size = self.raw.len().saturating_sub(*self.lines);
self.raw.initialize(self.max_scroll_limit - history_size, Row::new(self.cols, template)); self.raw.initialize(self.max_scroll_limit - history_size, Row::new(self.cols, template));
@ -581,10 +559,7 @@ impl<T> Grid<T> {
} }
pub fn iter_from(&self, point: Point<usize>) -> GridIterator<'_, T> { pub fn iter_from(&self, point: Point<usize>) -> GridIterator<'_, T> {
GridIterator { GridIterator { grid: self, cur: point }
grid: self,
cur: point,
}
} }
#[inline] #[inline]
@ -613,8 +588,7 @@ impl<'a, T> Iterator for GridIterator<'a, T> {
let last_col = self.grid.num_cols() - Column(1); let last_col = self.grid.num_cols() - Column(1);
match self.cur { match self.cur {
Point { line, col } if line == 0 && col == last_col => None, Point { line, col } if line == 0 && col == last_col => None,
Point { col, .. } if Point { col, .. } if (col == last_col) => {
(col == last_col) => {
self.cur.line -= 1; self.cur.line -= 1;
self.cur.col = Column(0); self.cur.col = Column(0);
Some(&self.grid[self.cur.line][self.cur.col]) Some(&self.grid[self.cur.line][self.cur.col])
@ -622,7 +596,7 @@ impl<'a, T> Iterator for GridIterator<'a, T> {
_ => { _ => {
self.cur.col += Column(1); self.cur.col += Column(1);
Some(&self.grid[self.cur.line][self.cur.col]) Some(&self.grid[self.cur.line][self.cur.col])
} },
} }
} }
} }
@ -641,7 +615,7 @@ impl<'a, T> BidirectionalIterator for GridIterator<'a, T> {
_ => { _ => {
self.cur.col -= Column(1); self.cur.col -= Column(1);
Some(&self.grid[self.cur.line][self.cur.col]) Some(&self.grid[self.cur.line][self.cur.col])
} },
} }
} }
} }
@ -742,77 +716,48 @@ impl<T> IndexRegion<Range<Line>, T> for Grid<T> {
assert!(index.start < self.num_lines()); assert!(index.start < self.num_lines());
assert!(index.end <= self.num_lines()); assert!(index.end <= self.num_lines());
assert!(index.start <= index.end); assert!(index.start <= index.end);
Region { Region { start: index.start, end: index.end, raw: &self.raw }
start: index.start,
end: index.end,
raw: &self.raw
}
} }
fn region_mut(&mut self, index: Range<Line>) -> RegionMut<'_, T> { fn region_mut(&mut self, index: Range<Line>) -> RegionMut<'_, T> {
assert!(index.start < self.num_lines()); assert!(index.start < self.num_lines());
assert!(index.end <= self.num_lines()); assert!(index.end <= self.num_lines());
assert!(index.start <= index.end); assert!(index.start <= index.end);
RegionMut { RegionMut { start: index.start, end: index.end, raw: &mut self.raw }
start: index.start,
end: index.end,
raw: &mut self.raw
}
} }
} }
impl<T> IndexRegion<RangeTo<Line>, T> for Grid<T> { impl<T> IndexRegion<RangeTo<Line>, T> for Grid<T> {
fn region(&self, index: RangeTo<Line>) -> Region<'_, T> { fn region(&self, index: RangeTo<Line>) -> Region<'_, T> {
assert!(index.end <= self.num_lines()); assert!(index.end <= self.num_lines());
Region { Region { start: Line(0), end: index.end, raw: &self.raw }
start: Line(0),
end: index.end,
raw: &self.raw
}
} }
fn region_mut(&mut self, index: RangeTo<Line>) -> RegionMut<'_, T> { fn region_mut(&mut self, index: RangeTo<Line>) -> RegionMut<'_, T> {
assert!(index.end <= self.num_lines()); assert!(index.end <= self.num_lines());
RegionMut { RegionMut { start: Line(0), end: index.end, raw: &mut self.raw }
start: Line(0),
end: index.end,
raw: &mut self.raw
}
} }
} }
impl<T> IndexRegion<RangeFrom<Line>, T> for Grid<T> { impl<T> IndexRegion<RangeFrom<Line>, T> for Grid<T> {
fn region(&self, index: RangeFrom<Line>) -> Region<'_, T> { fn region(&self, index: RangeFrom<Line>) -> Region<'_, T> {
assert!(index.start < self.num_lines()); assert!(index.start < self.num_lines());
Region { Region { start: index.start, end: self.num_lines(), raw: &self.raw }
start: index.start,
end: self.num_lines(),
raw: &self.raw
}
} }
fn region_mut(&mut self, index: RangeFrom<Line>) -> RegionMut<'_, T> { fn region_mut(&mut self, index: RangeFrom<Line>) -> RegionMut<'_, T> {
assert!(index.start < self.num_lines()); assert!(index.start < self.num_lines());
RegionMut { RegionMut { start: index.start, end: self.num_lines(), raw: &mut self.raw }
start: index.start,
end: self.num_lines(),
raw: &mut self.raw
}
} }
} }
impl<T> IndexRegion<RangeFull, T> for Grid<T> { impl<T> IndexRegion<RangeFull, T> for Grid<T> {
fn region(&self, _: RangeFull) -> Region<'_, T> { fn region(&self, _: RangeFull) -> Region<'_, T> {
Region { Region { start: Line(0), end: self.num_lines(), raw: &self.raw }
start: Line(0),
end: self.num_lines(),
raw: &self.raw
}
} }
fn region_mut(&mut self, _: RangeFull) -> RegionMut<'_, T> { fn region_mut(&mut self, _: RangeFull) -> RegionMut<'_, T> {
RegionMut { RegionMut { start: Line(0), end: self.num_lines(), raw: &mut self.raw }
start: Line(0),
end: self.num_lines(),
raw: &mut self.raw
}
} }
} }
@ -829,33 +774,26 @@ pub struct RegionIterMut<'a, T> {
} }
impl<'a, T> IntoIterator for Region<'a, T> { impl<'a, T> IntoIterator for Region<'a, T> {
type Item = &'a Row<T>;
type IntoIter = RegionIter<'a, T>; type IntoIter = RegionIter<'a, T>;
type Item = &'a Row<T>;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
RegionIter { RegionIter { end: self.end, cur: self.start, raw: self.raw }
end: self.end,
cur: self.start,
raw: self.raw
}
} }
} }
impl<'a, T> IntoIterator for RegionMut<'a, T> { impl<'a, T> IntoIterator for RegionMut<'a, T> {
type Item = &'a mut Row<T>;
type IntoIter = RegionIterMut<'a, T>; type IntoIter = RegionIterMut<'a, T>;
type Item = &'a mut Row<T>;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
RegionIterMut { RegionIterMut { end: self.end, cur: self.start, raw: self.raw }
end: self.end,
cur: self.start,
raw: self.raw
}
} }
} }
impl<'a, T> Iterator for RegionIter<'a, T> { impl<'a, T> Iterator for RegionIter<'a, T> {
type Item = &'a Row<T>; type Item = &'a Row<T>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if self.cur < self.end { if self.cur < self.end {
let index = self.cur; let index = self.cur;
@ -869,13 +807,12 @@ impl<'a, T> Iterator for RegionIter<'a, T> {
impl<'a, T> Iterator for RegionIterMut<'a, T> { impl<'a, T> Iterator for RegionIterMut<'a, T> {
type Item = &'a mut Row<T>; type Item = &'a mut Row<T>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if self.cur < self.end { if self.cur < self.end {
let index = self.cur; let index = self.cur;
self.cur += 1; self.cur += 1;
unsafe { unsafe { Some(&mut *(&mut self.raw[index] as *mut _)) }
Some(&mut *(&mut self.raw[index] as *mut _))
}
} else { } else {
None None
} }
@ -898,7 +835,7 @@ pub struct DisplayIter<'a, T> {
impl<'a, T: 'a> DisplayIter<'a, T> { impl<'a, T: 'a> DisplayIter<'a, T> {
pub fn new(grid: &'a Grid<T>) -> DisplayIter<'a, T> { pub fn new(grid: &'a Grid<T>) -> DisplayIter<'a, T> {
let offset = grid.display_offset + *grid.num_lines() - 1; let offset = grid.display_offset + *grid.num_lines() - 1;
let limit = grid.display_offset; let limit = grid.display_offset;
let col = Column(0); let col = Column(0);
let line = Line(0); let line = Line(0);
@ -932,7 +869,7 @@ impl<'a, T: Copy + 'a> Iterator for DisplayIter<'a, T> {
let item = Some(Indexed { let item = Some(Indexed {
inner: self.grid.raw[self.offset][self.col], inner: self.grid.raw[self.offset][self.col],
line: self.line, line: self.line,
column: self.col column: self.col,
}); });
// Update line/col to point to next item // Update line/col to point to next item

View File

@ -14,9 +14,9 @@
//! Defines the Row type which makes up lines in the grid //! Defines the Row type which makes up lines in the grid
use std::cmp::{max, min};
use std::ops::{Index, IndexMut}; use std::ops::{Index, IndexMut};
use std::ops::{Range, RangeTo, RangeFrom, RangeFull, RangeToInclusive}; use std::ops::{Range, RangeFrom, RangeFull, RangeTo, RangeToInclusive};
use std::cmp::{min, max};
use std::slice; use std::slice;
use crate::grid::GridCell; use crate::grid::GridCell;
@ -46,10 +46,7 @@ impl<T: PartialEq> PartialEq for Row<T> {
impl<T: Copy> Row<T> { impl<T: Copy> Row<T> {
pub fn new(columns: Column, template: &T) -> Row<T> { pub fn new(columns: Column, template: &T) -> Row<T> {
Row { Row { inner: vec![*template; *columns], occ: 0 }
inner: vec![*template; *columns],
occ: 0,
}
} }
pub fn grow(&mut self, cols: Column, template: &T) { pub fn grow(&mut self, cols: Column, template: &T) {
@ -62,7 +59,7 @@ impl<T: Copy> Row<T> {
pub fn shrink(&mut self, cols: Column) -> Option<Vec<T>> pub fn shrink(&mut self, cols: Column) -> Option<Vec<T>>
where where
T: GridCell T: GridCell,
{ {
if self.inner.len() <= cols.0 { if self.inner.len() <= cols.0 {
return None; return None;
@ -96,10 +93,7 @@ impl<T: Copy> Row<T> {
impl<T> Row<T> { impl<T> Row<T> {
#[inline] #[inline]
pub fn from_vec(vec: Vec<T>, occ: usize) -> Row<T> { pub fn from_vec(vec: Vec<T>, occ: usize) -> Row<T> {
Row { Row { inner: vec, occ }
inner: vec,
occ,
}
} }
#[inline] #[inline]
@ -121,7 +115,7 @@ impl<T> Row<T> {
#[inline] #[inline]
pub fn append(&mut self, vec: &mut Vec<T>) pub fn append(&mut self, vec: &mut Vec<T>)
where where
T: GridCell T: GridCell,
{ {
self.inner.append(vec); self.inner.append(vec);
self.occ = self.inner.iter().rposition(|c| !c.is_empty()).map(|i| i + 1).unwrap_or(0); self.occ = self.inner.iter().rposition(|c| !c.is_empty()).map(|i| i + 1).unwrap_or(0);
@ -137,7 +131,7 @@ impl<T> Row<T> {
#[inline] #[inline]
pub fn is_empty(&self) -> bool pub fn is_empty(&self) -> bool
where where
T: GridCell T: GridCell,
{ {
self.inner.iter().all(|c| c.is_empty()) self.inner.iter().all(|c| c.is_empty())
} }
@ -153,8 +147,8 @@ impl<T> Row<T> {
} }
impl<'a, T> IntoIterator for &'a mut Row<T> { impl<'a, T> IntoIterator for &'a mut Row<T> {
type Item = &'a mut T;
type IntoIter = slice::IterMut<'a, T>; type IntoIter = slice::IterMut<'a, T>;
type Item = &'a mut T;
#[inline] #[inline]
fn into_iter(self) -> slice::IterMut<'a, T> { fn into_iter(self) -> slice::IterMut<'a, T> {

View File

@ -15,9 +15,9 @@ use std::ops::{Index, IndexMut};
use static_assertions::assert_eq_size; use static_assertions::assert_eq_size;
use crate::index::{Column, Line};
use crate::grid::GridCell;
use super::Row; use super::Row;
use crate::grid::GridCell;
use crate::index::{Column, Line};
/// Maximum number of invisible lines before buffer is resized /// Maximum number of invisible lines before buffer is resized
const TRUNCATE_STEP: usize = 100; const TRUNCATE_STEP: usize = 100;
@ -46,11 +46,8 @@ impl<T: PartialEq> ::std::cmp::PartialEq for Storage<T> {
} }
// Check which vec has the bigger zero // Check which vec has the bigger zero
let (ref bigger, ref smaller) = if self.zero >= other.zero { let (ref bigger, ref smaller) =
(self, other) if self.zero >= other.zero { (self, other) } else { (other, self) };
} else {
(other, self)
};
// Calculate the actual zero offset // Calculate the actual zero offset
let len = self.inner.len(); let len = self.inner.len();
@ -88,12 +85,7 @@ impl<T> Storage<T> {
// Initialize visible lines, the scrollback buffer is initialized dynamically // Initialize visible lines, the scrollback buffer is initialized dynamically
let inner = vec![template; lines.0]; let inner = vec![template; lines.0];
Storage { Storage { inner, zero: 0, visible_lines: lines - 1, len: lines.0 }
inner,
zero: 0,
visible_lines: lines - 1,
len: lines.0,
}
} }
/// Update the size of the scrollback history /// Update the size of the scrollback history
@ -179,7 +171,8 @@ impl<T> Storage<T> {
/// Dynamically grow the storage buffer at runtime /// Dynamically grow the storage buffer at runtime
pub fn initialize(&mut self, num_rows: usize, template_row: Row<T>) pub fn initialize(&mut self, num_rows: usize, template_row: Row<T>)
where T: Clone where
T: Clone,
{ {
let mut new = vec![template_row; num_rows]; let mut new = vec![template_row; num_rows];
@ -297,7 +290,7 @@ impl<T> Storage<T> {
#[inline] #[inline]
pub fn shrink_hidden(&mut self, cols: Column) pub fn shrink_hidden(&mut self, cols: Column)
where where
T: GridCell + Copy T: GridCell + Copy,
{ {
let start = self.zero + self.len; let start = self.zero + self.len;
let end = self.zero + self.inner.len(); let end = self.zero + self.inner.len();
@ -317,7 +310,7 @@ impl<T> Storage<T> {
#[inline] #[inline]
pub fn grow_hidden(&mut self, cols: Column, template: &T) pub fn grow_hidden(&mut self, cols: Column, template: &T)
where where
T: Copy + Clone T: Copy + Clone,
{ {
let start = self.zero + self.len; let start = self.zero + self.len;
let end = self.zero + self.inner.len(); let end = self.zero + self.inner.len();
@ -333,6 +326,7 @@ impl<T> Storage<T> {
impl<T> Index<usize> for Storage<T> { impl<T> Index<usize> for Storage<T> {
type Output = Row<T>; type Output = Row<T>;
#[inline] #[inline]
fn index(&self, index: usize) -> &Self::Output { fn index(&self, index: usize) -> &Self::Output {
&self.inner[self.compute_index(index)] &self.inner[self.compute_index(index)]
@ -349,6 +343,7 @@ impl<T> IndexMut<usize> for Storage<T> {
impl<T> Index<Line> for Storage<T> { impl<T> Index<Line> for Storage<T> {
type Output = Row<T>; type Output = Row<T>;
#[inline] #[inline]
fn index(&self, index: Line) -> &Self::Output { fn index(&self, index: Line) -> &Self::Output {
let index = self.visible_lines - index; let index = self.visible_lines - index;
@ -379,7 +374,11 @@ impl<T> IndexMut<Line> for Storage<T> {
fn grow_after_zero() { fn grow_after_zero() {
// Setup storage area // Setup storage area
let mut storage = Storage { let mut storage = Storage {
inner: vec![Row::new(Column(1), &'0'), Row::new(Column(1), &'1'), Row::new(Column(1), &'-')], inner: vec![
Row::new(Column(1), &'0'),
Row::new(Column(1), &'1'),
Row::new(Column(1), &'-'),
],
zero: 0, zero: 0,
visible_lines: Line(2), visible_lines: Line(2),
len: 3, len: 3,
@ -390,7 +389,12 @@ fn grow_after_zero() {
// Make sure the result is correct // Make sure the result is correct
let expected = Storage { let expected = Storage {
inner: vec![Row::new(Column(1), &'-'), Row::new(Column(1), &'0'), Row::new(Column(1), &'1'), Row::new(Column(1), &'-')], inner: vec![
Row::new(Column(1), &'-'),
Row::new(Column(1), &'0'),
Row::new(Column(1), &'1'),
Row::new(Column(1), &'-'),
],
zero: 1, zero: 1,
visible_lines: Line(0), visible_lines: Line(0),
len: 4, len: 4,
@ -415,7 +419,11 @@ fn grow_after_zero() {
fn grow_before_zero() { fn grow_before_zero() {
// Setup storage area // Setup storage area
let mut storage = Storage { let mut storage = Storage {
inner: vec![Row::new(Column(1), &'-'), Row::new(Column(1), &'0'), Row::new(Column(1), &'1')], inner: vec![
Row::new(Column(1), &'-'),
Row::new(Column(1), &'0'),
Row::new(Column(1), &'1'),
],
zero: 1, zero: 1,
visible_lines: Line(2), visible_lines: Line(2),
len: 3, len: 3,
@ -426,7 +434,12 @@ fn grow_before_zero() {
// Make sure the result is correct // Make sure the result is correct
let expected = Storage { let expected = Storage {
inner: vec![Row::new(Column(1), &'-'), Row::new(Column(1), &'-'), Row::new(Column(1), &'0'), Row::new(Column(1), &'1')], inner: vec![
Row::new(Column(1), &'-'),
Row::new(Column(1), &'-'),
Row::new(Column(1), &'0'),
Row::new(Column(1), &'1'),
],
zero: 2, zero: 2,
visible_lines: Line(0), visible_lines: Line(0),
len: 4, len: 4,
@ -450,7 +463,11 @@ fn grow_before_zero() {
fn shrink_before_zero() { fn shrink_before_zero() {
// Setup storage area // Setup storage area
let mut storage = Storage { let mut storage = Storage {
inner: vec![Row::new(Column(1), &'2'), Row::new(Column(1), &'0'), Row::new(Column(1), &'1')], inner: vec![
Row::new(Column(1), &'2'),
Row::new(Column(1), &'0'),
Row::new(Column(1), &'1'),
],
zero: 1, zero: 1,
visible_lines: Line(2), visible_lines: Line(2),
len: 3, len: 3,
@ -461,7 +478,11 @@ fn shrink_before_zero() {
// Make sure the result is correct // Make sure the result is correct
let expected = Storage { let expected = Storage {
inner: vec![Row::new(Column(1), &'2'), Row::new(Column(1), &'0'), Row::new(Column(1), &'1')], inner: vec![
Row::new(Column(1), &'2'),
Row::new(Column(1), &'0'),
Row::new(Column(1), &'1'),
],
zero: 1, zero: 1,
visible_lines: Line(0), visible_lines: Line(0),
len: 2, len: 2,
@ -485,7 +506,11 @@ fn shrink_before_zero() {
fn shrink_after_zero() { fn shrink_after_zero() {
// Setup storage area // Setup storage area
let mut storage = Storage { let mut storage = Storage {
inner: vec![Row::new(Column(1), &'0'), Row::new(Column(1), &'1'), Row::new(Column(1), &'2')], inner: vec![
Row::new(Column(1), &'0'),
Row::new(Column(1), &'1'),
Row::new(Column(1), &'2'),
],
zero: 0, zero: 0,
visible_lines: Line(2), visible_lines: Line(2),
len: 3, len: 3,
@ -496,7 +521,11 @@ fn shrink_after_zero() {
// Make sure the result is correct // Make sure the result is correct
let expected = Storage { let expected = Storage {
inner: vec![Row::new(Column(1), &'0'), Row::new(Column(1), &'1'), Row::new(Column(1), &'2')], inner: vec![
Row::new(Column(1), &'0'),
Row::new(Column(1), &'1'),
Row::new(Column(1), &'2'),
],
zero: 0, zero: 0,
visible_lines: Line(0), visible_lines: Line(0),
len: 2, len: 2,
@ -526,7 +555,14 @@ fn shrink_after_zero() {
fn shrink_before_and_after_zero() { fn shrink_before_and_after_zero() {
// Setup storage area // Setup storage area
let mut storage = Storage { let mut storage = Storage {
inner: vec![Row::new(Column(1), &'4'), Row::new(Column(1), &'5'), Row::new(Column(1), &'0'), Row::new(Column(1), &'1'), Row::new(Column(1), &'2'), Row::new(Column(1), &'3')], inner: vec![
Row::new(Column(1), &'4'),
Row::new(Column(1), &'5'),
Row::new(Column(1), &'0'),
Row::new(Column(1), &'1'),
Row::new(Column(1), &'2'),
Row::new(Column(1), &'3'),
],
zero: 2, zero: 2,
visible_lines: Line(5), visible_lines: Line(5),
len: 6, len: 6,
@ -537,7 +573,14 @@ fn shrink_before_and_after_zero() {
// Make sure the result is correct // Make sure the result is correct
let expected = Storage { let expected = Storage {
inner: vec![Row::new(Column(1), &'4'), Row::new(Column(1), &'5'), Row::new(Column(1), &'0'), Row::new(Column(1), &'1'), Row::new(Column(1), &'2'), Row::new(Column(1), &'3')], inner: vec![
Row::new(Column(1), &'4'),
Row::new(Column(1), &'5'),
Row::new(Column(1), &'0'),
Row::new(Column(1), &'1'),
Row::new(Column(1), &'2'),
Row::new(Column(1), &'3'),
],
zero: 2, zero: 2,
visible_lines: Line(0), visible_lines: Line(0),
len: 2, len: 2,
@ -563,7 +606,14 @@ fn shrink_before_and_after_zero() {
fn truncate_invisible_lines() { fn truncate_invisible_lines() {
// Setup storage area // Setup storage area
let mut storage = Storage { let mut storage = Storage {
inner: vec![Row::new(Column(1), &'4'), Row::new(Column(1), &'5'), Row::new(Column(1), &'0'), Row::new(Column(1), &'1'), Row::new(Column(1), &'2'), Row::new(Column(1), &'3')], inner: vec![
Row::new(Column(1), &'4'),
Row::new(Column(1), &'5'),
Row::new(Column(1), &'0'),
Row::new(Column(1), &'1'),
Row::new(Column(1), &'2'),
Row::new(Column(1), &'3'),
],
zero: 2, zero: 2,
visible_lines: Line(1), visible_lines: Line(1),
len: 2, len: 2,
@ -598,7 +648,11 @@ fn truncate_invisible_lines() {
fn truncate_invisible_lines_beginning() { fn truncate_invisible_lines_beginning() {
// Setup storage area // Setup storage area
let mut storage = Storage { let mut storage = Storage {
inner: vec![Row::new(Column(1), &'1'), Row::new(Column(1), &'2'), Row::new(Column(1), &'0')], inner: vec![
Row::new(Column(1), &'1'),
Row::new(Column(1), &'2'),
Row::new(Column(1), &'0'),
],
zero: 2, zero: 2,
visible_lines: Line(1), visible_lines: Line(1),
len: 2, len: 2,

View File

@ -14,10 +14,10 @@
//! Tests for the Gird //! Tests for the Gird
use super::{Grid, BidirectionalIterator}; use super::{BidirectionalIterator, Grid};
use crate::index::{Point, Line, Column};
use crate::term::cell::{Cell, Flags};
use crate::grid::GridCell; use crate::grid::GridCell;
use crate::index::{Column, Line, Point};
use crate::term::cell::{Cell, Flags};
impl GridCell for usize { impl GridCell for usize {
fn is_empty(&self) -> bool { fn is_empty(&self) -> bool {
@ -101,14 +101,11 @@ fn test_iter() {
let mut grid = Grid::new(Line(5), Column(5), 0, 0); let mut grid = Grid::new(Line(5), Column(5), 0, 0);
for i in 0..5 { for i in 0..5 {
for j in 0..5 { for j in 0..5 {
grid[Line(i)][Column(j)] = i*5 + j; grid[Line(i)][Column(j)] = i * 5 + j;
} }
} }
let mut iter = grid.iter_from(Point { let mut iter = grid.iter_from(Point { line: 4, col: Column(0) });
line: 4,
col: Column(0),
});
assert_eq!(None, iter.prev()); assert_eq!(None, iter.prev());
assert_eq!(Some(&1), iter.next()); assert_eq!(Some(&1), iter.next());
@ -128,12 +125,8 @@ fn test_iter() {
assert_eq!(Column(4), iter.cur.col); assert_eq!(Column(4), iter.cur.col);
assert_eq!(4, iter.cur.line); assert_eq!(4, iter.cur.line);
// test that iter ends at end of grid // test that iter ends at end of grid
let mut final_iter = grid.iter_from(Point { let mut final_iter = grid.iter_from(Point { line: 0, col: Column(4) });
line: 0,
col: Column(4),
});
assert_eq!(None, final_iter.next()); assert_eq!(None, final_iter.next());
assert_eq!(Some(&23), final_iter.prev()); assert_eq!(Some(&23), final_iter.prev());
} }

View File

@ -17,18 +17,18 @@
/// Indexing types and implementations for Grid and Line /// Indexing types and implementations for Grid and Line
use std::cmp::{Ord, Ordering}; use std::cmp::{Ord, Ordering};
use std::fmt; use std::fmt;
use std::ops::{self, Deref, Range, RangeInclusive, Add, Sub, AddAssign, SubAssign}; use std::ops::{self, Add, AddAssign, Deref, Range, RangeInclusive, Sub, SubAssign};
/// The side of a cell /// The side of a cell
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Side { pub enum Side {
Left, Left,
Right Right,
} }
/// Index in the grid using row, column notation /// Index in the grid using row, column notation
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Serialize, Deserialize, PartialOrd)] #[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Serialize, Deserialize, PartialOrd)]
pub struct Point<L=Line> { pub struct Point<L = Line> {
pub line: L, pub line: L,
pub col: Column, pub col: Column,
} }
@ -43,11 +43,10 @@ impl Ord for Point {
fn cmp(&self, other: &Point) -> Ordering { fn cmp(&self, other: &Point) -> Ordering {
use std::cmp::Ordering::*; use std::cmp::Ordering::*;
match (self.line.cmp(&other.line), self.col.cmp(&other.col)) { match (self.line.cmp(&other.line), self.col.cmp(&other.col)) {
(Equal, Equal) => Equal, (Equal, Equal) => Equal,
(Equal, ord) | (Equal, ord) | (ord, Equal) => ord,
(ord, Equal) => ord, (Less, _) => Less,
(Less, _) => Less, (Greater, _) => Greater,
(Greater, _) => Greater,
} }
} }
} }
@ -156,7 +155,7 @@ macro_rules! forward_ref_binop {
$imp::$method(*self, *other) $imp::$method(*self, *other)
} }
} }
} };
} }
/// Macro for deriving deref /// Macro for deriving deref
@ -170,7 +169,7 @@ macro_rules! deref {
&self.0 &self.0
} }
} }
} };
} }
macro_rules! add { macro_rules! add {
@ -183,7 +182,7 @@ macro_rules! add {
$construct(self.0 + rhs.0) $construct(self.0 + rhs.0)
} }
} }
} };
} }
macro_rules! sub { macro_rules! sub {
@ -223,7 +222,7 @@ macro_rules! sub {
$construct(self.0 - rhs.0) $construct(self.0 - rhs.0)
} }
} }
} };
} }
/// This exists because we can't implement Iterator on Range /// This exists because we can't implement Iterator on Range
@ -246,6 +245,7 @@ pub trait Contains {
impl<T: PartialOrd<T>> Contains for Range<T> { impl<T: PartialOrd<T>> Contains for Range<T> {
type Content = T; type Content = T;
fn contains_(&self, item: Self::Content) -> bool { fn contains_(&self, item: Self::Content) -> bool {
(self.start <= item) && (item < self.end) (self.start <= item) && (item < self.end)
} }
@ -253,6 +253,7 @@ impl<T: PartialOrd<T>> Contains for Range<T> {
impl<T: PartialOrd<T>> Contains for RangeInclusive<T> { impl<T: PartialOrd<T>> Contains for RangeInclusive<T> {
type Content = T; type Content = T;
fn contains_(&self, item: Self::Content) -> bool { fn contains_(&self, item: Self::Content) -> bool {
(self.start() <= &item) && (&item <= self.end()) (self.start() <= &item) && (&item <= self.end())
} }
@ -383,7 +384,7 @@ ops!(Linear, Linear);
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{Line, Column, Point}; use super::{Column, Line, Point};
#[test] #[test]
fn location_ordering() { fn location_ordering() {

View File

@ -20,27 +20,27 @@
//! determine what to do when a non-modifier key is pressed. //! determine what to do when a non-modifier key is pressed.
use std::borrow::Cow; use std::borrow::Cow;
use std::mem; use std::mem;
use std::time::Instant;
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
use std::time::Instant;
use copypasta::{Clipboard, Load, Buffer as ClipboardBuffer}; use copypasta::{Buffer as ClipboardBuffer, Clipboard, Load};
use unicode_width::UnicodeWidthStr;
use glutin::{ use glutin::{
ElementState, KeyboardInput, ModifiersState, MouseButton, MouseCursor, MouseScrollDelta, ElementState, KeyboardInput, ModifiersState, MouseButton, MouseCursor, MouseScrollDelta,
TouchPhase, TouchPhase,
}; };
use unicode_width::UnicodeWidthStr;
use crate::ansi::{ClearMode, Handler};
use crate::config::{self, Key}; use crate::config::{self, Key};
use crate::grid::Scroll;
use crate::event::{ClickState, Mouse}; use crate::event::{ClickState, Mouse};
use crate::index::{Line, Column, Side, Point, Linear}; use crate::grid::Scroll;
use crate::term::{Term, SizeInfo, Search}; use crate::index::{Column, Line, Linear, Point, Side};
use crate::message_bar::{self, Message};
use crate::term::mode::TermMode; use crate::term::mode::TermMode;
use crate::term::{Search, SizeInfo, Term};
use crate::url::Url;
use crate::util::fmt::Red; use crate::util::fmt::Red;
use crate::util::start_daemon; use crate::util::start_daemon;
use crate::message_bar::{self, Message};
use crate::ansi::{Handler, ClearMode};
use crate::url::Url;
pub const FONT_SIZE_STEP: f32 = 0.5; pub const FONT_SIZE_STEP: f32 = 0.5;
@ -148,10 +148,10 @@ impl<T: Eq> Binding<T> {
// Check input first since bindings are stored in one big list. This is // Check input first since bindings are stored in one big list. This is
// the most likely item to fail so prioritizing it here allows more // the most likely item to fail so prioritizing it here allows more
// checks to be short circuited. // checks to be short circuited.
self.trigger == *input && self.trigger == *input
self.mode_matches(mode) && && self.mode_matches(mode)
self.not_mode_matches(mode) && && self.not_mode_matches(mode)
self.mods_match(mods, relaxed) && self.mods_match(mods, relaxed)
} }
#[inline] #[inline]
@ -267,8 +267,8 @@ impl Action {
}, },
Action::Paste => { Action::Paste => {
Clipboard::new() Clipboard::new()
.and_then(|clipboard| clipboard.load_primary() ) .and_then(|clipboard| clipboard.load_primary())
.map(|contents| { self.paste(ctx, &contents) }) .map(|contents| self.paste(ctx, &contents))
.unwrap_or_else(|err| { .unwrap_or_else(|err| {
error!("Error loading data from clipboard: {}", Red(err)); error!("Error loading data from clipboard: {}", Red(err));
}); });
@ -277,8 +277,8 @@ impl Action {
// Only paste if mouse events are not captured by an application // Only paste if mouse events are not captured by an application
if !mouse_mode { if !mouse_mode {
Clipboard::new() Clipboard::new()
.and_then(|clipboard| clipboard.load_selection() ) .and_then(|clipboard| clipboard.load_selection())
.map(|contents| { self.paste(ctx, &contents) }) .map(|contents| self.paste(ctx, &contents))
.unwrap_or_else(|err| { .unwrap_or_else(|err| {
error!("Error loading data from clipboard: {}", Red(err)); error!("Error loading data from clipboard: {}", Red(err));
}); });
@ -303,13 +303,13 @@ impl Action {
ctx.terminal_mut().exit(); ctx.terminal_mut().exit();
}, },
Action::IncreaseFontSize => { Action::IncreaseFontSize => {
ctx.terminal_mut().change_font_size(FONT_SIZE_STEP); ctx.terminal_mut().change_font_size(FONT_SIZE_STEP);
}, },
Action::DecreaseFontSize => { Action::DecreaseFontSize => {
ctx.terminal_mut().change_font_size(-FONT_SIZE_STEP); ctx.terminal_mut().change_font_size(-FONT_SIZE_STEP);
} },
Action::ResetFontSize => { Action::ResetFontSize => {
ctx.terminal_mut().reset_font_size(); ctx.terminal_mut().reset_font_size();
}, },
Action::ScrollPageUp => { Action::ScrollPageUp => {
ctx.scroll(Scroll::PageUp); ctx.scroll(Scroll::PageUp);
@ -339,7 +339,7 @@ impl Action {
fn paste<A: ActionContext>(&self, ctx: &mut A, contents: &str) { fn paste<A: ActionContext>(&self, ctx: &mut A, contents: &str) {
if ctx.terminal().mode().contains(TermMode::BRACKETED_PASTE) { if ctx.terminal().mode().contains(TermMode::BRACKETED_PASTE) {
ctx.write_to_pty(&b"\x1b[200~"[..]); ctx.write_to_pty(&b"\x1b[200~"[..]);
ctx.write_to_pty(contents.replace("\x1b","").into_bytes()); ctx.write_to_pty(contents.replace("\x1b", "").into_bytes());
ctx.write_to_pty(&b"\x1b[201~"[..]); ctx.write_to_pty(&b"\x1b[201~"[..]);
} else { } else {
// In non-bracketed (ie: normal) mode, terminal applications cannot distinguish // In non-bracketed (ie: normal) mode, terminal applications cannot distinguish
@ -348,7 +348,7 @@ impl Action {
// pasting... since that's neither practical nor sensible (and probably an impossible // pasting... since that's neither practical nor sensible (and probably an impossible
// task to solve in a general way), we'll just replace line breaks (windows and unix // task to solve in a general way), we'll just replace line breaks (windows and unix
// style) with a singe carriage return (\r, which is what the Enter key produces). // style) with a singe carriage return (\r, which is what the Enter key produces).
ctx.write_to_pty(contents.replace("\r\n","\r").replace("\n","\r").into_bytes()); ctx.write_to_pty(contents.replace("\r\n", "\r").replace("\n", "\r").into_bytes());
} }
} }
} }
@ -411,13 +411,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
if self.ctx.mouse().left_button_state == ElementState::Pressed if self.ctx.mouse().left_button_state == ElementState::Pressed
&& (modifiers.shift || !self.ctx.terminal().mode().intersects(report_mode)) && (modifiers.shift || !self.ctx.terminal().mode().intersects(report_mode))
{ {
self.ctx.update_selection( self.ctx.update_selection(Point { line: point.line, col: point.col }, cell_side);
Point {
line: point.line,
col: point.col,
},
cell_side,
);
} else if self.ctx.terminal().mode().intersects(motion_mode) } else if self.ctx.terminal().mode().intersects(motion_mode)
&& size_info.contains_point(x, y, false) && size_info.contains_point(x, y, false)
{ {
@ -443,7 +437,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
&& (!self.ctx.terminal().mode().intersects(mouse_mode) || modifiers.shift) && (!self.ctx.terminal().mode().intersects(mouse_mode) || modifiers.shift)
&& self.mouse_config.url.launcher.is_some() && self.mouse_config.url.launcher.is_some()
{ {
self.ctx.terminal().url_search(point.into()) self.ctx.terminal().url_search(point.into())
} else { } else {
None None
}; };
@ -582,14 +576,14 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
self.ctx.mouse_mut().block_url_launcher = true; self.ctx.mouse_mut().block_url_launcher = true;
self.on_mouse_double_click(button, point); self.on_mouse_double_click(button, point);
ClickState::DoubleClick ClickState::DoubleClick
}, }
ClickState::DoubleClick ClickState::DoubleClick
if !button_changed && elapsed < self.mouse_config.triple_click.threshold => if !button_changed && elapsed < self.mouse_config.triple_click.threshold =>
{ {
self.ctx.mouse_mut().block_url_launcher = true; self.ctx.mouse_mut().block_url_launcher = true;
self.on_mouse_triple_click(button, point); self.on_mouse_triple_click(button, point);
ClickState::TripleClick ClickState::TripleClick
}, }
_ => { _ => {
// Don't launch URLs if this click cleared the selection // Don't launch URLs if this click cleared the selection
self.ctx.mouse_mut().block_url_launcher = !self.ctx.selection_is_empty(); self.ctx.mouse_mut().block_url_launcher = !self.ctx.selection_is_empty();
@ -617,7 +611,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
} }
ClickState::Click ClickState::Click
} },
}; };
} }
@ -690,7 +684,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
}, },
_ => (), _ => (),
} }
} },
} }
} }
@ -744,11 +738,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
pub fn on_focus_change(&mut self, is_focused: bool) { pub fn on_focus_change(&mut self, is_focused: bool) {
if self.ctx.terminal().mode().contains(TermMode::FOCUS_IN_OUT) { if self.ctx.terminal().mode().contains(TermMode::FOCUS_IN_OUT) {
let chr = if is_focused { let chr = if is_focused { "I" } else { "O" };
"I"
} else {
"O"
};
let msg = format!("\x1b[{}", chr); let msg = format!("\x1b[{}", chr);
self.ctx.write_to_pty(msg.into_bytes()); self.ctx.write_to_pty(msg.into_bytes());
@ -759,7 +749,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
&mut self, &mut self,
state: ElementState, state: ElementState,
button: MouseButton, button: MouseButton,
modifiers: ModifiersState modifiers: ModifiersState,
) { ) {
match button { match button {
MouseButton::Left => self.ctx.mouse_mut().left_button_state = state, MouseButton::Left => self.ctx.mouse_mut().left_button_state = state,
@ -849,16 +839,18 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
&Key::Scancode(input.scancode), &Key::Scancode(input.scancode),
false, false,
), ),
_ => if let Some(key) = input.virtual_keycode { _ => {
let key = Key::from_glutin_input(key); if let Some(key) = input.virtual_keycode {
binding.is_triggered_by( let key = Key::from_glutin_input(key);
*self.ctx.terminal().mode(), binding.is_triggered_by(
input.modifiers, *self.ctx.terminal().mode(),
&key, input.modifiers,
&key,
false,
)
} else {
false false
) }
} else {
false
}, },
}; };
@ -883,11 +875,12 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
for binding in self.mouse_bindings { for binding in self.mouse_bindings {
if binding.is_triggered_by(*self.ctx.terminal().mode(), mods, &button, true) { if binding.is_triggered_by(*self.ctx.terminal().mode(), mods, &button, true) {
// binding was triggered; run the action // binding was triggered; run the action
let mouse_mode = !mods.shift && self.ctx.terminal().mode().intersects( let mouse_mode = !mods.shift
TermMode::MOUSE_REPORT_CLICK && self.ctx.terminal().mode().intersects(
| TermMode::MOUSE_DRAG TermMode::MOUSE_REPORT_CLICK
| TermMode::MOUSE_MOTION | TermMode::MOUSE_DRAG
); | TermMode::MOUSE_MOTION,
);
binding.execute(&mut self.ctx, mouse_mode); binding.execute(&mut self.ctx, mouse_mode);
has_binding = true; has_binding = true;
} }
@ -898,10 +891,9 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
/// Return the message bar's message if there is some at the specified point /// Return the message bar's message if there is some at the specified point
fn message_at_point(&mut self, point: Option<Point>) -> Option<Message> { fn message_at_point(&mut self, point: Option<Point>) -> Option<Message> {
if let (Some(point), Some(message)) = ( if let (Some(point), Some(message)) =
point, (point, self.ctx.terminal_mut().message_buffer_mut().message())
self.ctx.terminal_mut().message_buffer_mut().message(), {
) {
let size = self.ctx.size_info(); let size = self.ctx.size_info();
if point.line.0 >= size.lines().saturating_sub(message.text(&size).len()) { if point.line.0 >= size.lines().saturating_sub(message.text(&size).len()) {
return Some(message); return Some(message);
@ -924,7 +916,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
} }
self.ctx.clear_selection(); self.ctx.clear_selection();
} },
} }
} }
@ -942,15 +934,15 @@ mod tests {
use std::borrow::Cow; use std::borrow::Cow;
use std::time::Duration; use std::time::Duration;
use glutin::{VirtualKeyCode, Event, WindowEvent, ElementState, MouseButton, ModifiersState}; use glutin::{ElementState, Event, ModifiersState, MouseButton, VirtualKeyCode, WindowEvent};
use crate::term::{SizeInfo, Term, TermMode}; use crate::config::{self, ClickHandler, Config};
use crate::event::{Mouse, ClickState, WindowChanges}; use crate::event::{ClickState, Mouse, WindowChanges};
use crate::config::{self, Config, ClickHandler};
use crate::index::{Point, Side};
use crate::selection::Selection;
use crate::grid::Scroll; use crate::grid::Scroll;
use crate::index::{Point, Side};
use crate::message_bar::MessageBuffer; use crate::message_bar::MessageBuffer;
use crate::selection::Selection;
use crate::term::{SizeInfo, Term, TermMode};
use super::{Action, Binding, Processor}; use super::{Action, Binding, Processor};
use copypasta::Buffer as ClipboardBuffer; use copypasta::Buffer as ClipboardBuffer;
@ -976,13 +968,19 @@ mod tests {
pub window_changes: &'a mut WindowChanges, pub window_changes: &'a mut WindowChanges,
} }
impl <'a>super::ActionContext for ActionContext<'a> { impl<'a> super::ActionContext for ActionContext<'a> {
fn write_to_pty<B: Into<Cow<'static, [u8]>>>(&mut self, _val: B) {} fn write_to_pty<B: Into<Cow<'static, [u8]>>>(&mut self, _val: B) {}
fn update_selection(&mut self, _point: Point, _side: Side) {} fn update_selection(&mut self, _point: Point, _side: Side) {}
fn simple_selection(&mut self, _point: Point, _side: Side) {} fn simple_selection(&mut self, _point: Point, _side: Side) {}
fn copy_selection(&self, _buffer: ClipboardBuffer) {} fn copy_selection(&self, _buffer: ClipboardBuffer) {}
fn clear_selection(&mut self) {} fn clear_selection(&mut self) {}
fn hide_window(&mut self) {} fn hide_window(&mut self) {}
fn spawn_new_instance(&mut self) {} fn spawn_new_instance(&mut self) {}
fn terminal(&self) -> &Term { fn terminal(&self) -> &Term {

View File

@ -17,8 +17,10 @@
#![cfg_attr(feature = "nightly", feature(core_intrinsics))] #![cfg_attr(feature = "nightly", feature(core_intrinsics))]
#![cfg_attr(all(test, feature = "bench"), feature(test))] #![cfg_attr(all(test, feature = "bench"), feature(test))]
#[macro_use] extern crate log; #[macro_use]
#[macro_use] extern crate serde_derive; extern crate log;
#[macro_use]
extern crate serde_derive;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
#[macro_use] #[macro_use]
@ -37,6 +39,7 @@ pub mod index;
pub mod input; pub mod input;
pub mod locale; pub mod locale;
pub mod logging; pub mod logging;
pub mod message_bar;
pub mod meter; pub mod meter;
pub mod panic; pub mod panic;
pub mod renderer; pub mod renderer;
@ -44,10 +47,9 @@ pub mod selection;
pub mod sync; pub mod sync;
pub mod term; pub mod term;
pub mod tty; pub mod tty;
mod url;
pub mod util; pub mod util;
pub mod window; pub mod window;
pub mod message_bar;
mod url;
pub use crate::grid::Grid; pub use crate::grid::Grid;
pub use crate::term::Term; pub use crate::term::Term;

View File

@ -13,13 +13,13 @@
// limitations under the License. // limitations under the License.
#![allow(clippy::let_unit_value)] #![allow(clippy::let_unit_value)]
#![cfg(target_os = "macos")] #![cfg(target_os = "macos")]
use libc::{LC_CTYPE, setlocale}; use libc::{setlocale, LC_CTYPE};
use std::ffi::{CString, CStr}; use std::env;
use std::ffi::{CStr, CString};
use std::os::raw::c_char; use std::os::raw::c_char;
use std::ptr::null; use std::ptr::null;
use std::slice; use std::slice;
use std::str; use std::str;
use std::env;
use objc::runtime::{Class, Object}; use objc::runtime::{Class, Object};
@ -27,30 +27,32 @@ pub fn set_locale_environment() {
let locale_id = unsafe { let locale_id = unsafe {
let locale_class = Class::get("NSLocale").unwrap(); let locale_class = Class::get("NSLocale").unwrap();
let locale: *const Object = msg_send![locale_class, currentLocale]; let locale: *const Object = msg_send![locale_class, currentLocale];
let _ : () = msg_send![locale_class, release]; let _: () = msg_send![locale_class, release];
// `localeIdentifier` returns extra metadata with the locale (including currency and // `localeIdentifier` returns extra metadata with the locale (including currency and
// collator) on newer versions of macOS. This is not a valid locale, so we use // collator) on newer versions of macOS. This is not a valid locale, so we use
// `languageCode` and `countryCode`, if they're available (macOS 10.12+): // `languageCode` and `countryCode`, if they're available (macOS 10.12+):
// https://developer.apple.com/documentation/foundation/nslocale/1416263-localeidentifier?language=objc // https://developer.apple.com/documentation/foundation/nslocale/1416263-localeidentifier?language=objc
// https://developer.apple.com/documentation/foundation/nslocale/1643060-countrycode?language=objc // https://developer.apple.com/documentation/foundation/nslocale/1643060-countrycode?language=objc
// https://developer.apple.com/documentation/foundation/nslocale/1643026-languagecode?language=objc // https://developer.apple.com/documentation/foundation/nslocale/1643026-languagecode?language=objc
let is_language_code_supported: bool = msg_send![locale, respondsToSelector:sel!(languageCode)]; let is_language_code_supported: bool =
let is_country_code_supported: bool = msg_send![locale, respondsToSelector:sel!(countryCode)]; msg_send![locale, respondsToSelector: sel!(languageCode)];
let is_country_code_supported: bool =
msg_send![locale, respondsToSelector: sel!(countryCode)];
let locale_id = if is_language_code_supported && is_country_code_supported { let locale_id = if is_language_code_supported && is_country_code_supported {
let language_code: *const Object = msg_send![locale, languageCode]; let language_code: *const Object = msg_send![locale, languageCode];
let country_code: *const Object = msg_send![locale, countryCode]; let country_code: *const Object = msg_send![locale, countryCode];
let language_code_str = nsstring_as_str(language_code).to_owned(); let language_code_str = nsstring_as_str(language_code).to_owned();
let _ : () = msg_send![language_code, release]; let _: () = msg_send![language_code, release];
let country_code_str = nsstring_as_str(country_code).to_owned(); let country_code_str = nsstring_as_str(country_code).to_owned();
let _ : () = msg_send![country_code, release]; let _: () = msg_send![country_code, release];
format!("{}_{}.UTF-8", &language_code_str, &country_code_str) format!("{}_{}.UTF-8", &language_code_str, &country_code_str)
} else { } else {
let identifier: *const Object = msg_send![locale, localeIdentifier]; let identifier: *const Object = msg_send![locale, localeIdentifier];
let identifier_str = nsstring_as_str(identifier).to_owned(); let identifier_str = nsstring_as_str(identifier).to_owned();
let _ : () = msg_send![identifier, release]; let _: () = msg_send![identifier, release];
identifier_str + ".UTF-8" identifier_str + ".UTF-8"
}; };
let _ : () = msg_send![locale, release]; let _: () = msg_send![locale, release];
locale_id locale_id
}; };
// check if locale_id is valid // check if locale_id is valid
@ -66,11 +68,7 @@ pub fn set_locale_environment() {
}; };
// try setting `locale_id` // try setting `locale_id`
let modified = setlocale(LC_CTYPE, locale_ptr); let modified = setlocale(LC_CTYPE, locale_ptr);
let result = if modified.is_null() { let result = if modified.is_null() { String::new() } else { locale_id };
String::new()
} else {
locale_id
};
// restore original setting // restore original setting
setlocale(LC_CTYPE, saved_original.as_ptr()); setlocale(LC_CTYPE, saved_original.as_ptr());
result result

View File

@ -68,12 +68,7 @@ impl Logger {
let logfile = Mutex::new(OnDemandLogFile::new()); let logfile = Mutex::new(OnDemandLogFile::new());
let stdout = Mutex::new(LineWriter::new(io::stdout())); let stdout = Mutex::new(LineWriter::new(io::stdout()));
Logger { Logger { level, logfile, stdout, message_tx }
level,
logfile,
stdout,
message_tx,
}
} }
fn file_path(&self) -> Option<PathBuf> { fn file_path(&self) -> Option<PathBuf> {
@ -100,10 +95,7 @@ impl log::Log for Logger {
now, now,
record.level(), record.level(),
record.file().unwrap_or("?"), record.file().unwrap_or("?"),
record record.line().map(|l| l.to_string()).unwrap_or_else(|| "?".into()),
.line()
.map(|l| l.to_string())
.unwrap_or_else(|| "?".into()),
record.args() record.args()
) )
} else { } else {
@ -161,11 +153,7 @@ impl OnDemandLogFile {
// Set log path as an environment variable // Set log path as an environment variable
env::set_var(ALACRITTY_LOG_ENV, path.as_os_str()); env::set_var(ALACRITTY_LOG_ENV, path.as_os_str());
OnDemandLogFile { OnDemandLogFile { path, file: None, created: Arc::new(AtomicBool::new(false)) }
path,
file: None,
created: Arc::new(AtomicBool::new(false)),
}
} }
fn file(&mut self) -> Result<&mut LineWriter<File>, io::Error> { fn file(&mut self) -> Result<&mut LineWriter<File>, io::Error> {
@ -176,21 +164,18 @@ impl OnDemandLogFile {
// Create the file if it doesn't exist yet // Create the file if it doesn't exist yet
if self.file.is_none() { if self.file.is_none() {
let file = OpenOptions::new() let file = OpenOptions::new().append(true).create(true).open(&self.path);
.append(true)
.create(true)
.open(&self.path);
match file { match file {
Ok(file) => { Ok(file) => {
self.file = Some(io::LineWriter::new(file)); self.file = Some(io::LineWriter::new(file));
self.created.store(true, Ordering::Relaxed); self.created.store(true, Ordering::Relaxed);
let _ = writeln!(io::stdout(), "Created log file at {:?}", self.path); let _ = writeln!(io::stdout(), "Created log file at {:?}", self.path);
} },
Err(e) => { Err(e) => {
let _ = writeln!(io::stdout(), "Unable to create log file: {}", e); let _ = writeln!(io::stdout(), "Unable to create log file: {}", e);
return Err(e); return Err(e);
} },
} }
} }

View File

@ -16,7 +16,6 @@
#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use, clippy::wrong_pub_self_convention)] #![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use, clippy::wrong_pub_self_convention)]
#![cfg_attr(feature = "nightly", feature(core_intrinsics))] #![cfg_attr(feature = "nightly", feature(core_intrinsics))]
#![cfg_attr(all(test, feature = "bench"), feature(test))] #![cfg_attr(all(test, feature = "bench"), feature(test))]
// With the default subsystem, 'console', windows creates an additional console // With the default subsystem, 'console', windows creates an additional console
// window for the program. // window for the program.
// This is silently ignored on non-windows systems. // This is silently ignored on non-windows systems.
@ -29,12 +28,12 @@ use dirs;
#[cfg(windows)] #[cfg(windows)]
use winapi::um::wincon::{AttachConsole, FreeConsole, ATTACH_PARENT_PROCESS}; use winapi::um::wincon::{AttachConsole, FreeConsole, ATTACH_PARENT_PROCESS};
use log::{info, error}; use log::{error, info};
use std::error::Error; use std::error::Error;
use std::sync::Arc;
use std::io::{self, Write};
use std::fs; use std::fs;
use std::io::{self, Write};
use std::sync::Arc;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
use std::env; use std::env;
@ -42,19 +41,19 @@ use std::env;
#[cfg(not(windows))] #[cfg(not(windows))]
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
#[cfg(target_os = "macos")]
use alacritty::locale;
use alacritty::{cli, event, die};
use alacritty::config::{self, Config, Monitor}; use alacritty::config::{self, Config, Monitor};
use alacritty::display::Display; use alacritty::display::Display;
use alacritty::event_loop::{self, EventLoop, Msg}; use alacritty::event_loop::{self, EventLoop, Msg};
#[cfg(target_os = "macos")]
use alacritty::locale;
use alacritty::logging; use alacritty::logging;
use alacritty::message_bar::MessageBuffer;
use alacritty::panic; use alacritty::panic;
use alacritty::sync::FairMutex; use alacritty::sync::FairMutex;
use alacritty::term::Term; use alacritty::term::Term;
use alacritty::tty; use alacritty::tty;
use alacritty::util::fmt::Red; use alacritty::util::fmt::Red;
use alacritty::message_bar::MessageBuffer; use alacritty::{cli, die, event};
fn main() { fn main() {
panic::attach_handler(); panic::attach_handler();
@ -63,7 +62,9 @@ fn main() {
// to the console of the parent process, so we do it explicitly. This fails // to the console of the parent process, so we do it explicitly. This fails
// silently if the parent has no console. // silently if the parent has no console.
#[cfg(windows)] #[cfg(windows)]
unsafe { AttachConsole(ATTACH_PARENT_PROCESS); } unsafe {
AttachConsole(ATTACH_PARENT_PROCESS);
}
// Load command line options // Load command line options
let options = cli::Options::load(); let options = cli::Options::load();
@ -77,7 +78,8 @@ fn main() {
// Load configuration file // Load configuration file
// If the file is a command line argument, we won't write a generated default file // If the file is a command line argument, we won't write a generated default file
let config_path = options.config_path() let config_path = options
.config_path()
.or_else(Config::installed_config) .or_else(Config::installed_config)
.or_else(|| Config::write_defaults().ok()) .or_else(|| Config::write_defaults().ok())
.map(|path| path.to_path_buf()); .map(|path| path.to_path_buf());
@ -133,11 +135,7 @@ fn run(
// The display manages a window and can draw the terminal // The display manages a window and can draw the terminal
let mut display = Display::new(&config, options)?; let mut display = Display::new(&config, options)?;
info!( info!("PTY Dimensions: {:?} x {:?}", display.size().lines(), display.size().cols());
"PTY Dimensions: {:?} x {:?}",
display.size().lines(),
display.size().cols()
);
// Create the terminal // Create the terminal
// //
@ -173,12 +171,8 @@ fn run(
// renderer and input processing. Note that access to the terminal state is // renderer and input processing. Note that access to the terminal state is
// synchronized since the I/O loop updates the state, and the display // synchronized since the I/O loop updates the state, and the display
// consumes it periodically. // consumes it periodically.
let event_loop = EventLoop::new( let event_loop =
Arc::clone(&terminal), EventLoop::new(Arc::clone(&terminal), display.notifier(), pty, options.ref_test);
display.notifier(),
pty,
options.ref_test,
);
// The event loop channel allows write requests from the event processor // The event loop channel allows write requests from the event processor
// to be sent to the loop and ultimately written to the pty. // to be sent to the loop and ultimately written to the pty.
@ -259,16 +253,16 @@ fn run(
} }
} }
loop_tx loop_tx.send(Msg::Shutdown).expect("Error sending shutdown to event loop");
.send(Msg::Shutdown)
.expect("Error sending shutdown to event loop");
// FIXME patch notify library to have a shutdown method // FIXME patch notify library to have a shutdown method
// config_reloader.join().ok(); // config_reloader.join().ok();
// Without explicitly detaching the console cmd won't redraw it's prompt // Without explicitly detaching the console cmd won't redraw it's prompt
#[cfg(windows)] #[cfg(windows)]
unsafe { FreeConsole(); } unsafe {
FreeConsole();
}
info!("Goodbye"); info!("Goodbye");

View File

@ -33,11 +33,7 @@ pub struct Message {
impl Message { impl Message {
/// Create a new message /// Create a new message
pub fn new(text: String, color: Rgb) -> Message { pub fn new(text: String, color: Rgb) -> Message {
Message { Message { text, color, topic: None }
text,
color,
topic: None,
}
} }
/// Formatted message text lines /// Formatted message text lines
@ -135,11 +131,7 @@ impl MessageBuffer {
/// Create new message buffer /// Create new message buffer
pub fn new() -> MessageBuffer { pub fn new() -> MessageBuffer {
let (tx, messages) = crossbeam_channel::unbounded(); let (tx, messages) = crossbeam_channel::unbounded();
MessageBuffer { MessageBuffer { current: None, messages, tx }
current: None,
messages,
tx,
}
} }
/// Check if there are any messages queued /// Check if there are any messages queued
@ -215,10 +207,7 @@ mod test {
fn appends_close_button() { fn appends_close_button() {
let input = "a"; let input = "a";
let mut message_buffer = MessageBuffer::new(); let mut message_buffer = MessageBuffer::new();
message_buffer message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap();
.tx()
.send(Message::new(input.into(), color::RED))
.unwrap();
let size = SizeInfo { let size = SizeInfo {
width: 7., width: 7.,
height: 10., height: 10.,
@ -238,10 +227,7 @@ mod test {
fn multiline_close_button_first_line() { fn multiline_close_button_first_line() {
let input = "fo\nbar"; let input = "fo\nbar";
let mut message_buffer = MessageBuffer::new(); let mut message_buffer = MessageBuffer::new();
message_buffer message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap();
.tx()
.send(Message::new(input.into(), color::RED))
.unwrap();
let size = SizeInfo { let size = SizeInfo {
width: 6., width: 6.,
height: 10., height: 10.,
@ -261,10 +247,7 @@ mod test {
fn splits_on_newline() { fn splits_on_newline() {
let input = "a\nb"; let input = "a\nb";
let mut message_buffer = MessageBuffer::new(); let mut message_buffer = MessageBuffer::new();
message_buffer message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap();
.tx()
.send(Message::new(input.into(), color::RED))
.unwrap();
let size = SizeInfo { let size = SizeInfo {
width: 6., width: 6.,
height: 10., height: 10.,
@ -284,10 +267,7 @@ mod test {
fn splits_on_length() { fn splits_on_length() {
let input = "foobar1"; let input = "foobar1";
let mut message_buffer = MessageBuffer::new(); let mut message_buffer = MessageBuffer::new();
message_buffer message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap();
.tx()
.send(Message::new(input.into(), color::RED))
.unwrap();
let size = SizeInfo { let size = SizeInfo {
width: 6., width: 6.,
height: 10., height: 10.,
@ -307,10 +287,7 @@ mod test {
fn empty_with_shortterm() { fn empty_with_shortterm() {
let input = "foobar"; let input = "foobar";
let mut message_buffer = MessageBuffer::new(); let mut message_buffer = MessageBuffer::new();
message_buffer message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap();
.tx()
.send(Message::new(input.into(), color::RED))
.unwrap();
let size = SizeInfo { let size = SizeInfo {
width: 6., width: 6.,
height: 0., height: 0.,
@ -330,10 +307,7 @@ mod test {
fn truncates_long_messages() { fn truncates_long_messages() {
let input = "hahahahahahahahahahaha truncate this because it's too long for the term"; let input = "hahahahahahahahahahaha truncate this because it's too long for the term";
let mut message_buffer = MessageBuffer::new(); let mut message_buffer = MessageBuffer::new();
message_buffer message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap();
.tx()
.send(Message::new(input.into(), color::RED))
.unwrap();
let size = SizeInfo { let size = SizeInfo {
width: 22., width: 22.,
height: (MIN_FREE_LINES + 2) as f32, height: (MIN_FREE_LINES + 2) as f32,
@ -346,23 +320,17 @@ mod test {
let lines = message_buffer.message().unwrap().text(&size); let lines = message_buffer.message().unwrap().text(&size);
assert_eq!( assert_eq!(lines, vec![
lines, String::from("hahahahahahahahaha [X]"),
vec![ String::from("[MESSAGE TRUNCATED] ")
String::from("hahahahahahahahaha [X]"), ]);
String::from("[MESSAGE TRUNCATED] ")
]
);
} }
#[test] #[test]
fn hide_button_when_too_narrow() { fn hide_button_when_too_narrow() {
let input = "ha"; let input = "ha";
let mut message_buffer = MessageBuffer::new(); let mut message_buffer = MessageBuffer::new();
message_buffer message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap();
.tx()
.send(Message::new(input.into(), color::RED))
.unwrap();
let size = SizeInfo { let size = SizeInfo {
width: 2., width: 2.,
height: 10., height: 10.,
@ -382,10 +350,7 @@ mod test {
fn hide_truncated_when_too_narrow() { fn hide_truncated_when_too_narrow() {
let input = "hahahahahahahahaha"; let input = "hahahahahahahahaha";
let mut message_buffer = MessageBuffer::new(); let mut message_buffer = MessageBuffer::new();
message_buffer message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap();
.tx()
.send(Message::new(input.into(), color::RED))
.unwrap();
let size = SizeInfo { let size = SizeInfo {
width: 2., width: 2.,
height: (MIN_FREE_LINES + 2) as f32, height: (MIN_FREE_LINES + 2) as f32,
@ -405,10 +370,7 @@ mod test {
fn add_newline_for_button() { fn add_newline_for_button() {
let input = "test"; let input = "test";
let mut message_buffer = MessageBuffer::new(); let mut message_buffer = MessageBuffer::new();
message_buffer message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap();
.tx()
.send(Message::new(input.into(), color::RED))
.unwrap();
let size = SizeInfo { let size = SizeInfo {
width: 5., width: 5.,
height: 10., height: 10.,
@ -466,10 +428,7 @@ mod test {
fn wrap_on_words() { fn wrap_on_words() {
let input = "a\nbc defg"; let input = "a\nbc defg";
let mut message_buffer = MessageBuffer::new(); let mut message_buffer = MessageBuffer::new();
message_buffer message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap();
.tx()
.send(Message::new(input.into(), color::RED))
.unwrap();
let size = SizeInfo { let size = SizeInfo {
width: 5., width: 5.,
height: 10., height: 10.,
@ -482,14 +441,11 @@ mod test {
let lines = message_buffer.message().unwrap().text(&size); let lines = message_buffer.message().unwrap().text(&size);
assert_eq!( assert_eq!(lines, vec![
lines, String::from("a [X]"),
vec![ String::from("bc "),
String::from("a [X]"), String::from("defg ")
String::from("bc "), ]);
String::from("defg ")
]
);
} }
#[test] #[test]

View File

@ -31,7 +31,7 @@
//! // the average won't mean much until it's filled up at least once. //! // the average won't mean much until it's filled up at least once.
//! println!("Average time: {}", meter.average()); //! println!("Average time: {}", meter.average());
use std::time::{Instant, Duration}; use std::time::{Duration, Instant};
const NUM_SAMPLES: usize = 10; const NUM_SAMPLES: usize = 10;
@ -61,10 +61,7 @@ pub struct Sampler<'a> {
impl<'a> Sampler<'a> { impl<'a> Sampler<'a> {
fn new(meter: &'a mut Meter) -> Sampler<'a> { fn new(meter: &'a mut Meter) -> Sampler<'a> {
Sampler { Sampler { meter, created_at: Instant::now() }
meter,
created_at: Instant::now(),
}
} }
#[inline] #[inline]

View File

@ -22,17 +22,17 @@ use std::sync::mpsc;
use std::time::Duration; use std::time::Duration;
use fnv::FnvHasher; use fnv::FnvHasher;
use glutin::dpi::PhysicalSize;
use font::{self, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer}; use font::{self, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer};
use glutin::dpi::PhysicalSize;
use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
use crate::gl::types::*;
use crate::gl;
use crate::index::{Column, Line};
use crate::term::color::Rgb;
use crate::config::{self, Config, Delta}; use crate::config::{self, Config, Delta};
use crate::term::{self, cell, RenderableCell}; use crate::gl;
use crate::gl::types::*;
use crate::index::{Column, Line};
use crate::renderer::rects::{Rect, Rects}; use crate::renderer::rects::{Rect, Rects};
use crate::term::color::Rgb;
use crate::term::{self, cell, RenderableCell};
pub mod rects; pub mod rects;
@ -91,7 +91,7 @@ impl ::std::fmt::Display for Error {
match *self { match *self {
Error::ShaderCreation(ref err) => { Error::ShaderCreation(ref err) => {
write!(f, "There was an error initializing the shaders: {}", err) write!(f, "There was an error initializing the shaders: {}", err)
} },
} }
} }
} }
@ -214,11 +214,7 @@ impl GlyphCache {
fn load_glyphs_for_font<L: LoadGlyph>(&mut self, font: FontKey, loader: &mut L) { fn load_glyphs_for_font<L: LoadGlyph>(&mut self, font: FontKey, loader: &mut L) {
let size = self.font_size; let size = self.font_size;
for i in 32u8..=128u8 { for i in 32u8..=128u8 {
self.get(GlyphKey { self.get(GlyphKey { font_key: font, c: i as char, size }, loader);
font_key: font,
c: i as char,
size,
}, loader);
} }
} }
@ -230,7 +226,8 @@ impl GlyphCache {
let size = font.size(); let size = font.size();
// Load regular font // Load regular font
let regular_desc = Self::make_desc(&font.normal(), font::Slant::Normal, font::Weight::Normal); let regular_desc =
Self::make_desc(&font.normal(), font::Slant::Normal, font::Weight::Normal);
let regular = rasterizer.load_font(&regular_desc, size)?; let regular = rasterizer.load_font(&regular_desc, size)?;
@ -239,9 +236,7 @@ impl GlyphCache {
if desc == regular_desc { if desc == regular_desc {
regular regular
} else { } else {
rasterizer rasterizer.load_font(&desc, size).unwrap_or_else(|_| regular)
.load_font(&desc, size)
.unwrap_or_else(|_| regular)
} }
}; };
@ -251,7 +246,8 @@ impl GlyphCache {
let bold = load_or_regular(bold_desc); let bold = load_or_regular(bold_desc);
// Load italic font // Load italic font
let italic_desc = Self::make_desc(&font.italic(), font::Slant::Italic, font::Weight::Normal); let italic_desc =
Self::make_desc(&font.italic(), font::Slant::Italic, font::Weight::Normal);
let italic = load_or_regular(italic_desc); let italic = load_or_regular(italic_desc);
@ -278,30 +274,30 @@ impl GlyphCache {
} }
pub fn get<'a, L>(&'a mut self, glyph_key: GlyphKey, loader: &mut L) -> &'a Glyph pub fn get<'a, L>(&'a mut self, glyph_key: GlyphKey, loader: &mut L) -> &'a Glyph
where L: LoadGlyph where
L: LoadGlyph,
{ {
let glyph_offset = self.glyph_offset; let glyph_offset = self.glyph_offset;
let rasterizer = &mut self.rasterizer; let rasterizer = &mut self.rasterizer;
let metrics = &self.metrics; let metrics = &self.metrics;
self.cache self.cache.entry(glyph_key).or_insert_with(|| {
.entry(glyph_key) let mut rasterized =
.or_insert_with(|| { rasterizer.get_glyph(glyph_key).unwrap_or_else(|_| Default::default());
let mut rasterized = rasterizer.get_glyph(glyph_key)
.unwrap_or_else(|_| Default::default());
rasterized.left += i32::from(glyph_offset.x); rasterized.left += i32::from(glyph_offset.x);
rasterized.top += i32::from(glyph_offset.y); rasterized.top += i32::from(glyph_offset.y);
rasterized.top -= metrics.descent as i32; rasterized.top -= metrics.descent as i32;
loader.load_glyph(&rasterized) loader.load_glyph(&rasterized)
}) })
} }
pub fn update_font_size<L: LoadGlyph>( pub fn update_font_size<L: LoadGlyph>(
&mut self, &mut self,
font: &config::Font, font: &config::Font,
size: font::Size, size: font::Size,
dpr: f64, dpr: f64,
loader: &mut L loader: &mut L,
) -> Result<(), font::Error> { ) -> Result<(), font::Error> {
// Clear currently cached data in both GL and the registry // Clear currently cached data in both GL and the registry
loader.clear(); loader.clear();
@ -410,10 +406,7 @@ pub struct Batch {
impl Batch { impl Batch {
#[inline] #[inline]
pub fn new() -> Batch { pub fn new() -> Batch {
Batch { Batch { tex: 0, instances: Vec::with_capacity(BATCH_MAX) }
tex: 0,
instances: Vec::with_capacity(BATCH_MAX),
}
} }
pub fn add_item(&mut self, cell: &RenderableCell, glyph: &Glyph) { pub fn add_item(&mut self, cell: &RenderableCell, glyph: &Glyph) {
@ -592,16 +585,13 @@ impl QuadRenderer {
gl::GenBuffers(1, &mut rect_vbo); gl::GenBuffers(1, &mut rect_vbo);
gl::GenBuffers(1, &mut rect_ebo); gl::GenBuffers(1, &mut rect_ebo);
gl::BindVertexArray(rect_vao); gl::BindVertexArray(rect_vao);
let indices: [i32; 6] = [ let indices: [i32; 6] = [0, 1, 3, 1, 2, 3];
0, 1, 3,
1, 2, 3,
];
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, rect_ebo); gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, rect_ebo);
gl::BufferData( gl::BufferData(
gl::ELEMENT_ARRAY_BUFFER, gl::ELEMENT_ARRAY_BUFFER,
(size_of::<i32>() * indices.len()) as _, (size_of::<i32>() * indices.len()) as _,
indices.as_ptr() as *const _, indices.as_ptr() as *const _,
gl::STATIC_DRAW gl::STATIC_DRAW,
); );
// Cleanup // Cleanup
@ -629,13 +619,13 @@ impl QuadRenderer {
let event = rx.recv().expect("watcher event"); let event = rx.recv().expect("watcher event");
match event { match event {
DebouncedEvent::Rename(_, _) => continue, DebouncedEvent::Rename(..) => continue,
DebouncedEvent::Create(_) DebouncedEvent::Create(_)
| DebouncedEvent::Write(_) | DebouncedEvent::Write(_)
| DebouncedEvent::Chmod(_) => { | DebouncedEvent::Chmod(_) => {
msg_tx.send(Msg::ShaderReload).expect("msg send ok"); msg_tx.send(Msg::ShaderReload).expect("msg send ok");
} },
_ => {} _ => {},
} }
} }
}); });
@ -686,7 +676,14 @@ impl QuadRenderer {
gl::BindBuffer(gl::ARRAY_BUFFER, self.rect_vbo); gl::BindBuffer(gl::ARRAY_BUFFER, self.rect_vbo);
// Position // Position
gl::VertexAttribPointer(0, 2, gl::FLOAT, gl::FALSE, (size_of::<f32>() * 2) as _, ptr::null()); gl::VertexAttribPointer(
0,
2,
gl::FLOAT,
gl::FALSE,
(size_of::<f32>() * 2) as _,
ptr::null(),
);
gl::EnableVertexAttribArray(0); gl::EnableVertexAttribArray(0);
} }
@ -720,12 +717,7 @@ impl QuadRenderer {
} }
} }
pub fn with_api<F, T>( pub fn with_api<F, T>(&mut self, config: &Config, props: &term::SizeInfo, func: F) -> T
&mut self,
config: &Config,
props: &term::SizeInfo,
func: F,
) -> T
where where
F: FnOnce(RenderApi<'_>) -> T, F: FnOnce(RenderApi<'_>) -> T,
{ {
@ -788,11 +780,11 @@ impl QuadRenderer {
(Ok(program), Ok(rect_program)) => { (Ok(program), Ok(rect_program)) => {
info!("... successfully reloaded shaders"); info!("... successfully reloaded shaders");
(program, rect_program) (program, rect_program)
} },
(Err(err), _) | (_, Err(err)) => { (Err(err), _) | (_, Err(err)) => {
error!("{}", err); error!("{}", err);
return; return;
} },
}; };
self.active_tex = 0; self.active_tex = 0;
@ -842,19 +834,14 @@ impl QuadRenderer {
unsafe { unsafe {
// Setup vertices // Setup vertices
let vertices: [f32; 8] = [ let vertices: [f32; 8] = [x + width, y, x + width, y - height, x, y - height, x, y];
x + width, y ,
x + width, y - height,
x , y - height,
x , y ,
];
// Load vertex data into array buffer // Load vertex data into array buffer
gl::BufferData( gl::BufferData(
gl::ARRAY_BUFFER, gl::ARRAY_BUFFER,
(size_of::<f32>() * vertices.len()) as _, (size_of::<f32>() * vertices.len()) as _,
vertices.as_ptr() as *const _, vertices.as_ptr() as *const _,
gl::STATIC_DRAW gl::STATIC_DRAW,
); );
// Color // Color
@ -927,7 +914,7 @@ impl<'a> RenderApi<'a> {
string: &str, string: &str,
line: Line, line: Line,
glyph_cache: &mut GlyphCache, glyph_cache: &mut GlyphCache,
color: Option<Rgb> color: Option<Rgb>,
) { ) {
let bg_alpha = color.map(|_| 1.0).unwrap_or(0.0); let bg_alpha = color.map(|_| 1.0).unwrap_or(0.0);
let col = Column(0); let col = Column(0);
@ -943,7 +930,7 @@ impl<'a> RenderApi<'a> {
chars[0] = c; chars[0] = c;
chars chars
}, },
bg: color.unwrap_or(Rgb { r: 0, g: 0, b: 0}), bg: color.unwrap_or(Rgb { r: 0, g: 0, b: 0 }),
fg: Rgb { r: 0, g: 0, b: 0 }, fg: Rgb { r: 0, g: 0, b: 0 },
flags: cell::Flags::empty(), flags: cell::Flags::empty(),
bg_alpha, bg_alpha,
@ -993,11 +980,7 @@ impl<'a> RenderApi<'a> {
chars[0] = ' '; chars[0] = ' ';
} }
let mut glyph_key = GlyphKey { let mut glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, c: chars[0] };
font_key,
size: glyph_cache.font_size,
c: chars[0],
};
// Add cell to batch // Add cell to batch
let glyph = glyph_cache.get(glyph_key, self); let glyph = glyph_cache.get(glyph_key, self);
@ -1028,7 +1011,7 @@ fn load_glyph(
active_tex: &mut GLuint, active_tex: &mut GLuint,
atlas: &mut Vec<Atlas>, atlas: &mut Vec<Atlas>,
current_atlas: &mut usize, current_atlas: &mut usize,
rasterized: &RasterizedGlyph rasterized: &RasterizedGlyph,
) -> Glyph { ) -> Glyph {
// At least one atlas is guaranteed to be in the `self.atlas` list; thus // At least one atlas is guaranteed to be in the `self.atlas` list; thus
// the unwrap. // the unwrap.
@ -1042,20 +1025,18 @@ fn load_glyph(
atlas.push(new); atlas.push(new);
} }
load_glyph(active_tex, atlas, current_atlas, rasterized) load_glyph(active_tex, atlas, current_atlas, rasterized)
} },
Err(AtlasInsertError::GlyphTooLarge) => { Err(AtlasInsertError::GlyphTooLarge) => Glyph {
Glyph { tex_id: atlas[*current_atlas].id,
tex_id: atlas[*current_atlas].id, top: 0.0,
top: 0.0, left: 0.0,
left: 0.0, width: 0.0,
width: 0.0, height: 0.0,
height: 0.0, uv_bot: 0.0,
uv_bot: 0.0, uv_left: 0.0,
uv_left: 0.0, uv_width: 0.0,
uv_width: 0.0, uv_height: 0.0,
uv_height: 0.0, },
}
}
} }
} }
@ -1113,7 +1094,9 @@ impl TextShaderProgram {
} }
macro_rules! cptr { macro_rules! cptr {
($thing:expr) => { $thing.as_ptr() as *const _ } ($thing:expr) => {
$thing.as_ptr() as *const _
};
} }
macro_rules! assert_uniform_valid { macro_rules! assert_uniform_valid {
@ -1146,16 +1129,16 @@ impl TextShaderProgram {
shader.update_projection(size.width as f32, size.height as f32, 0., 0.); shader.update_projection(size.width as f32, size.height as f32, 0., 0.);
unsafe { gl::UseProgram(0); } unsafe {
gl::UseProgram(0);
}
Ok(shader) Ok(shader)
} }
fn update_projection(&self, width: f32, height: f32, padding_x: f32, padding_y: f32) { fn update_projection(&self, width: f32, height: f32, padding_x: f32, padding_y: f32) {
// Bounds check // Bounds check
if (width as u32) < (2 * padding_x as u32) || if (width as u32) < (2 * padding_x as u32) || (height as u32) < (2 * padding_y as u32) {
(height as u32) < (2 * padding_y as u32)
{
return; return;
} }
@ -1204,16 +1187,8 @@ impl RectShaderProgram {
} else { } else {
(Some(RECT_SHADER_V), Some(RECT_SHADER_F)) (Some(RECT_SHADER_V), Some(RECT_SHADER_F))
}; };
let vertex_shader = create_shader( let vertex_shader = create_shader(RECT_SHADER_V_PATH, gl::VERTEX_SHADER, vertex_src)?;
RECT_SHADER_V_PATH, let fragment_shader = create_shader(RECT_SHADER_F_PATH, gl::FRAGMENT_SHADER, fragment_src)?;
gl::VERTEX_SHADER,
vertex_src
)?;
let fragment_shader = create_shader(
RECT_SHADER_F_PATH,
gl::FRAGMENT_SHADER,
fragment_src
)?;
let program = create_program(vertex_shader, fragment_shader)?; let program = create_program(vertex_shader, fragment_shader)?;
unsafe { unsafe {
@ -1223,14 +1198,9 @@ impl RectShaderProgram {
} }
// get uniform locations // get uniform locations
let u_color = unsafe { let u_color = unsafe { gl::GetUniformLocation(program, b"color\0".as_ptr() as *const _) };
gl::GetUniformLocation(program, b"color\0".as_ptr() as *const _)
};
let shader = RectShaderProgram { let shader = RectShaderProgram { id: program, u_color };
id: program,
u_color,
};
unsafe { gl::UseProgram(0) } unsafe { gl::UseProgram(0) }
@ -1276,9 +1246,11 @@ fn create_program(vertex: GLuint, fragment: GLuint) -> Result<GLuint, ShaderCrea
} }
} }
fn create_shader(path: &str, kind: GLenum, source: Option<&'static str>) fn create_shader(
-> Result<GLuint, ShaderCreationError> path: &str,
{ kind: GLenum,
source: Option<&'static str>,
) -> Result<GLuint, ShaderCreationError> {
let from_disk; let from_disk;
let source = if let Some(src) = source { let source = if let Some(src) = source {
src src
@ -1308,7 +1280,9 @@ fn create_shader(path: &str, kind: GLenum, source: Option<&'static str>)
let log = get_shader_info_log(shader); let log = get_shader_info_log(shader);
// Cleanup // Cleanup
unsafe { gl::DeleteShader(shader); } unsafe {
gl::DeleteShader(shader);
}
Err(ShaderCreationError::Compile(PathBuf::from(path), log)) Err(ShaderCreationError::Compile(PathBuf::from(path), log))
} }
@ -1325,12 +1299,7 @@ fn get_program_info_log(program: GLuint) -> String {
let mut actual_length: GLint = 0; let mut actual_length: GLint = 0;
let mut buf: Vec<u8> = Vec::with_capacity(max_length as usize); let mut buf: Vec<u8> = Vec::with_capacity(max_length as usize);
unsafe { unsafe {
gl::GetProgramInfoLog( gl::GetProgramInfoLog(program, max_length, &mut actual_length, buf.as_mut_ptr() as *mut _);
program,
max_length,
&mut actual_length,
buf.as_mut_ptr() as *mut _,
);
} }
// Build a string // Build a string
@ -1353,12 +1322,7 @@ fn get_shader_info_log(shader: GLuint) -> String {
let mut actual_length: GLint = 0; let mut actual_length: GLint = 0;
let mut buf: Vec<u8> = Vec::with_capacity(max_length as usize); let mut buf: Vec<u8> = Vec::with_capacity(max_length as usize);
unsafe { unsafe {
gl::GetShaderInfoLog( gl::GetShaderInfoLog(shader, max_length, &mut actual_length, buf.as_mut_ptr() as *mut _);
shader,
max_length,
&mut actual_length,
buf.as_mut_ptr() as *mut _,
);
} }
// Build a string // Build a string
@ -1410,15 +1374,11 @@ impl ::std::error::Error for ShaderCreationError {
impl ::std::fmt::Display for ShaderCreationError { impl ::std::fmt::Display for ShaderCreationError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self { match *self {
ShaderCreationError::Io(ref err) => { ShaderCreationError::Io(ref err) => write!(f, "Couldn't read shader: {}", err),
write!(f, "Couldn't read shader: {}", err)
},
ShaderCreationError::Compile(ref path, ref log) => { ShaderCreationError::Compile(ref path, ref log) => {
write!(f, "Failed compiling shader at {}: {}", path.display(), log) write!(f, "Failed compiling shader at {}: {}", path.display(), log)
}, },
ShaderCreationError::Link(ref log) => { ShaderCreationError::Link(ref log) => write!(f, "Failed linking shader: {}", log),
write!(f, "Failed linking shader: {}", log)
},
} }
} }
} }
@ -1509,14 +1469,7 @@ impl Atlas {
gl::BindTexture(gl::TEXTURE_2D, 0); gl::BindTexture(gl::TEXTURE_2D, 0);
} }
Atlas { Atlas { id, width: size, height: size, row_extent: 0, row_baseline: 0, row_tallest: 0 }
id,
width: size,
height: size,
row_extent: 0,
row_baseline: 0,
row_tallest: 0,
}
} }
pub fn clear(&mut self) { pub fn clear(&mut self) {
@ -1529,7 +1482,7 @@ impl Atlas {
pub fn insert( pub fn insert(
&mut self, &mut self,
glyph: &RasterizedGlyph, glyph: &RasterizedGlyph,
active_tex: &mut u32 active_tex: &mut u32,
) -> Result<Glyph, AtlasInsertError> { ) -> Result<Glyph, AtlasInsertError> {
if glyph.width > self.width || glyph.height > self.height { if glyph.width > self.width || glyph.height > self.height {
return Err(AtlasInsertError::GlyphTooLarge); return Err(AtlasInsertError::GlyphTooLarge);

View File

@ -14,8 +14,8 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::term::cell::Flags; use crate::term::cell::Flags;
use crate::term::{RenderableCell, SizeInfo};
use crate::term::color::Rgb; use crate::term::color::Rgb;
use crate::term::{RenderableCell, SizeInfo};
use font::Metrics; use font::Metrics;
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@ -47,13 +47,7 @@ impl<'a> Rects<'a> {
last_starts.insert(Flags::UNDERLINE, None); last_starts.insert(Flags::UNDERLINE, None);
last_starts.insert(Flags::STRIKEOUT, None); last_starts.insert(Flags::STRIKEOUT, None);
Self { Self { inner: Vec::new(), last_cell: None, last_starts, metrics, size }
inner: Vec::new(),
last_cell: None,
last_starts,
metrics,
size,
}
} }
/// Convert the stored rects to rectangles for the renderer. /// Convert the stored rects to rectangles for the renderer.
@ -61,15 +55,13 @@ impl<'a> Rects<'a> {
// If there's still a line pending, draw it until the last cell // If there's still a line pending, draw it until the last cell
for (flag, start_cell) in self.last_starts.iter_mut() { for (flag, start_cell) in self.last_starts.iter_mut() {
if let Some(start) = start_cell { if let Some(start) = start_cell {
self.inner.push( self.inner.push(create_rect(
create_rect( &start,
&start, &self.last_cell.unwrap(),
&self.last_cell.unwrap(), *flag,
*flag, &self.metrics,
&self.metrics, &self.size,
&self.size, ));
)
);
} }
} }
@ -108,12 +100,14 @@ impl<'a> Rects<'a> {
} else { } else {
None None
} }
} },
// Check for new start of line // Check for new start of line
None => if cell.flags.contains(flag) { None => {
Some(*cell) if cell.flags.contains(flag) {
} else { Some(*cell)
None } else {
None
}
}, },
}; };
} }
@ -158,12 +152,8 @@ fn create_rect(
y = max_y; y = max_y;
} }
let rect = Rect::new( let rect =
start_x + size.padding_x, Rect::new(start_x + size.padding_x, y.round() + size.padding_y, width, height.round());
y.round() + size.padding_y,
width,
height.round(),
);
(rect, start.fg) (rect, start.fg)
} }

View File

@ -18,10 +18,10 @@
//! finalized when the button is released. The selection should be cleared //! finalized when the button is released. The selection should be cleared
//! when text is added/removed/scrolled on the screen. The selection should //! when text is added/removed/scrolled on the screen. The selection should
//! also be cleared if the user clicks off of the selection. //! also be cleared if the user clicks off of the selection.
use std::cmp::{min, max}; use std::cmp::{max, min};
use std::ops::Range; use std::ops::Range;
use crate::index::{Point, Column, Side}; use crate::index::{Column, Point, Side};
use crate::term::Search; use crate::term::Search;
/// Describes a region of a 2-dimensional area /// Describes a region of a 2-dimensional area
@ -55,8 +55,8 @@ pub enum Selection {
/// The line under the initial point. This is always selected regardless /// The line under the initial point. This is always selected regardless
/// of which way the cursor is moved. /// of which way the cursor is moved.
initial_line: isize initial_line: isize,
} },
} }
/// A Point and side within that point. /// A Point and side within that point.
@ -83,8 +83,8 @@ impl Selection {
Selection::Simple { Selection::Simple {
region: Range { region: Range {
start: Anchor::new(location.into(), side), start: Anchor::new(location.into(), side),
end: Anchor::new(location.into(), side) end: Anchor::new(location.into(), side),
} },
} }
} }
@ -102,25 +102,17 @@ impl Selection {
region.start.line += offset; region.start.line += offset;
region.end.line += offset; region.end.line += offset;
*initial_line += offset; *initial_line += offset;
} },
} }
} }
pub fn semantic(point: Point<usize>) -> Selection { pub fn semantic(point: Point<usize>) -> Selection {
Selection::Semantic { Selection::Semantic { region: Range { start: point.into(), end: point.into() } }
region: Range {
start: point.into(),
end: point.into(),
}
}
} }
pub fn lines(point: Point<usize>) -> Selection { pub fn lines(point: Point<usize>) -> Selection {
Selection::Lines { Selection::Lines {
region: Range { region: Range { start: point.into(), end: point.into() },
start: point.into(),
end: point.into(),
},
initial_line: point.line as isize, initial_line: point.line as isize,
} }
} }
@ -131,9 +123,7 @@ impl Selection {
Selection::Simple { ref mut region } => { Selection::Simple { ref mut region } => {
region.end = Anchor::new(location.into(), side); region.end = Anchor::new(location.into(), side);
}, },
Selection::Semantic { ref mut region } | Selection::Semantic { ref mut region } | Selection::Lines { ref mut region, .. } => {
Selection::Lines { ref mut region, .. } =>
{
region.end = location.into(); region.end = location.into();
}, },
} }
@ -144,36 +134,28 @@ impl Selection {
G: Search + Dimensions, G: Search + Dimensions,
{ {
match *self { match *self {
Selection::Simple { ref region } => { Selection::Simple { ref region } => Selection::span_simple(grid, region, alt_screen),
Selection::span_simple(grid, region, alt_screen)
},
Selection::Semantic { ref region } => { Selection::Semantic { ref region } => {
Selection::span_semantic(grid, region, alt_screen) Selection::span_semantic(grid, region, alt_screen)
}, },
Selection::Lines { ref region, initial_line } => { Selection::Lines { ref region, initial_line } => {
Selection::span_lines(grid, region, initial_line, alt_screen) Selection::span_lines(grid, region, initial_line, alt_screen)
} },
} }
} }
pub fn is_empty(&self) -> bool pub fn is_empty(&self) -> bool {
{
match *self { match *self {
Selection::Simple { ref region } => { Selection::Simple { ref region } => {
region.start == region.end && region.start.side == region.end.side region.start == region.end && region.start.side == region.end.side
}, },
Selection::Semantic { .. } | Selection::Lines { .. } => { Selection::Semantic { .. } | Selection::Lines { .. } => false,
false
},
} }
} }
fn span_semantic<G>( fn span_semantic<G>(grid: &G, region: &Range<Point<isize>>, alt_screen: bool) -> Option<Span>
grid: &G, where
region: &Range<Point<isize>>, G: Search + Dimensions,
alt_screen: bool,
) -> Option<Span>
where G: Search + Dimensions
{ {
let cols = grid.dimensions().col; let cols = grid.dimensions().col;
let lines = grid.dimensions().line.0 as isize; let lines = grid.dimensions().line.0 as isize;
@ -199,12 +181,7 @@ impl Selection {
::std::mem::swap(&mut start, &mut end); ::std::mem::swap(&mut start, &mut end);
} }
Some(Span { Some(Span { cols, front: start, tail: end, ty: SpanType::Inclusive })
cols,
front: start,
tail: end,
ty: SpanType::Inclusive,
})
} }
fn span_lines<G>( fn span_lines<G>(
@ -214,21 +191,15 @@ impl Selection {
alt_screen: bool, alt_screen: bool,
) -> Option<Span> ) -> Option<Span>
where where
G: Dimensions G: Dimensions,
{ {
let cols = grid.dimensions().col; let cols = grid.dimensions().col;
let lines = grid.dimensions().line.0 as isize; let lines = grid.dimensions().line.0 as isize;
// First, create start and end points based on initial line and the grid // First, create start and end points based on initial line and the grid
// dimensions. // dimensions.
let mut start = Point { let mut start = Point { col: cols - 1, line: initial_line };
col: cols - 1, let mut end = Point { col: Column(0), line: initial_line };
line: initial_line
};
let mut end = Point {
col: Column(0),
line: initial_line
};
// Now, expand lines based on where cursor started and ended. // Now, expand lines based on where cursor started and ended.
if region.start.line < region.end.line { if region.start.line < region.end.line {
@ -245,17 +216,12 @@ impl Selection {
Selection::alt_screen_clamp(&mut start, &mut end, lines, cols)?; Selection::alt_screen_clamp(&mut start, &mut end, lines, cols)?;
} }
Some(Span { Some(Span { cols, front: start.into(), tail: end.into(), ty: SpanType::Inclusive })
cols,
front: start.into(),
tail: end.into(),
ty: SpanType::Inclusive
})
} }
fn span_simple<G>(grid: &G, region: &Range<Anchor>, alt_screen: bool) -> Option<Span> fn span_simple<G>(grid: &G, region: &Range<Anchor>, alt_screen: bool) -> Option<Span>
where where
G: Dimensions G: Dimensions,
{ {
let start = region.start.point; let start = region.start.point;
let start_side = region.start.side; let start_side = region.start.side;
@ -276,7 +242,9 @@ impl Selection {
// No selection for single cell with identical sides or two cell with right+left sides // No selection for single cell with identical sides or two cell with right+left sides
if (front == tail && front_side == tail_side) if (front == tail && front_side == tail_side)
|| (tail_side == Side::Right && front_side == Side::Left && front.line == tail.line || (tail_side == Side::Right
&& front_side == Side::Left
&& front.line == tail.line
&& front.col == tail.col + 1) && front.col == tail.col + 1)
{ {
return None; return None;
@ -303,12 +271,7 @@ impl Selection {
} }
// Return the selection with all cells inclusive // Return the selection with all cells inclusive
Some(Span { Some(Span { cols, front: front.into(), tail: tail.into(), ty: SpanType::Inclusive })
cols,
front: front.into(),
tail: tail.into(),
ty: SpanType::Inclusive,
})
} }
// Clamp selection in the alternate screen to the visible region // Clamp selection in the alternate screen to the visible region
@ -387,7 +350,7 @@ impl Span {
(Span::wrap_start(self.front, self.cols), Span::wrap_end(self.tail, self.cols)) (Span::wrap_start(self.front, self.cols), Span::wrap_end(self.tail, self.cols))
}, },
SpanType::ExcludeFront => (Span::wrap_start(self.front, self.cols), self.tail), SpanType::ExcludeFront => (Span::wrap_start(self.front, self.cols), self.tail),
SpanType::ExcludeTail => (self.front, Span::wrap_end(self.tail, self.cols)) SpanType::ExcludeTail => (self.front, Span::wrap_end(self.tail, self.cols)),
}; };
Locations { start, end } Locations { start, end }
@ -395,10 +358,7 @@ impl Span {
fn wrap_start(mut start: Point<usize>, cols: Column) -> Point<usize> { fn wrap_start(mut start: Point<usize>, cols: Column) -> Point<usize> {
if start.col == cols - 1 { if start.col == cols - 1 {
Point { Point { line: start.line + 1, col: Column(0) }
line: start.line + 1,
col: Column(0),
}
} else { } else {
start.col += 1; start.col += 1;
start start
@ -407,15 +367,9 @@ impl Span {
fn wrap_end(end: Point<usize>, cols: Column) -> Point<usize> { fn wrap_end(end: Point<usize>, cols: Column) -> Point<usize> {
if end.col == Column(0) && end.line != 0 { if end.col == Column(0) && end.line != 0 {
Point { Point { line: end.line - 1, col: cols }
line: end.line - 1,
col: cols
}
} else { } else {
Point { Point { line: end.line, col: end.col - 1 }
line: end.line,
col: end.col - 1
}
} }
} }
} }
@ -431,8 +385,8 @@ impl Span {
/// look like [ B] and [E ]. /// look like [ B] and [E ].
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::index::{Line, Column, Side, Point};
use super::{Selection, Span, SpanType}; use super::{Selection, Span, SpanType};
use crate::index::{Column, Line, Point, Side};
use crate::url::Url; use crate::url::Url;
struct Dimensions(Point); struct Dimensions(Point);
@ -444,17 +398,22 @@ mod test {
impl Dimensions { impl Dimensions {
pub fn new(line: usize, col: usize) -> Self { pub fn new(line: usize, col: usize) -> Self {
Dimensions(Point { Dimensions(Point { line: Line(line), col: Column(col) })
line: Line(line),
col: Column(col)
})
} }
} }
impl super::Search for Dimensions { impl super::Search for Dimensions {
fn semantic_search_left(&self, point: Point<usize>) -> Point<usize> { point } fn semantic_search_left(&self, point: Point<usize>) -> Point<usize> {
fn semantic_search_right(&self, point: Point<usize>) -> Point<usize> { point } point
fn url_search(&self, _: Point<usize>) -> Option<Url> { None } }
fn semantic_search_right(&self, point: Point<usize>) -> Point<usize> {
point
}
fn url_search(&self, _: Point<usize>) -> Option<Url> {
None
}
} }
/// Test case of single cell selection /// Test case of single cell selection

View File

@ -31,10 +31,7 @@ pub struct FairMutex<T> {
impl<T> FairMutex<T> { impl<T> FairMutex<T> {
/// Create a new fair mutex /// Create a new fair mutex
pub fn new(data: T) -> FairMutex<T> { pub fn new(data: T) -> FairMutex<T> {
FairMutex { FairMutex { data: Mutex::new(data), next: Mutex::new(()) }
data: Mutex::new(data),
next: Mutex::new(()),
}
} }
/// Lock the mutex /// Lock the mutex

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
use bitflags::bitflags; use bitflags::bitflags;
use crate::ansi::{NamedColor, Color}; use crate::ansi::{Color, NamedColor};
use crate::grid::{self, GridCell}; use crate::grid::{self, GridCell};
use crate::index::Column; use crate::index::Column;
@ -47,19 +47,14 @@ pub struct Cell {
pub fg: Color, pub fg: Color,
pub bg: Color, pub bg: Color,
pub flags: Flags, pub flags: Flags,
#[serde(default="default_extra")] #[serde(default = "default_extra")]
pub extra: [char; MAX_ZEROWIDTH_CHARS], pub extra: [char; MAX_ZEROWIDTH_CHARS],
} }
impl Default for Cell { impl Default for Cell {
fn default() -> Cell { fn default() -> Cell {
Cell::new( Cell::new(' ', Color::Named(NamedColor::Foreground), Color::Named(NamedColor::Background))
' ',
Color::Named(NamedColor::Foreground),
Color::Named(NamedColor::Background)
)
} }
} }
impl GridCell for Cell { impl GridCell for Cell {
@ -130,13 +125,7 @@ impl Cell {
} }
pub fn new(c: char, fg: Color, bg: Color) -> Cell { pub fn new(c: char, fg: Color, bg: Color) -> Cell {
Cell { Cell { extra: [' '; MAX_ZEROWIDTH_CHARS], c, bg, fg, flags: Flags::empty() }
extra: [' '; MAX_ZEROWIDTH_CHARS],
c,
bg,
fg,
flags: Flags::empty(),
}
} }
#[inline] #[inline]

View File

@ -1,5 +1,5 @@
use std::ops::{Index, IndexMut, Mul};
use std::fmt; use std::fmt;
use std::ops::{Index, IndexMut, Mul};
use crate::ansi; use crate::ansi;
use crate::config::Colors; use crate::config::Colors;
@ -24,7 +24,7 @@ impl Mul<f32> for Rgb {
let result = Rgb { let result = Rgb {
r: (f32::from(self.r) * rhs).max(0.0).min(255.0) as u8, r: (f32::from(self.r) * rhs).max(0.0).min(255.0) as u8,
g: (f32::from(self.g) * rhs).max(0.0).min(255.0) as u8, g: (f32::from(self.g) * rhs).max(0.0).min(255.0) as u8,
b: (f32::from(self.b) * rhs).max(0.0).min(255.0) as u8 b: (f32::from(self.b) * rhs).max(0.0).min(255.0) as u8,
}; };
trace!("Scaling RGB by {} from {:?} to {:?}", rhs, self, result); trace!("Scaling RGB by {} from {:?} to {:?}", rhs, self, result);
@ -60,28 +60,26 @@ impl<'a> From<&'a Colors> for List {
impl List { impl List {
pub fn fill_named(&mut self, colors: &Colors) { pub fn fill_named(&mut self, colors: &Colors) {
// Normals // Normals
self[ansi::NamedColor::Black] = colors.normal.black; self[ansi::NamedColor::Black] = colors.normal.black;
self[ansi::NamedColor::Red] = colors.normal.red; self[ansi::NamedColor::Red] = colors.normal.red;
self[ansi::NamedColor::Green] = colors.normal.green; self[ansi::NamedColor::Green] = colors.normal.green;
self[ansi::NamedColor::Yellow] = colors.normal.yellow; self[ansi::NamedColor::Yellow] = colors.normal.yellow;
self[ansi::NamedColor::Blue] = colors.normal.blue; self[ansi::NamedColor::Blue] = colors.normal.blue;
self[ansi::NamedColor::Magenta] = colors.normal.magenta; self[ansi::NamedColor::Magenta] = colors.normal.magenta;
self[ansi::NamedColor::Cyan] = colors.normal.cyan; self[ansi::NamedColor::Cyan] = colors.normal.cyan;
self[ansi::NamedColor::White] = colors.normal.white; self[ansi::NamedColor::White] = colors.normal.white;
// Brights // Brights
self[ansi::NamedColor::BrightBlack] = colors.bright.black; self[ansi::NamedColor::BrightBlack] = colors.bright.black;
self[ansi::NamedColor::BrightRed] = colors.bright.red; self[ansi::NamedColor::BrightRed] = colors.bright.red;
self[ansi::NamedColor::BrightGreen] = colors.bright.green; self[ansi::NamedColor::BrightGreen] = colors.bright.green;
self[ansi::NamedColor::BrightYellow] = colors.bright.yellow; self[ansi::NamedColor::BrightYellow] = colors.bright.yellow;
self[ansi::NamedColor::BrightBlue] = colors.bright.blue; self[ansi::NamedColor::BrightBlue] = colors.bright.blue;
self[ansi::NamedColor::BrightMagenta] = colors.bright.magenta; self[ansi::NamedColor::BrightMagenta] = colors.bright.magenta;
self[ansi::NamedColor::BrightCyan] = colors.bright.cyan; self[ansi::NamedColor::BrightCyan] = colors.bright.cyan;
self[ansi::NamedColor::BrightWhite] = colors.bright.white; self[ansi::NamedColor::BrightWhite] = colors.bright.white;
self[ansi::NamedColor::BrightForeground] = colors self[ansi::NamedColor::BrightForeground] =
.primary colors.primary.bright_foreground.unwrap_or(colors.primary.foreground);
.bright_foreground
.unwrap_or(colors.primary.foreground);
// Foreground and background // Foreground and background
self[ansi::NamedColor::Foreground] = colors.primary.foreground; self[ansi::NamedColor::Foreground] = colors.primary.foreground;
@ -89,36 +87,34 @@ impl List {
// Foreground and background for custom cursor colors // Foreground and background for custom cursor colors
self[ansi::NamedColor::CursorText] = colors.cursor.text.unwrap_or_else(Rgb::default); self[ansi::NamedColor::CursorText] = colors.cursor.text.unwrap_or_else(Rgb::default);
self[ansi::NamedColor::Cursor] = colors.cursor.cursor.unwrap_or_else(Rgb::default); self[ansi::NamedColor::Cursor] = colors.cursor.cursor.unwrap_or_else(Rgb::default);
// Dims // Dims
self[ansi::NamedColor::DimForeground] = colors self[ansi::NamedColor::DimForeground] =
.primary colors.primary.dim_foreground.unwrap_or(colors.primary.foreground * 0.66);
.dim_foreground
.unwrap_or(colors.primary.foreground * 0.66);
match colors.dim { match colors.dim {
Some(ref dim) => { Some(ref dim) => {
trace!("Using config-provided dim colors"); trace!("Using config-provided dim colors");
self[ansi::NamedColor::DimBlack] = dim.black; self[ansi::NamedColor::DimBlack] = dim.black;
self[ansi::NamedColor::DimRed] = dim.red; self[ansi::NamedColor::DimRed] = dim.red;
self[ansi::NamedColor::DimGreen] = dim.green; self[ansi::NamedColor::DimGreen] = dim.green;
self[ansi::NamedColor::DimYellow] = dim.yellow; self[ansi::NamedColor::DimYellow] = dim.yellow;
self[ansi::NamedColor::DimBlue] = dim.blue; self[ansi::NamedColor::DimBlue] = dim.blue;
self[ansi::NamedColor::DimMagenta] = dim.magenta; self[ansi::NamedColor::DimMagenta] = dim.magenta;
self[ansi::NamedColor::DimCyan] = dim.cyan; self[ansi::NamedColor::DimCyan] = dim.cyan;
self[ansi::NamedColor::DimWhite] = dim.white; self[ansi::NamedColor::DimWhite] = dim.white;
} },
None => { None => {
trace!("Deriving dim colors from normal colors"); trace!("Deriving dim colors from normal colors");
self[ansi::NamedColor::DimBlack] = colors.normal.black * 0.66; self[ansi::NamedColor::DimBlack] = colors.normal.black * 0.66;
self[ansi::NamedColor::DimRed] = colors.normal.red * 0.66; self[ansi::NamedColor::DimRed] = colors.normal.red * 0.66;
self[ansi::NamedColor::DimGreen] = colors.normal.green * 0.66; self[ansi::NamedColor::DimGreen] = colors.normal.green * 0.66;
self[ansi::NamedColor::DimYellow] = colors.normal.yellow * 0.66; self[ansi::NamedColor::DimYellow] = colors.normal.yellow * 0.66;
self[ansi::NamedColor::DimBlue] = colors.normal.blue * 0.66; self[ansi::NamedColor::DimBlue] = colors.normal.blue * 0.66;
self[ansi::NamedColor::DimMagenta] = colors.normal.magenta * 0.66; self[ansi::NamedColor::DimMagenta] = colors.normal.magenta * 0.66;
self[ansi::NamedColor::DimCyan] = colors.normal.cyan * 0.66; self[ansi::NamedColor::DimCyan] = colors.normal.cyan * 0.66;
self[ansi::NamedColor::DimWhite] = colors.normal.white * 0.66; self[ansi::NamedColor::DimWhite] = colors.normal.white * 0.66;
} },
} }
} }
@ -129,14 +125,13 @@ impl List {
for g in 0..6 { for g in 0..6 {
for b in 0..6 { for b in 0..6 {
// Override colors 16..232 with the config (if present) // Override colors 16..232 with the config (if present)
if let Some(indexed_color) = colors if let Some(indexed_color) =
.indexed_colors colors.indexed_colors.iter().find(|ic| ic.index == index as u8)
.iter()
.find(|ic| ic.index == index as u8)
{ {
self[index] = indexed_color.color; self[index] = indexed_color.color;
} else { } else {
self[index] = Rgb { r: if r == 0 { 0 } else { r * 40 + 55 }, self[index] = Rgb {
r: if r == 0 { 0 } else { r * 40 + 55 },
b: if b == 0 { 0 } else { b * 40 + 55 }, b: if b == 0 { 0 } else { b * 40 + 55 },
g: if g == 0 { 0 } else { g * 40 + 55 }, g: if g == 0 { 0 } else { g * 40 + 55 },
}; };
@ -157,10 +152,8 @@ impl List {
let color_index = 16 + 216 + i; let color_index = 16 + 216 + i;
// Override colors 232..256 with the config (if present) // Override colors 232..256 with the config (if present)
if let Some(indexed_color) = colors if let Some(indexed_color) =
.indexed_colors colors.indexed_colors.iter().find(|ic| ic.index == color_index)
.iter()
.find(|ic| ic.index == color_index)
{ {
self[index] = indexed_color.color; self[index] = indexed_color.color;
index += 1; index += 1;
@ -168,11 +161,7 @@ impl List {
} }
let value = i * 10 + 8; let value = i * 10 + 8;
self[index] = Rgb { self[index] = Rgb { r: value, g: value, b: value };
r: value,
g: value,
b: value
};
index += 1; index += 1;
} }

View File

@ -13,30 +13,32 @@
// limitations under the License. // limitations under the License.
// //
//! Exports the `Term` type which is a high-level API for the Grid //! Exports the `Term` type which is a high-level API for the Grid
use std::ops::{Range, Index, IndexMut, RangeInclusive}; use std::cmp::{max, min};
use std::{ptr, io, mem}; use std::ops::{Index, IndexMut, Range, RangeInclusive};
use std::cmp::{min, max};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use std::{io, mem, ptr};
use arraydeque::ArrayDeque; use arraydeque::ArrayDeque;
use unicode_width::UnicodeWidthChar;
use glutin::MouseCursor; use glutin::MouseCursor;
use unicode_width::UnicodeWidthChar;
use font::{self, Size}; use crate::ansi::{
use crate::ansi::{self, Color, NamedColor, Attr, Handler, CharsetIndex, StandardCharset, CursorStyle}; self, Attr, CharsetIndex, Color, CursorStyle, Handler, NamedColor, StandardCharset,
};
use crate::config::{Config, VisualBellAnimation};
use crate::grid::{ use crate::grid::{
BidirectionalIterator, DisplayIter, Grid, GridCell, IndexRegion, Indexed, Scroll, BidirectionalIterator, DisplayIter, Grid, GridCell, IndexRegion, Indexed, Scroll,
ViewportPosition ViewportPosition,
}; };
use crate::index::{self, Point, Column, Line, IndexRange, Contains, Linear}; use crate::index::{self, Column, Contains, IndexRange, Line, Linear, Point};
use crate::selection::{self, Selection, Locations};
use crate::config::{Config, VisualBellAnimation};
use copypasta::{Clipboard, Load, Store};
use crate::input::FONT_SIZE_STEP; use crate::input::FONT_SIZE_STEP;
use crate::url::{Url, UrlParser};
use crate::message_bar::MessageBuffer; use crate::message_bar::MessageBuffer;
use crate::selection::{self, Locations, Selection};
use crate::term::cell::{Cell, Flags, LineLength};
use crate::term::color::Rgb; use crate::term::color::Rgb;
use crate::term::cell::{LineLength, Cell, Flags}; use crate::url::{Url, UrlParser};
use copypasta::{Clipboard, Load, Store};
use font::{self, Size};
#[cfg(windows)] #[cfg(windows)]
use crate::tty; use crate::tty;
@ -139,10 +141,7 @@ impl Search for Term {
impl selection::Dimensions for Term { impl selection::Dimensions for Term {
fn dimensions(&self) -> Point { fn dimensions(&self) -> Point {
Point { Point { col: self.grid.num_cols(), line: self.grid.num_lines() }
col: self.grid.num_cols(),
line: self.grid.num_lines()
}
} }
} }
@ -200,7 +199,7 @@ impl<'a> RenderableCellsIter<'a> {
(ViewportPosition::Below, ViewportPosition::Visible(end_line)) => { (ViewportPosition::Below, ViewportPosition::Visible(end_line)) => {
Some((grid.num_lines(), Column(0), end_line, loc.end.col)) Some((grid.num_lines(), Column(0), end_line, loc.end.col))
}, },
(ViewportPosition::Below, ViewportPosition::Above) => { (ViewportPosition::Below, ViewportPosition::Above) => {
Some((grid.num_lines(), Column(0), Line(0), Column(0))) Some((grid.num_lines(), Column(0), Line(0), Column(0)))
}, },
_ => None, _ => None,
@ -209,14 +208,8 @@ impl<'a> RenderableCellsIter<'a> {
if let Some((start_line, start_col, end_line, end_col)) = locations { if let Some((start_line, start_col, end_line, end_col)) = locations {
// start and end *lines* are swapped as we switch from buffer to // start and end *lines* are swapped as we switch from buffer to
// Line coordinates. // Line coordinates.
let mut end = Point { let mut end = Point { line: start_line, col: start_col };
line: start_line, let mut start = Point { line: end_line, col: end_col };
col: start_col,
};
let mut start = Point {
line: end_line,
col: end_col,
};
if start > end { if start > end {
::std::mem::swap(&mut start, &mut end); ::std::mem::swap(&mut start, &mut end);
@ -242,32 +235,31 @@ impl<'a> RenderableCellsIter<'a> {
config, config,
colors: &term.colors, colors: &term.colors,
cursor_cells: ArrayDeque::new(), cursor_cells: ArrayDeque::new(),
}.initialize(cursor_style) }
.initialize(cursor_style)
} }
fn push_cursor_cells(&mut self, original: Cell, cursor: Cell, wide: Cell) { fn push_cursor_cells(&mut self, original: Cell, cursor: Cell, wide: Cell) {
// Prints the char under the cell if cursor is situated on a non-empty cell // Prints the char under the cell if cursor is situated on a non-empty cell
self.cursor_cells.push_back(Indexed { self.cursor_cells
line: self.cursor.line, .push_back(Indexed { line: self.cursor.line, column: self.cursor.col, inner: original })
column: self.cursor.col, .expect("won't exceed capacity");
inner: original,
}).expect("won't exceed capacity");
// Prints the cursor // Prints the cursor
self.cursor_cells.push_back(Indexed { self.cursor_cells
line: self.cursor.line, .push_back(Indexed { line: self.cursor.line, column: self.cursor.col, inner: cursor })
column: self.cursor.col, .expect("won't exceed capacity");
inner: cursor,
}).expect("won't exceed capacity");
// If cursor is over a wide (2 cell size) character, // If cursor is over a wide (2 cell size) character,
// print the second cursor cell // print the second cursor cell
if self.is_wide_cursor(&cursor) { if self.is_wide_cursor(&cursor) {
self.cursor_cells.push_back(Indexed { self.cursor_cells
line: self.cursor.line, .push_back(Indexed {
column: self.cursor.col + 1, line: self.cursor.line,
inner: wide, column: self.cursor.col + 1,
}).expect("won't exceed capacity"); inner: wide,
})
.expect("won't exceed capacity");
} }
} }
@ -321,11 +313,13 @@ impl<'a> RenderableCellsIter<'a> {
/// Populates list of cursor cells with the original cell /// Populates list of cursor cells with the original cell
fn populate_no_cursor(&mut self) { fn populate_no_cursor(&mut self) {
self.cursor_cells.push_back(Indexed { self.cursor_cells
line: self.cursor.line, .push_back(Indexed {
column: self.cursor.col, line: self.cursor.line,
inner: self.grid[self.cursor], column: self.cursor.col,
}).expect("won't exceed capacity"); inner: self.grid[self.cursor],
})
.expect("won't exceed capacity");
} }
fn initialize(mut self, cursor_style: CursorStyle) -> Self { fn initialize(mut self, cursor_style: CursorStyle) -> Self {
@ -342,7 +336,7 @@ impl<'a> RenderableCellsIter<'a> {
}, },
CursorStyle::Underline => { CursorStyle::Underline => {
self.populate_underline_cursor(); self.populate_underline_cursor();
} },
} }
} else { } else {
self.populate_no_cursor(); self.populate_no_cursor();
@ -361,37 +355,41 @@ impl<'a> RenderableCellsIter<'a> {
match fg { match fg {
Color::Spec(rgb) => rgb, Color::Spec(rgb) => rgb,
Color::Named(ansi) => { Color::Named(ansi) => {
match (self.config.draw_bold_text_with_bright_colors(), cell.flags & Flags::DIM_BOLD) { match (
self.config.draw_bold_text_with_bright_colors(),
cell.flags & Flags::DIM_BOLD,
) {
// If no bright foreground is set, treat it like the BOLD flag doesn't exist // If no bright foreground is set, treat it like the BOLD flag doesn't exist
(_, self::cell::Flags::DIM_BOLD) (_, self::cell::Flags::DIM_BOLD)
if ansi == NamedColor::Foreground if ansi == NamedColor::Foreground
&& self.config.colors().primary.bright_foreground.is_none() => && self.config.colors().primary.bright_foreground.is_none() =>
{ {
self.colors[NamedColor::DimForeground] self.colors[NamedColor::DimForeground]
} },
// Draw bold text in bright colors *and* contains bold flag. // Draw bold text in bright colors *and* contains bold flag.
(true, self::cell::Flags::BOLD) => self.colors[ansi.to_bright()], (true, self::cell::Flags::BOLD) => self.colors[ansi.to_bright()],
// Cell is marked as dim and not bold // Cell is marked as dim and not bold
(_, self::cell::Flags::DIM) | (_, self::cell::Flags::DIM) | (false, self::cell::Flags::DIM_BOLD) => {
(false, self::cell::Flags::DIM_BOLD) => self.colors[ansi.to_dim()], self.colors[ansi.to_dim()]
},
// None of the above, keep original color. // None of the above, keep original color.
_ => self.colors[ansi] _ => self.colors[ansi],
} }
}, },
Color::Indexed(idx) => { Color::Indexed(idx) => {
let idx = match ( let idx = match (
self.config.draw_bold_text_with_bright_colors(), self.config.draw_bold_text_with_bright_colors(),
cell.flags & Flags::DIM_BOLD, cell.flags & Flags::DIM_BOLD,
idx idx,
) { ) {
(true, self::cell::Flags::BOLD, 0..=7) => idx as usize + 8, (true, self::cell::Flags::BOLD, 0..=7) => idx as usize + 8,
(false, self::cell::Flags::DIM, 8..=15) => idx as usize - 8, (false, self::cell::Flags::DIM, 8..=15) => idx as usize - 8,
(false, self::cell::Flags::DIM, 0..=7) => idx as usize + 260, (false, self::cell::Flags::DIM, 0..=7) => idx as usize + 260,
_ => idx as usize, _ => idx as usize,
}; };
self.colors[idx] self.colors[idx]
} },
} }
} }
@ -399,7 +397,7 @@ impl<'a> RenderableCellsIter<'a> {
fn compute_bg_alpha(&self, bg: Color) -> f32 { fn compute_bg_alpha(&self, bg: Color) -> f32 {
match bg { match bg {
Color::Named(NamedColor::Background) => 0.0, Color::Named(NamedColor::Background) => 0.0,
_ => 1.0 _ => 1.0,
} }
} }
@ -435,8 +433,8 @@ impl<'a> Iterator for RenderableCellsIter<'a> {
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
loop { loop {
// Handle cursor // Handle cursor
let (cell, selected, highlighted) = if self.cursor_offset == self.inner.offset() && let (cell, selected, highlighted) = if self.cursor_offset == self.inner.offset()
self.inner.column() == self.cursor.col && self.inner.column() == self.cursor.col
{ {
// Cursor cell // Cursor cell
let mut cell = self.cursor_cells.pop_front().unwrap(); let mut cell = self.cursor_cells.pop_front().unwrap();
@ -454,9 +452,8 @@ impl<'a> Iterator for RenderableCellsIter<'a> {
let index = Linear::new(self.grid.num_cols(), cell.column, cell.line); let index = Linear::new(self.grid.num_cols(), cell.column, cell.line);
let selected = self.selection.as_ref() let selected =
.map(|range| range.contains_(index)) self.selection.as_ref().map(|range| range.contains_(index)).unwrap_or(false);
.unwrap_or(false);
// Skip empty cells // Skip empty cells
if cell.is_empty() && !selected { if cell.is_empty() && !selected {
@ -464,7 +461,9 @@ impl<'a> Iterator for RenderableCellsIter<'a> {
} }
// Underline URL highlights // Underline URL highlights
let highlighted = self.url_highlight.as_ref() let highlighted = self
.url_highlight
.as_ref()
.map(|range| range.contains_(index)) .map(|range| range.contains_(index))
.unwrap_or(false); .unwrap_or(false);
@ -506,10 +505,9 @@ impl<'a> Iterator for RenderableCellsIter<'a> {
bg: bg_rgb, bg: bg_rgb,
bg_alpha, bg_alpha,
flags, flags,
}) });
} }
} }
} }
pub mod mode { pub mod mode {
@ -558,41 +556,40 @@ impl CharsetMapping for StandardCharset {
fn map(&self, c: char) -> char { fn map(&self, c: char) -> char {
match *self { match *self {
StandardCharset::Ascii => c, StandardCharset::Ascii => c,
StandardCharset::SpecialCharacterAndLineDrawing => StandardCharset::SpecialCharacterAndLineDrawing => match c {
match c { '`' => '◆',
'`' => '◆', 'a' => '▒',
'a' => '▒', 'b' => '\t',
'b' => '\t', 'c' => '\u{000c}',
'c' => '\u{000c}', 'd' => '\r',
'd' => '\r', 'e' => '\n',
'e' => '\n', 'f' => '°',
'f' => '°', 'g' => '±',
'g' => '±', 'h' => '\u{2424}',
'h' => '\u{2424}', 'i' => '\u{000b}',
'i' => '\u{000b}', 'j' => '┘',
'j' => '┘', 'k' => '┐',
'k' => '┐', 'l' => '┌',
'l' => '┌', 'm' => '└',
'm' => '└', 'n' => '┼',
'n' => '┼', 'o' => '⎺',
'o' => '⎺', 'p' => '⎻',
'p' => '⎻', 'q' => '─',
'q' => '─', 'r' => '⎼',
'r' => '⎼', 's' => '⎽',
's' => '⎽', 't' => '├',
't' => '├', 'u' => '┤',
'u' => '┤', 'v' => '┴',
'v' => '┴', 'w' => '┬',
'w' => '┬', 'x' => '│',
'x' => '│', 'y' => '≤',
'y' => '≤', 'z' => '≥',
'z' => '≥', '{' => 'π',
'{' => 'π', '|' => '≠',
'|' => '≠', '}' => '£',
'}' => '£', '~' => '·',
'~' => '·', _ => c,
_ => c },
},
} }
} }
} }
@ -602,6 +599,7 @@ struct Charsets([StandardCharset; 4]);
impl Index<CharsetIndex> for Charsets { impl Index<CharsetIndex> for Charsets {
type Output = StandardCharset; type Output = StandardCharset;
fn index(&self, index: CharsetIndex) -> &StandardCharset { fn index(&self, index: CharsetIndex) -> &StandardCharset {
&self.0[index as usize] &self.0[index as usize]
} }
@ -637,10 +635,10 @@ pub struct VisualBell {
} }
fn cubic_bezier(p0: f64, p1: f64, p2: f64, p3: f64, x: f64) -> f64 { fn cubic_bezier(p0: f64, p1: f64, p2: f64, p3: f64, x: f64) -> f64 {
(1.0 - x).powi(3) * p0 + (1.0 - x).powi(3) * p0
3.0 * (1.0 - x).powi(2) * x * p1 + + 3.0 * (1.0 - x).powi(2) * x * p1
3.0 * (1.0 - x) * x.powi(2) * p2 + + 3.0 * (1.0 - x) * x.powi(2) * p2
x.powi(3) * p3 + x.powi(3) * p3
} }
impl VisualBell { impl VisualBell {
@ -675,7 +673,7 @@ impl VisualBell {
} }
false false
}, },
None => true None => true,
} }
} }
@ -703,10 +701,10 @@ impl VisualBell {
} }
let elapsed = instant.duration_since(earlier); let elapsed = instant.duration_since(earlier);
let elapsed_f = elapsed.as_secs() as f64 + let elapsed_f =
f64::from(elapsed.subsec_nanos()) / 1e9f64; elapsed.as_secs() as f64 + f64::from(elapsed.subsec_nanos()) / 1e9f64;
let duration_f = self.duration.as_secs() as f64 + let duration_f = self.duration.as_secs() as f64
f64::from(self.duration.subsec_nanos()) / 1e9f64; + f64::from(self.duration.subsec_nanos()) / 1e9f64;
// Otherwise, we compute a value `time` from 0.0 to 1.0 // Otherwise, we compute a value `time` from 0.0 to 1.0
// inclusive that represents the ratio of `elapsed` time to the // inclusive that represents the ratio of `elapsed` time to the
@ -722,7 +720,9 @@ impl VisualBell {
}, },
VisualBellAnimation::EaseOutSine => cubic_bezier(0.39, 0.575, 0.565, 1.0, time), VisualBellAnimation::EaseOutSine => cubic_bezier(0.39, 0.575, 0.565, 1.0, time),
VisualBellAnimation::EaseOutQuad => cubic_bezier(0.25, 0.46, 0.45, 0.94, time), VisualBellAnimation::EaseOutQuad => cubic_bezier(0.25, 0.46, 0.45, 0.94, time),
VisualBellAnimation::EaseOutCubic => cubic_bezier(0.215, 0.61, 0.355, 1.0, time), VisualBellAnimation::EaseOutCubic => {
cubic_bezier(0.215, 0.61, 0.355, 1.0, time)
},
VisualBellAnimation::EaseOutQuart => cubic_bezier(0.165, 0.84, 0.44, 1.0, time), VisualBellAnimation::EaseOutQuart => cubic_bezier(0.165, 0.84, 0.44, 1.0, time),
VisualBellAnimation::EaseOutQuint => cubic_bezier(0.23, 1.0, 0.32, 1.0, time), VisualBellAnimation::EaseOutQuint => cubic_bezier(0.23, 1.0, 0.32, 1.0, time),
VisualBellAnimation::EaseOutExpo => cubic_bezier(0.19, 1.0, 0.22, 1.0, time), VisualBellAnimation::EaseOutExpo => cubic_bezier(0.19, 1.0, 0.22, 1.0, time),
@ -733,7 +733,7 @@ impl VisualBell {
// Since we want the `intensity` of the VisualBell to decay over // Since we want the `intensity` of the VisualBell to decay over
// `time`, we subtract the `inverse_intensity` from 1.0. // `time`, we subtract the `inverse_intensity` from 1.0.
1.0 - inverse_intensity 1.0 - inverse_intensity
} },
} }
} }
@ -890,12 +890,11 @@ impl SizeInfo {
Point { Point {
line: min(line, Line(self.lines().saturating_sub(1))), line: min(line, Line(self.lines().saturating_sub(1))),
col: min(col, Column(self.cols().saturating_sub(1))) col: min(col, Column(self.cols().saturating_sub(1))),
} }
} }
} }
impl Term { impl Term {
pub fn selection(&self) -> &Option<Selection> { pub fn selection(&self) -> &Option<Selection> {
&self.grid.selection &self.grid.selection
@ -997,8 +996,7 @@ impl Term {
self.default_cursor_style = config.cursor_style(); self.default_cursor_style = config.cursor_style();
self.dynamic_title = config.dynamic_title(); self.dynamic_title = config.dynamic_title();
self.auto_scroll = config.scrolling().auto_scroll; self.auto_scroll = config.scrolling().auto_scroll;
self.grid self.grid.update_history(config.scrolling().history as usize, &self.cursor.template);
.update_history(config.scrolling().history as usize, &self.cursor.template);
} }
#[inline] #[inline]
@ -1011,7 +1009,9 @@ impl Term {
trait PushChar { trait PushChar {
fn push_char(&mut self, c: char); fn push_char(&mut self, c: char);
fn maybe_newline(&mut self, grid: &Grid<Cell>, line: usize, ending: Column) { fn maybe_newline(&mut self, grid: &Grid<Cell>, line: usize, ending: Column) {
if ending != Column(0) && !grid[line][ending - 1].flags.contains(cell::Flags::WRAPLINE) { if ending != Column(0)
&& !grid[line][ending - 1].flags.contains(cell::Flags::WRAPLINE)
{
self.push_char('\n'); self.push_char('\n');
} }
} }
@ -1026,7 +1026,7 @@ impl Term {
use std::ops::Range; use std::ops::Range;
trait Append : PushChar { trait Append: PushChar {
fn append( fn append(
&mut self, &mut self,
grid: &Grid<Cell>, grid: &Grid<Cell>,
@ -1042,7 +1042,7 @@ impl Term {
grid: &Grid<Cell>, grid: &Grid<Cell>,
tabs: &TabStops, tabs: &TabStops,
mut line: usize, mut line: usize,
cols: Range<Column> cols: Range<Column>,
) { ) {
// Select until last line still within the buffer // Select until last line still within the buffer
line = min(line, grid.len() - 1); line = min(line, grid.len() - 1);
@ -1115,7 +1115,6 @@ impl Term {
// Starting line // Starting line
res.append(&self.grid, &self.tabs, start.line, Column(0)..start.col); res.append(&self.grid, &self.tabs, start.line, Column(0)..start.col);
}, },
// Multi line selection // Multi line selection
@ -1130,7 +1129,7 @@ impl Term {
// Starting line // Starting line
res.append(&self.grid, &self.tabs, start.line, Column(0)..start.col); res.append(&self.grid, &self.tabs, start.line, Column(0)..start.col);
} },
} }
Some(res) Some(res)
@ -1180,11 +1179,12 @@ impl Term {
window_focused: bool, window_focused: bool,
) -> RenderableCellsIter<'_> { ) -> RenderableCellsIter<'_> {
let alt_screen = self.mode.contains(TermMode::ALT_SCREEN); let alt_screen = self.mode.contains(TermMode::ALT_SCREEN);
let selection = self.grid.selection.as_ref() let selection = self
.grid
.selection
.as_ref()
.and_then(|s| s.to_span(self, alt_screen)) .and_then(|s| s.to_span(self, alt_screen))
.map(|span| { .map(|span| span.to_locations());
span.to_locations()
});
let cursor = if window_focused || !config.unfocused_hollow_cursor() { let cursor = if window_focused || !config.unfocused_hollow_cursor() {
self.cursor_style.unwrap_or(self.default_cursor_style) self.cursor_style.unwrap_or(self.default_cursor_style)
@ -1192,12 +1192,7 @@ impl Term {
CursorStyle::HollowBlock CursorStyle::HollowBlock
}; };
RenderableCellsIter::new( RenderableCellsIter::new(&self, config, selection, cursor)
&self,
config,
selection,
cursor,
)
} }
/// Resize terminal to new dimensions /// Resize terminal to new dimensions
@ -1205,8 +1200,8 @@ impl Term {
debug!("Resizing terminal"); debug!("Resizing terminal");
// Bounds check; lots of math assumes width and height are > 0 // Bounds check; lots of math assumes width and height are > 0
if size.width as usize <= 2 * self.size_info.padding_x as usize || if size.width as usize <= 2 * self.size_info.padding_x as usize
size.height as usize <= 2 * self.size_info.padding_y as usize || size.height as usize <= 2 * self.size_info.padding_y as usize
{ {
return; return;
} }
@ -1455,10 +1450,7 @@ impl ansi::Handler for Term {
trace!("Wrapping input"); trace!("Wrapping input");
{ {
let location = Point { let location = Point { line: self.cursor.point.line, col: self.cursor.point.col };
line: self.cursor.point.line,
col: self.cursor.point.col
};
let cell = &mut self.grid[&location]; let cell = &mut self.grid[&location];
cell.flags.insert(cell::Flags::WRAPLINE); cell.flags.insert(cell::Flags::WRAPLINE);
@ -1498,8 +1490,7 @@ impl ansi::Handler for Term {
if width == 0 { if width == 0 {
let col = self.cursor.point.col.0.saturating_sub(1); let col = self.cursor.point.col.0.saturating_sub(1);
let line = self.cursor.point.line; let line = self.cursor.point.line;
if self.grid[line][Column(col)].flags.contains(cell::Flags::WIDE_CHAR_SPACER) if self.grid[line][Column(col)].flags.contains(cell::Flags::WIDE_CHAR_SPACER) {
{
col.saturating_sub(1); col.saturating_sub(1);
} }
self.grid[line][Column(col)].push_extra(c); self.grid[line][Column(col)].push_extra(c);
@ -1536,8 +1527,7 @@ impl ansi::Handler for Term {
let mut template = self.cursor.template; let mut template = self.cursor.template;
template.c = 'E'; template.c = 'E';
self.grid.region_mut(..) self.grid.region_mut(..).each(|c| c.reset(&template));
.each(|c| c.reset(&template));
} }
#[inline] #[inline]
@ -1858,11 +1848,7 @@ impl ansi::Handler for Term {
#[inline] #[inline]
fn save_cursor_position(&mut self) { fn save_cursor_position(&mut self) {
trace!("Saving cursor position"); trace!("Saving cursor position");
let cursor = if self.alt { let cursor = if self.alt { &mut self.cursor_save_alt } else { &mut self.cursor_save };
&mut self.cursor_save_alt
} else {
&mut self.cursor_save
};
*cursor = self.cursor; *cursor = self.cursor;
} }
@ -1870,11 +1856,7 @@ impl ansi::Handler for Term {
#[inline] #[inline]
fn restore_cursor_position(&mut self) { fn restore_cursor_position(&mut self) {
trace!("Restoring cursor position"); trace!("Restoring cursor position");
let source = if self.alt { let source = if self.alt { &self.cursor_save_alt } else { &self.cursor_save };
&self.cursor_save_alt
} else {
&self.cursor_save
};
self.cursor = *source; self.cursor = *source;
self.cursor.point.line = min(self.cursor.point.line, self.grid.num_lines() - 1); self.cursor.point.line = min(self.cursor.point.line, self.grid.num_lines() - 1);
@ -1887,7 +1869,7 @@ impl ansi::Handler for Term {
let mut template = self.cursor.template; let mut template = self.cursor.template;
template.flags ^= template.flags; template.flags ^= template.flags;
let col = self.cursor.point.col; let col = self.cursor.point.col;
match mode { match mode {
ansi::LineClearMode::Right => { ansi::LineClearMode::Right => {
@ -1929,13 +1911,12 @@ impl ansi::Handler for Term {
/// Set the clipboard /// Set the clipboard
#[inline] #[inline]
fn set_clipboard(&mut self, string: &str) fn set_clipboard(&mut self, string: &str) {
{ Clipboard::new().and_then(|mut clipboard| clipboard.store_primary(string)).unwrap_or_else(
Clipboard::new() |err| {
.and_then(|mut clipboard| clipboard.store_primary(string))
.unwrap_or_else(|err| {
warn!("Error storing selection to clipboard: {}", err); warn!("Error storing selection to clipboard: {}", err);
}); },
);
} }
#[inline] #[inline]
@ -1954,7 +1935,8 @@ impl ansi::Handler for Term {
cell.reset(&template); cell.reset(&template);
} }
if self.cursor.point.line < self.grid.num_lines() - 1 { if self.cursor.point.line < self.grid.num_lines() - 1 {
self.grid.region_mut((self.cursor.point.line + 1)..) self.grid
.region_mut((self.cursor.point.line + 1)..)
.each(|cell| cell.reset(&template)); .each(|cell| cell.reset(&template));
} }
}, },
@ -1963,7 +1945,8 @@ impl ansi::Handler for Term {
// If clearing more than one line // If clearing more than one line
if self.cursor.point.line > Line(1) { if self.cursor.point.line > Line(1) {
// Fully clear all lines before the current line // Fully clear all lines before the current line
self.grid.region_mut(..self.cursor.point.line) self.grid
.region_mut(..self.cursor.point.line)
.each(|cell| cell.reset(&template)); .each(|cell| cell.reset(&template));
} }
// Clear up to the current column in the current line // Clear up to the current column in the current line
@ -1987,7 +1970,7 @@ impl ansi::Handler for Term {
}, },
ansi::TabulationClearMode::All => { ansi::TabulationClearMode::All => {
self.tabs.clear_all(); self.tabs.clear_all();
} },
} }
} }
@ -2042,7 +2025,9 @@ impl ansi::Handler for Term {
Attr::Bold => self.cursor.template.flags.insert(cell::Flags::BOLD), Attr::Bold => self.cursor.template.flags.insert(cell::Flags::BOLD),
Attr::CancelBold => self.cursor.template.flags.remove(cell::Flags::BOLD), Attr::CancelBold => self.cursor.template.flags.remove(cell::Flags::BOLD),
Attr::Dim => self.cursor.template.flags.insert(cell::Flags::DIM), Attr::Dim => self.cursor.template.flags.insert(cell::Flags::DIM),
Attr::CancelBoldDim => self.cursor.template.flags.remove(cell::Flags::BOLD | cell::Flags::DIM), Attr::CancelBoldDim => {
self.cursor.template.flags.remove(cell::Flags::BOLD | cell::Flags::DIM)
},
Attr::Italic => self.cursor.template.flags.insert(cell::Flags::ITALIC), Attr::Italic => self.cursor.template.flags.insert(cell::Flags::ITALIC),
Attr::CancelItalic => self.cursor.template.flags.remove(cell::Flags::ITALIC), Attr::CancelItalic => self.cursor.template.flags.remove(cell::Flags::ITALIC),
Attr::Underscore => self.cursor.template.flags.insert(cell::Flags::UNDERLINE), Attr::Underscore => self.cursor.template.flags.insert(cell::Flags::UNDERLINE),
@ -2053,7 +2038,7 @@ impl ansi::Handler for Term {
Attr::CancelStrike => self.cursor.template.flags.remove(cell::Flags::STRIKEOUT), Attr::CancelStrike => self.cursor.template.flags.remove(cell::Flags::STRIKEOUT),
_ => { _ => {
debug!("Term got unhandled attr: {:?}", attr); debug!("Term got unhandled attr: {:?}", attr);
} },
} }
} }
@ -2093,12 +2078,12 @@ impl ansi::Handler for Term {
ansi::Mode::Insert => self.mode.insert(mode::TermMode::INSERT), // heh ansi::Mode::Insert => self.mode.insert(mode::TermMode::INSERT), // heh
ansi::Mode::BlinkingCursor => { ansi::Mode::BlinkingCursor => {
trace!("... unimplemented mode"); trace!("... unimplemented mode");
} },
} }
} }
#[inline] #[inline]
fn unset_mode(&mut self,mode: ansi::Mode) { fn unset_mode(&mut self, mode: ansi::Mode) {
trace!("Unsetting mode: {:?}", mode); trace!("Unsetting mode: {:?}", mode);
match mode { match mode {
ansi::Mode::SwapScreenAndSetRestoreCursor => { ansi::Mode::SwapScreenAndSetRestoreCursor => {
@ -2133,7 +2118,7 @@ impl ansi::Handler for Term {
ansi::Mode::Insert => self.mode.remove(mode::TermMode::INSERT), ansi::Mode::Insert => self.mode.remove(mode::TermMode::INSERT),
ansi::Mode::BlinkingCursor => { ansi::Mode::BlinkingCursor => {
trace!("... unimplemented mode"); trace!("... unimplemented mode");
} },
} }
} }
@ -2177,7 +2162,7 @@ impl ansi::Handler for Term {
} }
struct TabStops { struct TabStops {
tabs: Vec<bool> tabs: Vec<bool>,
} }
impl TabStops { impl TabStops {
@ -2185,7 +2170,7 @@ impl TabStops {
TabStops { TabStops {
tabs: IndexRange::from(Column(0)..num_cols) tabs: IndexRange::from(Column(0)..num_cols)
.map(|i| (*i as usize) % tabspaces == 0) .map(|i| (*i as usize) % tabspaces == 0)
.collect::<Vec<bool>>() .collect::<Vec<bool>>(),
} }
} }
@ -2214,18 +2199,18 @@ impl IndexMut<Column> for TabStops {
mod tests { mod tests {
use serde_json; use serde_json;
use super::{Cell, Term, SizeInfo}; use super::{Cell, SizeInfo, Term};
use crate::term::cell; use crate::term::cell;
use crate::grid::{Grid, Scroll}; use crate::ansi::{self, CharsetIndex, Handler, StandardCharset};
use crate::index::{Point, Line, Column, Side};
use crate::ansi::{self, Handler, CharsetIndex, StandardCharset};
use crate::selection::Selection;
use std::mem;
use crate::input::FONT_SIZE_STEP;
use font::Size;
use crate::config::Config; use crate::config::Config;
use crate::grid::{Grid, Scroll};
use crate::index::{Column, Line, Point, Side};
use crate::input::FONT_SIZE_STEP;
use crate::message_bar::MessageBuffer; use crate::message_bar::MessageBuffer;
use crate::selection::Selection;
use font::Size;
use std::mem;
#[test] #[test]
fn semantic_selection_works() { fn semantic_selection_works() {
@ -2290,7 +2275,6 @@ mod tests {
grid[Line(0)][Column(0)].c = '"'; grid[Line(0)][Column(0)].c = '"';
grid[Line(0)][Column(3)].c = '"'; grid[Line(0)][Column(3)].c = '"';
mem::swap(&mut term.grid, &mut grid); mem::swap(&mut term.grid, &mut grid);
*term.selection_mut() = Some(Selection::lines(Point { line: 0, col: Column(3) })); *term.selection_mut() = Some(Selection::lines(Point { line: 0, col: Column(3) }));
@ -2336,8 +2320,7 @@ mod tests {
let grid: Grid<Cell> = Grid::new(Line(24), Column(80), 0, template); let grid: Grid<Cell> = Grid::new(Line(24), Column(80), 0, template);
let serialized = serde_json::to_string(&grid).expect("ser"); let serialized = serde_json::to_string(&grid).expect("ser");
let deserialized = serde_json::from_str::<Grid<Cell>>(&serialized) let deserialized = serde_json::from_str::<Grid<Cell>>(&serialized).expect("de");
.expect("de");
assert_eq!(deserialized, grid); assert_eq!(deserialized, grid);
} }
@ -2355,8 +2338,7 @@ mod tests {
}; };
let mut term = Term::new(&Default::default(), size, MessageBuffer::new()); let mut term = Term::new(&Default::default(), size, MessageBuffer::new());
let cursor = Point::new(Line(0), Column(0)); let cursor = Point::new(Line(0), Column(0));
term.configure_charset(CharsetIndex::G0, term.configure_charset(CharsetIndex::G0, StandardCharset::SpecialCharacterAndLineDrawing);
StandardCharset::SpecialCharacterAndLineDrawing);
term.input('a'); term.input('a');
assert_eq!(term.grid()[&cursor].c, '▒'); assert_eq!(term.grid()[&cursor].c, '▒');
@ -2440,7 +2422,7 @@ mod tests {
cell_height: 3.0, cell_height: 3.0,
padding_x: 0.0, padding_x: 0.0,
padding_y: 0.0, padding_y: 0.0,
dpr: 1.0 dpr: 1.0,
}; };
let config: Config = Default::default(); let config: Config = Default::default();
let mut term: Term = Term::new(&config, size, MessageBuffer::new()); let mut term: Term = Term::new(&config, size, MessageBuffer::new());
@ -2460,27 +2442,27 @@ mod tests {
#[cfg(all(test, feature = "bench"))] #[cfg(all(test, feature = "bench"))]
mod benches { mod benches {
extern crate test;
extern crate serde_json as json; extern crate serde_json as json;
extern crate test;
use std::io::Read;
use std::fs::File; use std::fs::File;
use std::io::Read;
use std::mem; use std::mem;
use std::path::Path; use std::path::Path;
use crate::grid::Grid;
use crate::config::Config; use crate::config::Config;
use crate::grid::Grid;
use crate::message_bar::MessageBuffer; use crate::message_bar::MessageBuffer;
use super::{SizeInfo, Term};
use super::cell::Cell; use super::cell::Cell;
use super::{SizeInfo, Term};
fn read_string<P>(path: P) -> String fn read_string<P>(path: P) -> String
where P: AsRef<Path> where
P: AsRef<Path>,
{ {
let mut res = String::new(); let mut res = String::new();
File::open(path.as_ref()).unwrap() File::open(path.as_ref()).unwrap().read_to_string(&mut res).unwrap();
.read_to_string(&mut res).unwrap();
res res
} }
@ -2497,12 +2479,14 @@ mod benches {
#[bench] #[bench]
fn render_iter(b: &mut test::Bencher) { fn render_iter(b: &mut test::Bencher) {
// Need some realistic grid state; using one of the ref files. // Need some realistic grid state; using one of the ref files.
let serialized_grid = read_string( let serialized_grid = read_string(concat!(
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/ref/vim_large_window_scroll/grid.json") env!("CARGO_MANIFEST_DIR"),
); "/tests/ref/vim_large_window_scroll/grid.json"
let serialized_size = read_string( ));
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/ref/vim_large_window_scroll/size.json") let serialized_size = read_string(concat!(
); env!("CARGO_MANIFEST_DIR"),
"/tests/ref/vim_large_window_scroll/size.json"
));
let mut grid: Grid<Cell> = json::from_str(&serialized_grid).unwrap(); let mut grid: Grid<Cell> = json::from_str(&serialized_grid).unwrap();
let size: SizeInfo = json::from_str(&serialized_size).unwrap(); let size: SizeInfo = json::from_str(&serialized_size).unwrap();

View File

@ -57,7 +57,7 @@ pub trait EventedReadWrite {
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum ChildEvent { pub enum ChildEvent {
/// Indicates the child has exited /// Indicates the child has exited
Exited Exited,
} }
/// A pseudoterminal (or PTY) /// A pseudoterminal (or PTY)
@ -65,7 +65,7 @@ pub enum ChildEvent {
/// This is a refinement of EventedReadWrite that also provides a channel through which we can be /// This is a refinement of EventedReadWrite that also provides a channel through which we can be
/// notified if the PTY child process does something we care about (other than writing to the TTY). /// notified if the PTY child process does something we care about (other than writing to the TTY).
/// In particular, this allows for race-free child exit notification on UNIX (cf. `SIGCHLD`). /// In particular, this allows for race-free child exit notification on UNIX (cf. `SIGCHLD`).
pub trait EventedPty : EventedReadWrite { pub trait EventedPty: EventedReadWrite {
#[cfg(unix)] #[cfg(unix)]
fn child_event_token(&self) -> mio::Token; fn child_event_token(&self) -> mio::Token;
@ -83,11 +83,7 @@ pub fn setup_env(config: &Config) {
// below. // below.
env::set_var( env::set_var(
"TERM", "TERM",
if Database::from_name("alacritty").is_ok() { if Database::from_name("alacritty").is_ok() { "alacritty" } else { "xterm-256color" },
"alacritty"
} else {
"xterm-256color"
},
); );
// Advertise 24-bit color support // Advertise 24-bit color support

View File

@ -15,24 +15,27 @@
//! tty related functionality //! tty related functionality
//! //!
use crate::tty::{EventedReadWrite, EventedPty, ChildEvent};
use crate::term::SizeInfo;
use crate::display::OnResize;
use crate::config::{Config, Shell};
use crate::cli::Options; use crate::cli::Options;
use crate::config::{Config, Shell};
use crate::display::OnResize;
use crate::term::SizeInfo;
use crate::tty::{ChildEvent, EventedPty, EventedReadWrite};
use mio; use mio;
use libc::{self, c_int, pid_t, winsize, TIOCSCTTY}; use libc::{self, c_int, pid_t, winsize, TIOCSCTTY};
use nix::pty::openpty; use nix::pty::openpty;
use signal_hook::{self as sighook, iterator::Signals}; use signal_hook::{self as sighook, iterator::Signals};
use std::os::unix::{process::CommandExt, io::{FromRawFd, AsRawFd, RawFd}};
use std::fs::File;
use std::process::{Command, Stdio, Child};
use std::ffi::CStr;
use std::ptr;
use mio::unix::EventedFd; use mio::unix::EventedFd;
use std::ffi::CStr;
use std::fs::File;
use std::io; use std::io;
use std::os::unix::{
io::{AsRawFd, FromRawFd, RawFd},
process::CommandExt,
};
use std::process::{Child, Command, Stdio};
use std::ptr;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
/// Process ID of child process /// Process ID of child process
@ -55,8 +58,7 @@ fn make_pty(size: winsize) -> (RawFd, RawFd) {
win_size.ws_xpixel = 0; win_size.ws_xpixel = 0;
win_size.ws_ypixel = 0; win_size.ws_ypixel = 0;
let ends = openpty(Some(&win_size), None) let ends = openpty(Some(&win_size), None).expect("openpty failed");
.expect("openpty failed");
(ends.master, ends.slave) (ends.master, ends.slave)
} }
@ -134,7 +136,6 @@ pub struct Pty {
token: mio::Token, token: mio::Token,
signals: Signals, signals: Signals,
signals_token: mio::Token, signals_token: mio::Token,
} }
impl Pty { impl Pty {
@ -145,9 +146,7 @@ impl Pty {
pub fn resize<T: ToWinsize>(&self, size: &T) { pub fn resize<T: ToWinsize>(&self, size: &T) {
let win = size.to_winsize(); let win = size.to_winsize();
let res = unsafe { let res = unsafe { libc::ioctl(self.fd.as_raw_fd(), libc::TIOCSWINSZ, &win as *const _) };
libc::ioctl(self.fd.as_raw_fd(), libc::TIOCSWINSZ, &win as *const _)
};
if res < 0 { if res < 0 {
die!("ioctl TIOCSWINSZ failed: {}", errno()); die!("ioctl TIOCSWINSZ failed: {}", errno());
@ -170,10 +169,7 @@ pub fn new<T: ToWinsize>(
let default_shell = if cfg!(target_os = "macos") { let default_shell = if cfg!(target_os = "macos") {
let shell_name = pw.shell.rsplit('/').next().unwrap(); let shell_name = pw.shell.rsplit('/').next().unwrap();
let argv = vec![ let argv = vec![String::from("-c"), format!("exec -a -{} {}", shell_name, pw.shell)];
String::from("-c"),
format!("exec -a -{} {}", shell_name, pw.shell),
];
Shell::new_with_args("/bin/bash", argv) Shell::new_with_args("/bin/bash", argv)
} else { } else {
@ -265,7 +261,7 @@ pub fn new<T: ToWinsize>(
}, },
Err(err) => { Err(err) => {
die!("Failed to spawn command: {}", err); die!("Failed to spawn command: {}", err);
} },
} }
} }
@ -282,19 +278,14 @@ impl EventedReadWrite for Pty {
poll_opts: mio::PollOpt, poll_opts: mio::PollOpt,
) -> io::Result<()> { ) -> io::Result<()> {
self.token = token.next().unwrap(); self.token = token.next().unwrap();
poll.register( poll.register(&EventedFd(&self.fd.as_raw_fd()), self.token, interest, poll_opts)?;
&EventedFd(&self.fd.as_raw_fd()),
self.token,
interest,
poll_opts
)?;
self.signals_token = token.next().unwrap(); self.signals_token = token.next().unwrap();
poll.register( poll.register(
&self.signals, &self.signals,
self.signals_token, self.signals_token,
mio::Ready::readable(), mio::Ready::readable(),
mio::PollOpt::level() mio::PollOpt::level(),
) )
} }
@ -303,20 +294,15 @@ impl EventedReadWrite for Pty {
&mut self, &mut self,
poll: &mio::Poll, poll: &mio::Poll,
interest: mio::Ready, interest: mio::Ready,
poll_opts: mio::PollOpt poll_opts: mio::PollOpt,
) -> io::Result<()> { ) -> io::Result<()> {
poll.reregister( poll.reregister(&EventedFd(&self.fd.as_raw_fd()), self.token, interest, poll_opts)?;
&EventedFd(&self.fd.as_raw_fd()),
self.token,
interest,
poll_opts
)?;
poll.reregister( poll.reregister(
&self.signals, &self.signals,
self.signals_token, self.signals_token,
mio::Ready::readable(), mio::Ready::readable(),
mio::PollOpt::level() mio::PollOpt::level(),
) )
} }
@ -350,23 +336,20 @@ impl EventedReadWrite for Pty {
impl EventedPty for Pty { impl EventedPty for Pty {
#[inline] #[inline]
fn next_child_event(&mut self) -> Option<ChildEvent> { fn next_child_event(&mut self) -> Option<ChildEvent> {
self.signals self.signals.pending().next().and_then(|signal| {
.pending() if signal != sighook::SIGCHLD {
.next() return None;
.and_then(|signal| { }
if signal != sighook::SIGCHLD {
return None;
}
match self.child.try_wait() { match self.child.try_wait() {
Err(e) => { Err(e) => {
error!("Error checking child process termination: {}", e); error!("Error checking child process termination: {}", e);
None None
}, },
Ok(None) => None, Ok(None) => None,
Ok(_) => Some(ChildEvent::Exited), Ok(_) => Some(ChildEvent::Exited),
} }
}) })
} }
#[inline] #[inline]
@ -400,9 +383,7 @@ impl OnResize for i32 {
fn on_resize(&mut self, size: &SizeInfo) { fn on_resize(&mut self, size: &SizeInfo) {
let win = size.to_winsize(); let win = size.to_winsize();
let res = unsafe { let res = unsafe { libc::ioctl(*self, libc::TIOCSWINSZ, &win as *const _) };
libc::ioctl(*self, libc::TIOCSWINSZ, &win as *const _)
};
if res < 0 { if res < 0 {
die!("ioctl TIOCSWINSZ failed: {}", errno()); die!("ioctl TIOCSWINSZ failed: {}", errno());

View File

@ -214,10 +214,7 @@ pub fn new<'a>(
cmdline.insert(0, initial_command.program().into()); cmdline.insert(0, initial_command.program().into());
// Warning, here be borrow hell // Warning, here be borrow hell
let cwd = options let cwd = options.working_dir.as_ref().map(|dir| canonicalize(dir).unwrap());
.working_dir
.as_ref()
.map(|dir| canonicalize(dir).unwrap());
let cwd = cwd.as_ref().map(|dir| dir.to_str().unwrap()); let cwd = cwd.as_ref().map(|dir| dir.to_str().unwrap());
// Create the client application, using startup info containing ConPTY info // Create the client application, using startup info containing ConPTY info
@ -250,10 +247,7 @@ pub fn new<'a>(
let conin = EventedAnonWrite::new(conin); let conin = EventedAnonWrite::new(conin);
let conout = EventedAnonRead::new(conout); let conout = EventedAnonRead::new(conout);
let agent = Conpty { let agent = Conpty { handle: pty_handle, api };
handle: pty_handle,
api,
};
Some(Pty { Some(Pty {
handle: super::PtyHandle::Conpty(ConptyHandle::new(agent)), handle: super::PtyHandle::Conpty(ConptyHandle::new(agent)),
@ -279,10 +273,7 @@ fn coord_from_sizeinfo(sizeinfo: &SizeInfo) -> Option<COORD> {
let lines = sizeinfo.lines().0; let lines = sizeinfo.lines().0;
if cols <= i16::MAX as usize && lines <= i16::MAX as usize { if cols <= i16::MAX as usize && lines <= i16::MAX as usize {
Some(COORD { Some(COORD { X: sizeinfo.cols().0 as i16, Y: sizeinfo.lines().0 as i16 })
X: sizeinfo.cols().0 as i16,
Y: sizeinfo.lines().0 as i16,
})
} else { } else {
None None
} }

View File

@ -28,7 +28,7 @@ use crate::cli::Options;
use crate::config::Config; use crate::config::Config;
use crate::display::OnResize; use crate::display::OnResize;
use crate::term::SizeInfo; use crate::term::SizeInfo;
use crate::tty::{EventedReadWrite, EventedPty}; use crate::tty::{EventedPty, EventedReadWrite};
mod conpty; mod conpty;
mod winpty; mod winpty;
@ -44,14 +44,14 @@ pub fn process_should_exit() -> bool {
WAIT_OBJECT_0 => { WAIT_OBJECT_0 => {
info!("wait_object_0"); info!("wait_object_0");
true true
} },
// Reached timeout of 0, process has not exited // Reached timeout of 0, process has not exited
WAIT_TIMEOUT => false, WAIT_TIMEOUT => false,
// Error checking process, winpty gave us a bad agent handle? // Error checking process, winpty gave us a bad agent handle?
_ => { _ => {
info!("Bad exit: {}", ::std::io::Error::last_os_error()); info!("Bad exit: {}", ::std::io::Error::last_os_error());
true true
} },
} }
} }
} }
@ -219,7 +219,7 @@ impl<'a> OnResize for PtyHandle<'a> {
PtyHandle::Conpty(c) => { PtyHandle::Conpty(c) => {
let mut handle = c.clone(); let mut handle = c.clone();
handle.on_resize(sizeinfo) handle.on_resize(sizeinfo)
} },
} }
} }
} }
@ -240,34 +240,14 @@ impl<'a> EventedReadWrite for Pty<'a> {
self.write_token = token.next().unwrap(); self.write_token = token.next().unwrap();
if interest.is_readable() { if interest.is_readable() {
poll.register( poll.register(&self.conout, self.read_token, mio::Ready::readable(), poll_opts)?
&self.conout,
self.read_token,
mio::Ready::readable(),
poll_opts,
)?
} else { } else {
poll.register( poll.register(&self.conout, self.read_token, mio::Ready::empty(), poll_opts)?
&self.conout,
self.read_token,
mio::Ready::empty(),
poll_opts,
)?
} }
if interest.is_writable() { if interest.is_writable() {
poll.register( poll.register(&self.conin, self.write_token, mio::Ready::writable(), poll_opts)?
&self.conin,
self.write_token,
mio::Ready::writable(),
poll_opts,
)?
} else { } else {
poll.register( poll.register(&self.conin, self.write_token, mio::Ready::empty(), poll_opts)?
&self.conin,
self.write_token,
mio::Ready::empty(),
poll_opts,
)?
} }
Ok(()) Ok(())
} }
@ -280,34 +260,14 @@ impl<'a> EventedReadWrite for Pty<'a> {
poll_opts: mio::PollOpt, poll_opts: mio::PollOpt,
) -> io::Result<()> { ) -> io::Result<()> {
if interest.is_readable() { if interest.is_readable() {
poll.reregister( poll.reregister(&self.conout, self.read_token, mio::Ready::readable(), poll_opts)?;
&self.conout,
self.read_token,
mio::Ready::readable(),
poll_opts,
)?;
} else { } else {
poll.reregister( poll.reregister(&self.conout, self.read_token, mio::Ready::empty(), poll_opts)?;
&self.conout,
self.read_token,
mio::Ready::empty(),
poll_opts,
)?;
} }
if interest.is_writable() { if interest.is_writable() {
poll.reregister( poll.reregister(&self.conin, self.write_token, mio::Ready::writable(), poll_opts)?;
&self.conin,
self.write_token,
mio::Ready::writable(),
poll_opts,
)?;
} else { } else {
poll.reregister( poll.reregister(&self.conin, self.write_token, mio::Ready::empty(), poll_opts)?;
&self.conin,
self.write_token,
mio::Ready::empty(),
poll_opts,
)?;
} }
Ok(()) Ok(())
} }
@ -340,4 +300,4 @@ impl<'a> EventedReadWrite for Pty<'a> {
} }
} }
impl<'a> EventedPty for Pty<'a> { } impl<'a> EventedPty for Pty<'a> {}

View File

@ -14,29 +14,29 @@
use super::{Pty, HANDLE}; use super::{Pty, HANDLE};
use std::io;
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::os::windows::io::{FromRawHandle, IntoRawHandle}; use std::io;
use std::os::windows::fs::OpenOptionsExt; use std::os::windows::fs::OpenOptionsExt;
use std::os::windows::io::{FromRawHandle, IntoRawHandle};
use std::sync::Arc; use std::sync::Arc;
use std::u16; use std::u16;
use dunce::canonicalize; use dunce::canonicalize;
use mio_named_pipes::NamedPipe; use mio_named_pipes::NamedPipe;
use winapi::um::winbase::FILE_FLAG_OVERLAPPED; use winapi::um::winbase::FILE_FLAG_OVERLAPPED;
use winpty::{ConfigFlags, MouseMode, SpawnConfig, SpawnFlags, Winpty};
use winpty::Config as WinptyConfig; use winpty::Config as WinptyConfig;
use winpty::{ConfigFlags, MouseMode, SpawnConfig, SpawnFlags, Winpty};
use crate::cli::Options;
use crate::config::{Config, Shell}; use crate::config::{Config, Shell};
use crate::display::OnResize; use crate::display::OnResize;
use crate::cli::Options;
use crate::term::SizeInfo; use crate::term::SizeInfo;
// We store a raw pointer because we need mutable access to call // We store a raw pointer because we need mutable access to call
// on_resize from a separate thread. Winpty internally uses a mutex // on_resize from a separate thread. Winpty internally uses a mutex
// so this is safe, despite outwards appearance. // so this is safe, despite outwards appearance.
pub struct Agent<'a> { pub struct Agent<'a> {
winpty: *mut Winpty<'a> winpty: *mut Winpty<'a>,
} }
/// Handle can be cloned freely and moved between threads. /// Handle can be cloned freely and moved between threads.
@ -48,9 +48,7 @@ unsafe impl<'a> Sync for Agent<'a> {}
impl<'a> Agent<'a> { impl<'a> Agent<'a> {
pub fn new(winpty: Winpty<'a>) -> Self { pub fn new(winpty: Winpty<'a>) -> Self {
Self { Self { winpty: Box::into_raw(Box::new(winpty)) }
winpty: Box::into_raw(Box::new(winpty))
}
} }
/// Get immutable access to Winpty. /// Get immutable access to Winpty.
@ -68,11 +66,12 @@ impl<'a> Agent<'a> {
impl<'a> Drop for Agent<'a> { impl<'a> Drop for Agent<'a> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { Box::from_raw(self.winpty); } unsafe {
Box::from_raw(self.winpty);
}
} }
} }
/// How long the winpty agent should wait for any RPC request /// How long the winpty agent should wait for any RPC request
/// This is a placeholder value until we see how often long responses happen /// This is a placeholder value until we see how often long responses happen
const AGENT_TIMEOUT: u32 = 10000; const AGENT_TIMEOUT: u32 = 10000;
@ -112,30 +111,19 @@ pub fn new<'a>(
Some(&cmdline.join(" ")), Some(&cmdline.join(" ")),
cwd, cwd,
None, // Env None, // Env
).unwrap(); )
.unwrap();
let default_opts = &mut OpenOptions::new(); let default_opts = &mut OpenOptions::new();
default_opts default_opts.share_mode(0).custom_flags(FILE_FLAG_OVERLAPPED);
.share_mode(0)
.custom_flags(FILE_FLAG_OVERLAPPED);
let (conout_pipe, conin_pipe); let (conout_pipe, conin_pipe);
unsafe { unsafe {
conout_pipe = NamedPipe::from_raw_handle( conout_pipe = NamedPipe::from_raw_handle(
default_opts default_opts.clone().read(true).open(conout).unwrap().into_raw_handle(),
.clone()
.read(true)
.open(conout)
.unwrap()
.into_raw_handle(),
); );
conin_pipe = NamedPipe::from_raw_handle( conin_pipe = NamedPipe::from_raw_handle(
default_opts default_opts.clone().write(true).open(conin).unwrap().into_raw_handle(),
.clone()
.write(true)
.open(conin)
.unwrap()
.into_raw_handle(),
); );
}; };
@ -166,7 +154,7 @@ pub fn new<'a>(
conout: super::EventedReadablePipe::Named(conout_pipe), conout: super::EventedReadablePipe::Named(conout_pipe),
conin: super::EventedWritablePipe::Named(conin_pipe), conin: super::EventedWritablePipe::Named(conin_pipe),
read_token: 0.into(), read_token: 0.into(),
write_token: 0.into() write_token: 0.into(),
} }
} }

View File

@ -12,17 +12,15 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use url;
use unicode_width::UnicodeWidthChar; use unicode_width::UnicodeWidthChar;
use url;
use crate::term::cell::{Cell, Flags}; use crate::term::cell::{Cell, Flags};
// See https://tools.ietf.org/html/rfc3987#page-13 // See https://tools.ietf.org/html/rfc3987#page-13
const URL_SEPARATOR_CHARS: [char; 10] = ['<', '>', '"', ' ', '{', '}', '|', '\\', '^', '`']; const URL_SEPARATOR_CHARS: [char; 10] = ['<', '>', '"', ' ', '{', '}', '|', '\\', '^', '`'];
const URL_DENY_END_CHARS: [char; 8] = ['.', ',', ';', ':', '?', '!', '/', '(']; const URL_DENY_END_CHARS: [char; 8] = ['.', ',', ';', ':', '?', '!', '/', '('];
const URL_SCHEMES: [&str; 8] = [ const URL_SCHEMES: [&str; 8] = ["http", "https", "mailto", "news", "file", "git", "ssh", "ftp"];
"http", "https", "mailto", "news", "file", "git", "ssh", "ftp",
];
/// URL text and origin of the original click position. /// URL text and origin of the original click position.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -39,10 +37,7 @@ pub struct UrlParser {
impl UrlParser { impl UrlParser {
pub fn new() -> Self { pub fn new() -> Self {
UrlParser { UrlParser { state: String::new(), origin: 0 }
state: String::new(),
origin: 0,
}
} }
/// Advance the parser one character to the left. /// Advance the parser one character to the left.
@ -74,19 +69,17 @@ impl UrlParser {
// Remove non-alphabetical characters before the scheme // Remove non-alphabetical characters before the scheme
// https://tools.ietf.org/html/rfc3986#section-3.1 // https://tools.ietf.org/html/rfc3986#section-3.1
if let Some(index) = self.state.find("://") { if let Some(index) = self.state.find("://") {
let iter = self let iter =
.state self.state.char_indices().rev().skip_while(|(byte_index, _)| *byte_index >= index);
.char_indices()
.rev()
.skip_while(|(byte_index, _)| *byte_index >= index);
for (byte_index, c) in iter { for (byte_index, c) in iter {
match c { match c {
'a'...'z' | 'A'...'Z' => (), 'a'...'z' | 'A'...'Z' => (),
_ => { _ => {
self.origin = self.origin.saturating_sub(byte_index + c.width().unwrap_or(1)); self.origin =
self.origin.saturating_sub(byte_index + c.width().unwrap_or(1));
self.state = self.state.split_off(byte_index + c.len_utf8()); self.state = self.state.split_off(byte_index + c.len_utf8());
break; break;
} },
} }
} }
} }
@ -103,7 +96,7 @@ impl UrlParser {
')' | ']' => { ')' | ']' => {
self.state.truncate(i); self.state.truncate(i);
break; break;
} },
_ => (), _ => (),
} }
} }
@ -127,14 +120,11 @@ impl UrlParser {
match url::Url::parse(&self.state) { match url::Url::parse(&self.state) {
Ok(url) => { Ok(url) => {
if URL_SCHEMES.contains(&url.scheme()) && self.origin > 0 { if URL_SCHEMES.contains(&url.scheme()) && self.origin > 0 {
Some(Url { Some(Url { origin: self.origin - 1, text: self.state })
origin: self.origin - 1,
text: self.state,
})
} else { } else {
None None
} }
} },
Err(_) => None, Err(_) => None,
} }
} }
@ -160,9 +150,9 @@ mod tests {
use crate::grid::Grid; use crate::grid::Grid;
use crate::index::{Column, Line, Point}; use crate::index::{Column, Line, Point};
use crate::term::{Search, SizeInfo, Term};
use crate::term::cell::{Cell, Flags};
use crate::message_bar::MessageBuffer; use crate::message_bar::MessageBuffer;
use crate::term::cell::{Cell, Flags};
use crate::term::{Search, SizeInfo, Term};
fn url_create_term(input: &str) -> Term { fn url_create_term(input: &str) -> Term {
let size = SizeInfo { let size = SizeInfo {

View File

@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use std::{cmp, io};
use std::ffi::OsStr; use std::ffi::OsStr;
use std::process::Command; use std::process::Command;
use std::process::Stdio; use std::process::Stdio;
use std::{cmp, io};
#[cfg(not(windows))] #[cfg(not(windows))]
use std::os::unix::process::CommandExt; use std::os::unix::process::CommandExt;
@ -35,10 +35,7 @@ pub mod thread {
T: Send + 'static, T: Send + 'static,
S: Into<String>, S: Into<String>,
{ {
::std::thread::Builder::new() ::std::thread::Builder::new().name(name.into()).spawn(f).expect("thread spawn works")
.name(name.into())
.spawn(f)
.expect("thread spawn works")
} }
pub use std::thread::*; pub use std::thread::*;
@ -87,9 +84,9 @@ pub mod fmt {
#[cfg(not(windows))] #[cfg(not(windows))]
pub fn start_daemon<I, S>(program: &str, args: I) -> io::Result<()> pub fn start_daemon<I, S>(program: &str, args: I) -> io::Result<()>
where where
I: IntoIterator<Item = S>, I: IntoIterator<Item = S>,
S: AsRef<OsStr>, S: AsRef<OsStr>,
{ {
Command::new(program) Command::new(program)
.args(args) .args(args)
@ -109,9 +106,9 @@ pub fn start_daemon<I, S>(program: &str, args: I) -> io::Result<()>
#[cfg(windows)] #[cfg(windows)]
pub fn start_daemon<I, S>(program: &str, args: I) -> io::Result<()> pub fn start_daemon<I, S>(program: &str, args: I) -> io::Result<()>
where where
I: IntoIterator<Item = S>, I: IntoIterator<Item = S>,
S: AsRef<OsStr>, S: AsRef<OsStr>,
{ {
// Setting all the I/O handles to null and setting the // Setting all the I/O handles to null and setting the
// CREATE_NEW_PROCESS_GROUP and CREATE_NO_WINDOW has the effect // CREATE_NEW_PROCESS_GROUP and CREATE_NO_WINDOW has the effect

View File

@ -15,17 +15,16 @@ use std::convert::From;
use std::fmt::Display; use std::fmt::Display;
use crate::gl; use crate::gl;
#[cfg(windows)]
use glutin::Icon;
#[cfg(windows)]
use image::ImageFormat;
use glutin::{
self, ContextBuilder, ControlFlow, Event, EventsLoop,
MouseCursor, WindowBuilder, ContextTrait
};
use glutin::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}; use glutin::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize};
#[cfg(not(any(target_os = "macos", windows)))] #[cfg(not(any(target_os = "macos", windows)))]
use glutin::os::unix::EventsLoopExt; use glutin::os::unix::EventsLoopExt;
#[cfg(windows)]
use glutin::Icon;
use glutin::{
self, ContextBuilder, ContextTrait, ControlFlow, Event, EventsLoop, MouseCursor, WindowBuilder,
};
#[cfg(windows)]
use image::ImageFormat;
use crate::cli::Options; use crate::cli::Options;
use crate::config::{Decorations, WindowConfig}; use crate::config::{Decorations, WindowConfig};
@ -160,12 +159,7 @@ impl Window {
// Set OpenGL symbol loader. This call MUST be after window.make_current on windows. // Set OpenGL symbol loader. This call MUST be after window.make_current on windows.
gl::load_with(|symbol| window.get_proc_address(symbol) as *const _); gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
let window = Window { let window = Window { event_loop, window, mouse_visible: true, is_focused: false };
event_loop,
window,
mouse_visible: true,
is_focused: false,
};
window.run_os_extensions(); window.run_os_extensions();
@ -177,9 +171,7 @@ impl Window {
/// Some window properties are provided since subsystems like font /// Some window properties are provided since subsystems like font
/// rasterization depend on DPI and scale factor. /// rasterization depend on DPI and scale factor.
pub fn device_properties(&self) -> DeviceProperties { pub fn device_properties(&self) -> DeviceProperties {
DeviceProperties { DeviceProperties { scale_factor: self.window.get_hidpi_factor() }
scale_factor: self.window.get_hidpi_factor(),
}
} }
pub fn inner_size_pixels(&self) -> Option<LogicalSize> { pub fn inner_size_pixels(&self) -> Option<LogicalSize> {
@ -204,9 +196,7 @@ impl Window {
#[inline] #[inline]
pub fn create_window_proxy(&self) -> Proxy { pub fn create_window_proxy(&self) -> Proxy {
Proxy { Proxy { inner: self.event_loop.create_proxy() }
inner: self.event_loop.create_proxy(),
}
} }
#[inline] #[inline]
@ -260,7 +250,7 @@ impl Window {
pub fn get_platform_window( pub fn get_platform_window(
title: &str, title: &str,
class: &str, class: &str,
window_config: &WindowConfig window_config: &WindowConfig,
) -> WindowBuilder { ) -> WindowBuilder {
use glutin::os::unix::WindowBuilderExt; use glutin::os::unix::WindowBuilderExt;
@ -285,7 +275,7 @@ impl Window {
pub fn get_platform_window( pub fn get_platform_window(
title: &str, title: &str,
_class: &str, _class: &str,
window_config: &WindowConfig window_config: &WindowConfig,
) -> WindowBuilder { ) -> WindowBuilder {
let icon = Icon::from_bytes_with_format(WINDOW_ICON, ImageFormat::ICO).unwrap(); let icon = Icon::from_bytes_with_format(WINDOW_ICON, ImageFormat::ICO).unwrap();
@ -307,7 +297,7 @@ impl Window {
pub fn get_platform_window( pub fn get_platform_window(
title: &str, title: &str,
_class: &str, _class: &str,
window_config: &WindowConfig window_config: &WindowConfig,
) -> WindowBuilder { ) -> WindowBuilder {
use glutin::os::macos::WindowBuilderExt; use glutin::os::macos::WindowBuilderExt;
@ -328,19 +318,16 @@ impl Window {
.with_titlebar_buttons_hidden(true) .with_titlebar_buttons_hidden(true)
.with_titlebar_transparent(true) .with_titlebar_transparent(true)
.with_fullsize_content_view(true), .with_fullsize_content_view(true),
Decorations::None => window Decorations::None => window.with_titlebar_hidden(true),
.with_titlebar_hidden(true),
} }
} }
#[cfg( #[cfg(any(
any( target_os = "linux",
target_os = "linux", target_os = "freebsd",
target_os = "freebsd", target_os = "dragonfly",
target_os = "dragonfly", target_os = "openbsd"
target_os = "openbsd" ))]
)
)]
pub fn set_urgent(&self, is_urgent: bool) { pub fn set_urgent(&self, is_urgent: bool) {
use glutin::os::unix::WindowExt; use glutin::os::unix::WindowExt;
self.window.set_urgent(is_urgent); self.window.set_urgent(is_urgent);
@ -384,21 +371,20 @@ pub trait OsExtensions {
fn run_os_extensions(&self) {} fn run_os_extensions(&self) {}
} }
#[cfg( #[cfg(not(any(
not( target_os = "linux",
any( target_os = "freebsd",
target_os = "linux", target_os = "dragonfly",
target_os = "freebsd", target_os = "openbsd"
target_os = "dragonfly", )))]
target_os = "openbsd"
)
)
)]
impl OsExtensions for Window {} impl OsExtensions for Window {}
#[cfg( #[cfg(any(
any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd") target_os = "linux",
)] target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd"
))]
impl OsExtensions for Window { impl OsExtensions for Window {
fn run_os_extensions(&self) { fn run_os_extensions(&self) {
use glutin::os::unix::WindowExt; use glutin::os::unix::WindowExt;

View File

@ -6,15 +6,15 @@ use std::fs::File;
use std::io::{self, Read}; use std::io::{self, Read};
use std::path::Path; use std::path::Path;
use alacritty::Grid;
use alacritty::Term;
use alacritty::ansi; use alacritty::ansi;
use alacritty::config::Config;
use alacritty::index::Column; use alacritty::index::Column;
use alacritty::message_bar::MessageBuffer;
use alacritty::term::cell::Cell; use alacritty::term::cell::Cell;
use alacritty::term::SizeInfo; use alacritty::term::SizeInfo;
use alacritty::util::fmt::{Red, Green}; use alacritty::util::fmt::{Green, Red};
use alacritty::config::Config; use alacritty::Grid;
use alacritty::message_bar::MessageBuffer; use alacritty::Term;
macro_rules! ref_tests { macro_rules! ref_tests {
($($name:ident)*) => { ($($name:ident)*) => {
@ -55,17 +55,18 @@ ref_tests! {
} }
fn read_u8<P>(path: P) -> Vec<u8> fn read_u8<P>(path: P) -> Vec<u8>
where P: AsRef<Path> where
P: AsRef<Path>,
{ {
let mut res = Vec::new(); let mut res = Vec::new();
File::open(path.as_ref()).unwrap() File::open(path.as_ref()).unwrap().read_to_end(&mut res).unwrap();
.read_to_end(&mut res).unwrap();
res res
} }
fn read_string<P>(path: P) -> Result<String, ::std::io::Error> fn read_string<P>(path: P) -> Result<String, ::std::io::Error>
where P: AsRef<Path> where
P: AsRef<Path>,
{ {
let mut res = String::new(); let mut res = String::new();
File::open(path.as_ref()).and_then(|mut f| f.read_to_string(&mut res))?; File::open(path.as_ref()).and_then(|mut f| f.read_to_string(&mut res))?;
@ -109,8 +110,13 @@ fn ref_test(dir: &Path) {
let cell = term_grid[i][Column(j)]; let cell = term_grid[i][Column(j)];
let original_cell = grid[i][Column(j)]; let original_cell = grid[i][Column(j)];
if original_cell != cell { if original_cell != cell {
println!("[{i}][{j}] {original:?} => {now:?}", println!(
i=i, j=j, original=Green(original_cell), now=Red(cell)); "[{i}][{j}] {original:?} => {now:?}",
i = i,
j = j,
original = Green(original_cell),
now = Red(cell)
);
} }
} }
} }

View File

@ -5,8 +5,10 @@ fn main() {
// The working directory for `cargo test` is in the deps folder, not the debug/release root // The working directory for `cargo test` is in the deps folder, not the debug/release root
if cfg!(test) && Path::new("target").exists() { if cfg!(test) && Path::new("target").exists() {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
copy("../assets/windows/x86_64/winpty-agent.exe", "target/debug/deps/winpty-agent.exe").unwrap(); copy("../assets/windows/x86_64/winpty-agent.exe", "target/debug/deps/winpty-agent.exe")
.unwrap();
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
copy("../assets/windows/x86_64/winpty-agent.exe", "target/release/deps/winpty-agent.exe").unwrap(); copy("../assets/windows/x86_64/winpty-agent.exe", "target/release/deps/winpty-agent.exe")
.unwrap();
} }
} }

View File

@ -1,9 +1,9 @@
use std::error::Error; use std::error::Error;
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use std::path::PathBuf;
use std::result::Result;
use std::os::windows::io::RawHandle; use std::os::windows::io::RawHandle;
use std::path::PathBuf;
use std::ptr::{null, null_mut}; use std::ptr::{null, null_mut};
use std::result::Result;
use winpty_sys::*; use winpty_sys::*;
@ -166,10 +166,7 @@ impl<'a, 'b> Winpty<'a> {
pub fn conin_name(&mut self) -> PathBuf { pub fn conin_name(&mut self) -> PathBuf {
unsafe { unsafe {
let raw = winpty_conin_name(self.0); let raw = winpty_conin_name(self.0);
PathBuf::from(&String::from_utf16_lossy(std::slice::from_raw_parts( PathBuf::from(&String::from_utf16_lossy(std::slice::from_raw_parts(raw, wcslen(raw))))
raw,
wcslen(raw),
)))
} }
} }
@ -178,10 +175,7 @@ impl<'a, 'b> Winpty<'a> {
pub fn conout_name(&mut self) -> PathBuf { pub fn conout_name(&mut self) -> PathBuf {
unsafe { unsafe {
let raw = winpty_conout_name(self.0); let raw = winpty_conout_name(self.0);
PathBuf::from(&String::from_utf16_lossy(std::slice::from_raw_parts( PathBuf::from(&String::from_utf16_lossy(std::slice::from_raw_parts(raw, wcslen(raw))))
raw,
wcslen(raw),
)))
} }
} }
@ -191,10 +185,7 @@ impl<'a, 'b> Winpty<'a> {
pub fn conerr_name(&mut self) -> PathBuf { pub fn conerr_name(&mut self) -> PathBuf {
unsafe { unsafe {
let raw = winpty_conerr_name(self.0); let raw = winpty_conerr_name(self.0);
PathBuf::from(&String::from_utf16_lossy(std::slice::from_raw_parts( PathBuf::from(&String::from_utf16_lossy(std::slice::from_raw_parts(raw, wcslen(raw))))
raw,
wcslen(raw),
)))
} }
} }
@ -227,7 +218,12 @@ impl<'a, 'b> Winpty<'a> {
let mut process_list = Vec::with_capacity(count); let mut process_list = Vec::with_capacity(count);
unsafe { unsafe {
let len = winpty_get_console_process_list(self.0, process_list.as_mut_ptr(), count as i32, &mut err) as usize; let len = winpty_get_console_process_list(
self.0,
process_list.as_mut_ptr(),
count as i32,
&mut err,
) as usize;
process_list.set_len(len); process_list.set_len(len);
} }
@ -246,10 +242,7 @@ impl<'a, 'b> Winpty<'a> {
/// (https://blogs.msdn.microsoft.com/oldnewthing/20110107-00/?p=11803) /// (https://blogs.msdn.microsoft.com/oldnewthing/20110107-00/?p=11803)
// TODO: Support getting the process and thread handle of the spawned process (Not the agent) // TODO: Support getting the process and thread handle of the spawned process (Not the agent)
// TODO: Support returning the error from CreateProcess // TODO: Support returning the error from CreateProcess
pub fn spawn( pub fn spawn(&mut self, cfg: &SpawnConfig) -> Result<(), Err> {
&mut self,
cfg: &SpawnConfig,
) -> Result<(), Err> {
let mut err = null_mut() as *mut winpty_error_t; let mut err = null_mut() as *mut winpty_error_t;
unsafe { unsafe {
@ -261,7 +254,9 @@ impl<'a, 'b> Winpty<'a> {
null_mut(), // Create process error null_mut(), // Create process error
&mut err, &mut err,
); );
if ok == 0 { return Ok(());} if ok == 0 {
return Ok(());
}
} }
if let Some(err) = check_err(err) { if let Some(err) = check_err(err) {
@ -354,80 +349,70 @@ mod tests {
#[test] #[test]
// Test that we can start a process in winpty // Test that we can start a process in winpty
fn spawn_process() { fn spawn_process() {
let mut winpty = Winpty::open( let mut winpty =
&Config::new(ConfigFlags::empty()).expect("failed to create config") Winpty::open(&Config::new(ConfigFlags::empty()).expect("failed to create config"))
).expect("failed to create winpty instance"); .expect("failed to create winpty instance");
winpty.spawn( winpty
&SpawnConfig::new( .spawn(
SpawnFlags::empty(), &SpawnConfig::new(SpawnFlags::empty(), None, Some("cmd"), None, None)
None, .expect("failed to create spawn config"),
Some("cmd"), )
None, .unwrap();
None
).expect("failed to create spawn config")
).unwrap();
} }
#[test] #[test]
// Test that pipes connected before winpty is spawned can be connected to // Test that pipes connected before winpty is spawned can be connected to
fn valid_pipe_connect_before() { fn valid_pipe_connect_before() {
let mut winpty = Winpty::open( let mut winpty =
&Config::new(ConfigFlags::empty()).expect("failed to create config") Winpty::open(&Config::new(ConfigFlags::empty()).expect("failed to create config"))
).expect("failed to create winpty instance"); .expect("failed to create winpty instance");
// Check we can connect to both pipes // Check we can connect to both pipes
PipeClient::connect_ms(winpty.conout_name(), 1000).expect("failed to connect to conout pipe"); PipeClient::connect_ms(winpty.conout_name(), 1000)
.expect("failed to connect to conout pipe");
PipeClient::connect_ms(winpty.conin_name(), 1000).expect("failed to connect to conin pipe"); PipeClient::connect_ms(winpty.conin_name(), 1000).expect("failed to connect to conin pipe");
winpty.spawn( winpty
&SpawnConfig::new( .spawn(
SpawnFlags::empty(), &SpawnConfig::new(SpawnFlags::empty(), None, Some("cmd"), None, None)
None, .expect("failed to create spawn config"),
Some("cmd"), )
None, .unwrap();
None
).expect("failed to create spawn config")
).unwrap();
} }
#[test] #[test]
// Test that pipes connected after winpty is spawned can be connected to // Test that pipes connected after winpty is spawned can be connected to
fn valid_pipe_connect_after() { fn valid_pipe_connect_after() {
let mut winpty = Winpty::open( let mut winpty =
&Config::new(ConfigFlags::empty()).expect("failed to create config") Winpty::open(&Config::new(ConfigFlags::empty()).expect("failed to create config"))
).expect("failed to create winpty instance"); .expect("failed to create winpty instance");
winpty.spawn( winpty
&SpawnConfig::new( .spawn(
SpawnFlags::empty(), &SpawnConfig::new(SpawnFlags::empty(), None, Some("cmd"), None, None)
None, .expect("failed to create spawn config"),
Some("cmd"), )
None, .unwrap();
None
).expect("failed to create spawn config")
).unwrap();
// Check we can connect to both pipes // Check we can connect to both pipes
PipeClient::connect_ms(winpty.conout_name(), 1000).expect("failed to connect to conout pipe"); PipeClient::connect_ms(winpty.conout_name(), 1000)
.expect("failed to connect to conout pipe");
PipeClient::connect_ms(winpty.conin_name(), 1000).expect("failed to connect to conin pipe"); PipeClient::connect_ms(winpty.conin_name(), 1000).expect("failed to connect to conin pipe");
} }
#[test] #[test]
fn resize() { fn resize() {
let mut winpty = Winpty::open( let mut winpty =
&Config::new(ConfigFlags::empty()).expect("failed to create config") Winpty::open(&Config::new(ConfigFlags::empty()).expect("failed to create config"))
).expect("failed to create winpty instance"); .expect("failed to create winpty instance");
winpty.spawn( winpty
&SpawnConfig::new( .spawn(
SpawnFlags::empty(), &SpawnConfig::new(SpawnFlags::empty(), None, Some("cmd"), None, None)
None, .expect("failed to create spawn config"),
Some("cmd"), )
None, .unwrap();
None
).expect("failed to create spawn config")
).unwrap();
winpty.set_size(1, 1).unwrap(); winpty.set_size(1, 1).unwrap();
} }
@ -436,21 +421,19 @@ mod tests {
#[ignore] #[ignore]
// Test that each id returned by cosole_process_list points to an actual process // Test that each id returned by cosole_process_list points to an actual process
fn console_process_list_valid() { fn console_process_list_valid() {
let mut winpty = Winpty::open( let mut winpty =
&Config::new(ConfigFlags::empty()).expect("failed to create config") Winpty::open(&Config::new(ConfigFlags::empty()).expect("failed to create config"))
).expect("failed to create winpty instance"); .expect("failed to create winpty instance");
winpty.spawn( winpty
&SpawnConfig::new( .spawn(
SpawnFlags::empty(), &SpawnConfig::new(SpawnFlags::empty(), None, Some("cmd"), None, None)
None, .expect("failed to create spawn config"),
Some("cmd"), )
None, .unwrap();
None
).expect("failed to create spawn config")
).unwrap();
let processes = winpty.console_process_list(1000).expect("failed to get console process list"); let processes =
winpty.console_process_list(1000).expect("failed to get console process list");
// Check that each id is valid // Check that each id is valid
processes.iter().for_each(|id| { processes.iter().for_each(|id| {
@ -458,7 +441,7 @@ mod tests {
OpenProcess( OpenProcess(
READ_CONTROL, // permissions READ_CONTROL, // permissions
false as i32, // inheret false as i32, // inheret
*id as u32 *id as u32,
) )
}; };
assert!(!handle.is_null()); assert!(!handle.is_null());