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
env: CLIPPY=true
rust: stable
- name: "Rustfmt"
os: linux
env: RUSTFMT=true
rust: nightly
allow_failures:
- rust: nightly

View File

@ -89,7 +89,7 @@ Changes compared to the latest Alacritty release which have a direct effect on t
### 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

View File

@ -14,10 +14,10 @@
#[cfg(windows)]
use embed_resource;
#[cfg(windows)]
use tempfile;
#[cfg(windows)]
use reqwest;
#[cfg(windows)]
use tempfile;
#[cfg(windows)]
use zip;
use gl_generator::{Api, Fallbacks, GlobalGenerator, Profile, Registry};
@ -26,25 +26,21 @@ use std::env;
use std::fs::File;
use std::path::Path;
#[cfg(windows)]
use std::io;
#[cfg(windows)]
use std::fs::OpenOptions;
#[cfg(windows)]
use std::io;
#[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() {
let dest = env::var("OUT_DIR").unwrap();
let mut file = File::create(&Path::new(&dest).join("gl_bindings.rs")).unwrap();
Registry::new(
Api::Gl,
(4, 5),
Profile::Core,
Fallbacks::All,
["GL_ARB_blend_func_extended"],
).write_bindings(GlobalGenerator, &mut file)
Registry::new(Api::Gl, (4, 5), Profile::Core, Fallbacks::All, ["GL_ARB_blend_func_extended"])
.write_bindings(GlobalGenerator, &mut file)
.unwrap();
#[cfg(windows)]
@ -68,7 +64,8 @@ fn aquire_winpty_agent(out_path: &Path) {
.read(true)
.write(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();
@ -77,7 +74,7 @@ fn aquire_winpty_agent(out_path: &Path) {
let target = match env::var("TARGET").unwrap().split("-").next().unwrap() {
"x86_64" => "x64",
"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();

View File

@ -1,6 +1,11 @@
#!/bin/bash
# Add clippy for linting with nightly builds
# Add clippy for lint validation
if [ "$CLIPPY" == "true" ]; then
rustup component add clippy
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
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
if [ -n "$TRAVIS_TAG" ]; then
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
mod ns {
extern crate objc_id;
extern crate objc_foundation;
extern crate objc_id;
#[link(name = "AppKit", kind = "framework")]
extern {}
extern "C" {}
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_foundation::{NSArray, NSObject, NSDictionary, NSString};
use self::objc_foundation::{INSString, INSArray, INSObject};
use objc::runtime::{Class, Object};
/// Rust API for NSPasteboard
pub struct Pasteboard(Id<Object>);
@ -55,6 +55,7 @@ mod ns {
impl PasteboardReadObject<String> for Pasteboard {
type Err = ReadStringError;
fn read_object(&self) -> Result<String, ReadStringError> {
// Get string class; need this for passing to readObjectsForClasses
let ns_string_class = match Class::get("NSString") {
@ -133,9 +134,7 @@ mod ns {
// The writeObjects method returns true in case of success, and
// false otherwise.
let ok: bool = unsafe {
msg_send![self.0, writeObjects:objects]
};
let ok: bool = unsafe { msg_send![self.0, writeObjects: objects] };
if ok {
Ok(())
@ -175,9 +174,7 @@ mod ns {
impl ::std::error::Error for NewPasteboardError {
fn description(&self) -> &str {
match *self {
NewPasteboardError::GetPasteboardClass => {
"NSPasteboard class not found"
},
NewPasteboardError::GetPasteboardClass => "NSPasteboard class not found",
NewPasteboardError::LoadGeneralPasteboard => {
"[NSPasteboard generalPasteboard] failed"
},
@ -209,9 +206,7 @@ mod ns {
}
};
let id = unsafe {
Id::from_ptr(ptr)
};
let id = unsafe { Id::from_ptr(ptr) };
Ok(Pasteboard(id))
}
@ -222,9 +217,7 @@ mod ns {
/// This is the first step in providing data on the pasteboard. The
/// return value is the change count of the pasteboard
pub fn clear_contents(&mut self) -> usize {
unsafe {
msg_send![self.0, clearContents]
}
unsafe { msg_send![self.0, clearContents] }
}
}
}
@ -236,7 +229,6 @@ pub enum Error {
WriteString(ns::WriteStringError),
}
impl ::std::error::Error for Error {
fn cause(&self) -> Option<&::std::error::Error> {
match *self {
@ -258,9 +250,7 @@ impl ::std::error::Error for Error {
impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match *self {
Error::CreatePasteboard(ref err) => {
write!(f, "Failed to create pasteboard: {}", err)
},
Error::CreatePasteboard(ref err) => write!(f, "Failed to create pasteboard: {}", err),
Error::ReadString(ref 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> {
use self::ns::PasteboardReadObject;
self.0.read_object()
.map_err(::std::convert::From::from)
self.0.read_object().map_err(::std::convert::From::from)
}
}
impl super::Store for Clipboard {
fn store_primary<S>(&mut self, contents: S) -> Result<(), Self::Err>
where S: Into<String>
where
S: Into<String>,
{
use self::ns::PasteboardWriteObject;
self.0.write_object(contents.into())
.map_err(::std::convert::From::from)
self.0.write_object(contents.into()).map_err(::std::convert::From::from)
}
fn store_selection<S>(&mut self, _contents: S) -> Result<(), Self::Err>
where S: Into<String>
where
S: Into<String>,
{
// No such thing on macOS
Ok(())
@ -327,7 +317,7 @@ impl super::Store for Clipboard {
#[cfg(test)]
mod tests {
use super::Clipboard;
use ::{Load, Store};
use {Load, Store};
#[test]
fn create_clipboard_save_load_contents() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -15,9 +15,9 @@ use std::ops::Deref;
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! {
type CType = FcFontSet;
@ -33,13 +33,13 @@ impl FontSet {
config: &ConfigRef,
source: &mut FontSetRef,
pattern: &PatternRef,
objects: &ObjectSetRef
objects: &ObjectSetRef,
) -> FontSet {
let raw = unsafe {
FcFontSetList(
config.as_ptr(),
&mut source.as_ptr(),
1 /* nsets */,
1, // nsets
pattern.as_ptr(),
objects.as_ptr(),
)
@ -56,38 +56,28 @@ pub struct Iter<'a> {
}
impl<'a> IntoIterator for &'a FontSet {
type Item = &'a PatternRef;
type IntoIter = Iter<'a>;
type Item = &'a PatternRef;
fn into_iter(self) -> Iter<'a> {
let num_fonts = unsafe {
(*self.as_ptr()).nfont as isize
};
let num_fonts = unsafe { (*self.as_ptr()).nfont as isize };
trace!("Number of fonts is {}", num_fonts);
Iter {
font_set: self.deref(),
num_fonts: num_fonts as _,
current: 0,
}
Iter { font_set: self.deref(), num_fonts: num_fonts as _, current: 0 }
}
}
impl<'a> IntoIterator for &'a FontSetRef {
type Item = &'a PatternRef;
type IntoIter = Iter<'a>;
type Item = &'a PatternRef;
fn into_iter(self) -> Iter<'a> {
let num_fonts = unsafe {
(*self.as_ptr()).nfont as isize
};
let num_fonts = unsafe { (*self.as_ptr()).nfont as isize };
trace!("Number of fonts is {}", num_fonts);
Iter {
font_set: self,
num_fonts: num_fonts as _,
current: 0,
}
Iter { 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
// limitations under the License.
//
use std::ptr;
use std::fmt;
use std::ptr;
use foreign_types::{ForeignType, ForeignTypeRef};
use fontconfig::fontconfig as ffi;
use self::ffi::{FcSetSystem, FcSetApplication};
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::{FC_SLANT_OBLIQUE, FC_SLANT_ITALIC, FC_SLANT_ROMAN};
use self::ffi::{FC_WEIGHT_THIN, FC_WEIGHT_EXTRALIGHT, FC_WEIGHT_LIGHT};
use self::ffi::{FC_WEIGHT_BOOK, FC_WEIGHT_REGULAR, FC_WEIGHT_MEDIUM, FC_WEIGHT_SEMIBOLD};
use self::ffi::{FC_WEIGHT_BOLD, FC_WEIGHT_EXTRABOLD, FC_WEIGHT_BLACK, FC_WEIGHT_EXTRABLACK};
use self::ffi::{FcSetApplication, FcSetSystem};
use self::ffi::{FC_SLANT_ITALIC, FC_SLANT_OBLIQUE, FC_SLANT_ROMAN};
use self::ffi::{FC_WEIGHT_BLACK, FC_WEIGHT_BOLD, FC_WEIGHT_EXTRABLACK, FC_WEIGHT_EXTRABOLD};
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 use self::config::{Config, ConfigRef};
@ -46,10 +46,7 @@ pub use self::pattern::{Pattern, PatternRef};
/// Find the font closest matching the provided pattern.
///
/// The returned pattern is the result of Pattern::render_prepare.
pub fn font_match(
config: &ConfigRef,
pattern: &mut PatternRef,
) -> Option<Pattern> {
pub fn font_match(config: &ConfigRef, pattern: &mut PatternRef) -> Option<Pattern> {
pattern.config_substitute(config, MatchKind::Pattern);
pattern.default_substitute();
@ -57,11 +54,7 @@ pub fn font_match(
// What is this result actually used for? Seems redundant with
// return type.
let mut result = FcResultNoMatch;
let ptr = FcFontMatch(
config.as_ptr(),
pattern.as_ptr(),
&mut result,
);
let ptr = FcFontMatch(config.as_ptr(), pattern.as_ptr(), &mut result);
if ptr.is_null() {
None
@ -72,10 +65,7 @@ pub fn font_match(
}
/// list fonts by closeness to the pattern
pub fn font_sort(
config: &ConfigRef,
pattern: &mut PatternRef,
) -> Option<FontSet> {
pub fn font_sort(config: &ConfigRef, pattern: &mut PatternRef) -> Option<FontSet> {
pattern.config_substitute(config, MatchKind::Pattern);
pattern.default_substitute();
@ -112,11 +102,7 @@ pub fn font_list(
pattern.default_substitute();
unsafe {
let ptr = FcFontList(
config.as_ptr(),
pattern.as_ptr(),
objects.as_ptr(),
);
let ptr = FcFontList(config.as_ptr(), pattern.as_ptr(), objects.as_ptr());
if ptr.is_null() {
None
@ -174,7 +160,7 @@ pub enum Width {
Expanded,
Extraexpanded,
Ultraexpanded,
Other(i32)
Other(i32),
}
impl Width {
@ -190,7 +176,7 @@ impl Width {
Expanded => 125,
Extraexpanded => 150,
Ultraexpanded => 200,
Other(value) => value as isize
Other(value) => value as isize,
}
}
}
@ -207,7 +193,7 @@ impl From<isize> for Width {
125 => Width::Expanded,
150 => Width::Extraexpanded,
200 => Width::Ultraexpanded,
_ => Width::Other(value as _)
_ => Width::Other(value as _),
}
}
}
@ -219,7 +205,7 @@ pub enum Rgba {
Bgr,
Vrgb,
Vbgr,
None
None,
}
impl Rgba {
@ -230,7 +216,7 @@ impl Rgba {
Rgba::Bgr => 2,
Rgba::Vrgb => 3,
Rgba::Vbgr => 4,
Rgba::None => 5
Rgba::None => 5,
}
}
}
@ -268,7 +254,7 @@ pub enum HintStyle {
None,
Slight,
Medium,
Full
Full,
}
impl fmt::Display for HintStyle {
@ -287,7 +273,7 @@ pub enum LcdFilter {
None,
Default,
Light,
Legacy
Legacy,
}
impl fmt::Display for LcdFilter {
@ -334,8 +320,7 @@ mod tests {
pattern.set_slant(Slant::Italic);
let config = Config::get_current();
let fonts = super::font_sort(config, &mut pattern)
.expect("sort font monospace");
let fonts = super::font_sort(config, &mut pattern).expect("sort font monospace");
for font in fonts.into_iter().take(10) {
let font = font.render_prepare(&config, &pattern);

View File

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

View File

@ -11,38 +11,34 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::ptr;
use std::fmt;
use std::ffi::{CStr, CString};
use std::path::PathBuf;
use std::str;
use std::fmt;
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 libc::{c_char, c_double, c_int};
use super::ffi::FcResultMatch;
use super::ffi::{FcPatternDestroy, FcPatternAddCharSet};
use super::ffi::{FcPatternGetString, FcPatternCreate, FcPatternAddString, FcPatternAddDouble};
use super::ffi::{FcPatternGetInteger, FcPatternAddInteger, FcPatternPrint};
use super::ffi::{FcChar8, FcPattern, FcDefaultSubstitute, FcConfigSubstitute};
use super::ffi::{FcFontRenderPrepare, FcPatternGetBool, FcBool, FcPatternGetDouble};
use super::ffi::{FcBool, FcFontRenderPrepare, FcPatternGetBool, FcPatternGetDouble};
use super::ffi::{FcChar8, FcConfigSubstitute, FcDefaultSubstitute, FcPattern};
use super::ffi::{FcPatternAddCharSet, FcPatternDestroy};
use super::ffi::{FcPatternAddDouble, FcPatternAddString, FcPatternCreate, FcPatternGetString};
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> {
pattern: &'a PatternRef,
object: &'a [u8],
index: usize
index: usize,
}
impl<'a> StringPropertyIter<'a> {
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> StringPropertyIter<'b> {
StringPropertyIter {
pattern,
object,
index: 0
}
StringPropertyIter { pattern, object, index: 0 }
}
fn get_value(&self, index: usize) -> Option<&'a str> {
@ -53,7 +49,7 @@ impl<'a> StringPropertyIter<'a> {
self.pattern.as_ptr(),
self.object.as_ptr() as *mut c_char,
index as c_int,
&mut value
&mut value,
)
};
@ -75,17 +71,12 @@ impl<'a> StringPropertyIter<'a> {
pub struct BooleanPropertyIter<'a> {
pattern: &'a PatternRef,
object: &'a [u8],
index: usize
index: usize,
}
impl<'a> BooleanPropertyIter<'a> {
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> BooleanPropertyIter<'b> {
BooleanPropertyIter {
pattern,
object,
index: 0
}
BooleanPropertyIter { pattern, object, index: 0 }
}
fn get_value(&self, index: usize) -> Option<bool> {
@ -96,7 +87,7 @@ impl<'a> BooleanPropertyIter<'a> {
self.pattern.as_ptr(),
self.object.as_ptr() as *mut c_char,
index as c_int,
&mut value
&mut value,
)
};
@ -112,16 +103,12 @@ impl<'a> BooleanPropertyIter<'a> {
pub struct IntPropertyIter<'a> {
pattern: &'a PatternRef,
object: &'a [u8],
index: usize
index: usize,
}
impl<'a> IntPropertyIter<'a> {
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> IntPropertyIter<'b> {
IntPropertyIter {
pattern,
object,
index: 0
}
IntPropertyIter { pattern, object, index: 0 }
}
fn get_value(&self, index: usize) -> Option<isize> {
@ -132,7 +119,7 @@ impl<'a> IntPropertyIter<'a> {
self.pattern.as_ptr(),
self.object.as_ptr() as *mut c_char,
index as c_int,
&mut value
&mut value,
)
};
@ -150,9 +137,7 @@ pub struct RgbaPropertyIter<'a> {
impl<'a> RgbaPropertyIter<'a> {
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> RgbaPropertyIter<'b> {
RgbaPropertyIter {
inner: IntPropertyIter::new(pattern, object)
}
RgbaPropertyIter { inner: IntPropertyIter::new(pattern, object) }
}
#[inline]
@ -161,8 +146,7 @@ impl<'a> RgbaPropertyIter<'a> {
}
fn get_value(&self, index: usize) -> Option<Rgba> {
self.inner.get_value(index)
.map(Rgba::from)
self.inner.get_value(index).map(Rgba::from)
}
}
@ -172,9 +156,7 @@ pub struct HintStylePropertyIter<'a> {
impl<'a> HintStylePropertyIter<'a> {
fn new(pattern: &PatternRef) -> HintStylePropertyIter {
HintStylePropertyIter {
inner: IntPropertyIter::new(pattern, b"hintstyle\0")
}
HintStylePropertyIter { inner: IntPropertyIter::new(pattern, b"hintstyle\0") }
}
#[inline]
@ -183,16 +165,15 @@ impl<'a> HintStylePropertyIter<'a> {
}
fn get_value(&self, index: usize) -> Option<HintStyle> {
self.inner.get_value(index)
.and_then(|hint_style| {
Some(match hint_style {
0 => HintStyle::None,
1 => HintStyle::Slight,
2 => HintStyle::Medium,
3 => HintStyle::Full,
_ => return None
})
self.inner.get_value(index).and_then(|hint_style| {
Some(match hint_style {
0 => HintStyle::None,
1 => HintStyle::Slight,
2 => HintStyle::Medium,
3 => HintStyle::Full,
_ => return None,
})
})
}
}
@ -202,9 +183,7 @@ pub struct LcdFilterPropertyIter<'a> {
impl<'a> LcdFilterPropertyIter<'a> {
fn new(pattern: &PatternRef) -> LcdFilterPropertyIter {
LcdFilterPropertyIter {
inner: IntPropertyIter::new(pattern, b"lcdfilter\0")
}
LcdFilterPropertyIter { inner: IntPropertyIter::new(pattern, b"lcdfilter\0") }
}
#[inline]
@ -213,16 +192,15 @@ impl<'a> LcdFilterPropertyIter<'a> {
}
fn get_value(&self, index: usize) -> Option<LcdFilter> {
self.inner.get_value(index)
.and_then(|hint_style| {
Some(match hint_style {
0 => LcdFilter::None,
1 => LcdFilter::Default,
2 => LcdFilter::Light,
3 => LcdFilter::Legacy,
_ => return None
})
self.inner.get_value(index).and_then(|hint_style| {
Some(match hint_style {
0 => LcdFilter::None,
1 => LcdFilter::Default,
2 => LcdFilter::Light,
3 => LcdFilter::Legacy,
_ => return None,
})
})
}
}
@ -230,16 +208,12 @@ impl<'a> LcdFilterPropertyIter<'a> {
pub struct DoublePropertyIter<'a> {
pattern: &'a PatternRef,
object: &'a [u8],
index: usize
index: usize,
}
impl<'a> DoublePropertyIter<'a> {
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> DoublePropertyIter<'b> {
DoublePropertyIter {
pattern,
object,
index: 0
}
DoublePropertyIter { pattern, object, index: 0 }
}
fn get_value(&self, index: usize) -> Option<f64> {
@ -250,7 +224,7 @@ impl<'a> DoublePropertyIter<'a> {
self.pattern.as_ptr(),
self.object.as_ptr() as *mut c_char,
index as c_int,
&mut value
&mut value,
)
};
@ -277,13 +251,13 @@ macro_rules! impl_property_iter_debug {
write!(f, "{}", val)?;
}
},
_ => break
_ => break,
}
}
write!(f, "]")
}
}
}
};
}
/// Implement Iterator and Debug for a property iterator
@ -427,76 +401,6 @@ macro_rules! double_getter {
}
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! {
antialias() => b"antialias\0",
hinting() => b"hinting\0",
@ -535,36 +439,85 @@ impl PatternRef {
[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 {
unsafe {
self.add_integer(b"slant\0", slant as isize)
}
unsafe { self.add_integer(b"slant\0", slant as isize) }
}
pub fn add_pixelsize(&mut self, size: f64) -> bool {
unsafe {
self.add_double(b"pixelsize\0", size)
}
unsafe { self.add_double(b"pixelsize\0", size) }
}
pub fn set_weight(&mut self, weight: Weight) -> bool {
unsafe {
self.add_integer(b"weight\0", weight as isize)
}
unsafe { self.add_integer(b"weight\0", weight as isize) }
}
pub fn set_width(&mut self, width: Width) -> bool {
unsafe {
self.add_integer(b"width\0", width.to_isize())
}
unsafe { self.add_integer(b"width\0", width.to_isize()) }
}
pub fn get_width(&self) -> Option<Width> {
unsafe {
self.get_integer(b"width\0")
.nth(0)
.map(Width::from)
}
unsafe { self.get_integer(b"width\0").nth(0).map(Width::from) }
}
pub fn rgba(&self) -> RgbaPropertyIter {
@ -572,9 +525,7 @@ impl PatternRef {
}
pub fn set_rgba(&self, rgba: &Rgba) -> bool {
unsafe {
self.add_integer(b"rgba\0", rgba.to_isize())
}
unsafe { self.add_integer(b"rgba\0", rgba.to_isize()) }
}
pub fn render_prepare(&self, config: &ConfigRef, request: &PatternRef) -> Pattern {
@ -595,19 +546,13 @@ impl PatternRef {
FcPatternAddCharSet(
self.as_ptr(),
b"charset\0".as_ptr() as *mut c_char,
charset.as_ptr()
charset.as_ptr(),
) == 1
}
}
pub fn file(&self, index: usize) -> Option<PathBuf> {
unsafe {
self.get_string(b"file\0").nth(index)
}.map(From::from)
}
pattern_get_integer! {
index() => b"index\0"
unsafe { self.get_string(b"file\0").nth(index) }.map(From::from)
}
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.
//
//! Rasterization powered by FreeType and FontConfig
use std::collections::HashMap;
use std::cmp::min;
use std::path::PathBuf;
use std::collections::HashMap;
use std::fmt;
use std::path::PathBuf;
use freetype::tt_os2::TrueTypeOS2Table;
use freetype::{self, Library};
use libc::c_uint;
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 {
pixelsize: f64,
@ -37,7 +36,7 @@ struct Face {
load_flags: freetype::face::LoadFlag,
render_mode: freetype::RenderMode,
lcd_filter: c_uint,
non_scalable: Option<FixedSize>
non_scalable: Option<FixedSize>,
}
impl fmt::Debug for Face {
@ -46,14 +45,17 @@ impl fmt::Debug for Face {
.field("ft_face", &self.ft_face)
.field("key", &self.key)
.field("load_flags", &self.load_flags)
.field("render_mode", &match self.render_mode {
freetype::RenderMode::Normal => "Normal",
freetype::RenderMode::Light => "Light",
freetype::RenderMode::Mono => "Mono",
freetype::RenderMode::Lcd => "Lcd",
freetype::RenderMode::LcdV => "LcdV",
freetype::RenderMode::Max => "Max",
})
.field(
"render_mode",
&match self.render_mode {
freetype::RenderMode::Normal => "Normal",
freetype::RenderMode::Light => "Light",
freetype::RenderMode::Mono => "Mono",
freetype::RenderMode::Lcd => "Lcd",
freetype::RenderMode::LcdV => "LcdV",
freetype::RenderMode::Max => "Max",
},
)
.field("lcd_filter", &self.lcd_filter)
.finish()
}
@ -87,9 +89,7 @@ impl ::Rasterize for FreeTypeRasterizer {
}
fn metrics(&self, key: FontKey, _size: Size) -> Result<Metrics, Error> {
let face = self.faces
.get(&key)
.ok_or(Error::FontNotLoaded)?;
let face = self.faces.get(&key).ok_or(Error::FontNotLoaded)?;
let full = self.full_metrics(key)?;
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
let (strikeout_position, strikeout_thickness) =
match TrueTypeOS2Table::from_face(&mut face.ft_face.clone())
{
Some(os2) => {
let strikeout_position = f32::from(os2.y_strikeout_position()) * x_scale / 64.;
let strikeout_thickness = f32::from(os2.y_strikeout_size()) * x_scale / 64.;
(strikeout_position, strikeout_thickness)
},
_ => {
// Fallback if font doesn't provide info about strikeout
trace!("Using fallback strikeout metrics");
let strikeout_position = height as f32 / 2. + descent;
(strikeout_position, underline_thickness)
},
};
match TrueTypeOS2Table::from_face(&mut face.ft_face.clone()) {
Some(os2) => {
let strikeout_position = f32::from(os2.y_strikeout_position()) * x_scale / 64.;
let strikeout_thickness = f32::from(os2.y_strikeout_size()) * x_scale / 64.;
(strikeout_position, strikeout_thickness)
},
_ => {
// Fallback if font doesn't provide info about strikeout
trace!("Using fallback strikeout metrics");
let strikeout_position = height as f32 / 2. + descent;
(strikeout_position, underline_thickness)
},
};
Ok(Metrics {
average_advance: full.cell_width,
@ -154,6 +153,7 @@ pub trait IntoFontconfigType {
impl IntoFontconfigType for Slant {
type FcType = fc::Slant;
fn into_fontconfig_type(&self) -> Self::FcType {
match *self {
Slant::Normal => fc::Slant::Roman,
@ -176,7 +176,7 @@ impl IntoFontconfigType for Weight {
struct FullMetrics {
size_metrics: freetype::ffi::FT_Size_Metrics,
cell_width: f64
cell_width: f64,
}
impl FreeTypeRasterizer {
@ -189,31 +189,25 @@ impl FreeTypeRasterizer {
Style::Description { slant, weight } => {
// Match nearest font
self.get_matching_face(&desc, slant, weight, size)
}
},
Style::Specific(ref style) => {
// If a name was specified, try and load specifically that font.
self.get_specific_face(&desc, &style, size)
}
},
}
}
fn full_metrics(&self, key: FontKey) -> Result<FullMetrics, Error> {
let face = self.faces
.get(&key)
.ok_or(Error::FontNotLoaded)?;
let face = self.faces.get(&key).ok_or(Error::FontNotLoaded)?;
let size_metrics = face.ft_face.size_metrics()
.ok_or(Error::MissingSizeMetrics)?;
let size_metrics = face.ft_face.size_metrics().ok_or(Error::MissingSizeMetrics)?;
let width = match face.ft_face.load_char('0' as usize, face.load_flags) {
Ok(_) => face.ft_face.glyph().metrics().horiAdvance / 64,
Err(_) => size_metrics.max_advance / 64
Ok(_) => face.ft_face.glyph().metrics().horiAdvance / 64,
Err(_) => size_metrics.max_advance / 64,
} as f64;
Ok(FullMetrics {
size_metrics,
cell_width: width
})
Ok(FullMetrics { size_metrics, cell_width: width })
}
fn get_matching_face(
@ -232,12 +226,9 @@ impl FreeTypeRasterizer {
let font = fc::font_match(fc::Config::get_current(), &mut pattern)
.ok_or_else(|| Error::MissingFont(desc.to_owned()))?;
self.face_from_pattern(&font)
.and_then(|pattern| {
pattern
.map(Ok)
.unwrap_or_else(|| Err(Error::MissingFont(desc.to_owned())))
})
self.face_from_pattern(&font).and_then(|pattern| {
pattern.map(Ok).unwrap_or_else(|| Err(Error::MissingFont(desc.to_owned())))
})
}
fn get_specific_face(
@ -253,12 +244,9 @@ impl FreeTypeRasterizer {
let font = fc::font_match(fc::Config::get_current(), &mut pattern)
.ok_or_else(|| Error::MissingFont(desc.to_owned()))?;
self.face_from_pattern(&font)
.and_then(|pattern| {
pattern
.map(Ok)
.unwrap_or_else(|| Err(Error::MissingFont(desc.to_owned())))
})
self.face_from_pattern(&font).and_then(|pattern| {
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> {
@ -277,9 +265,7 @@ impl FreeTypeRasterizer {
let mut pixelsize = pattern.pixelsize();
debug!("pixelsizes: {:?}", pixelsize);
Some(FixedSize {
pixelsize: pixelsize.next().expect("has 1+ pixelsize"),
})
Some(FixedSize { pixelsize: pixelsize.next().expect("has 1+ pixelsize") })
};
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 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)
-> Result<RasterizedGlyph, Error> {
fn get_rendered_glyph(&mut self, glyph_key: GlyphKey) -> Result<RasterizedGlyph, Error> {
// Render a custom symbol for the underline and beam cursor
match glyph_key.c {
super::UNDERLINE_CURSOR_CHAR => {
@ -370,9 +359,10 @@ impl FreeTypeRasterizer {
let face = &self.faces[&font_key];
let index = face.ft_face.get_char_index(glyph_key.c as usize);
let size = face.non_scalable.as_ref()
.map(|v| v.pixelsize as f32)
.unwrap_or_else(|| glyph_key.size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.);
let size =
face.non_scalable.as_ref().map(|v| v.pixelsize as f32).unwrap_or_else(|| {
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)?;
@ -405,7 +395,7 @@ impl FreeTypeRasterizer {
use freetype::face::LoadFlag;
match (antialias, hinting, rgba) {
(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,
// hintslight does *not* use LCD hinting even when a subpixel mode
// is selected.
@ -459,7 +449,9 @@ impl FreeTypeRasterizer {
/// 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.
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;
let buf = bitmap.buffer();
@ -475,7 +467,7 @@ impl FreeTypeRasterizer {
Ok((bitmap.rows(), bitmap.width() / 3, packed))
},
PixelMode::LcdV => {
for i in 0..bitmap.rows()/3 {
for i in 0..bitmap.rows() / 3 {
for j in 0..bitmap.width() {
for k in 0..3 {
let offset = ((i as usize) * 3 + k) * pitch + (j as usize);
@ -529,14 +521,11 @@ impl FreeTypeRasterizer {
}
Ok((bitmap.rows(), bitmap.width(), packed))
},
mode => panic!("unhandled pixel mode: {:?}", mode)
mode => panic!("unhandled pixel mode: {:?}", mode),
}
}
fn load_face_with_glyph(
&mut self,
glyph: char,
) -> Result<FontKey, Error> {
fn load_face_with_glyph(&mut self, glyph: char) -> Result<FontKey, Error> {
let mut charset = fc::CharSet::new();
charset.add(glyph);
let mut pattern = fc::Pattern::new();
@ -560,19 +549,19 @@ impl FreeTypeRasterizer {
// and index above.
let key = self.face_from_pattern(&pattern)?.unwrap();
Ok(key)
}
},
}
}
else {
Err(Error::MissingFont(
FontDesc::new("fallback-without-path", Style::Specific(glyph.to_string()))))
} else {
Err(Error::MissingFont(FontDesc::new(
"fallback-without-path",
Style::Specific(glyph.to_string()),
)))
}
},
None => {
Err(Error::MissingFont(
FontDesc::new("no-fallback-for", Style::Specific(glyph.to_string()))
))
}
None => Err(Error::MissingFont(FontDesc::new(
"no-fallback-for",
Style::Specific(glyph.to_string()),
))),
}
}
}
@ -614,19 +603,17 @@ impl ::std::error::Error for Error {
impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match *self {
Error::FreeType(ref err) => {
err.fmt(f)
},
Error::MissingFont(ref desc) => {
write!(f, "Couldn't find a font with {}\
\n\tPlease check the font config in your alacritty.yml.", desc)
},
Error::FontNotLoaded => {
f.write_str("Tried to use a font that hasn't been loaded")
},
Error::FreeType(ref err) => err.fmt(f),
Error::MissingFont(ref desc) => write!(
f,
"Couldn't find a font with {}\n\tPlease check the font config in your \
alacritty.yml.",
desc
),
Error::FontNotLoaded => f.write_str("Tried to use a font that hasn't been loaded"),
Error::MissingSizeMetrics => {
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;
use std::hash::{Hash, Hasher};
use std::{fmt, cmp};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::{cmp, fmt};
// If target isn't macos or windows, reexport everything from ft
#[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::Description { slant, weight } => {
write!(f, "slant={:?}, weight={:?}", slant, weight)
}
},
}
}
}
@ -123,10 +123,7 @@ impl FontDesc {
where
S: Into<String>,
{
FontDesc {
name: name.into(),
style,
}
FontDesc { name: name.into(), style }
}
}
@ -149,9 +146,7 @@ impl FontKey {
pub fn next() -> FontKey {
static TOKEN: AtomicUsize = AtomicUsize::new(0);
FontKey {
token: TOKEN.fetch_add(1, Ordering::SeqCst) as _,
}
FontKey { 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
// - Result is being used for hashing and has no fields (it's a u64)
::std::mem::transmute::<GlyphKey, u64>(*self)
}.hash(state);
}
.hash(state);
}
}
@ -228,14 +224,7 @@ pub struct RasterizedGlyph {
impl Default for RasterizedGlyph {
fn default() -> RasterizedGlyph {
RasterizedGlyph {
c: ' ',
width: 0,
height: 0,
top: 0,
left: 0,
buf: Vec::new(),
}
RasterizedGlyph { 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);
for y in 0..height {
for x in 0..width {
if y < border_width || y >= height - border_width ||
x < border_width || x >= width - border_width {
if y < border_width
|| y >= height - border_width
|| x < border_width
|| x >= width - border_width
{
buf.append(&mut vec![255u8; 3]);
} else {
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
Ok(RasterizedGlyph {
c: BOX_CURSOR_CHAR,
top: ascent,
left: 0,
height,
width,
buf,
})
Ok(RasterizedGlyph { c: BOX_CURSOR_CHAR, top: ascent, left: 0, height, width, buf })
}
struct BufDebugger<'a>(&'a [u8]);
impl<'a> fmt::Debug for BufDebugger<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("GlyphBuffer")
.field("len", &self.0.len())
.field("bytes", &self.0)
.finish()
f.debug_struct("GlyphBuffer").field("len", &self.0.len()).field("bytes", &self.0).finish()
}
}

View File

@ -15,10 +15,7 @@ impl crate::Rasterize for RustTypeRasterizer {
type Err = Error;
fn new(device_pixel_ratio: f32, _: bool) -> Result<RustTypeRasterizer, Error> {
Ok(RustTypeRasterizer {
fonts: Vec::new(),
dpi_ratio: device_pixel_ratio,
})
Ok(RustTypeRasterizer { fonts: Vec::new(), dpi_ratio: device_pixel_ratio })
}
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> {
let fp = system_fonts::FontPropertyBuilder::new()
.family(&desc.name)
.monospace();
let fp = system_fonts::FontPropertyBuilder::new().family(&desc.name).monospace();
let fp = match desc.style {
Style::Specific(ref style) => {
match style.to_lowercase().as_str() {
"italic" => fp.italic(),
"bold" => fp.bold(),
_ => fp,
}
Style::Specific(ref style) => match style.to_lowercase().as_str() {
"italic" => fp.italic(),
"bold" => fp.bold(),
_ => fp,
},
Style::Description { slant, weight } => {
let fp = match slant {
@ -79,52 +72,52 @@ impl crate::Rasterize for RustTypeRasterizer {
Weight::Bold => fp.bold(),
Weight::Normal => fp,
}
}
},
};
self.fonts.push(FontCollection::from_bytes(
system_fonts::get(&fp.build())
.ok_or_else(|| Error::MissingFont(desc.clone()))?
.0,
).into_font()
.ok_or(Error::UnsupportedFont)?);
Ok(FontKey {
token: (self.fonts.len() - 1) as u16,
})
self.fonts.push(
FontCollection::from_bytes(
system_fonts::get(&fp.build()).ok_or_else(|| Error::MissingFont(desc.clone()))?.0,
)
.into_font()
.ok_or(Error::UnsupportedFont)?,
);
Ok(FontKey { token: (self.fonts.len() - 1) as u16 })
}
fn get_glyph(&mut self, glyph_key: GlyphKey) -> Result<RasterizedGlyph, Error> {
match glyph_key.c {
super::UNDERLINE_CURSOR_CHAR => {
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 => {
let metrics = self.metrics(glyph_key.font_key, glyph_key.size)?;
return super::get_beam_cursor_glyph(
(metrics.line_height + f64::from(metrics.descent)).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 => {
let metrics = self.metrics(glyph_key.font_key, glyph_key.size)?;
return super::get_box_cursor_glyph(
(metrics.line_height + f64::from(metrics.descent)).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]
.glyph(glyph_key.c)
.ok_or(Error::MissingGlyph)?
.scaled(Scale::uniform(
glyph_key.size.as_f32_pts() * self.dpi_ratio * 96. / 72.,
));
.scaled(Scale::uniform(glyph_key.size.as_f32_pts() * self.dpi_ratio * 96. / 72.));
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() {
Some(bb) => bb,
// Bounding box calculation fails for spaces so we provide a placeholder bounding box
None => rusttype::Rect {
min: point(0, 0),
max: point(0, 0),
},
None => rusttype::Rect { min: point(0, 0), max: point(0, 0) },
};
let mut buf = Vec::with_capacity((bb.width() * bb.height()) as usize);
@ -185,8 +175,8 @@ impl ::std::fmt::Display for Error {
match *self {
Error::MissingFont(ref desc) => write!(
f,
"Couldn't find a font with {}\
\n\tPlease check the font config in your alacritty.yml.",
"Couldn't find a font with {}\n\tPlease check the font config in your \
alacritty.yml.",
desc
),
Error::UnsupportedFont => write!(
@ -195,7 +185,7 @@ impl ::std::fmt::Display for Error {
),
Error::UnsupportedStyle => {
write!(f, "The selected font style is not supported by rusttype.")
}
},
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::str;
use vte;
use crate::index::{Column, Contains, Line};
use base64;
use glutin::MouseCursor;
use crate::index::{Column, Line, Contains};
use vte;
use crate::term::color::Rgb;
@ -33,7 +33,7 @@ fn parse_rgb_color(color: &[u8]) -> Option<Rgb> {
macro_rules! next {
() => {
iter.next().map(|v| *v as char)
}
};
}
macro_rules! parse_hex {
@ -50,32 +50,36 @@ fn parse_rgb_color(color: &[u8]) -> Option<Rgb> {
digit += value as u8;
}
digit
}}
}};
}
match next!() {
Some('r') => {
if next!() != Some('g') { return None; }
if next!() != Some('b') { return None; }
if next!() != Some(':') { return None; }
if next!() != Some('g') {
return None;
}
if next!() != Some('b') {
return None;
}
if next!() != Some(':') {
return None;
}
let r = parse_hex!();
let val = next!();
if val != Some('/') { return None; }
if val != Some('/') {
return None;
}
let g = parse_hex!();
if next!() != Some('/') { return None; }
if next!() != Some('/') {
return None;
}
let b = parse_hex!();
Some(Rgb { r, g, b })
}
Some('#') => {
Some(Rgb {
r: parse_hex!(),
g: parse_hex!(),
b: parse_hex!(),
})
}
_ => None
},
Some('#') => Some(Rgb { r: parse_hex!(), g: parse_hex!(), b: parse_hex!() }),
_ => None,
}
}
@ -106,7 +110,7 @@ pub struct Processor {
/// Internal state for VTE processor
struct ProcessorState {
preceding_char: Option<char>
preceding_char: Option<char>,
}
/// Helper type that implements `vte::Perform`.
@ -116,7 +120,7 @@ struct ProcessorState {
struct Performer<'a, H: Handler + TermInfo, W: io::Write> {
_state: &'a mut ProcessorState,
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> {
@ -127,20 +131,13 @@ impl<'a, H: Handler + TermInfo + 'a, W: io::Write> Performer<'a, H, W> {
handler: &'b mut H,
writer: &'b mut W,
) -> Performer<'b, H, W> {
Performer {
_state: state,
handler,
writer,
}
Performer { _state: state, handler, writer }
}
}
impl Default for Processor {
fn default() -> Processor {
Processor {
state: ProcessorState { preceding_char: None },
parser: vte::Parser::new(),
}
Processor { state: ProcessorState { preceding_char: None }, parser: vte::Parser::new() }
}
}
@ -150,21 +147,16 @@ impl Processor {
}
#[inline]
pub fn advance<H, W>(
&mut self,
handler: &mut H,
byte: u8,
writer: &mut W
)
where H: Handler + TermInfo,
W: io::Write
pub fn advance<H, W>(&mut self, handler: &mut H, byte: u8, writer: &mut W)
where
H: Handler + TermInfo,
W: io::Write,
{
let mut performer = Performer::new(&mut self.state, handler, writer);
self.parser.advance(&mut performer, byte);
}
}
/// Trait that provides properties of terminal
pub trait TermInfo {
fn lines(&self) -> Line;
@ -447,14 +439,14 @@ impl Mode {
2004 => Mode::BracketedPaste,
_ => {
trace!("[unimplemented] primitive mode: {}", num);
return None
}
return None;
},
})
} else {
Some(match num {
4 => Mode::Insert,
20 => Mode::LineFeedNewLine,
_ => return None
_ => return None,
})
}
}
@ -485,7 +477,7 @@ pub enum ClearMode {
/// Clear entire terminal
All,
/// Clear 'saved' lines (scrollback)
Saved
Saved,
}
/// Mode for clearing tab stops
@ -586,7 +578,7 @@ impl NamedColor {
NamedColor::DimMagenta => NamedColor::Magenta,
NamedColor::DimCyan => NamedColor::Cyan,
NamedColor::DimWhite => NamedColor::White,
val => val
val => val,
}
}
@ -610,7 +602,7 @@ impl NamedColor {
NamedColor::BrightCyan => NamedColor::Cyan,
NamedColor::BrightWhite => NamedColor::White,
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>
where H: Handler + TermInfo + 'a,
W: io::Write + 'a
where
H: Handler + TermInfo + 'a,
W: io::Write + 'a,
{
#[inline]
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::HTS => self.handler.set_horizontal_tabstop(),
C1::DECID => self.handler.identify_terminal(self.writer),
_ => debug!("[unhandled] execute byte={:02x}", byte)
_ => debug!("[unhandled] execute byte={:02x}", byte),
}
}
#[inline]
fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool) {
debug!("[unhandled hook] params={:?}, ints: {:?}, ignore: {:?}",
params, intermediates, ignore);
debug!(
"[unhandled hook] params={:?}, ints: {:?}, ignore: {:?}",
params, intermediates, ignore
);
}
#[inline]
@ -788,7 +783,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
}
}
unhandled(params);
}
},
// Set foreground color
b"10" => {
@ -799,7 +794,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
}
}
unhandled(params);
}
},
// Set background color
b"11" => {
@ -810,7 +805,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
}
}
unhandled(params);
}
},
// Set text cursor color
b"12" => {
@ -821,11 +816,14 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
}
}
unhandled(params);
}
},
// Set cursor style
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 {
'0' => CursorStyle::Block,
'1' => CursorStyle::Beam,
@ -836,7 +834,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
return;
}
unhandled(params);
}
},
// Set clipboard
b"52" => {
@ -852,9 +850,9 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
self.handler.set_clipboard(utf8_string);
}
}
}
},
}
}
},
// Reset color index
b"104" => {
@ -873,7 +871,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
None => unhandled(params),
}
}
}
},
// Reset foreground color
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]
fn csi_dispatch(
&mut self,
args: &[i64],
intermediates: &[u8],
_ignore: bool,
action: char
) {
fn csi_dispatch(&mut self, args: &[i64], intermediates: &[u8], _ignore: bool, action: char) {
let private = intermediates.get(0).map(|b| *b == b'?').unwrap_or(false);
let handler = &mut self.handler;
let writer = &mut self.writer;
macro_rules! unhandled {
() => {{
debug!("[Unhandled CSI] action={:?}, args={:?}, intermediates={:?}",
action, args, intermediates);
debug!(
"[Unhandled CSI] action={:?}, args={:?}, intermediates={:?}",
action, args, intermediates
);
return;
}}
}};
}
macro_rules! arg_or_default {
(idx: $idx:expr, default: $default:expr) => {
args.get($idx).and_then(|v| {
if *v == 0 {
None
} else {
Some(*v)
}
}).unwrap_or($default)
}
args.get($idx)
.and_then(|v| if *v == 0 { None } else { Some(*v) })
.unwrap_or($default)
};
}
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) {
handler.input(c);
}
}
else {
} else {
debug!("tried to repeat with no preceding char");
}
},
@ -1012,7 +1001,8 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
return;
}
loop {
if i >= args.len() { // C-for condition
if i >= args.len() {
// C-for condition
break;
}
@ -1061,7 +1051,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
45 => Attr::Background(Color::Named(NamedColor::Magenta)),
46 => Attr::Background(Color::Named(NamedColor::Cyan)),
47 => Attr::Background(Color::Named(NamedColor::White)),
48 => {
48 => {
let mut start = 0;
if let Some(color) = parse_color(&args[i..], &mut start) {
i += start;
@ -1094,7 +1084,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
i += 1; // C-for expr
}
}
},
'n' => handler.device_status(writer, arg_or_default!(idx: 0, default: 0) as usize),
'r' => {
if private {
@ -1119,29 +1109,25 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
1 | 2 => Some(CursorStyle::Block),
3 | 4 => Some(CursorStyle::Underline),
5 | 6 => Some(CursorStyle::Beam),
_ => unhandled!()
_ => unhandled!(),
};
handler.set_cursor_style(style);
}
},
_ => unhandled!(),
}
}
#[inline]
fn esc_dispatch(
&mut self,
params: &[i64],
intermediates: &[u8],
_ignore: bool,
byte: u8
) {
fn esc_dispatch(&mut self, params: &[i64], intermediates: &[u8], _ignore: bool, byte: u8) {
macro_rules! unhandled {
() => {{
debug!("[unhandled] esc_dispatch params={:?}, ints={:?}, byte={:?} ({:02x})",
params, intermediates, byte as char, byte);
debug!(
"[unhandled] esc_dispatch params={:?}, ints={:?}, byte={:?} ({:02x})",
params, intermediates, byte as char, byte
);
return;
}}
}};
}
macro_rules! configure_charset {
@ -1154,7 +1140,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
_ => unhandled!(),
};
self.handler.configure_charset(index, $charset)
}}
}};
}
match byte {
@ -1163,7 +1149,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
b'E' => {
self.handler.linefeed();
self.handler.carriage_return();
}
},
b'H' => self.handler.set_horizontal_tabstop(),
b'M' => self.handler.reverse_index(),
b'Z' => self.handler.identify_terminal(self.writer),
@ -1176,7 +1162,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
} else {
self.handler.restore_cursor_position();
}
}
},
b'=' => self.handler.set_keypad_application_mode(),
b'>' => self.handler.unset_keypad_application_mode(),
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
fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Color> {
if attrs.len() < 2 {
return None;
}
match attrs[*i+1] {
match attrs[*i + 1] {
2 => {
// RGB color spec
if attrs.len() < 5 {
@ -1200,9 +1185,9 @@ fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Color> {
return None;
}
let r = attrs[*i+2];
let g = attrs[*i+3];
let b = attrs[*i+4];
let r = attrs[*i + 2];
let g = attrs[*i + 3];
let b = attrs[*i + 4];
*i += 4;
@ -1212,11 +1197,7 @@ fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Color> {
return None;
}
Some(Color::Spec(Rgb {
r: r as u8,
g: g as u8,
b: b as u8
}))
Some(Color::Spec(Rgb { r: r as u8, g: g as u8, b: b as u8 }))
},
5 => {
if attrs.len() < 3 {
@ -1226,20 +1207,18 @@ fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Color> {
*i += 2;
let idx = attrs[*i];
match idx {
0 ..= 255 => {
Some(Color::Indexed(idx as u8))
},
0..=255 => Some(Color::Indexed(idx as u8)),
_ => {
debug!("Invalid color index: {}", idx);
None
}
},
}
}
},
_ => {
debug!("Unexpected color attr: {}", attrs[*i+1]);
debug!("Unexpected color attr: {}", attrs[*i + 1]);
None
}
},
}
}
@ -1314,7 +1293,6 @@ pub mod C0 {
pub const DEL: u8 = 0x7f;
}
/// C1 set of 8-bit control characters (from ANSI X3.64-1979)
///
/// 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.
#[cfg(test)]
mod tests {
use std::io;
use crate::index::{Line, Column};
use super::{Processor, Handler, Attr, TermInfo, Color, StandardCharset, CharsetIndex, parse_rgb_color, parse_number};
use super::{
parse_number, parse_rgb_color, Attr, CharsetIndex, Color, Handler, Processor,
StandardCharset, TermInfo,
};
use crate::index::{Column, Line};
use crate::term::color::Rgb;
use std::io;
/// The /dev/null of `io::Write`
struct Void;
@ -1434,9 +1415,7 @@ mod tests {
#[test]
fn parse_control_attribute() {
static BYTES: &'static [u8] = &[
0x1b, 0x5b, 0x31, 0x6d
];
static BYTES: &'static [u8] = &[0x1b, 0x5b, 0x31, 0x6d];
let mut parser = Processor::new();
let mut handler = AttrHandler::default();
@ -1451,8 +1430,8 @@ mod tests {
#[test]
fn parse_truecolor_attr() {
static BYTES: &'static [u8] = &[
0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x32, 0x3b, 0x31, 0x32,
0x38, 0x3b, 0x36, 0x36, 0x3b, 0x32, 0x35, 0x35, 0x6d
0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x32, 0x3b, 0x31, 0x32, 0x38, 0x3b, 0x36, 0x36, 0x3b,
0x32, 0x35, 0x35, 0x6d,
];
let mut parser = Processor::new();
@ -1462,11 +1441,7 @@ mod tests {
parser.advance(&mut handler, *byte, &mut Void);
}
let spec = Rgb {
r: 128,
g: 66,
b: 255
};
let spec = Rgb { r: 128, g: 66, b: 255 };
assert_eq!(handler.attr, Some(Attr::Foreground(Color::Spec(spec))));
}
@ -1475,22 +1450,19 @@ mod tests {
#[test]
fn parse_zsh_startup() {
static BYTES: &'static [u8] = &[
0x1b, 0x5b, 0x31, 0x6d, 0x1b, 0x5b, 0x37, 0x6d, 0x25, 0x1b, 0x5b,
0x32, 0x37, 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, 0x0d, 0x20, 0x0d, 0x0d, 0x1b, 0x5b, 0x30, 0x6d, 0x1b,
0x5b, 0x32, 0x37, 0x6d, 0x1b, 0x5b, 0x32, 0x34, 0x6d, 0x1b, 0x5b,
0x4a, 0x6a, 0x77, 0x69, 0x6c, 0x6d, 0x40, 0x6a, 0x77, 0x69, 0x6c,
0x6d, 0x2d, 0x64, 0x65, 0x73, 0x6b, 0x20, 0x1b, 0x5b, 0x30, 0x31,
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
0x1b, 0x5b, 0x31, 0x6d, 0x1b, 0x5b, 0x37, 0x6d, 0x25, 0x1b, 0x5b, 0x32, 0x37, 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, 0x0d, 0x20, 0x0d, 0x0d, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x32,
0x37, 0x6d, 0x1b, 0x5b, 0x32, 0x34, 0x6d, 0x1b, 0x5b, 0x4a, 0x6a, 0x77, 0x69, 0x6c,
0x6d, 0x40, 0x6a, 0x77, 0x69, 0x6c, 0x6d, 0x2d, 0x64, 0x65, 0x73, 0x6b, 0x20, 0x1b,
0x5b, 0x30, 0x31, 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();
@ -1508,10 +1480,7 @@ mod tests {
impl Default for CharsetHandler {
fn default() -> CharsetHandler {
CharsetHandler {
index: CharsetIndex::G0,
charset: StandardCharset::Ascii,
}
CharsetHandler { index: CharsetIndex::G0, charset: StandardCharset::Ascii }
}
}
@ -1527,8 +1496,13 @@ mod tests {
}
impl TermInfo for CharsetHandler {
fn lines(&self) -> Line { Line(200) }
fn cols(&self) -> Column { Column(90) }
fn lines(&self) -> Line {
Line(200)
}
fn cols(&self) -> Column {
Column(90)
}
}
#[test]

View File

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

View File

@ -11,11 +11,11 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use glutin::{MouseButton, ModifiersState};
use glutin::{ModifiersState, MouseButton};
use crate::input::{MouseBinding, KeyBinding, Action};
use crate::term::TermMode;
use super::Key;
use crate::input::{Action, KeyBinding, MouseBinding};
use crate::term::TermMode;
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
//! GPU drawing.
use std::sync::mpsc;
use std::f64;
use std::sync::mpsc;
use parking_lot::MutexGuard;
use glutin::dpi::{PhysicalPosition, PhysicalSize};
use parking_lot::MutexGuard;
use crate::cli;
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::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)]
pub enum Error {
@ -152,8 +152,8 @@ impl Display {
info!("Device pixel ratio: {}", dpr);
// get window properties for initializing the other subsystems
let mut viewport_size = window.inner_size_pixels()
.expect("glutin returns window size").to_physical(dpr);
let mut viewport_size =
window.inner_size_pixels().expect("glutin returns window size").to_physical(dpr);
// Create renderer
let mut renderer = QuadRenderer::new(viewport_size)?;
@ -161,8 +161,7 @@ impl Display {
let (glyph_cache, cell_width, cell_height) =
Self::new_glyph_cache(dpr, &mut renderer, config)?;
let dimensions = options.dimensions()
.unwrap_or_else(|| config.dimensions());
let dimensions = options.dimensions().unwrap_or_else(|| config.dimensions());
let mut padding_x = f64::from(config.padding().x) * dpr;
let mut padding_y = f64::from(config.padding().y) * dpr;
@ -216,13 +215,9 @@ impl Display {
// Clear screen
let background_color = config.colors().primary.background;
renderer.with_api(
config,
&size_info,
|api| {
api.clear(background_color);
},
);
renderer.with_api(config, &size_info, |api| {
api.clear(background_color);
});
Ok(Display {
window,
@ -238,9 +233,11 @@ impl Display {
})
}
fn new_glyph_cache(dpr: f64, renderer: &mut QuadRenderer, config: &Config)
-> Result<(GlyphCache, f32, f32), Error>
{
fn new_glyph_cache(
dpr: f64,
renderer: &mut QuadRenderer,
config: &Config,
) -> Result<(GlyphCache, f32, f32), Error> {
let font = config.font().clone();
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))?;
let stop = init_start.elapsed();
let stop_f = stop.as_secs() as f64 +
f64::from(stop.subsec_nanos()) / 1_000_000_000f64;
let stop_f = stop.as_secs() as f64 + f64::from(stop.subsec_nanos()) / 1_000_000_000f64;
info!("... finished initializing glyph cache in {}s", stop_f);
cache
@ -322,8 +318,8 @@ impl Display {
let dpr = self.window.hidpi_factor();
// Font size/DPI factor modification detected
let font_changed = terminal.font_size != self.font_size
|| (dpr - self.size_info.dpr).abs() > f64::EPSILON;
let font_changed =
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 new_size == None {
@ -391,9 +387,8 @@ impl Display {
let background_color = terminal.background_color();
let window_focused = self.window.is_focused;
let grid_cells: Vec<RenderableCell> = terminal
.renderable_cells(config, window_focused)
.collect();
let grid_cells: Vec<RenderableCell> =
terminal.renderable_cells(config, window_focused).collect();
// Get message from terminal to ignore modifications after lock is dropped
let message_buffer = terminal.message_buffer_mut().message();
@ -486,20 +481,14 @@ impl Display {
// Draw render timer
if self.render_timer {
let timing = format!("{:.3} usec", self.meter.average());
let color = Rgb {
r: 0xd5,
g: 0x4e,
b: 0x53,
};
let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 };
self.renderer.with_api(config, &size_info, |mut api| {
api.render_string(&timing[..], size_info.lines() - 2, glyph_cache, Some(color));
});
}
}
self.window
.swap_buffers()
.expect("swap buffers");
self.window.swap_buffers().expect("swap buffers");
}
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
pub fn update_ime_position(&mut self, terminal: &Term) {
let point = terminal.cursor().point;
let SizeInfo {
cell_width: cw,
cell_height: ch,
padding_x: px,
padding_y: py,
..
} = *terminal.size_info();
let SizeInfo { cell_width: cw, cell_height: ch, padding_x: px, padding_y: py, .. } =
*terminal.size_info();
let dpr = self.window().hidpi_factor();
let nspot_x = f64::from(px + point.col.0 as f32 * cw);

View File

@ -1,33 +1,33 @@
//! Process window events
use std::borrow::Cow;
use std::env;
#[cfg(unix)]
use std::fs;
use std::borrow::Cow;
use std::fs::File;
use std::io::Write;
use std::sync::mpsc;
use std::time::{Instant};
use std::env;
use std::time::Instant;
use serde_json as json;
use parking_lot::MutexGuard;
use glutin::{self, ModifiersState, Event, ElementState, MouseButton};
use copypasta::{Clipboard, Load, Store, Buffer as ClipboardBuffer};
use copypasta::{Buffer as ClipboardBuffer, Clipboard, Load, Store};
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::config::{self, Config};
use crate::display::OnResize;
use crate::index::{Line, Column, Side, Point};
use crate::input::{self, MouseBinding, KeyBinding};
use crate::grid::Scroll;
use crate::index::{Column, Line, Point, Side};
use crate::input::{self, KeyBinding, MouseBinding};
use crate::selection::Selection;
use crate::sync::FairMutex;
use crate::term::{Term, SizeInfo};
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::{limit, start_daemon};
use crate::window::Window;
/// 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 point = size_info.pixels_to_coords(x, y);
let cell_side = self.mouse().cell_side;
self.update_selection(Point {
line: point.line,
col: point.col
}, cell_side);
self.update_selection(Point { line: point.line, col: point.col }, cell_side);
}
}
@ -209,9 +206,7 @@ impl WindowChanges {
impl Default for WindowChanges {
fn default() -> WindowChanges {
WindowChanges {
hide: false,
}
WindowChanges { hide: false }
}
}
@ -358,16 +353,14 @@ impl<N: Notify> Processor<N> {
grid.initialize_all(&Cell::default());
grid.truncate();
let serialized_grid = json::to_string(&grid)
.expect("serialize grid");
let serialized_grid = json::to_string(&grid).expect("serialize grid");
let serialized_size = json::to_string(processor.ctx.terminal.size_info())
.expect("serialize size");
let serialized_size =
json::to_string(processor.ctx.terminal.size_info())
.expect("serialize size");
let serialized_config = format!(
"{{\"history_size\":{}}}",
grid.history_size()
);
let serialized_config =
format!("{{\"history_size\":{}}}", grid.history_size());
File::create("./grid.json")
.and_then(|mut f| f.write_all(serialized_grid.as_bytes()))
@ -453,7 +446,7 @@ impl<N: Notify> Processor<N> {
},
Event::Awakened => {
processor.ctx.terminal.dirty = true;
}
},
}
}
@ -462,7 +455,7 @@ impl<N: Notify> Processor<N> {
pub fn process_events<'a>(
&mut self,
term: &'a FairMutex<Term>,
window: &mut Window
window: &mut Window,
) -> MutexGuard<'a, Term> {
// Terminal is lazily initialized the first time an event is returned
// 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
use std::borrow::Cow;
use std::collections::VecDeque;
use std::io::{self, ErrorKind, Read, Write};
use std::fs::File;
use std::sync::Arc;
use std::io::{self, ErrorKind, Read, Write};
use std::marker::Send;
use std::sync::Arc;
use mio::{self, Events, PollOpt, Ready};
use mio_extras::channel::{self, Receiver, Sender};
@ -15,10 +15,10 @@ use mio::unix::UnixReady;
use crate::ansi;
use crate::display;
use crate::event;
use crate::tty;
use crate::term::Term;
use crate::util::thread;
use crate::sync::FairMutex;
use crate::term::Term;
use crate::tty;
use crate::util::thread;
/// Messages that may be sent to the `EventLoop`
#[derive(Debug)]
@ -58,14 +58,14 @@ enum DrainResult {
/// Nothing was available to receive
Empty,
/// A shutdown message was received
Shutdown
Shutdown,
}
impl DrainResult {
pub fn is_shutdown(&self) -> bool {
match *self {
DrainResult::Shutdown => true,
_ => false
_ => false,
}
}
}
@ -84,12 +84,13 @@ pub struct Notifier(pub Sender<Msg>);
impl event::Notify for Notifier {
fn notify<B>(&mut self, bytes: B)
where B: Into<Cow<'static, [u8]>>,
where
B: Into<Cow<'static, [u8]>>,
{
let bytes = bytes.into();
// terminal hangs if we send 0 bytes through.
if bytes.len() == 0 {
return
return;
}
if self.0.send(Msg::Input(bytes)).is_err() {
panic!("expected send event loop msg");
@ -99,11 +100,7 @@ impl event::Notify for Notifier {
impl Default for State {
fn default() -> State {
State {
write_list: VecDeque::new(),
parser: ansi::Processor::new(),
writing: None,
}
State { write_list: VecDeque::new(), parser: ansi::Processor::new(), writing: None }
}
}
@ -117,9 +114,7 @@ impl State {
#[inline]
fn goto_next(&mut self) {
self.writing = self.write_list
.pop_front()
.map(Writing::new);
self.writing = self.write_list.pop_front().map(Writing::new);
}
#[inline]
@ -161,8 +156,8 @@ impl Writing {
}
impl<T> EventLoop<T>
where
T: tty::EventedPty + Send + 'static,
where
T: tty::EventedPty + Send + 'static,
{
/// Create a new event loop
pub fn new(
@ -198,10 +193,10 @@ impl<T> EventLoop<T>
match msg {
Msg::Input(input) => {
state.write_list.push_back(input);
}
},
Msg::Shutdown => {
return DrainResult::Shutdown;
}
},
}
}
@ -233,8 +228,8 @@ impl<T> EventLoop<T>
buf: &mut [u8],
mut writer: Option<&mut X>,
) -> io::Result<()>
where
X: Write,
where
X: Write,
{
const MAX_READ: usize = 0x1_0000;
let mut processed = 0;
@ -271,20 +266,18 @@ impl<T> EventLoop<T>
// Run the parser
for byte in &buf[..got] {
state
.parser
.advance(&mut **terminal, *byte, &mut self.pty.writer());
state.parser.advance(&mut **terminal, *byte, &mut self.pty.writer());
}
// Exit if we've processed enough bytes
if processed > MAX_READ {
break;
}
}
},
Err(err) => match err.kind() {
ErrorKind::Interrupted | ErrorKind::WouldBlock => {
break;
}
},
_ => return Err(err),
},
}
@ -311,21 +304,21 @@ impl<T> EventLoop<T>
Ok(0) => {
state.set_current(Some(current));
break 'write_many;
}
},
Ok(n) => {
current.advance(n);
if current.finished() {
state.goto_next();
break 'write_one;
}
}
},
Err(err) => {
state.set_current(Some(current));
match err.kind() {
ErrorKind::Interrupted | ErrorKind::WouldBlock => break 'write_many,
_ => return Err(err),
}
}
},
}
}
}
@ -343,14 +336,10 @@ impl<T> EventLoop<T>
let poll_opts = PollOpt::edge() | PollOpt::oneshot();
let channel_token = tokens.next().unwrap();
self.poll
.register(&self.rx, channel_token, Ready::readable(), poll_opts)
.unwrap();
self.poll.register(&self.rx, channel_token, Ready::readable(), poll_opts).unwrap();
// Register TTY through EventedRW interface
self.pty
.register(&self.poll, &mut tokens, Ready::readable(), poll_opts)
.unwrap();
self.pty.register(&self.poll, &mut tokens, Ready::readable(), poll_opts).unwrap();
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() => {
#[cfg(unix)] {
token
if token == self.pty.read_token()
|| token == self.pty.write_token() =>
{
#[cfg(unix)]
{
if UnixReady::from(event.readiness()).is_hup() {
// don't try to do I/O on a dead PTY
continue;
@ -395,7 +388,8 @@ impl<T> EventLoop<T>
if event.readiness().is_readable() {
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
// with `EIO` if the client side hangs up. In that case,
// 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.
use std::cmp::{min, max, Ordering};
use std::ops::{Deref, Range, Index, IndexMut, RangeTo, RangeFrom, RangeFull, RangeInclusive};
use std::cmp::{max, min, Ordering};
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;
mod row;
@ -55,13 +55,13 @@ impl<T> Deref for Indexed<T> {
impl<T: PartialEq> ::std::cmp::PartialEq for Grid<T> {
fn eq(&self, other: &Self) -> bool {
// Compare struct fields and check result of grid comparison
self.raw.eq(&other.raw) &&
self.cols.eq(&other.cols) &&
self.lines.eq(&other.lines) &&
self.display_offset.eq(&other.display_offset) &&
self.scroll_limit.eq(&other.scroll_limit) &&
self.selection.eq(&other.selection) &&
self.url_highlight.eq(&other.url_highlight)
self.raw.eq(&other.raw)
&& self.cols.eq(&other.cols)
&& self.lines.eq(&other.lines)
&& self.display_offset.eq(&other.display_offset)
&& self.scroll_limit.eq(&other.scroll_limit)
&& self.selection.eq(&other.selection)
&& 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> {
Point {
line: self.visible_line_to_buffer(point.line),
col: point.col
}
Point { line: self.visible_line_to_buffer(point.line), col: point.col }
}
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
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.max_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) => {
self.display_offset = min(
max((self.display_offset as isize) + count, 0isize) as usize,
self.scroll_limit
self.scroll_limit,
);
},
Scroll::PageUp => {
self.display_offset = min(
self.display_offset + self.lines.0,
self.scroll_limit
);
self.display_offset = min(self.display_offset + self.lines.0, self.scroll_limit);
},
Scroll::PageDown => {
self.display_offset -= min(
self.display_offset,
self.lines.0
);
self.display_offset -= min(self.display_offset, self.lines.0);
},
Scroll::Top => self.display_offset = self.scroll_limit,
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);
// 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
/// is scrollback available. Once scrollback is exhausted, new lines are
/// simply added to the bottom of the screen.
fn grow_lines(
&mut self,
new_line_count: index::Line,
template: &T,
) {
fn grow_lines(&mut self, new_line_count: index::Line, template: &T) {
let lines_added = new_line_count - self.lines;
// Need to "resize" before updating buffer
@ -435,7 +420,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
// Now, restore any scroll region 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);
}
@ -449,7 +434,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
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);
}
}
@ -458,19 +443,12 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
/// scroll_up moves lines at the bottom towards the top
///
/// This is the performance-sensitive part of scrolling.
pub fn scroll_up(
&mut self,
region: &Range<index::Line>,
positions: index::Line,
template: &T
) {
pub fn scroll_up(&mut self, region: &Range<index::Line>, positions: index::Line, template: &T) {
if region.start == Line(0) {
// Update display offset when not pinned to active area
if self.display_offset != 0 {
self.display_offset = min(
self.display_offset + *positions,
self.len() - self.num_lines().0,
);
self.display_offset =
min(self.display_offset + *positions, self.len() - self.num_lines().0);
}
self.increase_scroll_limit(*positions, template);
@ -506,7 +484,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
}
// 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);
}
}
@ -569,7 +547,7 @@ impl<T> Grid<T> {
/// This is used only for initializing after loading ref-tests
pub fn initialize_all(&mut self, template: &T)
where
T: Copy
T: Copy,
{
let history_size = self.raw.len().saturating_sub(*self.lines);
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> {
GridIterator {
grid: self,
cur: point,
}
GridIterator { grid: self, cur: point }
}
#[inline]
@ -613,8 +588,7 @@ impl<'a, T> Iterator for GridIterator<'a, T> {
let last_col = self.grid.num_cols() - Column(1);
match self.cur {
Point { line, col } if line == 0 && col == last_col => None,
Point { col, .. } if
(col == last_col) => {
Point { col, .. } if (col == last_col) => {
self.cur.line -= 1;
self.cur.col = Column(0);
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);
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);
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.end <= self.num_lines());
assert!(index.start <= index.end);
Region {
start: index.start,
end: index.end,
raw: &self.raw
}
Region { start: index.start, end: index.end, raw: &self.raw }
}
fn region_mut(&mut self, index: Range<Line>) -> RegionMut<'_, T> {
assert!(index.start < self.num_lines());
assert!(index.end <= self.num_lines());
assert!(index.start <= index.end);
RegionMut {
start: index.start,
end: index.end,
raw: &mut self.raw
}
RegionMut { start: index.start, end: index.end, raw: &mut self.raw }
}
}
impl<T> IndexRegion<RangeTo<Line>, T> for Grid<T> {
fn region(&self, index: RangeTo<Line>) -> Region<'_, T> {
assert!(index.end <= self.num_lines());
Region {
start: Line(0),
end: index.end,
raw: &self.raw
}
Region { start: Line(0), end: index.end, raw: &self.raw }
}
fn region_mut(&mut self, index: RangeTo<Line>) -> RegionMut<'_, T> {
assert!(index.end <= self.num_lines());
RegionMut {
start: Line(0),
end: index.end,
raw: &mut self.raw
}
RegionMut { start: Line(0), end: index.end, raw: &mut self.raw }
}
}
impl<T> IndexRegion<RangeFrom<Line>, T> for Grid<T> {
fn region(&self, index: RangeFrom<Line>) -> Region<'_, T> {
assert!(index.start < self.num_lines());
Region {
start: index.start,
end: self.num_lines(),
raw: &self.raw
}
Region { start: index.start, end: self.num_lines(), raw: &self.raw }
}
fn region_mut(&mut self, index: RangeFrom<Line>) -> RegionMut<'_, T> {
assert!(index.start < self.num_lines());
RegionMut {
start: index.start,
end: self.num_lines(),
raw: &mut self.raw
}
RegionMut { start: index.start, end: self.num_lines(), raw: &mut self.raw }
}
}
impl<T> IndexRegion<RangeFull, T> for Grid<T> {
fn region(&self, _: RangeFull) -> Region<'_, T> {
Region {
start: Line(0),
end: self.num_lines(),
raw: &self.raw
}
Region { start: Line(0), end: self.num_lines(), raw: &self.raw }
}
fn region_mut(&mut self, _: RangeFull) -> RegionMut<'_, T> {
RegionMut {
start: Line(0),
end: self.num_lines(),
raw: &mut self.raw
}
RegionMut { 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> {
type Item = &'a Row<T>;
type IntoIter = RegionIter<'a, T>;
type Item = &'a Row<T>;
fn into_iter(self) -> Self::IntoIter {
RegionIter {
end: self.end,
cur: self.start,
raw: self.raw
}
RegionIter { end: self.end, cur: self.start, raw: self.raw }
}
}
impl<'a, T> IntoIterator for RegionMut<'a, T> {
type Item = &'a mut Row<T>;
type IntoIter = RegionIterMut<'a, T>;
type Item = &'a mut Row<T>;
fn into_iter(self) -> Self::IntoIter {
RegionIterMut {
end: self.end,
cur: self.start,
raw: self.raw
}
RegionIterMut { end: self.end, cur: self.start, raw: self.raw }
}
}
impl<'a, T> Iterator for RegionIter<'a, T> {
type Item = &'a Row<T>;
fn next(&mut self) -> Option<Self::Item> {
if self.cur < self.end {
let index = self.cur;
@ -869,13 +807,12 @@ impl<'a, T> Iterator for RegionIter<'a, T> {
impl<'a, T> Iterator for RegionIterMut<'a, T> {
type Item = &'a mut Row<T>;
fn next(&mut self) -> Option<Self::Item> {
if self.cur < self.end {
let index = self.cur;
self.cur += 1;
unsafe {
Some(&mut *(&mut self.raw[index] as *mut _))
}
unsafe { Some(&mut *(&mut self.raw[index] as *mut _)) }
} else {
None
}
@ -898,7 +835,7 @@ pub struct DisplayIter<'a, T> {
impl<'a, T: 'a> DisplayIter<'a, T> {
pub fn new(grid: &'a Grid<T>) -> DisplayIter<'a, T> {
let offset = grid.display_offset + *grid.num_lines() - 1;
let limit = grid.display_offset;
let limit = grid.display_offset;
let col = Column(0);
let line = Line(0);
@ -932,7 +869,7 @@ impl<'a, T: Copy + 'a> Iterator for DisplayIter<'a, T> {
let item = Some(Indexed {
inner: self.grid.raw[self.offset][self.col],
line: self.line,
column: self.col
column: self.col,
});
// 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
use std::cmp::{max, min};
use std::ops::{Index, IndexMut};
use std::ops::{Range, RangeTo, RangeFrom, RangeFull, RangeToInclusive};
use std::cmp::{min, max};
use std::ops::{Range, RangeFrom, RangeFull, RangeTo, RangeToInclusive};
use std::slice;
use crate::grid::GridCell;
@ -46,10 +46,7 @@ impl<T: PartialEq> PartialEq for Row<T> {
impl<T: Copy> Row<T> {
pub fn new(columns: Column, template: &T) -> Row<T> {
Row {
inner: vec![*template; *columns],
occ: 0,
}
Row { inner: vec![*template; *columns], occ: 0 }
}
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>>
where
T: GridCell
T: GridCell,
{
if self.inner.len() <= cols.0 {
return None;
@ -96,10 +93,7 @@ impl<T: Copy> Row<T> {
impl<T> Row<T> {
#[inline]
pub fn from_vec(vec: Vec<T>, occ: usize) -> Row<T> {
Row {
inner: vec,
occ,
}
Row { inner: vec, occ }
}
#[inline]
@ -121,7 +115,7 @@ impl<T> Row<T> {
#[inline]
pub fn append(&mut self, vec: &mut Vec<T>)
where
T: GridCell
T: GridCell,
{
self.inner.append(vec);
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]
pub fn is_empty(&self) -> bool
where
T: GridCell
T: GridCell,
{
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> {
type Item = &'a mut T;
type IntoIter = slice::IterMut<'a, T>;
type Item = &'a mut T;
#[inline]
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 crate::index::{Column, Line};
use crate::grid::GridCell;
use super::Row;
use crate::grid::GridCell;
use crate::index::{Column, Line};
/// Maximum number of invisible lines before buffer is resized
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
let (ref bigger, ref smaller) = if self.zero >= other.zero {
(self, other)
} else {
(other, self)
};
let (ref bigger, ref smaller) =
if self.zero >= other.zero { (self, other) } else { (other, self) };
// Calculate the actual zero offset
let len = self.inner.len();
@ -88,12 +85,7 @@ impl<T> Storage<T> {
// Initialize visible lines, the scrollback buffer is initialized dynamically
let inner = vec![template; lines.0];
Storage {
inner,
zero: 0,
visible_lines: lines - 1,
len: lines.0,
}
Storage { inner, zero: 0, visible_lines: lines - 1, len: lines.0 }
}
/// Update the size of the scrollback history
@ -179,7 +171,8 @@ impl<T> Storage<T> {
/// Dynamically grow the storage buffer at runtime
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];
@ -297,7 +290,7 @@ impl<T> Storage<T> {
#[inline]
pub fn shrink_hidden(&mut self, cols: Column)
where
T: GridCell + Copy
T: GridCell + Copy,
{
let start = self.zero + self.len;
let end = self.zero + self.inner.len();
@ -317,7 +310,7 @@ impl<T> Storage<T> {
#[inline]
pub fn grow_hidden(&mut self, cols: Column, template: &T)
where
T: Copy + Clone
T: Copy + Clone,
{
let start = self.zero + self.len;
let end = self.zero + self.inner.len();
@ -333,6 +326,7 @@ impl<T> Storage<T> {
impl<T> Index<usize> for Storage<T> {
type Output = Row<T>;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
&self.inner[self.compute_index(index)]
@ -349,6 +343,7 @@ impl<T> IndexMut<usize> for Storage<T> {
impl<T> Index<Line> for Storage<T> {
type Output = Row<T>;
#[inline]
fn index(&self, index: Line) -> &Self::Output {
let index = self.visible_lines - index;
@ -379,7 +374,11 @@ impl<T> IndexMut<Line> for Storage<T> {
fn grow_after_zero() {
// Setup storage area
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,
visible_lines: Line(2),
len: 3,
@ -390,7 +389,12 @@ fn grow_after_zero() {
// Make sure the result is correct
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,
visible_lines: Line(0),
len: 4,
@ -415,7 +419,11 @@ fn grow_after_zero() {
fn grow_before_zero() {
// Setup storage area
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,
visible_lines: Line(2),
len: 3,
@ -426,7 +434,12 @@ fn grow_before_zero() {
// Make sure the result is correct
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,
visible_lines: Line(0),
len: 4,
@ -450,7 +463,11 @@ fn grow_before_zero() {
fn shrink_before_zero() {
// Setup storage area
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,
visible_lines: Line(2),
len: 3,
@ -461,7 +478,11 @@ fn shrink_before_zero() {
// Make sure the result is correct
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,
visible_lines: Line(0),
len: 2,
@ -485,7 +506,11 @@ fn shrink_before_zero() {
fn shrink_after_zero() {
// Setup storage area
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,
visible_lines: Line(2),
len: 3,
@ -496,7 +521,11 @@ fn shrink_after_zero() {
// Make sure the result is correct
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,
visible_lines: Line(0),
len: 2,
@ -526,7 +555,14 @@ fn shrink_after_zero() {
fn shrink_before_and_after_zero() {
// Setup storage area
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,
visible_lines: Line(5),
len: 6,
@ -537,7 +573,14 @@ fn shrink_before_and_after_zero() {
// Make sure the result is correct
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,
visible_lines: Line(0),
len: 2,
@ -563,7 +606,14 @@ fn shrink_before_and_after_zero() {
fn truncate_invisible_lines() {
// Setup storage area
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,
visible_lines: Line(1),
len: 2,
@ -598,7 +648,11 @@ fn truncate_invisible_lines() {
fn truncate_invisible_lines_beginning() {
// Setup storage area
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,
visible_lines: Line(1),
len: 2,

View File

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

View File

@ -17,18 +17,18 @@
/// Indexing types and implementations for Grid and Line
use std::cmp::{Ord, Ordering};
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
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Side {
Left,
Right
Right,
}
/// Index in the grid using row, column notation
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Serialize, Deserialize, PartialOrd)]
pub struct Point<L=Line> {
pub struct Point<L = Line> {
pub line: L,
pub col: Column,
}
@ -43,11 +43,10 @@ impl Ord for Point {
fn cmp(&self, other: &Point) -> Ordering {
use std::cmp::Ordering::*;
match (self.line.cmp(&other.line), self.col.cmp(&other.col)) {
(Equal, Equal) => Equal,
(Equal, ord) |
(ord, Equal) => ord,
(Less, _) => Less,
(Greater, _) => Greater,
(Equal, Equal) => Equal,
(Equal, ord) | (ord, Equal) => ord,
(Less, _) => Less,
(Greater, _) => Greater,
}
}
}
@ -156,7 +155,7 @@ macro_rules! forward_ref_binop {
$imp::$method(*self, *other)
}
}
}
};
}
/// Macro for deriving deref
@ -170,7 +169,7 @@ macro_rules! deref {
&self.0
}
}
}
};
}
macro_rules! add {
@ -183,7 +182,7 @@ macro_rules! add {
$construct(self.0 + rhs.0)
}
}
}
};
}
macro_rules! sub {
@ -223,7 +222,7 @@ macro_rules! sub {
$construct(self.0 - rhs.0)
}
}
}
};
}
/// 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> {
type Content = T;
fn contains_(&self, item: Self::Content) -> bool {
(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> {
type Content = T;
fn contains_(&self, item: Self::Content) -> bool {
(self.start() <= &item) && (&item <= self.end())
}
@ -383,7 +384,7 @@ ops!(Linear, Linear);
#[cfg(test)]
mod tests {
use super::{Line, Column, Point};
use super::{Column, Line, Point};
#[test]
fn location_ordering() {

View File

@ -20,27 +20,27 @@
//! determine what to do when a non-modifier key is pressed.
use std::borrow::Cow;
use std::mem;
use std::time::Instant;
use std::ops::RangeInclusive;
use std::time::Instant;
use copypasta::{Clipboard, Load, Buffer as ClipboardBuffer};
use unicode_width::UnicodeWidthStr;
use copypasta::{Buffer as ClipboardBuffer, Clipboard, Load};
use glutin::{
ElementState, KeyboardInput, ModifiersState, MouseButton, MouseCursor, MouseScrollDelta,
TouchPhase,
};
use unicode_width::UnicodeWidthStr;
use crate::ansi::{ClearMode, Handler};
use crate::config::{self, Key};
use crate::grid::Scroll;
use crate::event::{ClickState, Mouse};
use crate::index::{Line, Column, Side, Point, Linear};
use crate::term::{Term, SizeInfo, Search};
use crate::grid::Scroll;
use crate::index::{Column, Line, Linear, Point, Side};
use crate::message_bar::{self, Message};
use crate::term::mode::TermMode;
use crate::term::{Search, SizeInfo, Term};
use crate::url::Url;
use crate::util::fmt::Red;
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;
@ -148,10 +148,10 @@ impl<T: Eq> Binding<T> {
// 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
// checks to be short circuited.
self.trigger == *input &&
self.mode_matches(mode) &&
self.not_mode_matches(mode) &&
self.mods_match(mods, relaxed)
self.trigger == *input
&& self.mode_matches(mode)
&& self.not_mode_matches(mode)
&& self.mods_match(mods, relaxed)
}
#[inline]
@ -267,8 +267,8 @@ impl Action {
},
Action::Paste => {
Clipboard::new()
.and_then(|clipboard| clipboard.load_primary() )
.map(|contents| { self.paste(ctx, &contents) })
.and_then(|clipboard| clipboard.load_primary())
.map(|contents| self.paste(ctx, &contents))
.unwrap_or_else(|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
if !mouse_mode {
Clipboard::new()
.and_then(|clipboard| clipboard.load_selection() )
.map(|contents| { self.paste(ctx, &contents) })
.and_then(|clipboard| clipboard.load_selection())
.map(|contents| self.paste(ctx, &contents))
.unwrap_or_else(|err| {
error!("Error loading data from clipboard: {}", Red(err));
});
@ -303,13 +303,13 @@ impl Action {
ctx.terminal_mut().exit();
},
Action::IncreaseFontSize => {
ctx.terminal_mut().change_font_size(FONT_SIZE_STEP);
ctx.terminal_mut().change_font_size(FONT_SIZE_STEP);
},
Action::DecreaseFontSize => {
ctx.terminal_mut().change_font_size(-FONT_SIZE_STEP);
}
ctx.terminal_mut().change_font_size(-FONT_SIZE_STEP);
},
Action::ResetFontSize => {
ctx.terminal_mut().reset_font_size();
ctx.terminal_mut().reset_font_size();
},
Action::ScrollPageUp => {
ctx.scroll(Scroll::PageUp);
@ -339,7 +339,7 @@ impl Action {
fn paste<A: ActionContext>(&self, ctx: &mut A, contents: &str) {
if ctx.terminal().mode().contains(TermMode::BRACKETED_PASTE) {
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~"[..]);
} else {
// 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
// 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).
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
&& (modifiers.shift || !self.ctx.terminal().mode().intersects(report_mode))
{
self.ctx.update_selection(
Point {
line: point.line,
col: point.col,
},
cell_side,
);
self.ctx.update_selection(Point { line: point.line, col: point.col }, cell_side);
} else if self.ctx.terminal().mode().intersects(motion_mode)
&& 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.mouse_config.url.launcher.is_some()
{
self.ctx.terminal().url_search(point.into())
self.ctx.terminal().url_search(point.into())
} else {
None
};
@ -582,14 +576,14 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
self.ctx.mouse_mut().block_url_launcher = true;
self.on_mouse_double_click(button, point);
ClickState::DoubleClick
},
}
ClickState::DoubleClick
if !button_changed && elapsed < self.mouse_config.triple_click.threshold =>
{
self.ctx.mouse_mut().block_url_launcher = true;
self.on_mouse_triple_click(button, point);
ClickState::TripleClick
},
}
_ => {
// Don't launch URLs if this click cleared the selection
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
}
},
};
}
@ -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) {
if self.ctx.terminal().mode().contains(TermMode::FOCUS_IN_OUT) {
let chr = if is_focused {
"I"
} else {
"O"
};
let chr = if is_focused { "I" } else { "O" };
let msg = format!("\x1b[{}", chr);
self.ctx.write_to_pty(msg.into_bytes());
@ -759,7 +749,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
&mut self,
state: ElementState,
button: MouseButton,
modifiers: ModifiersState
modifiers: ModifiersState,
) {
match button {
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),
false,
),
_ => if let Some(key) = input.virtual_keycode {
let key = Key::from_glutin_input(key);
binding.is_triggered_by(
*self.ctx.terminal().mode(),
input.modifiers,
&key,
_ => {
if let Some(key) = input.virtual_keycode {
let key = Key::from_glutin_input(key);
binding.is_triggered_by(
*self.ctx.terminal().mode(),
input.modifiers,
&key,
false,
)
} else {
false
)
} else {
false
}
},
};
@ -883,11 +875,12 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
for binding in self.mouse_bindings {
if binding.is_triggered_by(*self.ctx.terminal().mode(), mods, &button, true) {
// binding was triggered; run the action
let mouse_mode = !mods.shift && self.ctx.terminal().mode().intersects(
TermMode::MOUSE_REPORT_CLICK
| TermMode::MOUSE_DRAG
| TermMode::MOUSE_MOTION
);
let mouse_mode = !mods.shift
&& self.ctx.terminal().mode().intersects(
TermMode::MOUSE_REPORT_CLICK
| TermMode::MOUSE_DRAG
| TermMode::MOUSE_MOTION,
);
binding.execute(&mut self.ctx, mouse_mode);
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
fn message_at_point(&mut self, point: Option<Point>) -> Option<Message> {
if let (Some(point), Some(message)) = (
point,
self.ctx.terminal_mut().message_buffer_mut().message(),
) {
if let (Some(point), Some(message)) =
(point, self.ctx.terminal_mut().message_buffer_mut().message())
{
let size = self.ctx.size_info();
if point.line.0 >= size.lines().saturating_sub(message.text(&size).len()) {
return Some(message);
@ -924,7 +916,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
}
self.ctx.clear_selection();
}
},
}
}
@ -942,15 +934,15 @@ mod tests {
use std::borrow::Cow;
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::event::{Mouse, ClickState, WindowChanges};
use crate::config::{self, Config, ClickHandler};
use crate::index::{Point, Side};
use crate::selection::Selection;
use crate::config::{self, ClickHandler, Config};
use crate::event::{ClickState, Mouse, WindowChanges};
use crate::grid::Scroll;
use crate::index::{Point, Side};
use crate::message_bar::MessageBuffer;
use crate::selection::Selection;
use crate::term::{SizeInfo, Term, TermMode};
use super::{Action, Binding, Processor};
use copypasta::Buffer as ClipboardBuffer;
@ -976,13 +968,19 @@ mod tests {
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 update_selection(&mut self, _point: Point, _side: Side) {}
fn simple_selection(&mut self, _point: Point, _side: Side) {}
fn copy_selection(&self, _buffer: ClipboardBuffer) {}
fn clear_selection(&mut self) {}
fn hide_window(&mut self) {}
fn spawn_new_instance(&mut self) {}
fn terminal(&self) -> &Term {

View File

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

View File

@ -13,13 +13,13 @@
// limitations under the License.
#![allow(clippy::let_unit_value)]
#![cfg(target_os = "macos")]
use libc::{LC_CTYPE, setlocale};
use std::ffi::{CString, CStr};
use libc::{setlocale, LC_CTYPE};
use std::env;
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use std::ptr::null;
use std::slice;
use std::str;
use std::env;
use objc::runtime::{Class, Object};
@ -27,30 +27,32 @@ pub fn set_locale_environment() {
let locale_id = unsafe {
let locale_class = Class::get("NSLocale").unwrap();
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
// 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+):
// 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/1643026-languagecode?language=objc
let is_language_code_supported: bool = msg_send![locale, respondsToSelector:sel!(languageCode)];
let is_country_code_supported: bool = msg_send![locale, respondsToSelector:sel!(countryCode)];
let is_language_code_supported: bool =
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 language_code: *const Object = msg_send![locale, languageCode];
let country_code: *const Object = msg_send![locale, countryCode];
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 _ : () = msg_send![country_code, release];
let _: () = msg_send![country_code, release];
format!("{}_{}.UTF-8", &language_code_str, &country_code_str)
} else {
let identifier: *const Object = msg_send![locale, localeIdentifier];
let identifier_str = nsstring_as_str(identifier).to_owned();
let _ : () = msg_send![identifier, release];
let _: () = msg_send![identifier, release];
identifier_str + ".UTF-8"
};
let _ : () = msg_send![locale, release];
let _: () = msg_send![locale, release];
locale_id
};
// check if locale_id is valid
@ -66,11 +68,7 @@ pub fn set_locale_environment() {
};
// try setting `locale_id`
let modified = setlocale(LC_CTYPE, locale_ptr);
let result = if modified.is_null() {
String::new()
} else {
locale_id
};
let result = if modified.is_null() { String::new() } else { locale_id };
// restore original setting
setlocale(LC_CTYPE, saved_original.as_ptr());
result

View File

@ -68,12 +68,7 @@ impl Logger {
let logfile = Mutex::new(OnDemandLogFile::new());
let stdout = Mutex::new(LineWriter::new(io::stdout()));
Logger {
level,
logfile,
stdout,
message_tx,
}
Logger { level, logfile, stdout, message_tx }
}
fn file_path(&self) -> Option<PathBuf> {
@ -100,10 +95,7 @@ impl log::Log for Logger {
now,
record.level(),
record.file().unwrap_or("?"),
record
.line()
.map(|l| l.to_string())
.unwrap_or_else(|| "?".into()),
record.line().map(|l| l.to_string()).unwrap_or_else(|| "?".into()),
record.args()
)
} else {
@ -161,11 +153,7 @@ impl OnDemandLogFile {
// Set log path as an environment variable
env::set_var(ALACRITTY_LOG_ENV, path.as_os_str());
OnDemandLogFile {
path,
file: None,
created: Arc::new(AtomicBool::new(false)),
}
OnDemandLogFile { path, file: None, created: Arc::new(AtomicBool::new(false)) }
}
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
if self.file.is_none() {
let file = OpenOptions::new()
.append(true)
.create(true)
.open(&self.path);
let file = OpenOptions::new().append(true).create(true).open(&self.path);
match file {
Ok(file) => {
self.file = Some(io::LineWriter::new(file));
self.created.store(true, Ordering::Relaxed);
let _ = writeln!(io::stdout(), "Created log file at {:?}", self.path);
}
},
Err(e) => {
let _ = writeln!(io::stdout(), "Unable to create log file: {}", 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)]
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
#![cfg_attr(all(test, feature = "bench"), feature(test))]
// With the default subsystem, 'console', windows creates an additional console
// window for the program.
// This is silently ignored on non-windows systems.
@ -29,12 +28,12 @@ use dirs;
#[cfg(windows)]
use winapi::um::wincon::{AttachConsole, FreeConsole, ATTACH_PARENT_PROCESS};
use log::{info, error};
use log::{error, info};
use std::error::Error;
use std::sync::Arc;
use std::io::{self, Write};
use std::fs;
use std::io::{self, Write};
use std::sync::Arc;
#[cfg(target_os = "macos")]
use std::env;
@ -42,19 +41,19 @@ use std::env;
#[cfg(not(windows))]
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::display::Display;
use alacritty::event_loop::{self, EventLoop, Msg};
#[cfg(target_os = "macos")]
use alacritty::locale;
use alacritty::logging;
use alacritty::message_bar::MessageBuffer;
use alacritty::panic;
use alacritty::sync::FairMutex;
use alacritty::term::Term;
use alacritty::tty;
use alacritty::util::fmt::Red;
use alacritty::message_bar::MessageBuffer;
use alacritty::{cli, die, event};
fn main() {
panic::attach_handler();
@ -63,7 +62,9 @@ fn main() {
// to the console of the parent process, so we do it explicitly. This fails
// silently if the parent has no console.
#[cfg(windows)]
unsafe { AttachConsole(ATTACH_PARENT_PROCESS); }
unsafe {
AttachConsole(ATTACH_PARENT_PROCESS);
}
// Load command line options
let options = cli::Options::load();
@ -77,7 +78,8 @@ fn main() {
// Load configuration 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::write_defaults().ok())
.map(|path| path.to_path_buf());
@ -133,11 +135,7 @@ fn run(
// The display manages a window and can draw the terminal
let mut display = Display::new(&config, options)?;
info!(
"PTY Dimensions: {:?} x {:?}",
display.size().lines(),
display.size().cols()
);
info!("PTY Dimensions: {:?} x {:?}", display.size().lines(), display.size().cols());
// Create the terminal
//
@ -173,12 +171,8 @@ fn run(
// renderer and input processing. Note that access to the terminal state is
// synchronized since the I/O loop updates the state, and the display
// consumes it periodically.
let event_loop = EventLoop::new(
Arc::clone(&terminal),
display.notifier(),
pty,
options.ref_test,
);
let event_loop =
EventLoop::new(Arc::clone(&terminal), display.notifier(), pty, options.ref_test);
// The event loop channel allows write requests from the event processor
// to be sent to the loop and ultimately written to the pty.
@ -259,16 +253,16 @@ fn run(
}
}
loop_tx
.send(Msg::Shutdown)
.expect("Error sending shutdown to event loop");
loop_tx.send(Msg::Shutdown).expect("Error sending shutdown to event loop");
// FIXME patch notify library to have a shutdown method
// config_reloader.join().ok();
// Without explicitly detaching the console cmd won't redraw it's prompt
#[cfg(windows)]
unsafe { FreeConsole(); }
unsafe {
FreeConsole();
}
info!("Goodbye");

View File

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

View File

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

View File

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

View File

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

View File

@ -18,10 +18,10 @@
//! finalized when the button is released. The selection should be cleared
//! when text is added/removed/scrolled on the screen. The selection should
//! 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 crate::index::{Point, Column, Side};
use crate::index::{Column, Point, Side};
use crate::term::Search;
/// 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
/// of which way the cursor is moved.
initial_line: isize
}
initial_line: isize,
},
}
/// A Point and side within that point.
@ -83,8 +83,8 @@ impl Selection {
Selection::Simple {
region: Range {
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.end.line += offset;
*initial_line += offset;
}
},
}
}
pub fn semantic(point: Point<usize>) -> Selection {
Selection::Semantic {
region: Range {
start: point.into(),
end: point.into(),
}
}
Selection::Semantic { region: Range { start: point.into(), end: point.into() } }
}
pub fn lines(point: Point<usize>) -> Selection {
Selection::Lines {
region: Range {
start: point.into(),
end: point.into(),
},
region: Range { start: point.into(), end: point.into() },
initial_line: point.line as isize,
}
}
@ -131,9 +123,7 @@ impl Selection {
Selection::Simple { ref mut region } => {
region.end = Anchor::new(location.into(), side);
},
Selection::Semantic { ref mut region } |
Selection::Lines { ref mut region, .. } =>
{
Selection::Semantic { ref mut region } | Selection::Lines { ref mut region, .. } => {
region.end = location.into();
},
}
@ -144,36 +134,28 @@ impl Selection {
G: Search + Dimensions,
{
match *self {
Selection::Simple { ref region } => {
Selection::span_simple(grid, region, alt_screen)
},
Selection::Simple { ref region } => Selection::span_simple(grid, region, alt_screen),
Selection::Semantic { ref region } => {
Selection::span_semantic(grid, region, alt_screen)
},
Selection::Lines { ref region, initial_line } => {
Selection::span_lines(grid, region, initial_line, alt_screen)
}
},
}
}
pub fn is_empty(&self) -> bool
{
pub fn is_empty(&self) -> bool {
match *self {
Selection::Simple { ref region } => {
region.start == region.end && region.start.side == region.end.side
},
Selection::Semantic { .. } | Selection::Lines { .. } => {
false
},
Selection::Semantic { .. } | Selection::Lines { .. } => false,
}
}
fn span_semantic<G>(
grid: &G,
region: &Range<Point<isize>>,
alt_screen: bool,
) -> Option<Span>
where G: Search + Dimensions
fn span_semantic<G>(grid: &G, region: &Range<Point<isize>>, alt_screen: bool) -> Option<Span>
where
G: Search + Dimensions,
{
let cols = grid.dimensions().col;
let lines = grid.dimensions().line.0 as isize;
@ -199,12 +181,7 @@ impl Selection {
::std::mem::swap(&mut start, &mut end);
}
Some(Span {
cols,
front: start,
tail: end,
ty: SpanType::Inclusive,
})
Some(Span { cols, front: start, tail: end, ty: SpanType::Inclusive })
}
fn span_lines<G>(
@ -214,21 +191,15 @@ impl Selection {
alt_screen: bool,
) -> Option<Span>
where
G: Dimensions
G: Dimensions,
{
let cols = grid.dimensions().col;
let lines = grid.dimensions().line.0 as isize;
// First, create start and end points based on initial line and the grid
// dimensions.
let mut start = Point {
col: cols - 1,
line: initial_line
};
let mut end = Point {
col: Column(0),
line: initial_line
};
let mut start = Point { col: cols - 1, line: initial_line };
let mut end = Point { col: Column(0), line: initial_line };
// Now, expand lines based on where cursor started and ended.
if region.start.line < region.end.line {
@ -245,17 +216,12 @@ impl Selection {
Selection::alt_screen_clamp(&mut start, &mut end, lines, cols)?;
}
Some(Span {
cols,
front: start.into(),
tail: end.into(),
ty: SpanType::Inclusive
})
Some(Span { cols, front: start.into(), tail: end.into(), ty: SpanType::Inclusive })
}
fn span_simple<G>(grid: &G, region: &Range<Anchor>, alt_screen: bool) -> Option<Span>
where
G: Dimensions
G: Dimensions,
{
let start = region.start.point;
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
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)
{
return None;
@ -303,12 +271,7 @@ impl Selection {
}
// Return the selection with all cells inclusive
Some(Span {
cols,
front: front.into(),
tail: tail.into(),
ty: SpanType::Inclusive,
})
Some(Span { cols, front: front.into(), tail: tail.into(), ty: SpanType::Inclusive })
}
// 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))
},
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 }
@ -395,10 +358,7 @@ impl Span {
fn wrap_start(mut start: Point<usize>, cols: Column) -> Point<usize> {
if start.col == cols - 1 {
Point {
line: start.line + 1,
col: Column(0),
}
Point { line: start.line + 1, col: Column(0) }
} else {
start.col += 1;
start
@ -407,15 +367,9 @@ impl Span {
fn wrap_end(end: Point<usize>, cols: Column) -> Point<usize> {
if end.col == Column(0) && end.line != 0 {
Point {
line: end.line - 1,
col: cols
}
Point { line: end.line - 1, col: cols }
} else {
Point {
line: end.line,
col: end.col - 1
}
Point { line: end.line, col: end.col - 1 }
}
}
}
@ -431,8 +385,8 @@ impl Span {
/// look like [ B] and [E ].
#[cfg(test)]
mod test {
use crate::index::{Line, Column, Side, Point};
use super::{Selection, Span, SpanType};
use crate::index::{Column, Line, Point, Side};
use crate::url::Url;
struct Dimensions(Point);
@ -444,17 +398,22 @@ mod test {
impl Dimensions {
pub fn new(line: usize, col: usize) -> Self {
Dimensions(Point {
line: Line(line),
col: Column(col)
})
Dimensions(Point { line: Line(line), col: Column(col) })
}
}
impl super::Search for Dimensions {
fn semantic_search_left(&self, point: Point<usize>) -> Point<usize> { point }
fn semantic_search_right(&self, point: Point<usize>) -> Point<usize> { point }
fn url_search(&self, _: Point<usize>) -> Option<Url> { None }
fn semantic_search_left(&self, point: Point<usize>) -> Point<usize> {
point
}
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

View File

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

View File

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

View File

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

View File

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

View File

@ -57,7 +57,7 @@ pub trait EventedReadWrite {
#[derive(PartialEq)]
pub enum ChildEvent {
/// Indicates the child has exited
Exited
Exited,
}
/// 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
/// 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`).
pub trait EventedPty : EventedReadWrite {
pub trait EventedPty: EventedReadWrite {
#[cfg(unix)]
fn child_event_token(&self) -> mio::Token;
@ -83,11 +83,7 @@ pub fn setup_env(config: &Config) {
// below.
env::set_var(
"TERM",
if Database::from_name("alacritty").is_ok() {
"alacritty"
} else {
"xterm-256color"
},
if Database::from_name("alacritty").is_ok() { "alacritty" } else { "xterm-256color" },
);
// Advertise 24-bit color support

View File

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

View File

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

View File

@ -28,7 +28,7 @@ use crate::cli::Options;
use crate::config::Config;
use crate::display::OnResize;
use crate::term::SizeInfo;
use crate::tty::{EventedReadWrite, EventedPty};
use crate::tty::{EventedPty, EventedReadWrite};
mod conpty;
mod winpty;
@ -44,14 +44,14 @@ pub fn process_should_exit() -> bool {
WAIT_OBJECT_0 => {
info!("wait_object_0");
true
}
},
// Reached timeout of 0, process has not exited
WAIT_TIMEOUT => false,
// Error checking process, winpty gave us a bad agent handle?
_ => {
info!("Bad exit: {}", ::std::io::Error::last_os_error());
true
}
},
}
}
}
@ -219,7 +219,7 @@ impl<'a> OnResize for PtyHandle<'a> {
PtyHandle::Conpty(c) => {
let mut handle = c.clone();
handle.on_resize(sizeinfo)
}
},
}
}
}
@ -240,34 +240,14 @@ impl<'a> EventedReadWrite for Pty<'a> {
self.write_token = token.next().unwrap();
if interest.is_readable() {
poll.register(
&self.conout,
self.read_token,
mio::Ready::readable(),
poll_opts,
)?
poll.register(&self.conout, self.read_token, mio::Ready::readable(), poll_opts)?
} else {
poll.register(
&self.conout,
self.read_token,
mio::Ready::empty(),
poll_opts,
)?
poll.register(&self.conout, self.read_token, mio::Ready::empty(), poll_opts)?
}
if interest.is_writable() {
poll.register(
&self.conin,
self.write_token,
mio::Ready::writable(),
poll_opts,
)?
poll.register(&self.conin, self.write_token, mio::Ready::writable(), poll_opts)?
} else {
poll.register(
&self.conin,
self.write_token,
mio::Ready::empty(),
poll_opts,
)?
poll.register(&self.conin, self.write_token, mio::Ready::empty(), poll_opts)?
}
Ok(())
}
@ -280,34 +260,14 @@ impl<'a> EventedReadWrite for Pty<'a> {
poll_opts: mio::PollOpt,
) -> io::Result<()> {
if interest.is_readable() {
poll.reregister(
&self.conout,
self.read_token,
mio::Ready::readable(),
poll_opts,
)?;
poll.reregister(&self.conout, self.read_token, mio::Ready::readable(), poll_opts)?;
} else {
poll.reregister(
&self.conout,
self.read_token,
mio::Ready::empty(),
poll_opts,
)?;
poll.reregister(&self.conout, self.read_token, mio::Ready::empty(), poll_opts)?;
}
if interest.is_writable() {
poll.reregister(
&self.conin,
self.write_token,
mio::Ready::writable(),
poll_opts,
)?;
poll.reregister(&self.conin, self.write_token, mio::Ready::writable(), poll_opts)?;
} else {
poll.reregister(
&self.conin,
self.write_token,
mio::Ready::empty(),
poll_opts,
)?;
poll.reregister(&self.conin, self.write_token, mio::Ready::empty(), poll_opts)?;
}
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 std::io;
use std::fs::OpenOptions;
use std::os::windows::io::{FromRawHandle, IntoRawHandle};
use std::io;
use std::os::windows::fs::OpenOptionsExt;
use std::os::windows::io::{FromRawHandle, IntoRawHandle};
use std::sync::Arc;
use std::u16;
use dunce::canonicalize;
use mio_named_pipes::NamedPipe;
use winapi::um::winbase::FILE_FLAG_OVERLAPPED;
use winpty::{ConfigFlags, MouseMode, SpawnConfig, SpawnFlags, Winpty};
use winpty::Config as WinptyConfig;
use winpty::{ConfigFlags, MouseMode, SpawnConfig, SpawnFlags, Winpty};
use crate::cli::Options;
use crate::config::{Config, Shell};
use crate::display::OnResize;
use crate::cli::Options;
use crate::term::SizeInfo;
// We store a raw pointer because we need mutable access to call
// on_resize from a separate thread. Winpty internally uses a mutex
// so this is safe, despite outwards appearance.
pub struct Agent<'a> {
winpty: *mut Winpty<'a>
winpty: *mut Winpty<'a>,
}
/// Handle can be cloned freely and moved between threads.
@ -48,9 +48,7 @@ unsafe impl<'a> Sync for Agent<'a> {}
impl<'a> Agent<'a> {
pub fn new(winpty: Winpty<'a>) -> Self {
Self {
winpty: Box::into_raw(Box::new(winpty))
}
Self { winpty: Box::into_raw(Box::new(winpty)) }
}
/// Get immutable access to Winpty.
@ -68,11 +66,12 @@ impl<'a> Agent<'a> {
impl<'a> Drop for Agent<'a> {
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
/// This is a placeholder value until we see how often long responses happen
const AGENT_TIMEOUT: u32 = 10000;
@ -112,30 +111,19 @@ pub fn new<'a>(
Some(&cmdline.join(" ")),
cwd,
None, // Env
).unwrap();
)
.unwrap();
let default_opts = &mut OpenOptions::new();
default_opts
.share_mode(0)
.custom_flags(FILE_FLAG_OVERLAPPED);
default_opts.share_mode(0).custom_flags(FILE_FLAG_OVERLAPPED);
let (conout_pipe, conin_pipe);
unsafe {
conout_pipe = NamedPipe::from_raw_handle(
default_opts
.clone()
.read(true)
.open(conout)
.unwrap()
.into_raw_handle(),
default_opts.clone().read(true).open(conout).unwrap().into_raw_handle(),
);
conin_pipe = NamedPipe::from_raw_handle(
default_opts
.clone()
.write(true)
.open(conin)
.unwrap()
.into_raw_handle(),
default_opts.clone().write(true).open(conin).unwrap().into_raw_handle(),
);
};
@ -166,7 +154,7 @@ pub fn new<'a>(
conout: super::EventedReadablePipe::Named(conout_pipe),
conin: super::EventedWritablePipe::Named(conin_pipe),
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
// limitations under the License.
use url;
use unicode_width::UnicodeWidthChar;
use url;
use crate::term::cell::{Cell, Flags};
// See https://tools.ietf.org/html/rfc3987#page-13
const URL_SEPARATOR_CHARS: [char; 10] = ['<', '>', '"', ' ', '{', '}', '|', '\\', '^', '`'];
const URL_DENY_END_CHARS: [char; 8] = ['.', ',', ';', ':', '?', '!', '/', '('];
const URL_SCHEMES: [&str; 8] = [
"http", "https", "mailto", "news", "file", "git", "ssh", "ftp",
];
const URL_SCHEMES: [&str; 8] = ["http", "https", "mailto", "news", "file", "git", "ssh", "ftp"];
/// URL text and origin of the original click position.
#[derive(Debug, PartialEq)]
@ -39,10 +37,7 @@ pub struct UrlParser {
impl UrlParser {
pub fn new() -> Self {
UrlParser {
state: String::new(),
origin: 0,
}
UrlParser { state: String::new(), origin: 0 }
}
/// Advance the parser one character to the left.
@ -74,19 +69,17 @@ impl UrlParser {
// Remove non-alphabetical characters before the scheme
// https://tools.ietf.org/html/rfc3986#section-3.1
if let Some(index) = self.state.find("://") {
let iter = self
.state
.char_indices()
.rev()
.skip_while(|(byte_index, _)| *byte_index >= index);
let iter =
self.state.char_indices().rev().skip_while(|(byte_index, _)| *byte_index >= index);
for (byte_index, c) in iter {
match c {
'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());
break;
}
},
}
}
}
@ -103,7 +96,7 @@ impl UrlParser {
')' | ']' => {
self.state.truncate(i);
break;
}
},
_ => (),
}
}
@ -127,14 +120,11 @@ impl UrlParser {
match url::Url::parse(&self.state) {
Ok(url) => {
if URL_SCHEMES.contains(&url.scheme()) && self.origin > 0 {
Some(Url {
origin: self.origin - 1,
text: self.state,
})
Some(Url { origin: self.origin - 1, text: self.state })
} else {
None
}
}
},
Err(_) => None,
}
}
@ -160,9 +150,9 @@ mod tests {
use crate::grid::Grid;
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::term::cell::{Cell, Flags};
use crate::term::{Search, SizeInfo, Term};
fn url_create_term(input: &str) -> Term {
let size = SizeInfo {

View File

@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::{cmp, io};
use std::ffi::OsStr;
use std::process::Command;
use std::process::Stdio;
use std::{cmp, io};
#[cfg(not(windows))]
use std::os::unix::process::CommandExt;
@ -35,10 +35,7 @@ pub mod thread {
T: Send + 'static,
S: Into<String>,
{
::std::thread::Builder::new()
.name(name.into())
.spawn(f)
.expect("thread spawn works")
::std::thread::Builder::new().name(name.into()).spawn(f).expect("thread spawn works")
}
pub use std::thread::*;
@ -87,9 +84,9 @@ pub mod fmt {
#[cfg(not(windows))]
pub fn start_daemon<I, S>(program: &str, args: I) -> io::Result<()>
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
Command::new(program)
.args(args)
@ -109,9 +106,9 @@ pub fn start_daemon<I, S>(program: &str, args: I) -> io::Result<()>
#[cfg(windows)]
pub fn start_daemon<I, S>(program: &str, args: I) -> io::Result<()>
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
// Setting all the I/O handles to null and setting the
// 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 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};
#[cfg(not(any(target_os = "macos", windows)))]
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::config::{Decorations, WindowConfig};
@ -160,12 +159,7 @@ impl Window {
// 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 _);
let window = Window {
event_loop,
window,
mouse_visible: true,
is_focused: false,
};
let window = Window { event_loop, window, mouse_visible: true, is_focused: false };
window.run_os_extensions();
@ -177,9 +171,7 @@ impl Window {
/// Some window properties are provided since subsystems like font
/// rasterization depend on DPI and scale factor.
pub fn device_properties(&self) -> DeviceProperties {
DeviceProperties {
scale_factor: self.window.get_hidpi_factor(),
}
DeviceProperties { scale_factor: self.window.get_hidpi_factor() }
}
pub fn inner_size_pixels(&self) -> Option<LogicalSize> {
@ -204,9 +196,7 @@ impl Window {
#[inline]
pub fn create_window_proxy(&self) -> Proxy {
Proxy {
inner: self.event_loop.create_proxy(),
}
Proxy { inner: self.event_loop.create_proxy() }
}
#[inline]
@ -260,7 +250,7 @@ impl Window {
pub fn get_platform_window(
title: &str,
class: &str,
window_config: &WindowConfig
window_config: &WindowConfig,
) -> WindowBuilder {
use glutin::os::unix::WindowBuilderExt;
@ -285,7 +275,7 @@ impl Window {
pub fn get_platform_window(
title: &str,
_class: &str,
window_config: &WindowConfig
window_config: &WindowConfig,
) -> WindowBuilder {
let icon = Icon::from_bytes_with_format(WINDOW_ICON, ImageFormat::ICO).unwrap();
@ -307,7 +297,7 @@ impl Window {
pub fn get_platform_window(
title: &str,
_class: &str,
window_config: &WindowConfig
window_config: &WindowConfig,
) -> WindowBuilder {
use glutin::os::macos::WindowBuilderExt;
@ -328,19 +318,16 @@ impl Window {
.with_titlebar_buttons_hidden(true)
.with_titlebar_transparent(true)
.with_fullsize_content_view(true),
Decorations::None => window
.with_titlebar_hidden(true),
Decorations::None => window.with_titlebar_hidden(true),
}
}
#[cfg(
any(
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd"
)
)]
#[cfg(any(
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd"
))]
pub fn set_urgent(&self, is_urgent: bool) {
use glutin::os::unix::WindowExt;
self.window.set_urgent(is_urgent);
@ -384,21 +371,20 @@ pub trait OsExtensions {
fn run_os_extensions(&self) {}
}
#[cfg(
not(
any(
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd"
)
)
)]
#[cfg(not(any(
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd"
)))]
impl OsExtensions for Window {}
#[cfg(
any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd")
)]
#[cfg(any(
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd"
))]
impl OsExtensions for Window {
fn run_os_extensions(&self) {
use glutin::os::unix::WindowExt;

View File

@ -6,15 +6,15 @@ use std::fs::File;
use std::io::{self, Read};
use std::path::Path;
use alacritty::Grid;
use alacritty::Term;
use alacritty::ansi;
use alacritty::config::Config;
use alacritty::index::Column;
use alacritty::message_bar::MessageBuffer;
use alacritty::term::cell::Cell;
use alacritty::term::SizeInfo;
use alacritty::util::fmt::{Red, Green};
use alacritty::config::Config;
use alacritty::message_bar::MessageBuffer;
use alacritty::util::fmt::{Green, Red};
use alacritty::Grid;
use alacritty::Term;
macro_rules! ref_tests {
($($name:ident)*) => {
@ -55,17 +55,18 @@ ref_tests! {
}
fn read_u8<P>(path: P) -> Vec<u8>
where P: AsRef<Path>
where
P: AsRef<Path>,
{
let mut res = Vec::new();
File::open(path.as_ref()).unwrap()
.read_to_end(&mut res).unwrap();
File::open(path.as_ref()).unwrap().read_to_end(&mut res).unwrap();
res
}
fn read_string<P>(path: P) -> Result<String, ::std::io::Error>
where P: AsRef<Path>
where
P: AsRef<Path>,
{
let mut res = String::new();
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 original_cell = grid[i][Column(j)];
if original_cell != cell {
println!("[{i}][{j}] {original:?} => {now:?}",
i=i, j=j, original=Green(original_cell), now=Red(cell));
println!(
"[{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
if cfg!(test) && Path::new("target").exists() {
#[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))]
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::fmt::{self, Display, Formatter};
use std::path::PathBuf;
use std::result::Result;
use std::os::windows::io::RawHandle;
use std::path::PathBuf;
use std::ptr::{null, null_mut};
use std::result::Result;
use winpty_sys::*;
@ -166,10 +166,7 @@ impl<'a, 'b> Winpty<'a> {
pub fn conin_name(&mut self) -> PathBuf {
unsafe {
let raw = winpty_conin_name(self.0);
PathBuf::from(&String::from_utf16_lossy(std::slice::from_raw_parts(
raw,
wcslen(raw),
)))
PathBuf::from(&String::from_utf16_lossy(std::slice::from_raw_parts(raw, wcslen(raw))))
}
}
@ -178,10 +175,7 @@ impl<'a, 'b> Winpty<'a> {
pub fn conout_name(&mut self) -> PathBuf {
unsafe {
let raw = winpty_conout_name(self.0);
PathBuf::from(&String::from_utf16_lossy(std::slice::from_raw_parts(
raw,
wcslen(raw),
)))
PathBuf::from(&String::from_utf16_lossy(std::slice::from_raw_parts(raw, wcslen(raw))))
}
}
@ -191,10 +185,7 @@ impl<'a, 'b> Winpty<'a> {
pub fn conerr_name(&mut self) -> PathBuf {
unsafe {
let raw = winpty_conerr_name(self.0);
PathBuf::from(&String::from_utf16_lossy(std::slice::from_raw_parts(
raw,
wcslen(raw),
)))
PathBuf::from(&String::from_utf16_lossy(std::slice::from_raw_parts(raw, wcslen(raw))))
}
}
@ -227,7 +218,12 @@ impl<'a, 'b> Winpty<'a> {
let mut process_list = Vec::with_capacity(count);
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);
}
@ -246,10 +242,7 @@ impl<'a, 'b> Winpty<'a> {
/// (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 returning the error from CreateProcess
pub fn spawn(
&mut self,
cfg: &SpawnConfig,
) -> Result<(), Err> {
pub fn spawn(&mut self, cfg: &SpawnConfig) -> Result<(), Err> {
let mut err = null_mut() as *mut winpty_error_t;
unsafe {
@ -261,7 +254,9 @@ impl<'a, 'b> Winpty<'a> {
null_mut(), // Create process error
&mut err,
);
if ok == 0 { return Ok(());}
if ok == 0 {
return Ok(());
}
}
if let Some(err) = check_err(err) {
@ -354,80 +349,70 @@ mod tests {
#[test]
// Test that we can start a process in winpty
fn spawn_process() {
let mut winpty = Winpty::open(
&Config::new(ConfigFlags::empty()).expect("failed to create config")
).expect("failed to create winpty instance");
let mut winpty =
Winpty::open(&Config::new(ConfigFlags::empty()).expect("failed to create config"))
.expect("failed to create winpty instance");
winpty.spawn(
&SpawnConfig::new(
SpawnFlags::empty(),
None,
Some("cmd"),
None,
None
).expect("failed to create spawn config")
).unwrap();
winpty
.spawn(
&SpawnConfig::new(SpawnFlags::empty(), None, Some("cmd"), None, None)
.expect("failed to create spawn config"),
)
.unwrap();
}
#[test]
// Test that pipes connected before winpty is spawned can be connected to
fn valid_pipe_connect_before() {
let mut winpty = Winpty::open(
&Config::new(ConfigFlags::empty()).expect("failed to create config")
).expect("failed to create winpty instance");
let mut winpty =
Winpty::open(&Config::new(ConfigFlags::empty()).expect("failed to create config"))
.expect("failed to create winpty instance");
// 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");
winpty.spawn(
&SpawnConfig::new(
SpawnFlags::empty(),
None,
Some("cmd"),
None,
None
).expect("failed to create spawn config")
).unwrap();
winpty
.spawn(
&SpawnConfig::new(SpawnFlags::empty(), None, Some("cmd"), None, None)
.expect("failed to create spawn config"),
)
.unwrap();
}
#[test]
// Test that pipes connected after winpty is spawned can be connected to
fn valid_pipe_connect_after() {
let mut winpty = Winpty::open(
&Config::new(ConfigFlags::empty()).expect("failed to create config")
).expect("failed to create winpty instance");
let mut winpty =
Winpty::open(&Config::new(ConfigFlags::empty()).expect("failed to create config"))
.expect("failed to create winpty instance");
winpty.spawn(
&SpawnConfig::new(
SpawnFlags::empty(),
None,
Some("cmd"),
None,
None
).expect("failed to create spawn config")
).unwrap();
winpty
.spawn(
&SpawnConfig::new(SpawnFlags::empty(), None, Some("cmd"), None, None)
.expect("failed to create spawn config"),
)
.unwrap();
// 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");
}
#[test]
fn resize() {
let mut winpty = Winpty::open(
&Config::new(ConfigFlags::empty()).expect("failed to create config")
).expect("failed to create winpty instance");
let mut winpty =
Winpty::open(&Config::new(ConfigFlags::empty()).expect("failed to create config"))
.expect("failed to create winpty instance");
winpty.spawn(
&SpawnConfig::new(
SpawnFlags::empty(),
None,
Some("cmd"),
None,
None
).expect("failed to create spawn config")
).unwrap();
winpty
.spawn(
&SpawnConfig::new(SpawnFlags::empty(), None, Some("cmd"), None, None)
.expect("failed to create spawn config"),
)
.unwrap();
winpty.set_size(1, 1).unwrap();
}
@ -436,21 +421,19 @@ mod tests {
#[ignore]
// Test that each id returned by cosole_process_list points to an actual process
fn console_process_list_valid() {
let mut winpty = Winpty::open(
&Config::new(ConfigFlags::empty()).expect("failed to create config")
).expect("failed to create winpty instance");
let mut winpty =
Winpty::open(&Config::new(ConfigFlags::empty()).expect("failed to create config"))
.expect("failed to create winpty instance");
winpty.spawn(
&SpawnConfig::new(
SpawnFlags::empty(),
None,
Some("cmd"),
None,
None
).expect("failed to create spawn config")
).unwrap();
winpty
.spawn(
&SpawnConfig::new(SpawnFlags::empty(), None, Some("cmd"), None, 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
processes.iter().for_each(|id| {
@ -458,7 +441,7 @@ mod tests {
OpenProcess(
READ_CONTROL, // permissions
false as i32, // inheret
*id as u32
*id as u32,
)
};
assert!(!handle.is_null());