Add rustfmt style guide
This commit is contained in:
parent
91aa683bcd
commit
cfd025b528
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
25
build.rs
25
build.rs
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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<_>>()
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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."),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
270
src/ansi.rs
270
src/ansi.rs
|
@ -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]
|
||||
|
|
169
src/cli.rs
169
src/cli.rs
|
@ -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") {
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
|
|
57
src/event.rs
57
src/event.rs
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
159
src/grid/mod.rs
159
src/grid/mod.rs
|
@ -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
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
27
src/index.rs
27
src/index.rs
|
@ -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() {
|
||||
|
|
132
src/input.rs
132
src/input.rs
|
@ -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 {
|
||||
|
|
10
src/lib.rs
10
src/lib.rs
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
44
src/main.rs
44
src/main.rs
|
@ -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");
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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(®ular_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);
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
129
src/selection.rs
129
src/selection.rs
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
378
src/term/mod.rs
378
src/term/mod.rs
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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> {}
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
36
src/url.rs
36
src/url.rs
|
@ -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 {
|
||||
|
|
19
src/util.rs
19
src/util.rs
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
28
tests/ref.rs
28
tests/ref.rs
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in New Issue