Add rustfmt style guide
This commit is contained in:
parent
91aa683bcd
commit
cfd025b528
|
@ -35,6 +35,10 @@ matrix:
|
||||||
os: windows
|
os: windows
|
||||||
env: CLIPPY=true
|
env: CLIPPY=true
|
||||||
rust: stable
|
rust: stable
|
||||||
|
- name: "Rustfmt"
|
||||||
|
os: linux
|
||||||
|
env: RUSTFMT=true
|
||||||
|
rust: nightly
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- rust: nightly
|
- rust: nightly
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ Changes compared to the latest Alacritty release which have a direct effect on t
|
||||||
|
|
||||||
### Style
|
### Style
|
||||||
|
|
||||||
Alacritty currently does not have any automatically enforced style guidelines. As a result of that, it is not possible to run `rustfmt` on existing files. New code should however follow the default ruleset of `rustfmt` and for newly created files it is possible to run the `rustfmt` tool directly.
|
All Alacritty changes are automatically verified by CI to conform to its rustfmt guidelines. If a CI build is failing because of formatting issues, you can install rustfmt using `rustup component add rustfmt` and then format all code using `cargo fmt`.
|
||||||
|
|
||||||
# Contact
|
# Contact
|
||||||
|
|
||||||
|
|
25
build.rs
25
build.rs
|
@ -14,10 +14,10 @@
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use embed_resource;
|
use embed_resource;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use tempfile;
|
|
||||||
#[cfg(windows)]
|
|
||||||
use reqwest;
|
use reqwest;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
use tempfile;
|
||||||
|
#[cfg(windows)]
|
||||||
use zip;
|
use zip;
|
||||||
|
|
||||||
use gl_generator::{Api, Fallbacks, GlobalGenerator, Profile, Registry};
|
use gl_generator::{Api, Fallbacks, GlobalGenerator, Profile, Registry};
|
||||||
|
@ -26,25 +26,21 @@ use std::env;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
use std::io;
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
|
#[cfg(windows)]
|
||||||
|
use std::io;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
const WINPTY_PACKAGE_URL: &str = "https://github.com/rprichard/winpty/releases/download/0.4.3/winpty-0.4.3-msvc2015.zip";
|
const WINPTY_PACKAGE_URL: &str =
|
||||||
|
"https://github.com/rprichard/winpty/releases/download/0.4.3/winpty-0.4.3-msvc2015.zip";
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let dest = env::var("OUT_DIR").unwrap();
|
let dest = env::var("OUT_DIR").unwrap();
|
||||||
let mut file = File::create(&Path::new(&dest).join("gl_bindings.rs")).unwrap();
|
let mut file = File::create(&Path::new(&dest).join("gl_bindings.rs")).unwrap();
|
||||||
|
|
||||||
Registry::new(
|
Registry::new(Api::Gl, (4, 5), Profile::Core, Fallbacks::All, ["GL_ARB_blend_func_extended"])
|
||||||
Api::Gl,
|
.write_bindings(GlobalGenerator, &mut file)
|
||||||
(4, 5),
|
|
||||||
Profile::Core,
|
|
||||||
Fallbacks::All,
|
|
||||||
["GL_ARB_blend_func_extended"],
|
|
||||||
).write_bindings(GlobalGenerator, &mut file)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
@ -68,7 +64,8 @@ fn aquire_winpty_agent(out_path: &Path) {
|
||||||
.read(true)
|
.read(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
.create(true)
|
.create(true)
|
||||||
.open(tmp_dir.path().join("winpty_package.zip")).unwrap();
|
.open(tmp_dir.path().join("winpty_package.zip"))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
io::copy(&mut response, &mut file).unwrap();
|
io::copy(&mut response, &mut file).unwrap();
|
||||||
|
|
||||||
|
@ -77,7 +74,7 @@ fn aquire_winpty_agent(out_path: &Path) {
|
||||||
let target = match env::var("TARGET").unwrap().split("-").next().unwrap() {
|
let target = match env::var("TARGET").unwrap().split("-").next().unwrap() {
|
||||||
"x86_64" => "x64",
|
"x86_64" => "x64",
|
||||||
"i386" => "ia32",
|
"i386" => "ia32",
|
||||||
_ => panic!("architecture has no winpty binary")
|
_ => panic!("architecture has no winpty binary"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut winpty_agent = archive.by_name(&format!("{}/bin/winpty-agent.exe", target)).unwrap();
|
let mut winpty_agent = archive.by_name(&format!("{}/bin/winpty-agent.exe", target)).unwrap();
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Add clippy for linting with nightly builds
|
# Add clippy for lint validation
|
||||||
if [ "$CLIPPY" == "true" ]; then
|
if [ "$CLIPPY" == "true" ]; then
|
||||||
rustup component add clippy
|
rustup component add clippy
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Add rustfmt for format validation
|
||||||
|
if [ "$RUSTFMT" == "true" ]; then
|
||||||
|
rustup component add rustfmt
|
||||||
|
fi
|
||||||
|
|
|
@ -9,6 +9,12 @@ if [ "$CLIPPY" == "true" ]; then
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Run clippy rustfmt
|
||||||
|
if [ "$RUSTFMT" == "true" ]; then
|
||||||
|
cargo fmt -- --check
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
# Run test in release mode if a tag is present, to produce an optimized binary
|
# Run test in release mode if a tag is present, to produce an optimized binary
|
||||||
if [ -n "$TRAVIS_TAG" ]; then
|
if [ -n "$TRAVIS_TAG" ]; then
|
||||||
cargo test --release || error=true
|
cargo test --release || error=true
|
||||||
|
|
|
@ -4,18 +4,18 @@
|
||||||
//! https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/PasteboardGuide106/Articles/pbReading.html#//apple_ref/doc/uid/TP40008123-SW1
|
//! https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/PasteboardGuide106/Articles/pbReading.html#//apple_ref/doc/uid/TP40008123-SW1
|
||||||
|
|
||||||
mod ns {
|
mod ns {
|
||||||
extern crate objc_id;
|
|
||||||
extern crate objc_foundation;
|
extern crate objc_foundation;
|
||||||
|
extern crate objc_id;
|
||||||
|
|
||||||
#[link(name = "AppKit", kind = "framework")]
|
#[link(name = "AppKit", kind = "framework")]
|
||||||
extern {}
|
extern "C" {}
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use objc::runtime::{Class, Object};
|
use self::objc_foundation::{INSArray, INSObject, INSString};
|
||||||
|
use self::objc_foundation::{NSArray, NSDictionary, NSObject, NSString};
|
||||||
use self::objc_id::{Id, Owned};
|
use self::objc_id::{Id, Owned};
|
||||||
use self::objc_foundation::{NSArray, NSObject, NSDictionary, NSString};
|
use objc::runtime::{Class, Object};
|
||||||
use self::objc_foundation::{INSString, INSArray, INSObject};
|
|
||||||
|
|
||||||
/// Rust API for NSPasteboard
|
/// Rust API for NSPasteboard
|
||||||
pub struct Pasteboard(Id<Object>);
|
pub struct Pasteboard(Id<Object>);
|
||||||
|
@ -55,6 +55,7 @@ mod ns {
|
||||||
|
|
||||||
impl PasteboardReadObject<String> for Pasteboard {
|
impl PasteboardReadObject<String> for Pasteboard {
|
||||||
type Err = ReadStringError;
|
type Err = ReadStringError;
|
||||||
|
|
||||||
fn read_object(&self) -> Result<String, ReadStringError> {
|
fn read_object(&self) -> Result<String, ReadStringError> {
|
||||||
// Get string class; need this for passing to readObjectsForClasses
|
// Get string class; need this for passing to readObjectsForClasses
|
||||||
let ns_string_class = match Class::get("NSString") {
|
let ns_string_class = match Class::get("NSString") {
|
||||||
|
@ -133,9 +134,7 @@ mod ns {
|
||||||
|
|
||||||
// The writeObjects method returns true in case of success, and
|
// The writeObjects method returns true in case of success, and
|
||||||
// false otherwise.
|
// false otherwise.
|
||||||
let ok: bool = unsafe {
|
let ok: bool = unsafe { msg_send![self.0, writeObjects: objects] };
|
||||||
msg_send![self.0, writeObjects:objects]
|
|
||||||
};
|
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -175,9 +174,7 @@ mod ns {
|
||||||
impl ::std::error::Error for NewPasteboardError {
|
impl ::std::error::Error for NewPasteboardError {
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
match *self {
|
match *self {
|
||||||
NewPasteboardError::GetPasteboardClass => {
|
NewPasteboardError::GetPasteboardClass => "NSPasteboard class not found",
|
||||||
"NSPasteboard class not found"
|
|
||||||
},
|
|
||||||
NewPasteboardError::LoadGeneralPasteboard => {
|
NewPasteboardError::LoadGeneralPasteboard => {
|
||||||
"[NSPasteboard generalPasteboard] failed"
|
"[NSPasteboard generalPasteboard] failed"
|
||||||
},
|
},
|
||||||
|
@ -209,9 +206,7 @@ mod ns {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let id = unsafe {
|
let id = unsafe { Id::from_ptr(ptr) };
|
||||||
Id::from_ptr(ptr)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Pasteboard(id))
|
Ok(Pasteboard(id))
|
||||||
}
|
}
|
||||||
|
@ -222,9 +217,7 @@ mod ns {
|
||||||
/// This is the first step in providing data on the pasteboard. The
|
/// This is the first step in providing data on the pasteboard. The
|
||||||
/// return value is the change count of the pasteboard
|
/// return value is the change count of the pasteboard
|
||||||
pub fn clear_contents(&mut self) -> usize {
|
pub fn clear_contents(&mut self) -> usize {
|
||||||
unsafe {
|
unsafe { msg_send![self.0, clearContents] }
|
||||||
msg_send![self.0, clearContents]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,7 +229,6 @@ pub enum Error {
|
||||||
WriteString(ns::WriteStringError),
|
WriteString(ns::WriteStringError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl ::std::error::Error for Error {
|
impl ::std::error::Error for Error {
|
||||||
fn cause(&self) -> Option<&::std::error::Error> {
|
fn cause(&self) -> Option<&::std::error::Error> {
|
||||||
match *self {
|
match *self {
|
||||||
|
@ -258,9 +250,7 @@ impl ::std::error::Error for Error {
|
||||||
impl ::std::fmt::Display for Error {
|
impl ::std::fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
Error::CreatePasteboard(ref err) => {
|
Error::CreatePasteboard(ref err) => write!(f, "Failed to create pasteboard: {}", err),
|
||||||
write!(f, "Failed to create pasteboard: {}", err)
|
|
||||||
},
|
|
||||||
Error::ReadString(ref err) => {
|
Error::ReadString(ref err) => {
|
||||||
write!(f, "Failed to read string from pasteboard: {}", err)
|
write!(f, "Failed to read string from pasteboard: {}", err)
|
||||||
},
|
},
|
||||||
|
@ -301,23 +291,23 @@ impl super::Load for Clipboard {
|
||||||
fn load_primary(&self) -> Result<String, Self::Err> {
|
fn load_primary(&self) -> Result<String, Self::Err> {
|
||||||
use self::ns::PasteboardReadObject;
|
use self::ns::PasteboardReadObject;
|
||||||
|
|
||||||
self.0.read_object()
|
self.0.read_object().map_err(::std::convert::From::from)
|
||||||
.map_err(::std::convert::From::from)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::Store for Clipboard {
|
impl super::Store for Clipboard {
|
||||||
fn store_primary<S>(&mut self, contents: S) -> Result<(), Self::Err>
|
fn store_primary<S>(&mut self, contents: S) -> Result<(), Self::Err>
|
||||||
where S: Into<String>
|
where
|
||||||
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
use self::ns::PasteboardWriteObject;
|
use self::ns::PasteboardWriteObject;
|
||||||
|
|
||||||
self.0.write_object(contents.into())
|
self.0.write_object(contents.into()).map_err(::std::convert::From::from)
|
||||||
.map_err(::std::convert::From::from)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn store_selection<S>(&mut self, _contents: S) -> Result<(), Self::Err>
|
fn store_selection<S>(&mut self, _contents: S) -> Result<(), Self::Err>
|
||||||
where S: Into<String>
|
where
|
||||||
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
// No such thing on macOS
|
// No such thing on macOS
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -327,7 +317,7 @@ impl super::Store for Clipboard {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Clipboard;
|
use super::Clipboard;
|
||||||
use ::{Load, Store};
|
use {Load, Store};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create_clipboard_save_load_contents() {
|
fn create_clipboard_save_load_contents() {
|
||||||
|
|
|
@ -33,9 +33,7 @@ impl Load for Clipboard {
|
||||||
type Err = Error;
|
type Err = Error;
|
||||||
|
|
||||||
fn new() -> Result<Self, Error> {
|
fn new() -> Result<Self, Error> {
|
||||||
ClipboardContext::new()
|
ClipboardContext::new().map(Clipboard).map_err(Error::Clipboard)
|
||||||
.map(Clipboard)
|
|
||||||
.map_err(Error::Clipboard)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_primary(&self) -> Result<String, Self::Err> {
|
fn load_primary(&self) -> Result<String, Self::Err> {
|
||||||
|
@ -56,9 +54,7 @@ impl Store for Clipboard {
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
self.0
|
self.0.set_contents(contents.into()).map_err(Error::Clipboard)
|
||||||
.set_contents(contents.into())
|
|
||||||
.map_err(Error::Clipboard)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the secondary clipboard contents
|
/// Sets the secondary clipboard contents
|
||||||
|
@ -67,8 +63,6 @@ impl Store for Clipboard {
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
self.0
|
self.0.set_contents(contents.into()).map_err(Error::Clipboard)
|
||||||
.set_contents(contents.into())
|
|
||||||
.map_err(Error::Clipboard)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,10 @@
|
||||||
//!
|
//!
|
||||||
//! FIXME: Implement actual X11 clipboard API using the ICCCM reference
|
//! FIXME: Implement actual X11 clipboard API using the ICCCM reference
|
||||||
//! https://tronche.com/gui/x/icccm/
|
//! https://tronche.com/gui/x/icccm/
|
||||||
use std::io;
|
|
||||||
use std::process::{Output, Command};
|
|
||||||
use std::string::FromUtf8Error;
|
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
|
use std::io;
|
||||||
|
use std::process::{Command, Output};
|
||||||
|
use std::string::FromUtf8Error;
|
||||||
|
|
||||||
use super::{Load, Store};
|
use super::{Load, Store};
|
||||||
|
|
||||||
|
@ -45,13 +45,11 @@ impl ::std::error::Error for Error {
|
||||||
impl ::std::fmt::Display for Error {
|
impl ::std::fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
Error::Io(ref err) => {
|
Error::Io(ref err) => match err.kind() {
|
||||||
match err.kind() {
|
io::ErrorKind::NotFound => {
|
||||||
io::ErrorKind::NotFound => {
|
write!(f, "Please install `xclip` to enable clipboard support")
|
||||||
write!(f, "Please install `xclip` to enable clipboard support")
|
},
|
||||||
},
|
_ => write!(f, "Error calling xclip: {}", err),
|
||||||
_ => write!(f, "Error calling xclip: {}", err),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Error::Xclip(ref s) => write!(f, "Error from xclip: {}", s),
|
Error::Xclip(ref s) => write!(f, "Error from xclip: {}", s),
|
||||||
Error::Utf8(ref err) => write!(f, "Error parsing xclip output: {}", err),
|
Error::Utf8(ref err) => write!(f, "Error parsing xclip output: {}", err),
|
||||||
|
@ -79,17 +77,13 @@ impl Load for Clipboard {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_primary(&self) -> Result<String, Self::Err> {
|
fn load_primary(&self) -> Result<String, Self::Err> {
|
||||||
let output = Command::new("xclip")
|
let output = Command::new("xclip").args(&["-o", "-selection", "clipboard"]).output()?;
|
||||||
.args(&["-o", "-selection", "clipboard"])
|
|
||||||
.output()?;
|
|
||||||
|
|
||||||
Clipboard::process_xclip_output(output)
|
Clipboard::process_xclip_output(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_selection(&self) -> Result<String, Self::Err> {
|
fn load_selection(&self) -> Result<String, Self::Err> {
|
||||||
let output = Command::new("xclip")
|
let output = Command::new("xclip").args(&["-o"]).output()?;
|
||||||
.args(&["-o"])
|
|
||||||
.output()?;
|
|
||||||
|
|
||||||
Clipboard::process_xclip_output(output)
|
Clipboard::process_xclip_output(output)
|
||||||
}
|
}
|
||||||
|
@ -99,7 +93,8 @@ impl Store for Clipboard {
|
||||||
/// Sets the primary clipboard contents
|
/// Sets the primary clipboard contents
|
||||||
#[inline]
|
#[inline]
|
||||||
fn store_primary<S>(&mut self, contents: S) -> Result<(), Self::Err>
|
fn store_primary<S>(&mut self, contents: S) -> Result<(), Self::Err>
|
||||||
where S: Into<String>
|
where
|
||||||
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
self.store(contents, &["-i", "-selection", "clipboard"])
|
self.store(contents, &["-i", "-selection", "clipboard"])
|
||||||
}
|
}
|
||||||
|
@ -107,7 +102,8 @@ impl Store for Clipboard {
|
||||||
/// Sets the secondary clipboard contents
|
/// Sets the secondary clipboard contents
|
||||||
#[inline]
|
#[inline]
|
||||||
fn store_selection<S>(&mut self, contents: S) -> Result<(), Self::Err>
|
fn store_selection<S>(&mut self, contents: S) -> Result<(), Self::Err>
|
||||||
where S: Into<String>
|
where
|
||||||
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
self.store(contents, &["-i"])
|
self.store(contents, &["-i"])
|
||||||
}
|
}
|
||||||
|
@ -116,26 +112,22 @@ impl Store for Clipboard {
|
||||||
impl Clipboard {
|
impl Clipboard {
|
||||||
fn process_xclip_output(output: Output) -> Result<String, Error> {
|
fn process_xclip_output(output: Output) -> Result<String, Error> {
|
||||||
if output.status.success() {
|
if output.status.success() {
|
||||||
String::from_utf8(output.stdout)
|
String::from_utf8(output.stdout).map_err(::std::convert::From::from)
|
||||||
.map_err(::std::convert::From::from)
|
|
||||||
} else {
|
} else {
|
||||||
String::from_utf8(output.stderr)
|
String::from_utf8(output.stderr).map_err(::std::convert::From::from)
|
||||||
.map_err(::std::convert::From::from)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn store<C, S>(&mut self, contents: C, args: &[S]) -> Result<(), Error>
|
fn store<C, S>(&mut self, contents: C, args: &[S]) -> Result<(), Error>
|
||||||
where C: Into<String>,
|
where
|
||||||
S: AsRef<OsStr>,
|
C: Into<String>,
|
||||||
|
S: AsRef<OsStr>,
|
||||||
{
|
{
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
let contents = contents.into();
|
let contents = contents.into();
|
||||||
let mut child = Command::new("xclip")
|
let mut child = Command::new("xclip").args(args).stdin(Stdio::piped()).spawn()?;
|
||||||
.args(args)
|
|
||||||
.stdin(Stdio::piped())
|
|
||||||
.spawn()?;
|
|
||||||
|
|
||||||
if let Some(stdin) = child.stdin.as_mut() {
|
if let Some(stdin) = child.stdin.as_mut() {
|
||||||
stdin.write_all(contents.as_bytes())?;
|
stdin.write_all(contents.as_bytes())?;
|
||||||
|
@ -154,7 +146,7 @@ impl Clipboard {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Clipboard;
|
use super::Clipboard;
|
||||||
use ::{Load, Store};
|
use {Load, Store};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn clipboard_works() {
|
fn clipboard_works() {
|
||||||
|
|
|
@ -40,9 +40,10 @@ pub fn extract_rgb(bytes: &[u8]) -> Vec<u8> {
|
||||||
|
|
||||||
#[cfg(target_endian = "big")]
|
#[cfg(target_endian = "big")]
|
||||||
pub fn extract_rgb(bytes: Vec<u8>) -> Vec<u8> {
|
pub fn extract_rgb(bytes: Vec<u8>) -> Vec<u8> {
|
||||||
bytes.into_iter()
|
bytes
|
||||||
.enumerate()
|
.into_iter()
|
||||||
.filter(|&(index, _)| ((index) % 4) != 0)
|
.enumerate()
|
||||||
.map(|(_, val)| val)
|
.filter(|&(index, _)| ((index) % 4) != 0)
|
||||||
.collect::<Vec<_>>()
|
.map(|(_, val)| val)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,34 +17,37 @@
|
||||||
//! TODO error handling... just search for unwrap.
|
//! TODO error handling... just search for unwrap.
|
||||||
#![allow(improper_ctypes)]
|
#![allow(improper_ctypes)]
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ptr;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
use ::{Slant, Weight, Style};
|
use {Slant, Style, Weight};
|
||||||
|
|
||||||
use core_foundation::string::{CFString};
|
use core_foundation::array::{CFArray, CFIndex};
|
||||||
use core_foundation::array::{CFIndex, CFArray};
|
use core_foundation::string::CFString;
|
||||||
use core_graphics::base::kCGImageAlphaPremultipliedFirst;
|
use core_graphics::base::kCGImageAlphaPremultipliedFirst;
|
||||||
use core_graphics::color_space::CGColorSpace;
|
use core_graphics::color_space::CGColorSpace;
|
||||||
use core_graphics::context::{CGContext};
|
use core_graphics::context::CGContext;
|
||||||
use core_graphics::font::{CGFont, CGGlyph};
|
use core_graphics::font::{CGFont, CGGlyph};
|
||||||
use core_graphics::geometry::{CGPoint, CGRect, CGSize};
|
use core_graphics::geometry::{CGPoint, CGRect, CGSize};
|
||||||
use core_text::font::{CTFont, new_from_descriptor as ct_new_from_descriptor, cascade_list_for_languages as ct_cascade_list_for_languages};
|
use core_text::font::{
|
||||||
|
cascade_list_for_languages as ct_cascade_list_for_languages,
|
||||||
|
new_from_descriptor as ct_new_from_descriptor, CTFont,
|
||||||
|
};
|
||||||
use core_text::font_collection::create_for_family;
|
use core_text::font_collection::create_for_family;
|
||||||
use core_text::font_collection::get_family_names as ct_get_family_names;
|
use core_text::font_collection::get_family_names as ct_get_family_names;
|
||||||
use core_text::font_descriptor::kCTFontDefaultOrientation;
|
use core_text::font_descriptor::kCTFontDefaultOrientation;
|
||||||
use core_text::font_descriptor::kCTFontHorizontalOrientation;
|
use core_text::font_descriptor::kCTFontHorizontalOrientation;
|
||||||
use core_text::font_descriptor::kCTFontVerticalOrientation;
|
use core_text::font_descriptor::kCTFontVerticalOrientation;
|
||||||
use core_text::font_descriptor::{CTFontDescriptor, CTFontOrientation};
|
|
||||||
use core_text::font_descriptor::SymbolicTraitAccessors;
|
use core_text::font_descriptor::SymbolicTraitAccessors;
|
||||||
|
use core_text::font_descriptor::{CTFontDescriptor, CTFontOrientation};
|
||||||
|
|
||||||
use euclid::{Point2D, Rect, Size2D};
|
use euclid::{Point2D, Rect, Size2D};
|
||||||
|
|
||||||
use super::{FontDesc, RasterizedGlyph, Metrics, FontKey, GlyphKey};
|
use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph};
|
||||||
|
|
||||||
pub mod byte_order;
|
pub mod byte_order;
|
||||||
use self::byte_order::kCGBitmapByteOrder32Host;
|
|
||||||
use self::byte_order::extract_rgb;
|
use self::byte_order::extract_rgb;
|
||||||
|
use self::byte_order::kCGBitmapByteOrder32Host;
|
||||||
|
|
||||||
use super::Size;
|
use super::Size;
|
||||||
|
|
||||||
|
@ -59,11 +62,11 @@ pub struct Descriptor {
|
||||||
display_name: String,
|
display_name: String,
|
||||||
font_path: PathBuf,
|
font_path: PathBuf,
|
||||||
|
|
||||||
ct_descriptor: CTFontDescriptor
|
ct_descriptor: CTFontDescriptor,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Descriptor {
|
impl Descriptor {
|
||||||
fn new(desc:CTFontDescriptor) -> Descriptor {
|
fn new(desc: CTFontDescriptor) -> Descriptor {
|
||||||
Descriptor {
|
Descriptor {
|
||||||
family_name: desc.family_name(),
|
family_name: desc.family_name(),
|
||||||
font_name: desc.font_name(),
|
font_name: desc.font_name(),
|
||||||
|
@ -111,16 +114,14 @@ impl ::std::error::Error for Error {
|
||||||
impl ::std::fmt::Display for Error {
|
impl ::std::fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
Error::MissingGlyph(ref c) => {
|
Error::MissingGlyph(ref c) => write!(f, "Glyph not found for char {:?}", c),
|
||||||
write!(f, "Glyph not found for char {:?}", c)
|
Error::MissingFont(ref desc) => write!(
|
||||||
},
|
f,
|
||||||
Error::MissingFont(ref desc) => {
|
"Couldn't find a font with {}\n\tPlease check the font config in your \
|
||||||
write!(f, "Couldn't find a font with {}\
|
alacritty.yml.",
|
||||||
\n\tPlease check the font config in your alacritty.yml.", desc)
|
desc
|
||||||
},
|
),
|
||||||
Error::FontNotLoaded => {
|
Error::FontNotLoaded => f.write_str("Tried to use a font that hasn't been loaded"),
|
||||||
f.write_str("Tried to use a font that hasn't been loaded")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,50 +140,41 @@ impl ::Rasterize for Rasterizer {
|
||||||
|
|
||||||
/// Get metrics for font specified by FontKey
|
/// Get metrics for font specified by FontKey
|
||||||
fn metrics(&self, key: FontKey, _size: Size) -> Result<Metrics, Error> {
|
fn metrics(&self, key: FontKey, _size: Size) -> Result<Metrics, Error> {
|
||||||
let font = self.fonts
|
let font = self.fonts.get(&key).ok_or(Error::FontNotLoaded)?;
|
||||||
.get(&key)
|
|
||||||
.ok_or(Error::FontNotLoaded)?;
|
|
||||||
|
|
||||||
Ok(font.metrics())
|
Ok(font.metrics())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_font(&mut self, desc: &FontDesc, size: Size) -> Result<FontKey, Error> {
|
fn load_font(&mut self, desc: &FontDesc, size: Size) -> Result<FontKey, Error> {
|
||||||
let scaled_size = Size::new(size.as_f32_pts() * self.device_pixel_ratio);
|
let scaled_size = Size::new(size.as_f32_pts() * self.device_pixel_ratio);
|
||||||
self.keys
|
self.keys.get(&(desc.to_owned(), scaled_size)).map(|k| Ok(*k)).unwrap_or_else(|| {
|
||||||
.get(&(desc.to_owned(), scaled_size))
|
let font = self.get_font(desc, size)?;
|
||||||
.map(|k| Ok(*k))
|
let key = FontKey::next();
|
||||||
.unwrap_or_else(|| {
|
|
||||||
let font = self.get_font(desc, size)?;
|
|
||||||
let key = FontKey::next();
|
|
||||||
|
|
||||||
self.fonts.insert(key, font);
|
self.fonts.insert(key, font);
|
||||||
self.keys.insert((desc.clone(), scaled_size), key);
|
self.keys.insert((desc.clone(), scaled_size), key);
|
||||||
|
|
||||||
Ok(key)
|
Ok(key)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get rasterized glyph for given glyph key
|
/// Get rasterized glyph for given glyph key
|
||||||
fn get_glyph(&mut self, glyph: GlyphKey) -> Result<RasterizedGlyph, Error> {
|
fn get_glyph(&mut self, glyph: GlyphKey) -> Result<RasterizedGlyph, Error> {
|
||||||
|
|
||||||
// get loaded font
|
// get loaded font
|
||||||
let font = self.fonts
|
let font = self.fonts.get(&glyph.font_key).ok_or(Error::FontNotLoaded)?;
|
||||||
.get(&glyph.font_key)
|
|
||||||
.ok_or(Error::FontNotLoaded)?;
|
|
||||||
|
|
||||||
// first try the font itself as a direct hit
|
// first try the font itself as a direct hit
|
||||||
self.maybe_get_glyph(glyph, font)
|
self.maybe_get_glyph(glyph, font).unwrap_or_else(|| {
|
||||||
.unwrap_or_else(|| {
|
// then try fallbacks
|
||||||
// then try fallbacks
|
for fallback in &font.fallbacks {
|
||||||
for fallback in &font.fallbacks {
|
if let Some(result) = self.maybe_get_glyph(glyph, &fallback) {
|
||||||
if let Some(result) = self.maybe_get_glyph(glyph, &fallback) {
|
// found a fallback
|
||||||
// found a fallback
|
return result;
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// no fallback, give up.
|
}
|
||||||
Err(Error::MissingGlyph(glyph.c))
|
// no fallback, give up.
|
||||||
})
|
Err(Error::MissingGlyph(glyph.c))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_dpr(&mut self, device_pixel_ratio: f32) {
|
fn update_dpr(&mut self, device_pixel_ratio: f32) {
|
||||||
|
@ -195,7 +187,7 @@ impl Rasterizer {
|
||||||
&mut self,
|
&mut self,
|
||||||
desc: &FontDesc,
|
desc: &FontDesc,
|
||||||
style: &str,
|
style: &str,
|
||||||
size: Size
|
size: Size,
|
||||||
) -> Result<Font, Error> {
|
) -> Result<Font, Error> {
|
||||||
let descriptors = descriptors_for_family(&desc.name[..]);
|
let descriptors = descriptors_for_family(&desc.name[..]);
|
||||||
for descriptor in descriptors {
|
for descriptor in descriptors {
|
||||||
|
@ -215,11 +207,11 @@ impl Rasterizer {
|
||||||
desc: &FontDesc,
|
desc: &FontDesc,
|
||||||
slant: Slant,
|
slant: Slant,
|
||||||
weight: Weight,
|
weight: Weight,
|
||||||
size: Size
|
size: Size,
|
||||||
) -> Result<Font, Error> {
|
) -> Result<Font, Error> {
|
||||||
let bold = match weight {
|
let bold = match weight {
|
||||||
Weight::Bold => true,
|
Weight::Bold => true,
|
||||||
_ => false
|
_ => false,
|
||||||
};
|
};
|
||||||
let italic = match slant {
|
let italic = match slant {
|
||||||
Slant::Normal => false,
|
Slant::Normal => false,
|
||||||
|
@ -262,7 +254,6 @@ impl Rasterizer {
|
||||||
_ => Some(Err(e)),
|
_ => Some(Err(e)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies the intended rendering orientation of the font for obtaining glyph metrics
|
/// Specifies the intended rendering orientation of the font for obtaining glyph metrics
|
||||||
|
@ -302,18 +293,12 @@ pub fn get_family_names() -> Vec<String> {
|
||||||
owned_names
|
owned_names
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Return fallback descriptors for font/language list
|
/// Return fallback descriptors for font/language list
|
||||||
fn cascade_list_for_languages(
|
fn cascade_list_for_languages(ct_font: &CTFont, languages: &[String]) -> Vec<Descriptor> {
|
||||||
ct_font: &CTFont,
|
|
||||||
languages: &[String]
|
|
||||||
) -> Vec<Descriptor> {
|
|
||||||
|
|
||||||
// convert language type &Vec<String> -> CFArray
|
// convert language type &Vec<String> -> CFArray
|
||||||
let langarr:CFArray<CFString> = {
|
let langarr: CFArray<CFString> = {
|
||||||
let tmp:Vec<CFString> = languages.iter()
|
let tmp: Vec<CFString> =
|
||||||
.map(|language| CFString::new(&language))
|
languages.iter().map(|language| CFString::new(&language)).collect();
|
||||||
.collect();
|
|
||||||
CFArray::from_CFTypes(&tmp)
|
CFArray::from_CFTypes(&tmp)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -321,12 +306,9 @@ fn cascade_list_for_languages(
|
||||||
let list = ct_cascade_list_for_languages(ct_font, &langarr);
|
let list = ct_cascade_list_for_languages(ct_font, &langarr);
|
||||||
|
|
||||||
// convert CFArray to Vec<Descriptor>
|
// convert CFArray to Vec<Descriptor>
|
||||||
list.into_iter()
|
list.into_iter().map(|fontdesc| Descriptor::new(fontdesc.clone())).collect()
|
||||||
.map(|fontdesc| Descriptor::new(fontdesc.clone()))
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Get descriptors for family name
|
/// Get descriptors for family name
|
||||||
pub fn descriptors_for_family(family: &str) -> Vec<Descriptor> {
|
pub fn descriptors_for_family(family: &str) -> Vec<Descriptor> {
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
|
@ -350,7 +332,7 @@ pub fn descriptors_for_family(family: &str) -> Vec<Descriptor> {
|
||||||
|
|
||||||
impl Descriptor {
|
impl Descriptor {
|
||||||
/// Create a Font from this descriptor
|
/// Create a Font from this descriptor
|
||||||
pub fn to_font(&self, size: f64, load_fallbacks:bool) -> Font {
|
pub fn to_font(&self, size: f64, load_fallbacks: bool) -> Font {
|
||||||
let ct_font = ct_new_from_descriptor(&self.ct_descriptor, size);
|
let ct_font = ct_new_from_descriptor(&self.ct_descriptor, size);
|
||||||
let cg_font = ct_font.copy_to_CGFont();
|
let cg_font = ct_font.copy_to_CGFont();
|
||||||
|
|
||||||
|
@ -385,7 +367,7 @@ impl Descriptor {
|
||||||
fallbacks.insert(0, Font {
|
fallbacks.insert(0, Font {
|
||||||
cg_font: menlo.copy_to_CGFont(),
|
cg_font: menlo.copy_to_CGFont(),
|
||||||
ct_font: menlo,
|
ct_font: menlo,
|
||||||
fallbacks: Vec::new()
|
fallbacks: Vec::new(),
|
||||||
});
|
});
|
||||||
|
|
||||||
fallbacks
|
fallbacks
|
||||||
|
@ -395,25 +377,16 @@ impl Descriptor {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
Font {
|
Font { ct_font, cg_font, fallbacks }
|
||||||
ct_font,
|
|
||||||
cg_font,
|
|
||||||
fallbacks,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Font {
|
impl Font {
|
||||||
/// The the bounding rect of a glyph
|
/// The the bounding rect of a glyph
|
||||||
pub fn bounding_rect_for_glyph(
|
pub fn bounding_rect_for_glyph(&self, orientation: FontOrientation, index: u32) -> Rect<f64> {
|
||||||
&self,
|
let cg_rect = self
|
||||||
orientation: FontOrientation,
|
.ct_font
|
||||||
index: u32
|
.get_bounding_rects_for_glyphs(orientation as CTFontOrientation, &[index as CGGlyph]);
|
||||||
) -> Rect<f64> {
|
|
||||||
let cg_rect = self.ct_font.get_bounding_rects_for_glyphs(
|
|
||||||
orientation as CTFontOrientation,
|
|
||||||
&[index as CGGlyph]
|
|
||||||
);
|
|
||||||
|
|
||||||
Rect::new(
|
Rect::new(
|
||||||
Point2D::new(cg_rect.origin.x, cg_rect.origin.y),
|
Point2D::new(cg_rect.origin.x, cg_rect.origin.y),
|
||||||
|
@ -465,12 +438,17 @@ impl Font {
|
||||||
FontOrientation::Default as _,
|
FontOrientation::Default as _,
|
||||||
&indices[0],
|
&indices[0],
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
1
|
1,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_glyph(&self, character: char, _size: f64, use_thin_strokes: bool) -> Result<RasterizedGlyph, Error> {
|
pub fn get_glyph(
|
||||||
|
&self,
|
||||||
|
character: char,
|
||||||
|
_size: f64,
|
||||||
|
use_thin_strokes: bool,
|
||||||
|
) -> Result<RasterizedGlyph, Error> {
|
||||||
// Render custom symbols for underline and beam cursor
|
// Render custom symbols for underline and beam cursor
|
||||||
match character {
|
match character {
|
||||||
super::UNDERLINE_CURSOR_CHAR => {
|
super::UNDERLINE_CURSOR_CHAR => {
|
||||||
|
@ -480,7 +458,7 @@ impl Font {
|
||||||
let width = self.glyph_advance('0') as i32;
|
let width = self.glyph_advance('0') as i32;
|
||||||
// Return the new custom glyph
|
// Return the new custom glyph
|
||||||
return super::get_underline_cursor_glyph(descent, width);
|
return super::get_underline_cursor_glyph(descent, width);
|
||||||
}
|
},
|
||||||
super::BEAM_CURSOR_CHAR | super::BOX_CURSOR_CHAR => {
|
super::BEAM_CURSOR_CHAR | super::BOX_CURSOR_CHAR => {
|
||||||
// Get the top of the bounding box
|
// Get the top of the bounding box
|
||||||
let metrics = self.metrics();
|
let metrics = self.metrics();
|
||||||
|
@ -496,12 +474,12 @@ impl Font {
|
||||||
} else {
|
} else {
|
||||||
return super::get_box_cursor_glyph(ascent as i32, height as i32, width);
|
return super::get_box_cursor_glyph(ascent as i32, height as i32, width);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
_ => ()
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
let glyph_index = self.glyph_index(character)
|
let glyph_index =
|
||||||
.ok_or_else(|| Error::MissingGlyph(character))?;
|
self.glyph_index(character).ok_or_else(|| Error::MissingGlyph(character))?;
|
||||||
|
|
||||||
let bounds = self.bounding_rect_for_glyph(Default::default(), glyph_index);
|
let bounds = self.bounding_rect_for_glyph(Default::default(), glyph_index);
|
||||||
|
|
||||||
|
@ -519,7 +497,7 @@ impl Font {
|
||||||
height: 0,
|
height: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
buf: Vec::new()
|
buf: Vec::new(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -530,17 +508,14 @@ impl Font {
|
||||||
8, // bits per component
|
8, // bits per component
|
||||||
rasterized_width as usize * 4,
|
rasterized_width as usize * 4,
|
||||||
&CGColorSpace::create_device_rgb(),
|
&CGColorSpace::create_device_rgb(),
|
||||||
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host
|
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Give the context an opaque, black background
|
// Give the context an opaque, black background
|
||||||
cg_context.set_rgb_fill_color(0.0, 0.0, 0.0, 1.0);
|
cg_context.set_rgb_fill_color(0.0, 0.0, 0.0, 1.0);
|
||||||
let context_rect = CGRect::new(
|
let context_rect = CGRect::new(
|
||||||
&CGPoint::new(0.0, 0.0),
|
&CGPoint::new(0.0, 0.0),
|
||||||
&CGSize::new(
|
&CGSize::new(f64::from(rasterized_width), f64::from(rasterized_height)),
|
||||||
f64::from(rasterized_width),
|
|
||||||
f64::from(rasterized_height)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
cg_context.fill_rect(context_rect);
|
cg_context.fill_rect(context_rect);
|
||||||
|
@ -560,14 +535,14 @@ impl Font {
|
||||||
|
|
||||||
// Set fill color to white for drawing the glyph
|
// Set fill color to white for drawing the glyph
|
||||||
cg_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0);
|
cg_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0);
|
||||||
let rasterization_origin = CGPoint {
|
let rasterization_origin =
|
||||||
x: f64::from(-rasterized_left),
|
CGPoint { x: f64::from(-rasterized_left), y: f64::from(rasterized_descent) };
|
||||||
y: f64::from(rasterized_descent),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.ct_font.draw_glyphs(&[glyph_index as CGGlyph],
|
self.ct_font.draw_glyphs(
|
||||||
&[rasterization_origin],
|
&[glyph_index as CGGlyph],
|
||||||
cg_context.clone());
|
&[rasterization_origin],
|
||||||
|
cg_context.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
let rasterized_pixels = cg_context.data().to_vec();
|
let rasterized_pixels = cg_context.data().to_vec();
|
||||||
|
|
||||||
|
@ -586,22 +561,22 @@ impl Font {
|
||||||
fn glyph_index(&self, character: char) -> Option<u32> {
|
fn glyph_index(&self, character: char) -> Option<u32> {
|
||||||
// encode this char as utf-16
|
// encode this char as utf-16
|
||||||
let mut buf = [0; 2];
|
let mut buf = [0; 2];
|
||||||
let encoded:&[u16] = character.encode_utf16(&mut buf);
|
let encoded: &[u16] = character.encode_utf16(&mut buf);
|
||||||
// and use the utf-16 buffer to get the index
|
// and use the utf-16 buffer to get the index
|
||||||
self.glyph_index_utf16(encoded)
|
self.glyph_index_utf16(encoded)
|
||||||
}
|
}
|
||||||
fn glyph_index_utf16(&self, encoded: &[u16]) -> Option<u32> {
|
|
||||||
|
|
||||||
|
fn glyph_index_utf16(&self, encoded: &[u16]) -> Option<u32> {
|
||||||
// output buffer for the glyph. for non-BMP glyphs, like
|
// output buffer for the glyph. for non-BMP glyphs, like
|
||||||
// emojis, this will be filled with two chars the second
|
// emojis, this will be filled with two chars the second
|
||||||
// always being a 0.
|
// always being a 0.
|
||||||
let mut glyphs:[CGGlyph; 2] = [0; 2];
|
let mut glyphs: [CGGlyph; 2] = [0; 2];
|
||||||
|
|
||||||
let res = unsafe {
|
let res = unsafe {
|
||||||
self.ct_font.get_glyphs_for_characters(
|
self.ct_font.get_glyphs_for_characters(
|
||||||
encoded.as_ptr(),
|
encoded.as_ptr(),
|
||||||
glyphs.as_mut_ptr(),
|
glyphs.as_mut_ptr(),
|
||||||
encoded.len() as CFIndex
|
encoded.len() as CFIndex,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -629,9 +604,7 @@ mod tests {
|
||||||
println!("{:?}", list);
|
println!("{:?}", list);
|
||||||
|
|
||||||
// Check to_font
|
// Check to_font
|
||||||
let fonts = list.iter()
|
let fonts = list.iter().map(|desc| desc.to_font(72., false)).collect::<Vec<_>>();
|
||||||
.map(|desc| desc.to_font(72., false))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
for font in fonts {
|
for font in fonts {
|
||||||
// Get a glyph
|
// Get a glyph
|
||||||
|
@ -649,7 +622,7 @@ mod tests {
|
||||||
101...150 => '~',
|
101...150 => '~',
|
||||||
151...200 => '*',
|
151...200 => '*',
|
||||||
201...255 => '#',
|
201...255 => '#',
|
||||||
_ => unreachable!()
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
print!("{}", c);
|
print!("{}", c);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,10 @@
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
use foreign_types::{ForeignTypeRef};
|
use foreign_types::ForeignTypeRef;
|
||||||
|
|
||||||
use super::ffi::{FcCharSet, FcCharSetDestroy, FcCharSetAddChar};
|
use super::ffi::FcCharSetCreate;
|
||||||
use super::ffi::{FcCharSetCreate};
|
use super::ffi::{FcCharSet, FcCharSetAddChar, FcCharSetDestroy};
|
||||||
|
|
||||||
foreign_type! {
|
foreign_type! {
|
||||||
type CType = FcCharSet;
|
type CType = FcCharSet;
|
||||||
|
@ -37,12 +37,6 @@ impl Default for CharSet {
|
||||||
|
|
||||||
impl CharSetRef {
|
impl CharSetRef {
|
||||||
pub fn add(&mut self, glyph: char) -> bool {
|
pub fn add(&mut self, glyph: char) -> bool {
|
||||||
unsafe {
|
unsafe { FcCharSetAddChar(self.as_ptr(), glyph as _) == 1 }
|
||||||
FcCharSetAddChar(
|
|
||||||
self.as_ptr(),
|
|
||||||
glyph as _
|
|
||||||
) == 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,10 @@
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
use foreign_types::{ForeignTypeRef};
|
use foreign_types::ForeignTypeRef;
|
||||||
|
|
||||||
use super::{SetName, FontSetRef};
|
use super::ffi::{FcConfig, FcConfigDestroy, FcConfigGetCurrent, FcConfigGetFonts};
|
||||||
use super::ffi::{FcConfigGetCurrent, FcConfigGetFonts, FcConfig, FcConfigDestroy};
|
use super::{FontSetRef, SetName};
|
||||||
|
|
||||||
foreign_type! {
|
foreign_type! {
|
||||||
type CType = FcConfig;
|
type CType = FcConfig;
|
||||||
|
@ -25,13 +25,10 @@ foreign_type! {
|
||||||
pub struct ConfigRef;
|
pub struct ConfigRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
/// Get the current configuration
|
/// Get the current configuration
|
||||||
pub fn get_current() -> &'static ConfigRef {
|
pub fn get_current() -> &'static ConfigRef {
|
||||||
unsafe {
|
unsafe { ConfigRef::from_ptr(FcConfigGetCurrent()) }
|
||||||
ConfigRef::from_ptr(FcConfigGetCurrent())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,9 @@ use std::ops::Deref;
|
||||||
|
|
||||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||||
|
|
||||||
use super::{ConfigRef, PatternRef, ObjectSetRef};
|
use super::{ConfigRef, ObjectSetRef, PatternRef};
|
||||||
|
|
||||||
use super::ffi::{FcFontSetList, FcFontSetDestroy, FcFontSet};
|
use super::ffi::{FcFontSet, FcFontSetDestroy, FcFontSetList};
|
||||||
|
|
||||||
foreign_type! {
|
foreign_type! {
|
||||||
type CType = FcFontSet;
|
type CType = FcFontSet;
|
||||||
|
@ -33,13 +33,13 @@ impl FontSet {
|
||||||
config: &ConfigRef,
|
config: &ConfigRef,
|
||||||
source: &mut FontSetRef,
|
source: &mut FontSetRef,
|
||||||
pattern: &PatternRef,
|
pattern: &PatternRef,
|
||||||
objects: &ObjectSetRef
|
objects: &ObjectSetRef,
|
||||||
) -> FontSet {
|
) -> FontSet {
|
||||||
let raw = unsafe {
|
let raw = unsafe {
|
||||||
FcFontSetList(
|
FcFontSetList(
|
||||||
config.as_ptr(),
|
config.as_ptr(),
|
||||||
&mut source.as_ptr(),
|
&mut source.as_ptr(),
|
||||||
1 /* nsets */,
|
1, // nsets
|
||||||
pattern.as_ptr(),
|
pattern.as_ptr(),
|
||||||
objects.as_ptr(),
|
objects.as_ptr(),
|
||||||
)
|
)
|
||||||
|
@ -56,38 +56,28 @@ pub struct Iter<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a FontSet {
|
impl<'a> IntoIterator for &'a FontSet {
|
||||||
type Item = &'a PatternRef;
|
|
||||||
type IntoIter = Iter<'a>;
|
type IntoIter = Iter<'a>;
|
||||||
|
type Item = &'a PatternRef;
|
||||||
|
|
||||||
fn into_iter(self) -> Iter<'a> {
|
fn into_iter(self) -> Iter<'a> {
|
||||||
let num_fonts = unsafe {
|
let num_fonts = unsafe { (*self.as_ptr()).nfont as isize };
|
||||||
(*self.as_ptr()).nfont as isize
|
|
||||||
};
|
|
||||||
|
|
||||||
trace!("Number of fonts is {}", num_fonts);
|
trace!("Number of fonts is {}", num_fonts);
|
||||||
|
|
||||||
Iter {
|
Iter { font_set: self.deref(), num_fonts: num_fonts as _, current: 0 }
|
||||||
font_set: self.deref(),
|
|
||||||
num_fonts: num_fonts as _,
|
|
||||||
current: 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a FontSetRef {
|
impl<'a> IntoIterator for &'a FontSetRef {
|
||||||
type Item = &'a PatternRef;
|
|
||||||
type IntoIter = Iter<'a>;
|
type IntoIter = Iter<'a>;
|
||||||
|
type Item = &'a PatternRef;
|
||||||
|
|
||||||
fn into_iter(self) -> Iter<'a> {
|
fn into_iter(self) -> Iter<'a> {
|
||||||
let num_fonts = unsafe {
|
let num_fonts = unsafe { (*self.as_ptr()).nfont as isize };
|
||||||
(*self.as_ptr()).nfont as isize
|
|
||||||
};
|
|
||||||
|
|
||||||
trace!("Number of fonts is {}", num_fonts);
|
trace!("Number of fonts is {}", num_fonts);
|
||||||
|
|
||||||
Iter {
|
Iter { font_set: self, num_fonts: num_fonts as _, current: 0 }
|
||||||
font_set: self,
|
|
||||||
num_fonts: num_fonts as _,
|
|
||||||
current: 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,21 +12,21 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
use std::ptr;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||||
|
|
||||||
use fontconfig::fontconfig as ffi;
|
use fontconfig::fontconfig as ffi;
|
||||||
|
|
||||||
use self::ffi::{FcSetSystem, FcSetApplication};
|
|
||||||
use self::ffi::FcResultNoMatch;
|
use self::ffi::FcResultNoMatch;
|
||||||
use self::ffi::{FcFontMatch, FcFontList, FcFontSort};
|
use self::ffi::{FcFontList, FcFontMatch, FcFontSort};
|
||||||
use self::ffi::{FcMatchFont, FcMatchPattern, FcMatchScan};
|
use self::ffi::{FcMatchFont, FcMatchPattern, FcMatchScan};
|
||||||
use self::ffi::{FC_SLANT_OBLIQUE, FC_SLANT_ITALIC, FC_SLANT_ROMAN};
|
use self::ffi::{FcSetApplication, FcSetSystem};
|
||||||
use self::ffi::{FC_WEIGHT_THIN, FC_WEIGHT_EXTRALIGHT, FC_WEIGHT_LIGHT};
|
use self::ffi::{FC_SLANT_ITALIC, FC_SLANT_OBLIQUE, FC_SLANT_ROMAN};
|
||||||
use self::ffi::{FC_WEIGHT_BOOK, FC_WEIGHT_REGULAR, FC_WEIGHT_MEDIUM, FC_WEIGHT_SEMIBOLD};
|
use self::ffi::{FC_WEIGHT_BLACK, FC_WEIGHT_BOLD, FC_WEIGHT_EXTRABLACK, FC_WEIGHT_EXTRABOLD};
|
||||||
use self::ffi::{FC_WEIGHT_BOLD, FC_WEIGHT_EXTRABOLD, FC_WEIGHT_BLACK, FC_WEIGHT_EXTRABLACK};
|
use self::ffi::{FC_WEIGHT_BOOK, FC_WEIGHT_MEDIUM, FC_WEIGHT_REGULAR, FC_WEIGHT_SEMIBOLD};
|
||||||
|
use self::ffi::{FC_WEIGHT_EXTRALIGHT, FC_WEIGHT_LIGHT, FC_WEIGHT_THIN};
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub use self::config::{Config, ConfigRef};
|
pub use self::config::{Config, ConfigRef};
|
||||||
|
@ -46,10 +46,7 @@ pub use self::pattern::{Pattern, PatternRef};
|
||||||
/// Find the font closest matching the provided pattern.
|
/// Find the font closest matching the provided pattern.
|
||||||
///
|
///
|
||||||
/// The returned pattern is the result of Pattern::render_prepare.
|
/// The returned pattern is the result of Pattern::render_prepare.
|
||||||
pub fn font_match(
|
pub fn font_match(config: &ConfigRef, pattern: &mut PatternRef) -> Option<Pattern> {
|
||||||
config: &ConfigRef,
|
|
||||||
pattern: &mut PatternRef,
|
|
||||||
) -> Option<Pattern> {
|
|
||||||
pattern.config_substitute(config, MatchKind::Pattern);
|
pattern.config_substitute(config, MatchKind::Pattern);
|
||||||
pattern.default_substitute();
|
pattern.default_substitute();
|
||||||
|
|
||||||
|
@ -57,11 +54,7 @@ pub fn font_match(
|
||||||
// What is this result actually used for? Seems redundant with
|
// What is this result actually used for? Seems redundant with
|
||||||
// return type.
|
// return type.
|
||||||
let mut result = FcResultNoMatch;
|
let mut result = FcResultNoMatch;
|
||||||
let ptr = FcFontMatch(
|
let ptr = FcFontMatch(config.as_ptr(), pattern.as_ptr(), &mut result);
|
||||||
config.as_ptr(),
|
|
||||||
pattern.as_ptr(),
|
|
||||||
&mut result,
|
|
||||||
);
|
|
||||||
|
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
None
|
None
|
||||||
|
@ -72,10 +65,7 @@ pub fn font_match(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// list fonts by closeness to the pattern
|
/// list fonts by closeness to the pattern
|
||||||
pub fn font_sort(
|
pub fn font_sort(config: &ConfigRef, pattern: &mut PatternRef) -> Option<FontSet> {
|
||||||
config: &ConfigRef,
|
|
||||||
pattern: &mut PatternRef,
|
|
||||||
) -> Option<FontSet> {
|
|
||||||
pattern.config_substitute(config, MatchKind::Pattern);
|
pattern.config_substitute(config, MatchKind::Pattern);
|
||||||
pattern.default_substitute();
|
pattern.default_substitute();
|
||||||
|
|
||||||
|
@ -112,11 +102,7 @@ pub fn font_list(
|
||||||
pattern.default_substitute();
|
pattern.default_substitute();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = FcFontList(
|
let ptr = FcFontList(config.as_ptr(), pattern.as_ptr(), objects.as_ptr());
|
||||||
config.as_ptr(),
|
|
||||||
pattern.as_ptr(),
|
|
||||||
objects.as_ptr(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
None
|
None
|
||||||
|
@ -174,7 +160,7 @@ pub enum Width {
|
||||||
Expanded,
|
Expanded,
|
||||||
Extraexpanded,
|
Extraexpanded,
|
||||||
Ultraexpanded,
|
Ultraexpanded,
|
||||||
Other(i32)
|
Other(i32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Width {
|
impl Width {
|
||||||
|
@ -190,7 +176,7 @@ impl Width {
|
||||||
Expanded => 125,
|
Expanded => 125,
|
||||||
Extraexpanded => 150,
|
Extraexpanded => 150,
|
||||||
Ultraexpanded => 200,
|
Ultraexpanded => 200,
|
||||||
Other(value) => value as isize
|
Other(value) => value as isize,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,7 +193,7 @@ impl From<isize> for Width {
|
||||||
125 => Width::Expanded,
|
125 => Width::Expanded,
|
||||||
150 => Width::Extraexpanded,
|
150 => Width::Extraexpanded,
|
||||||
200 => Width::Ultraexpanded,
|
200 => Width::Ultraexpanded,
|
||||||
_ => Width::Other(value as _)
|
_ => Width::Other(value as _),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,7 +205,7 @@ pub enum Rgba {
|
||||||
Bgr,
|
Bgr,
|
||||||
Vrgb,
|
Vrgb,
|
||||||
Vbgr,
|
Vbgr,
|
||||||
None
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rgba {
|
impl Rgba {
|
||||||
|
@ -230,7 +216,7 @@ impl Rgba {
|
||||||
Rgba::Bgr => 2,
|
Rgba::Bgr => 2,
|
||||||
Rgba::Vrgb => 3,
|
Rgba::Vrgb => 3,
|
||||||
Rgba::Vbgr => 4,
|
Rgba::Vbgr => 4,
|
||||||
Rgba::None => 5
|
Rgba::None => 5,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -268,7 +254,7 @@ pub enum HintStyle {
|
||||||
None,
|
None,
|
||||||
Slight,
|
Slight,
|
||||||
Medium,
|
Medium,
|
||||||
Full
|
Full,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for HintStyle {
|
impl fmt::Display for HintStyle {
|
||||||
|
@ -287,7 +273,7 @@ pub enum LcdFilter {
|
||||||
None,
|
None,
|
||||||
Default,
|
Default,
|
||||||
Light,
|
Light,
|
||||||
Legacy
|
Legacy,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for LcdFilter {
|
impl fmt::Display for LcdFilter {
|
||||||
|
@ -334,8 +320,7 @@ mod tests {
|
||||||
pattern.set_slant(Slant::Italic);
|
pattern.set_slant(Slant::Italic);
|
||||||
|
|
||||||
let config = Config::get_current();
|
let config = Config::get_current();
|
||||||
let fonts = super::font_sort(config, &mut pattern)
|
let fonts = super::font_sort(config, &mut pattern).expect("sort font monospace");
|
||||||
.expect("sort font monospace");
|
|
||||||
|
|
||||||
for font in fonts.into_iter().take(10) {
|
for font in fonts.into_iter().take(10) {
|
||||||
let font = font.render_prepare(&config, &pattern);
|
let font = font.render_prepare(&config, &pattern);
|
||||||
|
|
|
@ -13,8 +13,8 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
use libc::c_char;
|
use libc::c_char;
|
||||||
|
|
||||||
|
use super::ffi::{FcObjectSet, FcObjectSetAdd, FcObjectSetCreate, FcObjectSetDestroy};
|
||||||
use foreign_types::ForeignTypeRef;
|
use foreign_types::ForeignTypeRef;
|
||||||
use super::ffi::{FcObjectSetCreate, FcObjectSetAdd, FcObjectSet, FcObjectSetDestroy};
|
|
||||||
|
|
||||||
foreign_type! {
|
foreign_type! {
|
||||||
type CType = FcObjectSet;
|
type CType = FcObjectSet;
|
||||||
|
@ -31,9 +31,7 @@ impl ObjectSet {
|
||||||
|
|
||||||
impl Default for ObjectSet {
|
impl Default for ObjectSet {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
ObjectSet(unsafe {
|
ObjectSet(unsafe { FcObjectSetCreate() })
|
||||||
FcObjectSetCreate()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,38 +11,34 @@
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
use std::ptr;
|
|
||||||
use std::fmt;
|
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::{CStr, CString};
|
||||||
use std::path::PathBuf;
|
use std::fmt;
|
||||||
use std::str;
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::ptr;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
use libc::{c_char, c_int, c_double};
|
|
||||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||||
|
use libc::{c_char, c_double, c_int};
|
||||||
|
|
||||||
use super::ffi::FcResultMatch;
|
use super::ffi::FcResultMatch;
|
||||||
use super::ffi::{FcPatternDestroy, FcPatternAddCharSet};
|
use super::ffi::{FcBool, FcFontRenderPrepare, FcPatternGetBool, FcPatternGetDouble};
|
||||||
use super::ffi::{FcPatternGetString, FcPatternCreate, FcPatternAddString, FcPatternAddDouble};
|
use super::ffi::{FcChar8, FcConfigSubstitute, FcDefaultSubstitute, FcPattern};
|
||||||
use super::ffi::{FcPatternGetInteger, FcPatternAddInteger, FcPatternPrint};
|
use super::ffi::{FcPatternAddCharSet, FcPatternDestroy};
|
||||||
use super::ffi::{FcChar8, FcPattern, FcDefaultSubstitute, FcConfigSubstitute};
|
use super::ffi::{FcPatternAddDouble, FcPatternAddString, FcPatternCreate, FcPatternGetString};
|
||||||
use super::ffi::{FcFontRenderPrepare, FcPatternGetBool, FcBool, FcPatternGetDouble};
|
use super::ffi::{FcPatternAddInteger, FcPatternGetInteger, FcPatternPrint};
|
||||||
|
|
||||||
use super::{MatchKind, ConfigRef, CharSetRef, Weight, Slant, Width, Rgba, HintStyle, LcdFilter};
|
use super::{CharSetRef, ConfigRef, HintStyle, LcdFilter, MatchKind, Rgba, Slant, Weight, Width};
|
||||||
|
|
||||||
pub struct StringPropertyIter<'a> {
|
pub struct StringPropertyIter<'a> {
|
||||||
pattern: &'a PatternRef,
|
pattern: &'a PatternRef,
|
||||||
object: &'a [u8],
|
object: &'a [u8],
|
||||||
index: usize
|
index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> StringPropertyIter<'a> {
|
impl<'a> StringPropertyIter<'a> {
|
||||||
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> StringPropertyIter<'b> {
|
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> StringPropertyIter<'b> {
|
||||||
StringPropertyIter {
|
StringPropertyIter { pattern, object, index: 0 }
|
||||||
pattern,
|
|
||||||
object,
|
|
||||||
index: 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_value(&self, index: usize) -> Option<&'a str> {
|
fn get_value(&self, index: usize) -> Option<&'a str> {
|
||||||
|
@ -53,7 +49,7 @@ impl<'a> StringPropertyIter<'a> {
|
||||||
self.pattern.as_ptr(),
|
self.pattern.as_ptr(),
|
||||||
self.object.as_ptr() as *mut c_char,
|
self.object.as_ptr() as *mut c_char,
|
||||||
index as c_int,
|
index as c_int,
|
||||||
&mut value
|
&mut value,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -75,17 +71,12 @@ impl<'a> StringPropertyIter<'a> {
|
||||||
pub struct BooleanPropertyIter<'a> {
|
pub struct BooleanPropertyIter<'a> {
|
||||||
pattern: &'a PatternRef,
|
pattern: &'a PatternRef,
|
||||||
object: &'a [u8],
|
object: &'a [u8],
|
||||||
index: usize
|
index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<'a> BooleanPropertyIter<'a> {
|
impl<'a> BooleanPropertyIter<'a> {
|
||||||
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> BooleanPropertyIter<'b> {
|
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> BooleanPropertyIter<'b> {
|
||||||
BooleanPropertyIter {
|
BooleanPropertyIter { pattern, object, index: 0 }
|
||||||
pattern,
|
|
||||||
object,
|
|
||||||
index: 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_value(&self, index: usize) -> Option<bool> {
|
fn get_value(&self, index: usize) -> Option<bool> {
|
||||||
|
@ -96,7 +87,7 @@ impl<'a> BooleanPropertyIter<'a> {
|
||||||
self.pattern.as_ptr(),
|
self.pattern.as_ptr(),
|
||||||
self.object.as_ptr() as *mut c_char,
|
self.object.as_ptr() as *mut c_char,
|
||||||
index as c_int,
|
index as c_int,
|
||||||
&mut value
|
&mut value,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -112,16 +103,12 @@ impl<'a> BooleanPropertyIter<'a> {
|
||||||
pub struct IntPropertyIter<'a> {
|
pub struct IntPropertyIter<'a> {
|
||||||
pattern: &'a PatternRef,
|
pattern: &'a PatternRef,
|
||||||
object: &'a [u8],
|
object: &'a [u8],
|
||||||
index: usize
|
index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntPropertyIter<'a> {
|
impl<'a> IntPropertyIter<'a> {
|
||||||
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> IntPropertyIter<'b> {
|
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> IntPropertyIter<'b> {
|
||||||
IntPropertyIter {
|
IntPropertyIter { pattern, object, index: 0 }
|
||||||
pattern,
|
|
||||||
object,
|
|
||||||
index: 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_value(&self, index: usize) -> Option<isize> {
|
fn get_value(&self, index: usize) -> Option<isize> {
|
||||||
|
@ -132,7 +119,7 @@ impl<'a> IntPropertyIter<'a> {
|
||||||
self.pattern.as_ptr(),
|
self.pattern.as_ptr(),
|
||||||
self.object.as_ptr() as *mut c_char,
|
self.object.as_ptr() as *mut c_char,
|
||||||
index as c_int,
|
index as c_int,
|
||||||
&mut value
|
&mut value,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -150,9 +137,7 @@ pub struct RgbaPropertyIter<'a> {
|
||||||
|
|
||||||
impl<'a> RgbaPropertyIter<'a> {
|
impl<'a> RgbaPropertyIter<'a> {
|
||||||
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> RgbaPropertyIter<'b> {
|
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> RgbaPropertyIter<'b> {
|
||||||
RgbaPropertyIter {
|
RgbaPropertyIter { inner: IntPropertyIter::new(pattern, object) }
|
||||||
inner: IntPropertyIter::new(pattern, object)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -161,8 +146,7 @@ impl<'a> RgbaPropertyIter<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_value(&self, index: usize) -> Option<Rgba> {
|
fn get_value(&self, index: usize) -> Option<Rgba> {
|
||||||
self.inner.get_value(index)
|
self.inner.get_value(index).map(Rgba::from)
|
||||||
.map(Rgba::from)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,9 +156,7 @@ pub struct HintStylePropertyIter<'a> {
|
||||||
|
|
||||||
impl<'a> HintStylePropertyIter<'a> {
|
impl<'a> HintStylePropertyIter<'a> {
|
||||||
fn new(pattern: &PatternRef) -> HintStylePropertyIter {
|
fn new(pattern: &PatternRef) -> HintStylePropertyIter {
|
||||||
HintStylePropertyIter {
|
HintStylePropertyIter { inner: IntPropertyIter::new(pattern, b"hintstyle\0") }
|
||||||
inner: IntPropertyIter::new(pattern, b"hintstyle\0")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -183,16 +165,15 @@ impl<'a> HintStylePropertyIter<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_value(&self, index: usize) -> Option<HintStyle> {
|
fn get_value(&self, index: usize) -> Option<HintStyle> {
|
||||||
self.inner.get_value(index)
|
self.inner.get_value(index).and_then(|hint_style| {
|
||||||
.and_then(|hint_style| {
|
Some(match hint_style {
|
||||||
Some(match hint_style {
|
0 => HintStyle::None,
|
||||||
0 => HintStyle::None,
|
1 => HintStyle::Slight,
|
||||||
1 => HintStyle::Slight,
|
2 => HintStyle::Medium,
|
||||||
2 => HintStyle::Medium,
|
3 => HintStyle::Full,
|
||||||
3 => HintStyle::Full,
|
_ => return None,
|
||||||
_ => return None
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,9 +183,7 @@ pub struct LcdFilterPropertyIter<'a> {
|
||||||
|
|
||||||
impl<'a> LcdFilterPropertyIter<'a> {
|
impl<'a> LcdFilterPropertyIter<'a> {
|
||||||
fn new(pattern: &PatternRef) -> LcdFilterPropertyIter {
|
fn new(pattern: &PatternRef) -> LcdFilterPropertyIter {
|
||||||
LcdFilterPropertyIter {
|
LcdFilterPropertyIter { inner: IntPropertyIter::new(pattern, b"lcdfilter\0") }
|
||||||
inner: IntPropertyIter::new(pattern, b"lcdfilter\0")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -213,16 +192,15 @@ impl<'a> LcdFilterPropertyIter<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_value(&self, index: usize) -> Option<LcdFilter> {
|
fn get_value(&self, index: usize) -> Option<LcdFilter> {
|
||||||
self.inner.get_value(index)
|
self.inner.get_value(index).and_then(|hint_style| {
|
||||||
.and_then(|hint_style| {
|
Some(match hint_style {
|
||||||
Some(match hint_style {
|
0 => LcdFilter::None,
|
||||||
0 => LcdFilter::None,
|
1 => LcdFilter::Default,
|
||||||
1 => LcdFilter::Default,
|
2 => LcdFilter::Light,
|
||||||
2 => LcdFilter::Light,
|
3 => LcdFilter::Legacy,
|
||||||
3 => LcdFilter::Legacy,
|
_ => return None,
|
||||||
_ => return None
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,16 +208,12 @@ impl<'a> LcdFilterPropertyIter<'a> {
|
||||||
pub struct DoublePropertyIter<'a> {
|
pub struct DoublePropertyIter<'a> {
|
||||||
pattern: &'a PatternRef,
|
pattern: &'a PatternRef,
|
||||||
object: &'a [u8],
|
object: &'a [u8],
|
||||||
index: usize
|
index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DoublePropertyIter<'a> {
|
impl<'a> DoublePropertyIter<'a> {
|
||||||
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> DoublePropertyIter<'b> {
|
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> DoublePropertyIter<'b> {
|
||||||
DoublePropertyIter {
|
DoublePropertyIter { pattern, object, index: 0 }
|
||||||
pattern,
|
|
||||||
object,
|
|
||||||
index: 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_value(&self, index: usize) -> Option<f64> {
|
fn get_value(&self, index: usize) -> Option<f64> {
|
||||||
|
@ -250,7 +224,7 @@ impl<'a> DoublePropertyIter<'a> {
|
||||||
self.pattern.as_ptr(),
|
self.pattern.as_ptr(),
|
||||||
self.object.as_ptr() as *mut c_char,
|
self.object.as_ptr() as *mut c_char,
|
||||||
index as c_int,
|
index as c_int,
|
||||||
&mut value
|
&mut value,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -277,13 +251,13 @@ macro_rules! impl_property_iter_debug {
|
||||||
write!(f, "{}", val)?;
|
write!(f, "{}", val)?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => break
|
_ => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
write!(f, "]")
|
write!(f, "]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implement Iterator and Debug for a property iterator
|
/// Implement Iterator and Debug for a property iterator
|
||||||
|
@ -427,76 +401,6 @@ macro_rules! double_getter {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PatternRef {
|
impl PatternRef {
|
||||||
// Prints the pattern to stdout
|
|
||||||
//
|
|
||||||
// FontConfig doesn't expose a way to iterate over all members of a pattern;
|
|
||||||
// instead, we just defer to FcPatternPrint. Otherwise, this could have been
|
|
||||||
// a `fmt::Debug` impl.
|
|
||||||
pub fn print(&self) {
|
|
||||||
unsafe {
|
|
||||||
FcPatternPrint(self.as_ptr())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a string value to the pattern
|
|
||||||
///
|
|
||||||
/// If the returned value is `true`, the value is added at the end of
|
|
||||||
/// any existing list, otherwise it is inserted at the beginning.
|
|
||||||
///
|
|
||||||
/// # Unsafety
|
|
||||||
///
|
|
||||||
/// `object` is not checked to be a valid null-terminated string
|
|
||||||
unsafe fn add_string(&mut self, object: &[u8], value: &str) -> bool {
|
|
||||||
let value = CString::new(&value[..]).unwrap();
|
|
||||||
let value = value.as_ptr();
|
|
||||||
|
|
||||||
FcPatternAddString(
|
|
||||||
self.as_ptr(),
|
|
||||||
object.as_ptr() as *mut c_char,
|
|
||||||
value as *mut FcChar8
|
|
||||||
) == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn add_integer(&self, object: &[u8], int: isize) -> bool {
|
|
||||||
FcPatternAddInteger(
|
|
||||||
self.as_ptr(),
|
|
||||||
object.as_ptr() as *mut c_char,
|
|
||||||
int as c_int
|
|
||||||
) == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn add_double(&self, object: &[u8], value: f64) -> bool {
|
|
||||||
FcPatternAddDouble(
|
|
||||||
self.as_ptr(),
|
|
||||||
object.as_ptr() as *mut c_char,
|
|
||||||
value as c_double
|
|
||||||
) == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn get_string<'a>(&'a self, object: &'a [u8]) -> StringPropertyIter<'a> {
|
|
||||||
StringPropertyIter::new(self, object)
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn get_integer<'a>(&'a self, object: &'a [u8]) -> IntPropertyIter<'a> {
|
|
||||||
IntPropertyIter::new(self, object)
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn get_double<'a>(&'a self, object: &'a [u8]) -> DoublePropertyIter<'a> {
|
|
||||||
DoublePropertyIter::new(self, object)
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn get_boolean<'a>(&'a self, object: &'a [u8]) -> BooleanPropertyIter<'a> {
|
|
||||||
BooleanPropertyIter::new(self, object)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hintstyle(&self) -> HintStylePropertyIter {
|
|
||||||
HintStylePropertyIter::new(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lcdfilter(&self) -> LcdFilterPropertyIter {
|
|
||||||
LcdFilterPropertyIter::new(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean_getter! {
|
boolean_getter! {
|
||||||
antialias() => b"antialias\0",
|
antialias() => b"antialias\0",
|
||||||
hinting() => b"hinting\0",
|
hinting() => b"hinting\0",
|
||||||
|
@ -535,36 +439,85 @@ impl PatternRef {
|
||||||
[postscriptname, add_postscriptname] => b"postscriptname\0"
|
[postscriptname, add_postscriptname] => b"postscriptname\0"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pattern_get_integer! {
|
||||||
|
index() => b"index\0"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints the pattern to stdout
|
||||||
|
//
|
||||||
|
// FontConfig doesn't expose a way to iterate over all members of a pattern;
|
||||||
|
// instead, we just defer to FcPatternPrint. Otherwise, this could have been
|
||||||
|
// a `fmt::Debug` impl.
|
||||||
|
pub fn print(&self) {
|
||||||
|
unsafe { FcPatternPrint(self.as_ptr()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a string value to the pattern
|
||||||
|
///
|
||||||
|
/// If the returned value is `true`, the value is added at the end of
|
||||||
|
/// any existing list, otherwise it is inserted at the beginning.
|
||||||
|
///
|
||||||
|
/// # Unsafety
|
||||||
|
///
|
||||||
|
/// `object` is not checked to be a valid null-terminated string
|
||||||
|
unsafe fn add_string(&mut self, object: &[u8], value: &str) -> bool {
|
||||||
|
let value = CString::new(&value[..]).unwrap();
|
||||||
|
let value = value.as_ptr();
|
||||||
|
|
||||||
|
FcPatternAddString(self.as_ptr(), object.as_ptr() as *mut c_char, value as *mut FcChar8)
|
||||||
|
== 1
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn add_integer(&self, object: &[u8], int: isize) -> bool {
|
||||||
|
FcPatternAddInteger(self.as_ptr(), object.as_ptr() as *mut c_char, int as c_int) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn add_double(&self, object: &[u8], value: f64) -> bool {
|
||||||
|
FcPatternAddDouble(self.as_ptr(), object.as_ptr() as *mut c_char, value as c_double) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn get_string<'a>(&'a self, object: &'a [u8]) -> StringPropertyIter<'a> {
|
||||||
|
StringPropertyIter::new(self, object)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn get_integer<'a>(&'a self, object: &'a [u8]) -> IntPropertyIter<'a> {
|
||||||
|
IntPropertyIter::new(self, object)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn get_double<'a>(&'a self, object: &'a [u8]) -> DoublePropertyIter<'a> {
|
||||||
|
DoublePropertyIter::new(self, object)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn get_boolean<'a>(&'a self, object: &'a [u8]) -> BooleanPropertyIter<'a> {
|
||||||
|
BooleanPropertyIter::new(self, object)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hintstyle(&self) -> HintStylePropertyIter {
|
||||||
|
HintStylePropertyIter::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lcdfilter(&self) -> LcdFilterPropertyIter {
|
||||||
|
LcdFilterPropertyIter::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_slant(&mut self, slant: Slant) -> bool {
|
pub fn set_slant(&mut self, slant: Slant) -> bool {
|
||||||
unsafe {
|
unsafe { self.add_integer(b"slant\0", slant as isize) }
|
||||||
self.add_integer(b"slant\0", slant as isize)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_pixelsize(&mut self, size: f64) -> bool {
|
pub fn add_pixelsize(&mut self, size: f64) -> bool {
|
||||||
unsafe {
|
unsafe { self.add_double(b"pixelsize\0", size) }
|
||||||
self.add_double(b"pixelsize\0", size)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_weight(&mut self, weight: Weight) -> bool {
|
pub fn set_weight(&mut self, weight: Weight) -> bool {
|
||||||
unsafe {
|
unsafe { self.add_integer(b"weight\0", weight as isize) }
|
||||||
self.add_integer(b"weight\0", weight as isize)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_width(&mut self, width: Width) -> bool {
|
pub fn set_width(&mut self, width: Width) -> bool {
|
||||||
unsafe {
|
unsafe { self.add_integer(b"width\0", width.to_isize()) }
|
||||||
self.add_integer(b"width\0", width.to_isize())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_width(&self) -> Option<Width> {
|
pub fn get_width(&self) -> Option<Width> {
|
||||||
unsafe {
|
unsafe { self.get_integer(b"width\0").nth(0).map(Width::from) }
|
||||||
self.get_integer(b"width\0")
|
|
||||||
.nth(0)
|
|
||||||
.map(Width::from)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rgba(&self) -> RgbaPropertyIter {
|
pub fn rgba(&self) -> RgbaPropertyIter {
|
||||||
|
@ -572,9 +525,7 @@ impl PatternRef {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_rgba(&self, rgba: &Rgba) -> bool {
|
pub fn set_rgba(&self, rgba: &Rgba) -> bool {
|
||||||
unsafe {
|
unsafe { self.add_integer(b"rgba\0", rgba.to_isize()) }
|
||||||
self.add_integer(b"rgba\0", rgba.to_isize())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_prepare(&self, config: &ConfigRef, request: &PatternRef) -> Pattern {
|
pub fn render_prepare(&self, config: &ConfigRef, request: &PatternRef) -> Pattern {
|
||||||
|
@ -595,19 +546,13 @@ impl PatternRef {
|
||||||
FcPatternAddCharSet(
|
FcPatternAddCharSet(
|
||||||
self.as_ptr(),
|
self.as_ptr(),
|
||||||
b"charset\0".as_ptr() as *mut c_char,
|
b"charset\0".as_ptr() as *mut c_char,
|
||||||
charset.as_ptr()
|
charset.as_ptr(),
|
||||||
) == 1
|
) == 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn file(&self, index: usize) -> Option<PathBuf> {
|
pub fn file(&self, index: usize) -> Option<PathBuf> {
|
||||||
unsafe {
|
unsafe { self.get_string(b"file\0").nth(index) }.map(From::from)
|
||||||
self.get_string(b"file\0").nth(index)
|
|
||||||
}.map(From::from)
|
|
||||||
}
|
|
||||||
|
|
||||||
pattern_get_integer! {
|
|
||||||
index() => b"index\0"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn config_substitute(&mut self, config: &ConfigRef, kind: MatchKind) {
|
pub fn config_substitute(&mut self, config: &ConfigRef, kind: MatchKind) {
|
||||||
|
@ -622,4 +567,3 @@ impl PatternRef {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,19 +13,18 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
//! Rasterization powered by FreeType and FontConfig
|
//! Rasterization powered by FreeType and FontConfig
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::path::PathBuf;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use freetype::tt_os2::TrueTypeOS2Table;
|
use freetype::tt_os2::TrueTypeOS2Table;
|
||||||
use freetype::{self, Library};
|
use freetype::{self, Library};
|
||||||
use libc::c_uint;
|
use libc::c_uint;
|
||||||
|
|
||||||
|
|
||||||
pub mod fc;
|
pub mod fc;
|
||||||
|
|
||||||
use super::{FontDesc, RasterizedGlyph, Metrics, Size, FontKey, GlyphKey, Weight, Slant, Style};
|
use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight};
|
||||||
|
|
||||||
struct FixedSize {
|
struct FixedSize {
|
||||||
pixelsize: f64,
|
pixelsize: f64,
|
||||||
|
@ -37,7 +36,7 @@ struct Face {
|
||||||
load_flags: freetype::face::LoadFlag,
|
load_flags: freetype::face::LoadFlag,
|
||||||
render_mode: freetype::RenderMode,
|
render_mode: freetype::RenderMode,
|
||||||
lcd_filter: c_uint,
|
lcd_filter: c_uint,
|
||||||
non_scalable: Option<FixedSize>
|
non_scalable: Option<FixedSize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Face {
|
impl fmt::Debug for Face {
|
||||||
|
@ -46,14 +45,17 @@ impl fmt::Debug for Face {
|
||||||
.field("ft_face", &self.ft_face)
|
.field("ft_face", &self.ft_face)
|
||||||
.field("key", &self.key)
|
.field("key", &self.key)
|
||||||
.field("load_flags", &self.load_flags)
|
.field("load_flags", &self.load_flags)
|
||||||
.field("render_mode", &match self.render_mode {
|
.field(
|
||||||
freetype::RenderMode::Normal => "Normal",
|
"render_mode",
|
||||||
freetype::RenderMode::Light => "Light",
|
&match self.render_mode {
|
||||||
freetype::RenderMode::Mono => "Mono",
|
freetype::RenderMode::Normal => "Normal",
|
||||||
freetype::RenderMode::Lcd => "Lcd",
|
freetype::RenderMode::Light => "Light",
|
||||||
freetype::RenderMode::LcdV => "LcdV",
|
freetype::RenderMode::Mono => "Mono",
|
||||||
freetype::RenderMode::Max => "Max",
|
freetype::RenderMode::Lcd => "Lcd",
|
||||||
})
|
freetype::RenderMode::LcdV => "LcdV",
|
||||||
|
freetype::RenderMode::Max => "Max",
|
||||||
|
},
|
||||||
|
)
|
||||||
.field("lcd_filter", &self.lcd_filter)
|
.field("lcd_filter", &self.lcd_filter)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
|
@ -87,9 +89,7 @@ impl ::Rasterize for FreeTypeRasterizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn metrics(&self, key: FontKey, _size: Size) -> Result<Metrics, Error> {
|
fn metrics(&self, key: FontKey, _size: Size) -> Result<Metrics, Error> {
|
||||||
let face = self.faces
|
let face = self.faces.get(&key).ok_or(Error::FontNotLoaded)?;
|
||||||
.get(&key)
|
|
||||||
.ok_or(Error::FontNotLoaded)?;
|
|
||||||
let full = self.full_metrics(key)?;
|
let full = self.full_metrics(key)?;
|
||||||
|
|
||||||
let height = (full.size_metrics.height / 64) as f64;
|
let height = (full.size_metrics.height / 64) as f64;
|
||||||
|
@ -108,20 +108,19 @@ impl ::Rasterize for FreeTypeRasterizer {
|
||||||
|
|
||||||
// Get strikeout position and thickness in device pixels
|
// Get strikeout position and thickness in device pixels
|
||||||
let (strikeout_position, strikeout_thickness) =
|
let (strikeout_position, strikeout_thickness) =
|
||||||
match TrueTypeOS2Table::from_face(&mut face.ft_face.clone())
|
match TrueTypeOS2Table::from_face(&mut face.ft_face.clone()) {
|
||||||
{
|
Some(os2) => {
|
||||||
Some(os2) => {
|
let strikeout_position = f32::from(os2.y_strikeout_position()) * x_scale / 64.;
|
||||||
let strikeout_position = f32::from(os2.y_strikeout_position()) * x_scale / 64.;
|
let strikeout_thickness = f32::from(os2.y_strikeout_size()) * x_scale / 64.;
|
||||||
let strikeout_thickness = f32::from(os2.y_strikeout_size()) * x_scale / 64.;
|
(strikeout_position, strikeout_thickness)
|
||||||
(strikeout_position, strikeout_thickness)
|
},
|
||||||
},
|
_ => {
|
||||||
_ => {
|
// Fallback if font doesn't provide info about strikeout
|
||||||
// Fallback if font doesn't provide info about strikeout
|
trace!("Using fallback strikeout metrics");
|
||||||
trace!("Using fallback strikeout metrics");
|
let strikeout_position = height as f32 / 2. + descent;
|
||||||
let strikeout_position = height as f32 / 2. + descent;
|
(strikeout_position, underline_thickness)
|
||||||
(strikeout_position, underline_thickness)
|
},
|
||||||
},
|
};
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Metrics {
|
Ok(Metrics {
|
||||||
average_advance: full.cell_width,
|
average_advance: full.cell_width,
|
||||||
|
@ -154,6 +153,7 @@ pub trait IntoFontconfigType {
|
||||||
|
|
||||||
impl IntoFontconfigType for Slant {
|
impl IntoFontconfigType for Slant {
|
||||||
type FcType = fc::Slant;
|
type FcType = fc::Slant;
|
||||||
|
|
||||||
fn into_fontconfig_type(&self) -> Self::FcType {
|
fn into_fontconfig_type(&self) -> Self::FcType {
|
||||||
match *self {
|
match *self {
|
||||||
Slant::Normal => fc::Slant::Roman,
|
Slant::Normal => fc::Slant::Roman,
|
||||||
|
@ -176,7 +176,7 @@ impl IntoFontconfigType for Weight {
|
||||||
|
|
||||||
struct FullMetrics {
|
struct FullMetrics {
|
||||||
size_metrics: freetype::ffi::FT_Size_Metrics,
|
size_metrics: freetype::ffi::FT_Size_Metrics,
|
||||||
cell_width: f64
|
cell_width: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FreeTypeRasterizer {
|
impl FreeTypeRasterizer {
|
||||||
|
@ -189,31 +189,25 @@ impl FreeTypeRasterizer {
|
||||||
Style::Description { slant, weight } => {
|
Style::Description { slant, weight } => {
|
||||||
// Match nearest font
|
// Match nearest font
|
||||||
self.get_matching_face(&desc, slant, weight, size)
|
self.get_matching_face(&desc, slant, weight, size)
|
||||||
}
|
},
|
||||||
Style::Specific(ref style) => {
|
Style::Specific(ref style) => {
|
||||||
// If a name was specified, try and load specifically that font.
|
// If a name was specified, try and load specifically that font.
|
||||||
self.get_specific_face(&desc, &style, size)
|
self.get_specific_face(&desc, &style, size)
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn full_metrics(&self, key: FontKey) -> Result<FullMetrics, Error> {
|
fn full_metrics(&self, key: FontKey) -> Result<FullMetrics, Error> {
|
||||||
let face = self.faces
|
let face = self.faces.get(&key).ok_or(Error::FontNotLoaded)?;
|
||||||
.get(&key)
|
|
||||||
.ok_or(Error::FontNotLoaded)?;
|
|
||||||
|
|
||||||
let size_metrics = face.ft_face.size_metrics()
|
let size_metrics = face.ft_face.size_metrics().ok_or(Error::MissingSizeMetrics)?;
|
||||||
.ok_or(Error::MissingSizeMetrics)?;
|
|
||||||
|
|
||||||
let width = match face.ft_face.load_char('0' as usize, face.load_flags) {
|
let width = match face.ft_face.load_char('0' as usize, face.load_flags) {
|
||||||
Ok(_) => face.ft_face.glyph().metrics().horiAdvance / 64,
|
Ok(_) => face.ft_face.glyph().metrics().horiAdvance / 64,
|
||||||
Err(_) => size_metrics.max_advance / 64
|
Err(_) => size_metrics.max_advance / 64,
|
||||||
} as f64;
|
} as f64;
|
||||||
|
|
||||||
Ok(FullMetrics {
|
Ok(FullMetrics { size_metrics, cell_width: width })
|
||||||
size_metrics,
|
|
||||||
cell_width: width
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_matching_face(
|
fn get_matching_face(
|
||||||
|
@ -232,12 +226,9 @@ impl FreeTypeRasterizer {
|
||||||
let font = fc::font_match(fc::Config::get_current(), &mut pattern)
|
let font = fc::font_match(fc::Config::get_current(), &mut pattern)
|
||||||
.ok_or_else(|| Error::MissingFont(desc.to_owned()))?;
|
.ok_or_else(|| Error::MissingFont(desc.to_owned()))?;
|
||||||
|
|
||||||
self.face_from_pattern(&font)
|
self.face_from_pattern(&font).and_then(|pattern| {
|
||||||
.and_then(|pattern| {
|
pattern.map(Ok).unwrap_or_else(|| Err(Error::MissingFont(desc.to_owned())))
|
||||||
pattern
|
})
|
||||||
.map(Ok)
|
|
||||||
.unwrap_or_else(|| Err(Error::MissingFont(desc.to_owned())))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_specific_face(
|
fn get_specific_face(
|
||||||
|
@ -253,12 +244,9 @@ impl FreeTypeRasterizer {
|
||||||
|
|
||||||
let font = fc::font_match(fc::Config::get_current(), &mut pattern)
|
let font = fc::font_match(fc::Config::get_current(), &mut pattern)
|
||||||
.ok_or_else(|| Error::MissingFont(desc.to_owned()))?;
|
.ok_or_else(|| Error::MissingFont(desc.to_owned()))?;
|
||||||
self.face_from_pattern(&font)
|
self.face_from_pattern(&font).and_then(|pattern| {
|
||||||
.and_then(|pattern| {
|
pattern.map(Ok).unwrap_or_else(|| Err(Error::MissingFont(desc.to_owned())))
|
||||||
pattern
|
})
|
||||||
.map(Ok)
|
|
||||||
.unwrap_or_else(|| Err(Error::MissingFont(desc.to_owned())))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn face_from_pattern(&mut self, pattern: &fc::Pattern) -> Result<Option<FontKey>, Error> {
|
fn face_from_pattern(&mut self, pattern: &fc::Pattern) -> Result<Option<FontKey>, Error> {
|
||||||
|
@ -277,9 +265,7 @@ impl FreeTypeRasterizer {
|
||||||
let mut pixelsize = pattern.pixelsize();
|
let mut pixelsize = pattern.pixelsize();
|
||||||
debug!("pixelsizes: {:?}", pixelsize);
|
debug!("pixelsizes: {:?}", pixelsize);
|
||||||
|
|
||||||
Some(FixedSize {
|
Some(FixedSize { pixelsize: pixelsize.next().expect("has 1+ pixelsize") })
|
||||||
pixelsize: pixelsize.next().expect("has 1+ pixelsize"),
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let face = Face {
|
let face = Face {
|
||||||
|
@ -303,7 +289,11 @@ impl FreeTypeRasterizer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn face_for_glyph(&mut self, glyph_key: GlyphKey, have_recursed: bool) -> Result<FontKey, Error> {
|
fn face_for_glyph(
|
||||||
|
&mut self,
|
||||||
|
glyph_key: GlyphKey,
|
||||||
|
have_recursed: bool,
|
||||||
|
) -> Result<FontKey, Error> {
|
||||||
let c = glyph_key.c;
|
let c = glyph_key.c;
|
||||||
|
|
||||||
let use_initial_face = if let Some(face) = self.faces.get(&glyph_key.font_key) {
|
let use_initial_face = if let Some(face) = self.faces.get(&glyph_key.font_key) {
|
||||||
|
@ -322,8 +312,7 @@ impl FreeTypeRasterizer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_rendered_glyph(&mut self, glyph_key: GlyphKey)
|
fn get_rendered_glyph(&mut self, glyph_key: GlyphKey) -> Result<RasterizedGlyph, Error> {
|
||||||
-> Result<RasterizedGlyph, Error> {
|
|
||||||
// Render a custom symbol for the underline and beam cursor
|
// Render a custom symbol for the underline and beam cursor
|
||||||
match glyph_key.c {
|
match glyph_key.c {
|
||||||
super::UNDERLINE_CURSOR_CHAR => {
|
super::UNDERLINE_CURSOR_CHAR => {
|
||||||
|
@ -370,9 +359,10 @@ impl FreeTypeRasterizer {
|
||||||
let face = &self.faces[&font_key];
|
let face = &self.faces[&font_key];
|
||||||
let index = face.ft_face.get_char_index(glyph_key.c as usize);
|
let index = face.ft_face.get_char_index(glyph_key.c as usize);
|
||||||
|
|
||||||
let size = face.non_scalable.as_ref()
|
let size =
|
||||||
.map(|v| v.pixelsize as f32)
|
face.non_scalable.as_ref().map(|v| v.pixelsize as f32).unwrap_or_else(|| {
|
||||||
.unwrap_or_else(|| glyph_key.size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.);
|
glyph_key.size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.
|
||||||
|
});
|
||||||
|
|
||||||
face.ft_face.set_char_size(to_freetype_26_6(size), 0, 0, 0)?;
|
face.ft_face.set_char_size(to_freetype_26_6(size), 0, 0, 0)?;
|
||||||
|
|
||||||
|
@ -405,7 +395,7 @@ impl FreeTypeRasterizer {
|
||||||
use freetype::face::LoadFlag;
|
use freetype::face::LoadFlag;
|
||||||
match (antialias, hinting, rgba) {
|
match (antialias, hinting, rgba) {
|
||||||
(false, fc::HintStyle::None, _) => LoadFlag::NO_HINTING | LoadFlag::MONOCHROME,
|
(false, fc::HintStyle::None, _) => LoadFlag::NO_HINTING | LoadFlag::MONOCHROME,
|
||||||
(false, _, _) => LoadFlag::TARGET_MONO | LoadFlag::MONOCHROME,
|
(false, ..) => LoadFlag::TARGET_MONO | LoadFlag::MONOCHROME,
|
||||||
(true, fc::HintStyle::None, _) => LoadFlag::NO_HINTING | LoadFlag::TARGET_NORMAL,
|
(true, fc::HintStyle::None, _) => LoadFlag::NO_HINTING | LoadFlag::TARGET_NORMAL,
|
||||||
// hintslight does *not* use LCD hinting even when a subpixel mode
|
// hintslight does *not* use LCD hinting even when a subpixel mode
|
||||||
// is selected.
|
// is selected.
|
||||||
|
@ -459,7 +449,9 @@ impl FreeTypeRasterizer {
|
||||||
/// Given a FreeType `Bitmap`, returns packed buffer with 1 byte per LCD channel.
|
/// Given a FreeType `Bitmap`, returns packed buffer with 1 byte per LCD channel.
|
||||||
///
|
///
|
||||||
/// The i32 value in the return type is the number of pixels per row.
|
/// The i32 value in the return type is the number of pixels per row.
|
||||||
fn normalize_buffer(bitmap: &freetype::bitmap::Bitmap) -> freetype::FtResult<(i32, i32, Vec<u8>)> {
|
fn normalize_buffer(
|
||||||
|
bitmap: &freetype::bitmap::Bitmap,
|
||||||
|
) -> freetype::FtResult<(i32, i32, Vec<u8>)> {
|
||||||
use freetype::bitmap::PixelMode;
|
use freetype::bitmap::PixelMode;
|
||||||
|
|
||||||
let buf = bitmap.buffer();
|
let buf = bitmap.buffer();
|
||||||
|
@ -475,7 +467,7 @@ impl FreeTypeRasterizer {
|
||||||
Ok((bitmap.rows(), bitmap.width() / 3, packed))
|
Ok((bitmap.rows(), bitmap.width() / 3, packed))
|
||||||
},
|
},
|
||||||
PixelMode::LcdV => {
|
PixelMode::LcdV => {
|
||||||
for i in 0..bitmap.rows()/3 {
|
for i in 0..bitmap.rows() / 3 {
|
||||||
for j in 0..bitmap.width() {
|
for j in 0..bitmap.width() {
|
||||||
for k in 0..3 {
|
for k in 0..3 {
|
||||||
let offset = ((i as usize) * 3 + k) * pitch + (j as usize);
|
let offset = ((i as usize) * 3 + k) * pitch + (j as usize);
|
||||||
|
@ -529,14 +521,11 @@ impl FreeTypeRasterizer {
|
||||||
}
|
}
|
||||||
Ok((bitmap.rows(), bitmap.width(), packed))
|
Ok((bitmap.rows(), bitmap.width(), packed))
|
||||||
},
|
},
|
||||||
mode => panic!("unhandled pixel mode: {:?}", mode)
|
mode => panic!("unhandled pixel mode: {:?}", mode),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_face_with_glyph(
|
fn load_face_with_glyph(&mut self, glyph: char) -> Result<FontKey, Error> {
|
||||||
&mut self,
|
|
||||||
glyph: char,
|
|
||||||
) -> Result<FontKey, Error> {
|
|
||||||
let mut charset = fc::CharSet::new();
|
let mut charset = fc::CharSet::new();
|
||||||
charset.add(glyph);
|
charset.add(glyph);
|
||||||
let mut pattern = fc::Pattern::new();
|
let mut pattern = fc::Pattern::new();
|
||||||
|
@ -560,19 +549,19 @@ impl FreeTypeRasterizer {
|
||||||
// and index above.
|
// and index above.
|
||||||
let key = self.face_from_pattern(&pattern)?.unwrap();
|
let key = self.face_from_pattern(&pattern)?.unwrap();
|
||||||
Ok(key)
|
Ok(key)
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
Err(Error::MissingFont(FontDesc::new(
|
||||||
Err(Error::MissingFont(
|
"fallback-without-path",
|
||||||
FontDesc::new("fallback-without-path", Style::Specific(glyph.to_string()))))
|
Style::Specific(glyph.to_string()),
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => {
|
None => Err(Error::MissingFont(FontDesc::new(
|
||||||
Err(Error::MissingFont(
|
"no-fallback-for",
|
||||||
FontDesc::new("no-fallback-for", Style::Specific(glyph.to_string()))
|
Style::Specific(glyph.to_string()),
|
||||||
))
|
))),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -614,19 +603,17 @@ impl ::std::error::Error for Error {
|
||||||
impl ::std::fmt::Display for Error {
|
impl ::std::fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
Error::FreeType(ref err) => {
|
Error::FreeType(ref err) => err.fmt(f),
|
||||||
err.fmt(f)
|
Error::MissingFont(ref desc) => write!(
|
||||||
},
|
f,
|
||||||
Error::MissingFont(ref desc) => {
|
"Couldn't find a font with {}\n\tPlease check the font config in your \
|
||||||
write!(f, "Couldn't find a font with {}\
|
alacritty.yml.",
|
||||||
\n\tPlease check the font config in your alacritty.yml.", desc)
|
desc
|
||||||
},
|
),
|
||||||
Error::FontNotLoaded => {
|
Error::FontNotLoaded => f.write_str("Tried to use a font that hasn't been loaded"),
|
||||||
f.write_str("Tried to use a font that hasn't been loaded")
|
|
||||||
},
|
|
||||||
Error::MissingSizeMetrics => {
|
Error::MissingSizeMetrics => {
|
||||||
f.write_str("Tried to get size metrics from a face without a size")
|
f.write_str("Tried to get size metrics from a face without a size")
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,8 +46,8 @@ extern crate foreign_types;
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::{fmt, cmp};
|
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
use std::{cmp, fmt};
|
||||||
|
|
||||||
// If target isn't macos or windows, reexport everything from ft
|
// If target isn't macos or windows, reexport everything from ft
|
||||||
#[cfg(not(any(target_os = "macos", windows)))]
|
#[cfg(not(any(target_os = "macos", windows)))]
|
||||||
|
@ -113,7 +113,7 @@ impl fmt::Display for Style {
|
||||||
Style::Specific(ref s) => f.write_str(&s),
|
Style::Specific(ref s) => f.write_str(&s),
|
||||||
Style::Description { slant, weight } => {
|
Style::Description { slant, weight } => {
|
||||||
write!(f, "slant={:?}, weight={:?}", slant, weight)
|
write!(f, "slant={:?}, weight={:?}", slant, weight)
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,10 +123,7 @@ impl FontDesc {
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
FontDesc {
|
FontDesc { name: name.into(), style }
|
||||||
name: name.into(),
|
|
||||||
style,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,9 +146,7 @@ impl FontKey {
|
||||||
pub fn next() -> FontKey {
|
pub fn next() -> FontKey {
|
||||||
static TOKEN: AtomicUsize = AtomicUsize::new(0);
|
static TOKEN: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
FontKey {
|
FontKey { token: TOKEN.fetch_add(1, Ordering::SeqCst) as _ }
|
||||||
token: TOKEN.fetch_add(1, Ordering::SeqCst) as _,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +165,8 @@ impl Hash for GlyphKey {
|
||||||
// - If GlyphKey ever becomes a different size, this will fail to compile
|
// - If GlyphKey ever becomes a different size, this will fail to compile
|
||||||
// - Result is being used for hashing and has no fields (it's a u64)
|
// - Result is being used for hashing and has no fields (it's a u64)
|
||||||
::std::mem::transmute::<GlyphKey, u64>(*self)
|
::std::mem::transmute::<GlyphKey, u64>(*self)
|
||||||
}.hash(state);
|
}
|
||||||
|
.hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,14 +224,7 @@ pub struct RasterizedGlyph {
|
||||||
|
|
||||||
impl Default for RasterizedGlyph {
|
impl Default for RasterizedGlyph {
|
||||||
fn default() -> RasterizedGlyph {
|
fn default() -> RasterizedGlyph {
|
||||||
RasterizedGlyph {
|
RasterizedGlyph { c: ' ', width: 0, height: 0, top: 0, left: 0, buf: Vec::new() }
|
||||||
c: ' ',
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
buf: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,8 +277,11 @@ pub fn get_box_cursor_glyph(
|
||||||
let mut buf = Vec::with_capacity((width * height * 3) as usize);
|
let mut buf = Vec::with_capacity((width * height * 3) as usize);
|
||||||
for y in 0..height {
|
for y in 0..height {
|
||||||
for x in 0..width {
|
for x in 0..width {
|
||||||
if y < border_width || y >= height - border_width ||
|
if y < border_width
|
||||||
x < border_width || x >= width - border_width {
|
|| y >= height - border_width
|
||||||
|
|| x < border_width
|
||||||
|
|| x >= width - border_width
|
||||||
|
{
|
||||||
buf.append(&mut vec![255u8; 3]);
|
buf.append(&mut vec![255u8; 3]);
|
||||||
} else {
|
} else {
|
||||||
buf.append(&mut vec![0u8; 3]);
|
buf.append(&mut vec![0u8; 3]);
|
||||||
|
@ -298,24 +290,14 @@ pub fn get_box_cursor_glyph(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a custom glyph with the rectangle data attached to it
|
// Create a custom glyph with the rectangle data attached to it
|
||||||
Ok(RasterizedGlyph {
|
Ok(RasterizedGlyph { c: BOX_CURSOR_CHAR, top: ascent, left: 0, height, width, buf })
|
||||||
c: BOX_CURSOR_CHAR,
|
|
||||||
top: ascent,
|
|
||||||
left: 0,
|
|
||||||
height,
|
|
||||||
width,
|
|
||||||
buf,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BufDebugger<'a>(&'a [u8]);
|
struct BufDebugger<'a>(&'a [u8]);
|
||||||
|
|
||||||
impl<'a> fmt::Debug for BufDebugger<'a> {
|
impl<'a> fmt::Debug for BufDebugger<'a> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
f.debug_struct("GlyphBuffer")
|
f.debug_struct("GlyphBuffer").field("len", &self.0.len()).field("bytes", &self.0).finish()
|
||||||
.field("len", &self.0.len())
|
|
||||||
.field("bytes", &self.0)
|
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,10 +15,7 @@ impl crate::Rasterize for RustTypeRasterizer {
|
||||||
type Err = Error;
|
type Err = Error;
|
||||||
|
|
||||||
fn new(device_pixel_ratio: f32, _: bool) -> Result<RustTypeRasterizer, Error> {
|
fn new(device_pixel_ratio: f32, _: bool) -> Result<RustTypeRasterizer, Error> {
|
||||||
Ok(RustTypeRasterizer {
|
Ok(RustTypeRasterizer { fonts: Vec::new(), dpi_ratio: device_pixel_ratio })
|
||||||
fonts: Vec::new(),
|
|
||||||
dpi_ratio: device_pixel_ratio,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn metrics(&self, key: FontKey, size: Size) -> Result<Metrics, Error> {
|
fn metrics(&self, key: FontKey, size: Size) -> Result<Metrics, Error> {
|
||||||
|
@ -56,17 +53,13 @@ impl crate::Rasterize for RustTypeRasterizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_font(&mut self, desc: &FontDesc, _size: Size) -> Result<FontKey, Error> {
|
fn load_font(&mut self, desc: &FontDesc, _size: Size) -> Result<FontKey, Error> {
|
||||||
let fp = system_fonts::FontPropertyBuilder::new()
|
let fp = system_fonts::FontPropertyBuilder::new().family(&desc.name).monospace();
|
||||||
.family(&desc.name)
|
|
||||||
.monospace();
|
|
||||||
|
|
||||||
let fp = match desc.style {
|
let fp = match desc.style {
|
||||||
Style::Specific(ref style) => {
|
Style::Specific(ref style) => match style.to_lowercase().as_str() {
|
||||||
match style.to_lowercase().as_str() {
|
"italic" => fp.italic(),
|
||||||
"italic" => fp.italic(),
|
"bold" => fp.bold(),
|
||||||
"bold" => fp.bold(),
|
_ => fp,
|
||||||
_ => fp,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Style::Description { slant, weight } => {
|
Style::Description { slant, weight } => {
|
||||||
let fp = match slant {
|
let fp = match slant {
|
||||||
|
@ -79,52 +72,52 @@ impl crate::Rasterize for RustTypeRasterizer {
|
||||||
Weight::Bold => fp.bold(),
|
Weight::Bold => fp.bold(),
|
||||||
Weight::Normal => fp,
|
Weight::Normal => fp,
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
self.fonts.push(FontCollection::from_bytes(
|
self.fonts.push(
|
||||||
system_fonts::get(&fp.build())
|
FontCollection::from_bytes(
|
||||||
.ok_or_else(|| Error::MissingFont(desc.clone()))?
|
system_fonts::get(&fp.build()).ok_or_else(|| Error::MissingFont(desc.clone()))?.0,
|
||||||
.0,
|
)
|
||||||
).into_font()
|
.into_font()
|
||||||
.ok_or(Error::UnsupportedFont)?);
|
.ok_or(Error::UnsupportedFont)?,
|
||||||
Ok(FontKey {
|
);
|
||||||
token: (self.fonts.len() - 1) as u16,
|
Ok(FontKey { token: (self.fonts.len() - 1) as u16 })
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_glyph(&mut self, glyph_key: GlyphKey) -> Result<RasterizedGlyph, Error> {
|
fn get_glyph(&mut self, glyph_key: GlyphKey) -> Result<RasterizedGlyph, Error> {
|
||||||
match glyph_key.c {
|
match glyph_key.c {
|
||||||
super::UNDERLINE_CURSOR_CHAR => {
|
super::UNDERLINE_CURSOR_CHAR => {
|
||||||
let metrics = self.metrics(glyph_key.font_key, glyph_key.size)?;
|
let metrics = self.metrics(glyph_key.font_key, glyph_key.size)?;
|
||||||
return super::get_underline_cursor_glyph(metrics.descent as i32, metrics.average_advance as i32);
|
return super::get_underline_cursor_glyph(
|
||||||
}
|
metrics.descent as i32,
|
||||||
|
metrics.average_advance as i32,
|
||||||
|
);
|
||||||
|
},
|
||||||
super::BEAM_CURSOR_CHAR => {
|
super::BEAM_CURSOR_CHAR => {
|
||||||
let metrics = self.metrics(glyph_key.font_key, glyph_key.size)?;
|
let metrics = self.metrics(glyph_key.font_key, glyph_key.size)?;
|
||||||
|
|
||||||
return super::get_beam_cursor_glyph(
|
return super::get_beam_cursor_glyph(
|
||||||
(metrics.line_height + f64::from(metrics.descent)).round() as i32,
|
(metrics.line_height + f64::from(metrics.descent)).round() as i32,
|
||||||
metrics.line_height.round() as i32,
|
metrics.line_height.round() as i32,
|
||||||
metrics.average_advance.round() as i32
|
metrics.average_advance.round() as i32,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
super::BOX_CURSOR_CHAR => {
|
super::BOX_CURSOR_CHAR => {
|
||||||
let metrics = self.metrics(glyph_key.font_key, glyph_key.size)?;
|
let metrics = self.metrics(glyph_key.font_key, glyph_key.size)?;
|
||||||
|
|
||||||
return super::get_box_cursor_glyph(
|
return super::get_box_cursor_glyph(
|
||||||
(metrics.line_height + f64::from(metrics.descent)).round() as i32,
|
(metrics.line_height + f64::from(metrics.descent)).round() as i32,
|
||||||
metrics.line_height.round() as i32,
|
metrics.line_height.round() as i32,
|
||||||
metrics.average_advance.round() as i32
|
metrics.average_advance.round() as i32,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
_ => ()
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
let scaled_glyph = self.fonts[glyph_key.font_key.token as usize]
|
let scaled_glyph = self.fonts[glyph_key.font_key.token as usize]
|
||||||
.glyph(glyph_key.c)
|
.glyph(glyph_key.c)
|
||||||
.ok_or(Error::MissingGlyph)?
|
.ok_or(Error::MissingGlyph)?
|
||||||
.scaled(Scale::uniform(
|
.scaled(Scale::uniform(glyph_key.size.as_f32_pts() * self.dpi_ratio * 96. / 72.));
|
||||||
glyph_key.size.as_f32_pts() * self.dpi_ratio * 96. / 72.,
|
|
||||||
));
|
|
||||||
|
|
||||||
let glyph = scaled_glyph.positioned(point(0.0, 0.0));
|
let glyph = scaled_glyph.positioned(point(0.0, 0.0));
|
||||||
|
|
||||||
|
@ -132,10 +125,7 @@ impl crate::Rasterize for RustTypeRasterizer {
|
||||||
let bb = match glyph.pixel_bounding_box() {
|
let bb = match glyph.pixel_bounding_box() {
|
||||||
Some(bb) => bb,
|
Some(bb) => bb,
|
||||||
// Bounding box calculation fails for spaces so we provide a placeholder bounding box
|
// Bounding box calculation fails for spaces so we provide a placeholder bounding box
|
||||||
None => rusttype::Rect {
|
None => rusttype::Rect { min: point(0, 0), max: point(0, 0) },
|
||||||
min: point(0, 0),
|
|
||||||
max: point(0, 0),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut buf = Vec::with_capacity((bb.width() * bb.height()) as usize);
|
let mut buf = Vec::with_capacity((bb.width() * bb.height()) as usize);
|
||||||
|
@ -185,8 +175,8 @@ impl ::std::fmt::Display for Error {
|
||||||
match *self {
|
match *self {
|
||||||
Error::MissingFont(ref desc) => write!(
|
Error::MissingFont(ref desc) => write!(
|
||||||
f,
|
f,
|
||||||
"Couldn't find a font with {}\
|
"Couldn't find a font with {}\n\tPlease check the font config in your \
|
||||||
\n\tPlease check the font config in your alacritty.yml.",
|
alacritty.yml.",
|
||||||
desc
|
desc
|
||||||
),
|
),
|
||||||
Error::UnsupportedFont => write!(
|
Error::UnsupportedFont => write!(
|
||||||
|
@ -195,7 +185,7 @@ impl ::std::fmt::Display for Error {
|
||||||
),
|
),
|
||||||
Error::UnsupportedStyle => {
|
Error::UnsupportedStyle => {
|
||||||
write!(f, "The selected font style is not supported by rusttype.")
|
write!(f, "The selected font style is not supported by rusttype.")
|
||||||
}
|
},
|
||||||
Error::MissingGlyph => write!(f, "The selected font did not have the requested glyph."),
|
Error::MissingGlyph => write!(f, "The selected font did not have the requested glyph."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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::ops::Range;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
use vte;
|
use crate::index::{Column, Contains, Line};
|
||||||
use base64;
|
use base64;
|
||||||
use glutin::MouseCursor;
|
use glutin::MouseCursor;
|
||||||
use crate::index::{Column, Line, Contains};
|
use vte;
|
||||||
|
|
||||||
use crate::term::color::Rgb;
|
use crate::term::color::Rgb;
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ fn parse_rgb_color(color: &[u8]) -> Option<Rgb> {
|
||||||
macro_rules! next {
|
macro_rules! next {
|
||||||
() => {
|
() => {
|
||||||
iter.next().map(|v| *v as char)
|
iter.next().map(|v| *v as char)
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! parse_hex {
|
macro_rules! parse_hex {
|
||||||
|
@ -50,32 +50,36 @@ fn parse_rgb_color(color: &[u8]) -> Option<Rgb> {
|
||||||
digit += value as u8;
|
digit += value as u8;
|
||||||
}
|
}
|
||||||
digit
|
digit
|
||||||
}}
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
match next!() {
|
match next!() {
|
||||||
Some('r') => {
|
Some('r') => {
|
||||||
if next!() != Some('g') { return None; }
|
if next!() != Some('g') {
|
||||||
if next!() != Some('b') { return None; }
|
return None;
|
||||||
if next!() != Some(':') { return None; }
|
}
|
||||||
|
if next!() != Some('b') {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if next!() != Some(':') {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
let r = parse_hex!();
|
let r = parse_hex!();
|
||||||
let val = next!();
|
let val = next!();
|
||||||
if val != Some('/') { return None; }
|
if val != Some('/') {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
let g = parse_hex!();
|
let g = parse_hex!();
|
||||||
if next!() != Some('/') { return None; }
|
if next!() != Some('/') {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
let b = parse_hex!();
|
let b = parse_hex!();
|
||||||
|
|
||||||
Some(Rgb { r, g, b })
|
Some(Rgb { r, g, b })
|
||||||
}
|
},
|
||||||
Some('#') => {
|
Some('#') => Some(Rgb { r: parse_hex!(), g: parse_hex!(), b: parse_hex!() }),
|
||||||
Some(Rgb {
|
_ => None,
|
||||||
r: parse_hex!(),
|
|
||||||
g: parse_hex!(),
|
|
||||||
b: parse_hex!(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
_ => None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +110,7 @@ pub struct Processor {
|
||||||
|
|
||||||
/// Internal state for VTE processor
|
/// Internal state for VTE processor
|
||||||
struct ProcessorState {
|
struct ProcessorState {
|
||||||
preceding_char: Option<char>
|
preceding_char: Option<char>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper type that implements `vte::Perform`.
|
/// Helper type that implements `vte::Perform`.
|
||||||
|
@ -116,7 +120,7 @@ struct ProcessorState {
|
||||||
struct Performer<'a, H: Handler + TermInfo, W: io::Write> {
|
struct Performer<'a, H: Handler + TermInfo, W: io::Write> {
|
||||||
_state: &'a mut ProcessorState,
|
_state: &'a mut ProcessorState,
|
||||||
handler: &'a mut H,
|
handler: &'a mut H,
|
||||||
writer: &'a mut W
|
writer: &'a mut W,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, H: Handler + TermInfo + 'a, W: io::Write> Performer<'a, H, W> {
|
impl<'a, H: Handler + TermInfo + 'a, W: io::Write> Performer<'a, H, W> {
|
||||||
|
@ -127,20 +131,13 @@ impl<'a, H: Handler + TermInfo + 'a, W: io::Write> Performer<'a, H, W> {
|
||||||
handler: &'b mut H,
|
handler: &'b mut H,
|
||||||
writer: &'b mut W,
|
writer: &'b mut W,
|
||||||
) -> Performer<'b, H, W> {
|
) -> Performer<'b, H, W> {
|
||||||
Performer {
|
Performer { _state: state, handler, writer }
|
||||||
_state: state,
|
|
||||||
handler,
|
|
||||||
writer,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Processor {
|
impl Default for Processor {
|
||||||
fn default() -> Processor {
|
fn default() -> Processor {
|
||||||
Processor {
|
Processor { state: ProcessorState { preceding_char: None }, parser: vte::Parser::new() }
|
||||||
state: ProcessorState { preceding_char: None },
|
|
||||||
parser: vte::Parser::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,21 +147,16 @@ impl Processor {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn advance<H, W>(
|
pub fn advance<H, W>(&mut self, handler: &mut H, byte: u8, writer: &mut W)
|
||||||
&mut self,
|
where
|
||||||
handler: &mut H,
|
H: Handler + TermInfo,
|
||||||
byte: u8,
|
W: io::Write,
|
||||||
writer: &mut W
|
|
||||||
)
|
|
||||||
where H: Handler + TermInfo,
|
|
||||||
W: io::Write
|
|
||||||
{
|
{
|
||||||
let mut performer = Performer::new(&mut self.state, handler, writer);
|
let mut performer = Performer::new(&mut self.state, handler, writer);
|
||||||
self.parser.advance(&mut performer, byte);
|
self.parser.advance(&mut performer, byte);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Trait that provides properties of terminal
|
/// Trait that provides properties of terminal
|
||||||
pub trait TermInfo {
|
pub trait TermInfo {
|
||||||
fn lines(&self) -> Line;
|
fn lines(&self) -> Line;
|
||||||
|
@ -447,14 +439,14 @@ impl Mode {
|
||||||
2004 => Mode::BracketedPaste,
|
2004 => Mode::BracketedPaste,
|
||||||
_ => {
|
_ => {
|
||||||
trace!("[unimplemented] primitive mode: {}", num);
|
trace!("[unimplemented] primitive mode: {}", num);
|
||||||
return None
|
return None;
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Some(match num {
|
Some(match num {
|
||||||
4 => Mode::Insert,
|
4 => Mode::Insert,
|
||||||
20 => Mode::LineFeedNewLine,
|
20 => Mode::LineFeedNewLine,
|
||||||
_ => return None
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -485,7 +477,7 @@ pub enum ClearMode {
|
||||||
/// Clear entire terminal
|
/// Clear entire terminal
|
||||||
All,
|
All,
|
||||||
/// Clear 'saved' lines (scrollback)
|
/// Clear 'saved' lines (scrollback)
|
||||||
Saved
|
Saved,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mode for clearing tab stops
|
/// Mode for clearing tab stops
|
||||||
|
@ -586,7 +578,7 @@ impl NamedColor {
|
||||||
NamedColor::DimMagenta => NamedColor::Magenta,
|
NamedColor::DimMagenta => NamedColor::Magenta,
|
||||||
NamedColor::DimCyan => NamedColor::Cyan,
|
NamedColor::DimCyan => NamedColor::Cyan,
|
||||||
NamedColor::DimWhite => NamedColor::White,
|
NamedColor::DimWhite => NamedColor::White,
|
||||||
val => val
|
val => val,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -610,7 +602,7 @@ impl NamedColor {
|
||||||
NamedColor::BrightCyan => NamedColor::Cyan,
|
NamedColor::BrightCyan => NamedColor::Cyan,
|
||||||
NamedColor::BrightWhite => NamedColor::White,
|
NamedColor::BrightWhite => NamedColor::White,
|
||||||
NamedColor::BrightForeground => NamedColor::Foreground,
|
NamedColor::BrightForeground => NamedColor::Foreground,
|
||||||
val => val
|
val => val,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -697,8 +689,9 @@ impl Default for StandardCharset {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, H, W> vte::Perform for Performer<'a, H, W>
|
impl<'a, H, W> vte::Perform for Performer<'a, H, W>
|
||||||
where H: Handler + TermInfo + 'a,
|
where
|
||||||
W: io::Write + 'a
|
H: Handler + TermInfo + 'a,
|
||||||
|
W: io::Write + 'a,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn print(&mut self, c: char) {
|
fn print(&mut self, c: char) {
|
||||||
|
@ -720,14 +713,16 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
|
||||||
C1::NEL => self.handler.newline(),
|
C1::NEL => self.handler.newline(),
|
||||||
C1::HTS => self.handler.set_horizontal_tabstop(),
|
C1::HTS => self.handler.set_horizontal_tabstop(),
|
||||||
C1::DECID => self.handler.identify_terminal(self.writer),
|
C1::DECID => self.handler.identify_terminal(self.writer),
|
||||||
_ => debug!("[unhandled] execute byte={:02x}", byte)
|
_ => debug!("[unhandled] execute byte={:02x}", byte),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool) {
|
fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool) {
|
||||||
debug!("[unhandled hook] params={:?}, ints: {:?}, ignore: {:?}",
|
debug!(
|
||||||
params, intermediates, ignore);
|
"[unhandled hook] params={:?}, ints: {:?}, ignore: {:?}",
|
||||||
|
params, intermediates, ignore
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -788,7 +783,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unhandled(params);
|
unhandled(params);
|
||||||
}
|
},
|
||||||
|
|
||||||
// Set foreground color
|
// Set foreground color
|
||||||
b"10" => {
|
b"10" => {
|
||||||
|
@ -799,7 +794,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unhandled(params);
|
unhandled(params);
|
||||||
}
|
},
|
||||||
|
|
||||||
// Set background color
|
// Set background color
|
||||||
b"11" => {
|
b"11" => {
|
||||||
|
@ -810,7 +805,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unhandled(params);
|
unhandled(params);
|
||||||
}
|
},
|
||||||
|
|
||||||
// Set text cursor color
|
// Set text cursor color
|
||||||
b"12" => {
|
b"12" => {
|
||||||
|
@ -821,11 +816,14 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unhandled(params);
|
unhandled(params);
|
||||||
}
|
},
|
||||||
|
|
||||||
// Set cursor style
|
// Set cursor style
|
||||||
b"50" => {
|
b"50" => {
|
||||||
if params.len() >= 2 && params[1].len() >= 13 && params[1][0..12] == *b"CursorShape=" {
|
if params.len() >= 2
|
||||||
|
&& params[1].len() >= 13
|
||||||
|
&& params[1][0..12] == *b"CursorShape="
|
||||||
|
{
|
||||||
let style = match params[1][12] as char {
|
let style = match params[1][12] as char {
|
||||||
'0' => CursorStyle::Block,
|
'0' => CursorStyle::Block,
|
||||||
'1' => CursorStyle::Beam,
|
'1' => CursorStyle::Beam,
|
||||||
|
@ -836,7 +834,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
unhandled(params);
|
unhandled(params);
|
||||||
}
|
},
|
||||||
|
|
||||||
// Set clipboard
|
// Set clipboard
|
||||||
b"52" => {
|
b"52" => {
|
||||||
|
@ -852,9 +850,9 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
|
||||||
self.handler.set_clipboard(utf8_string);
|
self.handler.set_clipboard(utf8_string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
// Reset color index
|
// Reset color index
|
||||||
b"104" => {
|
b"104" => {
|
||||||
|
@ -873,7 +871,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
|
||||||
None => unhandled(params),
|
None => unhandled(params),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
// Reset foreground color
|
// Reset foreground color
|
||||||
b"110" => self.handler.reset_color(NamedColor::Foreground as usize),
|
b"110" => self.handler.reset_color(NamedColor::Foreground as usize),
|
||||||
|
@ -889,35 +887,27 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn csi_dispatch(
|
fn csi_dispatch(&mut self, args: &[i64], intermediates: &[u8], _ignore: bool, action: char) {
|
||||||
&mut self,
|
|
||||||
args: &[i64],
|
|
||||||
intermediates: &[u8],
|
|
||||||
_ignore: bool,
|
|
||||||
action: char
|
|
||||||
) {
|
|
||||||
let private = intermediates.get(0).map(|b| *b == b'?').unwrap_or(false);
|
let private = intermediates.get(0).map(|b| *b == b'?').unwrap_or(false);
|
||||||
let handler = &mut self.handler;
|
let handler = &mut self.handler;
|
||||||
let writer = &mut self.writer;
|
let writer = &mut self.writer;
|
||||||
|
|
||||||
macro_rules! unhandled {
|
macro_rules! unhandled {
|
||||||
() => {{
|
() => {{
|
||||||
debug!("[Unhandled CSI] action={:?}, args={:?}, intermediates={:?}",
|
debug!(
|
||||||
action, args, intermediates);
|
"[Unhandled CSI] action={:?}, args={:?}, intermediates={:?}",
|
||||||
|
action, args, intermediates
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}}
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! arg_or_default {
|
macro_rules! arg_or_default {
|
||||||
(idx: $idx:expr, default: $default:expr) => {
|
(idx: $idx:expr, default: $default:expr) => {
|
||||||
args.get($idx).and_then(|v| {
|
args.get($idx)
|
||||||
if *v == 0 {
|
.and_then(|v| if *v == 0 { None } else { Some(*v) })
|
||||||
None
|
.unwrap_or($default)
|
||||||
} else {
|
};
|
||||||
Some(*v)
|
|
||||||
}
|
|
||||||
}).unwrap_or($default)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match action {
|
match action {
|
||||||
|
@ -930,8 +920,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
|
||||||
for _ in 0..arg_or_default!(idx: 0, default: 1) {
|
for _ in 0..arg_or_default!(idx: 0, default: 1) {
|
||||||
handler.input(c);
|
handler.input(c);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
debug!("tried to repeat with no preceding char");
|
debug!("tried to repeat with no preceding char");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1012,7 +1001,8 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
if i >= args.len() { // C-for condition
|
if i >= args.len() {
|
||||||
|
// C-for condition
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1061,7 +1051,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
|
||||||
45 => Attr::Background(Color::Named(NamedColor::Magenta)),
|
45 => Attr::Background(Color::Named(NamedColor::Magenta)),
|
||||||
46 => Attr::Background(Color::Named(NamedColor::Cyan)),
|
46 => Attr::Background(Color::Named(NamedColor::Cyan)),
|
||||||
47 => Attr::Background(Color::Named(NamedColor::White)),
|
47 => Attr::Background(Color::Named(NamedColor::White)),
|
||||||
48 => {
|
48 => {
|
||||||
let mut start = 0;
|
let mut start = 0;
|
||||||
if let Some(color) = parse_color(&args[i..], &mut start) {
|
if let Some(color) = parse_color(&args[i..], &mut start) {
|
||||||
i += start;
|
i += start;
|
||||||
|
@ -1094,7 +1084,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
|
||||||
|
|
||||||
i += 1; // C-for expr
|
i += 1; // C-for expr
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
'n' => handler.device_status(writer, arg_or_default!(idx: 0, default: 0) as usize),
|
'n' => handler.device_status(writer, arg_or_default!(idx: 0, default: 0) as usize),
|
||||||
'r' => {
|
'r' => {
|
||||||
if private {
|
if private {
|
||||||
|
@ -1119,29 +1109,25 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
|
||||||
1 | 2 => Some(CursorStyle::Block),
|
1 | 2 => Some(CursorStyle::Block),
|
||||||
3 | 4 => Some(CursorStyle::Underline),
|
3 | 4 => Some(CursorStyle::Underline),
|
||||||
5 | 6 => Some(CursorStyle::Beam),
|
5 | 6 => Some(CursorStyle::Beam),
|
||||||
_ => unhandled!()
|
_ => unhandled!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
handler.set_cursor_style(style);
|
handler.set_cursor_style(style);
|
||||||
}
|
},
|
||||||
_ => unhandled!(),
|
_ => unhandled!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn esc_dispatch(
|
fn esc_dispatch(&mut self, params: &[i64], intermediates: &[u8], _ignore: bool, byte: u8) {
|
||||||
&mut self,
|
|
||||||
params: &[i64],
|
|
||||||
intermediates: &[u8],
|
|
||||||
_ignore: bool,
|
|
||||||
byte: u8
|
|
||||||
) {
|
|
||||||
macro_rules! unhandled {
|
macro_rules! unhandled {
|
||||||
() => {{
|
() => {{
|
||||||
debug!("[unhandled] esc_dispatch params={:?}, ints={:?}, byte={:?} ({:02x})",
|
debug!(
|
||||||
params, intermediates, byte as char, byte);
|
"[unhandled] esc_dispatch params={:?}, ints={:?}, byte={:?} ({:02x})",
|
||||||
|
params, intermediates, byte as char, byte
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}}
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! configure_charset {
|
macro_rules! configure_charset {
|
||||||
|
@ -1154,7 +1140,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
|
||||||
_ => unhandled!(),
|
_ => unhandled!(),
|
||||||
};
|
};
|
||||||
self.handler.configure_charset(index, $charset)
|
self.handler.configure_charset(index, $charset)
|
||||||
}}
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
match byte {
|
match byte {
|
||||||
|
@ -1163,7 +1149,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
|
||||||
b'E' => {
|
b'E' => {
|
||||||
self.handler.linefeed();
|
self.handler.linefeed();
|
||||||
self.handler.carriage_return();
|
self.handler.carriage_return();
|
||||||
}
|
},
|
||||||
b'H' => self.handler.set_horizontal_tabstop(),
|
b'H' => self.handler.set_horizontal_tabstop(),
|
||||||
b'M' => self.handler.reverse_index(),
|
b'M' => self.handler.reverse_index(),
|
||||||
b'Z' => self.handler.identify_terminal(self.writer),
|
b'Z' => self.handler.identify_terminal(self.writer),
|
||||||
|
@ -1176,7 +1162,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
|
||||||
} else {
|
} else {
|
||||||
self.handler.restore_cursor_position();
|
self.handler.restore_cursor_position();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
b'=' => self.handler.set_keypad_application_mode(),
|
b'=' => self.handler.set_keypad_application_mode(),
|
||||||
b'>' => self.handler.unset_keypad_application_mode(),
|
b'>' => self.handler.unset_keypad_application_mode(),
|
||||||
b'\\' => (), // String terminator, do nothing (parser handles as string terminator)
|
b'\\' => (), // String terminator, do nothing (parser handles as string terminator)
|
||||||
|
@ -1185,14 +1171,13 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Parse a color specifier from list of attributes
|
/// Parse a color specifier from list of attributes
|
||||||
fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Color> {
|
fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Color> {
|
||||||
if attrs.len() < 2 {
|
if attrs.len() < 2 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
match attrs[*i+1] {
|
match attrs[*i + 1] {
|
||||||
2 => {
|
2 => {
|
||||||
// RGB color spec
|
// RGB color spec
|
||||||
if attrs.len() < 5 {
|
if attrs.len() < 5 {
|
||||||
|
@ -1200,9 +1185,9 @@ fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Color> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let r = attrs[*i+2];
|
let r = attrs[*i + 2];
|
||||||
let g = attrs[*i+3];
|
let g = attrs[*i + 3];
|
||||||
let b = attrs[*i+4];
|
let b = attrs[*i + 4];
|
||||||
|
|
||||||
*i += 4;
|
*i += 4;
|
||||||
|
|
||||||
|
@ -1212,11 +1197,7 @@ fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Color> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Color::Spec(Rgb {
|
Some(Color::Spec(Rgb { r: r as u8, g: g as u8, b: b as u8 }))
|
||||||
r: r as u8,
|
|
||||||
g: g as u8,
|
|
||||||
b: b as u8
|
|
||||||
}))
|
|
||||||
},
|
},
|
||||||
5 => {
|
5 => {
|
||||||
if attrs.len() < 3 {
|
if attrs.len() < 3 {
|
||||||
|
@ -1226,20 +1207,18 @@ fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Color> {
|
||||||
*i += 2;
|
*i += 2;
|
||||||
let idx = attrs[*i];
|
let idx = attrs[*i];
|
||||||
match idx {
|
match idx {
|
||||||
0 ..= 255 => {
|
0..=255 => Some(Color::Indexed(idx as u8)),
|
||||||
Some(Color::Indexed(idx as u8))
|
|
||||||
},
|
|
||||||
_ => {
|
_ => {
|
||||||
debug!("Invalid color index: {}", idx);
|
debug!("Invalid color index: {}", idx);
|
||||||
None
|
None
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
debug!("Unexpected color attr: {}", attrs[*i+1]);
|
debug!("Unexpected color attr: {}", attrs[*i + 1]);
|
||||||
None
|
None
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1314,7 +1293,6 @@ pub mod C0 {
|
||||||
pub const DEL: u8 = 0x7f;
|
pub const DEL: u8 = 0x7f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// C1 set of 8-bit control characters (from ANSI X3.64-1979)
|
/// C1 set of 8-bit control characters (from ANSI X3.64-1979)
|
||||||
///
|
///
|
||||||
/// 0x80 (@), 0x81 (A), 0x82 (B), 0x83 (C) are reserved
|
/// 0x80 (@), 0x81 (A), 0x82 (B), 0x83 (C) are reserved
|
||||||
|
@ -1393,10 +1371,13 @@ pub mod C1 {
|
||||||
// Byte sequences used in these tests are recording of pty stdout.
|
// Byte sequences used in these tests are recording of pty stdout.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::io;
|
use super::{
|
||||||
use crate::index::{Line, Column};
|
parse_number, parse_rgb_color, Attr, CharsetIndex, Color, Handler, Processor,
|
||||||
use super::{Processor, Handler, Attr, TermInfo, Color, StandardCharset, CharsetIndex, parse_rgb_color, parse_number};
|
StandardCharset, TermInfo,
|
||||||
|
};
|
||||||
|
use crate::index::{Column, Line};
|
||||||
use crate::term::color::Rgb;
|
use crate::term::color::Rgb;
|
||||||
|
use std::io;
|
||||||
|
|
||||||
/// The /dev/null of `io::Write`
|
/// The /dev/null of `io::Write`
|
||||||
struct Void;
|
struct Void;
|
||||||
|
@ -1434,9 +1415,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_control_attribute() {
|
fn parse_control_attribute() {
|
||||||
static BYTES: &'static [u8] = &[
|
static BYTES: &'static [u8] = &[0x1b, 0x5b, 0x31, 0x6d];
|
||||||
0x1b, 0x5b, 0x31, 0x6d
|
|
||||||
];
|
|
||||||
|
|
||||||
let mut parser = Processor::new();
|
let mut parser = Processor::new();
|
||||||
let mut handler = AttrHandler::default();
|
let mut handler = AttrHandler::default();
|
||||||
|
@ -1451,8 +1430,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_truecolor_attr() {
|
fn parse_truecolor_attr() {
|
||||||
static BYTES: &'static [u8] = &[
|
static BYTES: &'static [u8] = &[
|
||||||
0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x32, 0x3b, 0x31, 0x32,
|
0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x32, 0x3b, 0x31, 0x32, 0x38, 0x3b, 0x36, 0x36, 0x3b,
|
||||||
0x38, 0x3b, 0x36, 0x36, 0x3b, 0x32, 0x35, 0x35, 0x6d
|
0x32, 0x35, 0x35, 0x6d,
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut parser = Processor::new();
|
let mut parser = Processor::new();
|
||||||
|
@ -1462,11 +1441,7 @@ mod tests {
|
||||||
parser.advance(&mut handler, *byte, &mut Void);
|
parser.advance(&mut handler, *byte, &mut Void);
|
||||||
}
|
}
|
||||||
|
|
||||||
let spec = Rgb {
|
let spec = Rgb { r: 128, g: 66, b: 255 };
|
||||||
r: 128,
|
|
||||||
g: 66,
|
|
||||||
b: 255
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_eq!(handler.attr, Some(Attr::Foreground(Color::Spec(spec))));
|
assert_eq!(handler.attr, Some(Attr::Foreground(Color::Spec(spec))));
|
||||||
}
|
}
|
||||||
|
@ -1475,22 +1450,19 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_zsh_startup() {
|
fn parse_zsh_startup() {
|
||||||
static BYTES: &'static [u8] = &[
|
static BYTES: &'static [u8] = &[
|
||||||
0x1b, 0x5b, 0x31, 0x6d, 0x1b, 0x5b, 0x37, 0x6d, 0x25, 0x1b, 0x5b,
|
0x1b, 0x5b, 0x31, 0x6d, 0x1b, 0x5b, 0x37, 0x6d, 0x25, 0x1b, 0x5b, 0x32, 0x37, 0x6d,
|
||||||
0x32, 0x37, 0x6d, 0x1b, 0x5b, 0x31, 0x6d, 0x1b, 0x5b, 0x30, 0x6d,
|
0x1b, 0x5b, 0x31, 0x6d, 0x1b, 0x5b, 0x30, 0x6d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
0x20, 0x20, 0x20, 0x0d, 0x20, 0x0d, 0x0d, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x32,
|
||||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
0x37, 0x6d, 0x1b, 0x5b, 0x32, 0x34, 0x6d, 0x1b, 0x5b, 0x4a, 0x6a, 0x77, 0x69, 0x6c,
|
||||||
0x20, 0x20, 0x0d, 0x20, 0x0d, 0x0d, 0x1b, 0x5b, 0x30, 0x6d, 0x1b,
|
0x6d, 0x40, 0x6a, 0x77, 0x69, 0x6c, 0x6d, 0x2d, 0x64, 0x65, 0x73, 0x6b, 0x20, 0x1b,
|
||||||
0x5b, 0x32, 0x37, 0x6d, 0x1b, 0x5b, 0x32, 0x34, 0x6d, 0x1b, 0x5b,
|
0x5b, 0x30, 0x31, 0x3b, 0x33, 0x32, 0x6d, 0xe2, 0x9e, 0x9c, 0x20, 0x1b, 0x5b, 0x30,
|
||||||
0x4a, 0x6a, 0x77, 0x69, 0x6c, 0x6d, 0x40, 0x6a, 0x77, 0x69, 0x6c,
|
0x31, 0x3b, 0x33, 0x32, 0x6d, 0x20, 0x1b, 0x5b, 0x33, 0x36, 0x6d, 0x7e, 0x2f, 0x63,
|
||||||
0x6d, 0x2d, 0x64, 0x65, 0x73, 0x6b, 0x20, 0x1b, 0x5b, 0x30, 0x31,
|
0x6f, 0x64, 0x65,
|
||||||
0x3b, 0x33, 0x32, 0x6d, 0xe2, 0x9e, 0x9c, 0x20, 0x1b, 0x5b, 0x30,
|
|
||||||
0x31, 0x3b, 0x33, 0x32, 0x6d, 0x20, 0x1b, 0x5b, 0x33, 0x36, 0x6d,
|
|
||||||
0x7e, 0x2f, 0x63, 0x6f, 0x64, 0x65
|
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut handler = AttrHandler::default();
|
let mut handler = AttrHandler::default();
|
||||||
|
@ -1508,10 +1480,7 @@ mod tests {
|
||||||
|
|
||||||
impl Default for CharsetHandler {
|
impl Default for CharsetHandler {
|
||||||
fn default() -> CharsetHandler {
|
fn default() -> CharsetHandler {
|
||||||
CharsetHandler {
|
CharsetHandler { index: CharsetIndex::G0, charset: StandardCharset::Ascii }
|
||||||
index: CharsetIndex::G0,
|
|
||||||
charset: StandardCharset::Ascii,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1527,8 +1496,13 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TermInfo for CharsetHandler {
|
impl TermInfo for CharsetHandler {
|
||||||
fn lines(&self) -> Line { Line(200) }
|
fn lines(&self) -> Line {
|
||||||
fn cols(&self) -> Column { Column(90) }
|
Line(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cols(&self) -> Column {
|
||||||
|
Column(90)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
169
src/cli.rs
169
src/cli.rs
|
@ -11,14 +11,14 @@
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
use ::log;
|
use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg};
|
||||||
use clap::{Arg, App, crate_name, crate_version, crate_authors, crate_description};
|
use log;
|
||||||
|
|
||||||
use crate::index::{Line, Column};
|
use crate::config::{Delta, Dimensions, Shell};
|
||||||
use crate::config::{Dimensions, Delta, Shell};
|
use crate::index::{Column, Line};
|
||||||
use crate::window::{DEFAULT_NAME};
|
use crate::window::DEFAULT_NAME;
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
/// Options specified on the command line
|
/// Options specified on the command line
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
|
@ -64,70 +64,95 @@ impl Options {
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.author(crate_authors!("\n"))
|
.author(crate_authors!("\n"))
|
||||||
.about(crate_description!())
|
.about(crate_description!())
|
||||||
.arg(Arg::with_name("ref-test")
|
.arg(Arg::with_name("ref-test").long("ref-test").help("Generates ref test"))
|
||||||
.long("ref-test")
|
.arg(
|
||||||
.help("Generates ref test"))
|
Arg::with_name("live-config-reload")
|
||||||
.arg(Arg::with_name("live-config-reload")
|
.long("live-config-reload")
|
||||||
.long("live-config-reload")
|
.help("Enable automatic config reloading"),
|
||||||
.help("Enable automatic config reloading"))
|
)
|
||||||
.arg(Arg::with_name("no-live-config-reload")
|
.arg(
|
||||||
.long("no-live-config-reload")
|
Arg::with_name("no-live-config-reload")
|
||||||
.help("Disable automatic config reloading")
|
.long("no-live-config-reload")
|
||||||
.conflicts_with("live-config-reload"))
|
.help("Disable automatic config reloading")
|
||||||
.arg(Arg::with_name("print-events")
|
.conflicts_with("live-config-reload"),
|
||||||
.long("print-events")
|
)
|
||||||
.help("Print all events to stdout"))
|
.arg(
|
||||||
.arg(Arg::with_name("persistent-logging")
|
Arg::with_name("print-events")
|
||||||
.long("persistent-logging")
|
.long("print-events")
|
||||||
.help("Keep the log file after quitting Alacritty"))
|
.help("Print all events to stdout"),
|
||||||
.arg(Arg::with_name("dimensions")
|
)
|
||||||
.long("dimensions")
|
.arg(
|
||||||
.short("d")
|
Arg::with_name("persistent-logging")
|
||||||
.value_names(&["columns", "lines"])
|
.long("persistent-logging")
|
||||||
.help("Defines the window dimensions. Falls back to size specified by \
|
.help("Keep the log file after quitting Alacritty"),
|
||||||
window manager if set to 0x0 [default: 0x0]"))
|
)
|
||||||
.arg(Arg::with_name("position")
|
.arg(
|
||||||
.long("position")
|
Arg::with_name("dimensions")
|
||||||
.allow_hyphen_values(true)
|
.long("dimensions")
|
||||||
.value_names(&["x-pos", "y-pos"])
|
.short("d")
|
||||||
.help("Defines the window position. Falls back to position specified by \
|
.value_names(&["columns", "lines"])
|
||||||
window manager if unset [default: unset]"))
|
.help(
|
||||||
.arg(Arg::with_name("title")
|
"Defines the window dimensions. Falls back to size specified by window \
|
||||||
.long("title")
|
manager if set to 0x0 [default: 0x0]",
|
||||||
.short("t")
|
),
|
||||||
.takes_value(true)
|
)
|
||||||
.help(&format!("Defines the window title [default: {}]", DEFAULT_NAME)))
|
.arg(
|
||||||
.arg(Arg::with_name("class")
|
Arg::with_name("position")
|
||||||
.long("class")
|
.long("position")
|
||||||
.takes_value(true)
|
.allow_hyphen_values(true)
|
||||||
.help(&format!("Defines window class on Linux [default: {}]", DEFAULT_NAME)))
|
.value_names(&["x-pos", "y-pos"])
|
||||||
.arg(Arg::with_name("q")
|
.help(
|
||||||
.short("q")
|
"Defines the window position. Falls back to position specified by window \
|
||||||
.multiple(true)
|
manager if unset [default: unset]",
|
||||||
.conflicts_with("v")
|
),
|
||||||
.help("Reduces the level of verbosity (the min level is -qq)"))
|
)
|
||||||
.arg(Arg::with_name("v")
|
.arg(
|
||||||
.short("v")
|
Arg::with_name("title")
|
||||||
.multiple(true)
|
.long("title")
|
||||||
.conflicts_with("q")
|
.short("t")
|
||||||
.help("Increases the level of verbosity (the max level is -vvv)"))
|
.takes_value(true)
|
||||||
.arg(Arg::with_name("working-directory")
|
.help(&format!("Defines the window title [default: {}]", DEFAULT_NAME)),
|
||||||
.long("working-directory")
|
)
|
||||||
.takes_value(true)
|
.arg(
|
||||||
.help("Start the shell in the specified working directory"))
|
Arg::with_name("class")
|
||||||
.arg(Arg::with_name("config-file")
|
.long("class")
|
||||||
.long("config-file")
|
.takes_value(true)
|
||||||
.takes_value(true)
|
.help(&format!("Defines window class on Linux [default: {}]", DEFAULT_NAME)),
|
||||||
.help("Specify alternative configuration file \
|
)
|
||||||
[default: $XDG_CONFIG_HOME/alacritty/alacritty.yml]"))
|
.arg(
|
||||||
.arg(Arg::with_name("command")
|
Arg::with_name("q")
|
||||||
.long("command")
|
.short("q")
|
||||||
.short("e")
|
.multiple(true)
|
||||||
.multiple(true)
|
.conflicts_with("v")
|
||||||
.takes_value(true)
|
.help("Reduces the level of verbosity (the min level is -qq)"),
|
||||||
.min_values(1)
|
)
|
||||||
.allow_hyphen_values(true)
|
.arg(
|
||||||
.help("Command and args to execute (must be last argument)"))
|
Arg::with_name("v")
|
||||||
|
.short("v")
|
||||||
|
.multiple(true)
|
||||||
|
.conflicts_with("q")
|
||||||
|
.help("Increases the level of verbosity (the max level is -vvv)"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("working-directory")
|
||||||
|
.long("working-directory")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Start the shell in the specified working directory"),
|
||||||
|
)
|
||||||
|
.arg(Arg::with_name("config-file").long("config-file").takes_value(true).help(
|
||||||
|
"Specify alternative configuration file [default: \
|
||||||
|
$XDG_CONFIG_HOME/alacritty/alacritty.yml]",
|
||||||
|
))
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("command")
|
||||||
|
.long("command")
|
||||||
|
.short("e")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(true)
|
||||||
|
.min_values(1)
|
||||||
|
.allow_hyphen_values(true)
|
||||||
|
.help("Command and args to execute (must be last argument)"),
|
||||||
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
if matches.is_present("ref-test") {
|
if matches.is_present("ref-test") {
|
||||||
|
@ -170,14 +195,14 @@ impl Options {
|
||||||
match matches.occurrences_of("q") {
|
match matches.occurrences_of("q") {
|
||||||
0 => {},
|
0 => {},
|
||||||
1 => options.log_level = log::LevelFilter::Error,
|
1 => options.log_level = log::LevelFilter::Error,
|
||||||
2 | _ => options.log_level = log::LevelFilter::Off
|
2 | _ => options.log_level = log::LevelFilter::Off,
|
||||||
}
|
}
|
||||||
|
|
||||||
match matches.occurrences_of("v") {
|
match matches.occurrences_of("v") {
|
||||||
0 if !options.print_events => {},
|
0 if !options.print_events => {},
|
||||||
0 | 1 => options.log_level = log::LevelFilter::Info,
|
0 | 1 => options.log_level = log::LevelFilter::Info,
|
||||||
2 => options.log_level = log::LevelFilter::Debug,
|
2 => options.log_level = log::LevelFilter::Debug,
|
||||||
3 | _ => options.log_level = log::LevelFilter::Trace
|
3 | _ => options.log_level = log::LevelFilter::Trace,
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(dir) = matches.value_of("working-directory") {
|
if let Some(dir) = matches.value_of("working-directory") {
|
||||||
|
|
|
@ -11,11 +11,11 @@
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
use glutin::{MouseButton, ModifiersState};
|
use glutin::{ModifiersState, MouseButton};
|
||||||
|
|
||||||
use crate::input::{MouseBinding, KeyBinding, Action};
|
|
||||||
use crate::term::TermMode;
|
|
||||||
use super::Key;
|
use super::Key;
|
||||||
|
use crate::input::{Action, KeyBinding, MouseBinding};
|
||||||
|
use crate::term::TermMode;
|
||||||
|
|
||||||
macro_rules! bindings {
|
macro_rules! bindings {
|
||||||
(
|
(
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -14,24 +14,24 @@
|
||||||
|
|
||||||
//! The display subsystem including window management, font rasterization, and
|
//! The display subsystem including window management, font rasterization, and
|
||||||
//! GPU drawing.
|
//! GPU drawing.
|
||||||
use std::sync::mpsc;
|
|
||||||
use std::f64;
|
use std::f64;
|
||||||
|
use std::sync::mpsc;
|
||||||
|
|
||||||
use parking_lot::MutexGuard;
|
|
||||||
use glutin::dpi::{PhysicalPosition, PhysicalSize};
|
use glutin::dpi::{PhysicalPosition, PhysicalSize};
|
||||||
|
use parking_lot::MutexGuard;
|
||||||
|
|
||||||
use crate::cli;
|
use crate::cli;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use font::{self, Rasterize};
|
|
||||||
use crate::meter::Meter;
|
|
||||||
use crate::renderer::{self, GlyphCache, QuadRenderer};
|
|
||||||
use crate::renderer::rects::{Rects, Rect};
|
|
||||||
use crate::term::{Term, SizeInfo, RenderableCell};
|
|
||||||
use crate::sync::FairMutex;
|
|
||||||
use crate::window::{self, Window};
|
|
||||||
use crate::term::color::Rgb;
|
|
||||||
use crate::index::Line;
|
use crate::index::Line;
|
||||||
use crate::message_bar::Message;
|
use crate::message_bar::Message;
|
||||||
|
use crate::meter::Meter;
|
||||||
|
use crate::renderer::rects::{Rect, Rects};
|
||||||
|
use crate::renderer::{self, GlyphCache, QuadRenderer};
|
||||||
|
use crate::sync::FairMutex;
|
||||||
|
use crate::term::color::Rgb;
|
||||||
|
use crate::term::{RenderableCell, SizeInfo, Term};
|
||||||
|
use crate::window::{self, Window};
|
||||||
|
use font::{self, Rasterize};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -152,8 +152,8 @@ impl Display {
|
||||||
info!("Device pixel ratio: {}", dpr);
|
info!("Device pixel ratio: {}", dpr);
|
||||||
|
|
||||||
// get window properties for initializing the other subsystems
|
// get window properties for initializing the other subsystems
|
||||||
let mut viewport_size = window.inner_size_pixels()
|
let mut viewport_size =
|
||||||
.expect("glutin returns window size").to_physical(dpr);
|
window.inner_size_pixels().expect("glutin returns window size").to_physical(dpr);
|
||||||
|
|
||||||
// Create renderer
|
// Create renderer
|
||||||
let mut renderer = QuadRenderer::new(viewport_size)?;
|
let mut renderer = QuadRenderer::new(viewport_size)?;
|
||||||
|
@ -161,8 +161,7 @@ impl Display {
|
||||||
let (glyph_cache, cell_width, cell_height) =
|
let (glyph_cache, cell_width, cell_height) =
|
||||||
Self::new_glyph_cache(dpr, &mut renderer, config)?;
|
Self::new_glyph_cache(dpr, &mut renderer, config)?;
|
||||||
|
|
||||||
let dimensions = options.dimensions()
|
let dimensions = options.dimensions().unwrap_or_else(|| config.dimensions());
|
||||||
.unwrap_or_else(|| config.dimensions());
|
|
||||||
|
|
||||||
let mut padding_x = f64::from(config.padding().x) * dpr;
|
let mut padding_x = f64::from(config.padding().x) * dpr;
|
||||||
let mut padding_y = f64::from(config.padding().y) * dpr;
|
let mut padding_y = f64::from(config.padding().y) * dpr;
|
||||||
|
@ -216,13 +215,9 @@ impl Display {
|
||||||
|
|
||||||
// Clear screen
|
// Clear screen
|
||||||
let background_color = config.colors().primary.background;
|
let background_color = config.colors().primary.background;
|
||||||
renderer.with_api(
|
renderer.with_api(config, &size_info, |api| {
|
||||||
config,
|
api.clear(background_color);
|
||||||
&size_info,
|
});
|
||||||
|api| {
|
|
||||||
api.clear(background_color);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(Display {
|
Ok(Display {
|
||||||
window,
|
window,
|
||||||
|
@ -238,9 +233,11 @@ impl Display {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_glyph_cache(dpr: f64, renderer: &mut QuadRenderer, config: &Config)
|
fn new_glyph_cache(
|
||||||
-> Result<(GlyphCache, f32, f32), Error>
|
dpr: f64,
|
||||||
{
|
renderer: &mut QuadRenderer,
|
||||||
|
config: &Config,
|
||||||
|
) -> Result<(GlyphCache, f32, f32), Error> {
|
||||||
let font = config.font().clone();
|
let font = config.font().clone();
|
||||||
let rasterizer = font::Rasterizer::new(dpr as f32, config.use_thin_strokes())?;
|
let rasterizer = font::Rasterizer::new(dpr as f32, config.use_thin_strokes())?;
|
||||||
|
|
||||||
|
@ -253,8 +250,7 @@ impl Display {
|
||||||
renderer.with_loader(|mut api| GlyphCache::new(rasterizer, &font, &mut api))?;
|
renderer.with_loader(|mut api| GlyphCache::new(rasterizer, &font, &mut api))?;
|
||||||
|
|
||||||
let stop = init_start.elapsed();
|
let stop = init_start.elapsed();
|
||||||
let stop_f = stop.as_secs() as f64 +
|
let stop_f = stop.as_secs() as f64 + f64::from(stop.subsec_nanos()) / 1_000_000_000f64;
|
||||||
f64::from(stop.subsec_nanos()) / 1_000_000_000f64;
|
|
||||||
info!("... finished initializing glyph cache in {}s", stop_f);
|
info!("... finished initializing glyph cache in {}s", stop_f);
|
||||||
|
|
||||||
cache
|
cache
|
||||||
|
@ -322,8 +318,8 @@ impl Display {
|
||||||
let dpr = self.window.hidpi_factor();
|
let dpr = self.window.hidpi_factor();
|
||||||
|
|
||||||
// Font size/DPI factor modification detected
|
// Font size/DPI factor modification detected
|
||||||
let font_changed = terminal.font_size != self.font_size
|
let font_changed =
|
||||||
|| (dpr - self.size_info.dpr).abs() > f64::EPSILON;
|
terminal.font_size != self.font_size || (dpr - self.size_info.dpr).abs() > f64::EPSILON;
|
||||||
|
|
||||||
if font_changed || self.last_message != terminal.message_buffer_mut().message() {
|
if font_changed || self.last_message != terminal.message_buffer_mut().message() {
|
||||||
if new_size == None {
|
if new_size == None {
|
||||||
|
@ -391,9 +387,8 @@ impl Display {
|
||||||
let background_color = terminal.background_color();
|
let background_color = terminal.background_color();
|
||||||
|
|
||||||
let window_focused = self.window.is_focused;
|
let window_focused = self.window.is_focused;
|
||||||
let grid_cells: Vec<RenderableCell> = terminal
|
let grid_cells: Vec<RenderableCell> =
|
||||||
.renderable_cells(config, window_focused)
|
terminal.renderable_cells(config, window_focused).collect();
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Get message from terminal to ignore modifications after lock is dropped
|
// Get message from terminal to ignore modifications after lock is dropped
|
||||||
let message_buffer = terminal.message_buffer_mut().message();
|
let message_buffer = terminal.message_buffer_mut().message();
|
||||||
|
@ -486,20 +481,14 @@ impl Display {
|
||||||
// Draw render timer
|
// Draw render timer
|
||||||
if self.render_timer {
|
if self.render_timer {
|
||||||
let timing = format!("{:.3} usec", self.meter.average());
|
let timing = format!("{:.3} usec", self.meter.average());
|
||||||
let color = Rgb {
|
let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 };
|
||||||
r: 0xd5,
|
|
||||||
g: 0x4e,
|
|
||||||
b: 0x53,
|
|
||||||
};
|
|
||||||
self.renderer.with_api(config, &size_info, |mut api| {
|
self.renderer.with_api(config, &size_info, |mut api| {
|
||||||
api.render_string(&timing[..], size_info.lines() - 2, glyph_cache, Some(color));
|
api.render_string(&timing[..], size_info.lines() - 2, glyph_cache, Some(color));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.window
|
self.window.swap_buffers().expect("swap buffers");
|
||||||
.swap_buffers()
|
|
||||||
.expect("swap buffers");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_window_id(&self) -> Option<usize> {
|
pub fn get_window_id(&self) -> Option<usize> {
|
||||||
|
@ -509,13 +498,8 @@ impl Display {
|
||||||
/// Adjust the IME editor position according to the new location of the cursor
|
/// Adjust the IME editor position according to the new location of the cursor
|
||||||
pub fn update_ime_position(&mut self, terminal: &Term) {
|
pub fn update_ime_position(&mut self, terminal: &Term) {
|
||||||
let point = terminal.cursor().point;
|
let point = terminal.cursor().point;
|
||||||
let SizeInfo {
|
let SizeInfo { cell_width: cw, cell_height: ch, padding_x: px, padding_y: py, .. } =
|
||||||
cell_width: cw,
|
*terminal.size_info();
|
||||||
cell_height: ch,
|
|
||||||
padding_x: px,
|
|
||||||
padding_y: py,
|
|
||||||
..
|
|
||||||
} = *terminal.size_info();
|
|
||||||
|
|
||||||
let dpr = self.window().hidpi_factor();
|
let dpr = self.window().hidpi_factor();
|
||||||
let nspot_x = f64::from(px + point.col.0 as f32 * cw);
|
let nspot_x = f64::from(px + point.col.0 as f32 * cw);
|
||||||
|
|
57
src/event.rs
57
src/event.rs
|
@ -1,33 +1,33 @@
|
||||||
//! Process window events
|
//! Process window events
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::env;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::time::{Instant};
|
use std::time::Instant;
|
||||||
use std::env;
|
|
||||||
|
|
||||||
use serde_json as json;
|
use copypasta::{Buffer as ClipboardBuffer, Clipboard, Load, Store};
|
||||||
use parking_lot::MutexGuard;
|
|
||||||
use glutin::{self, ModifiersState, Event, ElementState, MouseButton};
|
|
||||||
use copypasta::{Clipboard, Load, Store, Buffer as ClipboardBuffer};
|
|
||||||
use glutin::dpi::PhysicalSize;
|
use glutin::dpi::PhysicalSize;
|
||||||
|
use glutin::{self, ElementState, Event, ModifiersState, MouseButton};
|
||||||
|
use parking_lot::MutexGuard;
|
||||||
|
use serde_json as json;
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
use crate::tty;
|
|
||||||
use crate::grid::Scroll;
|
|
||||||
use crate::config::{self, Config};
|
|
||||||
use crate::cli::Options;
|
use crate::cli::Options;
|
||||||
|
use crate::config::{self, Config};
|
||||||
use crate::display::OnResize;
|
use crate::display::OnResize;
|
||||||
use crate::index::{Line, Column, Side, Point};
|
use crate::grid::Scroll;
|
||||||
use crate::input::{self, MouseBinding, KeyBinding};
|
use crate::index::{Column, Line, Point, Side};
|
||||||
|
use crate::input::{self, KeyBinding, MouseBinding};
|
||||||
use crate::selection::Selection;
|
use crate::selection::Selection;
|
||||||
use crate::sync::FairMutex;
|
use crate::sync::FairMutex;
|
||||||
use crate::term::{Term, SizeInfo};
|
|
||||||
use crate::term::cell::Cell;
|
use crate::term::cell::Cell;
|
||||||
use crate::util::{limit, start_daemon};
|
use crate::term::{SizeInfo, Term};
|
||||||
|
#[cfg(unix)]
|
||||||
|
use crate::tty;
|
||||||
use crate::util::fmt::Red;
|
use crate::util::fmt::Red;
|
||||||
|
use crate::util::{limit, start_daemon};
|
||||||
use crate::window::Window;
|
use crate::window::Window;
|
||||||
|
|
||||||
/// Byte sequences are sent to a `Notify` in response to some events
|
/// Byte sequences are sent to a `Notify` in response to some events
|
||||||
|
@ -66,10 +66,7 @@ impl<'a, N: Notify + 'a> input::ActionContext for ActionContext<'a, N> {
|
||||||
let size_info = self.size_info();
|
let size_info = self.size_info();
|
||||||
let point = size_info.pixels_to_coords(x, y);
|
let point = size_info.pixels_to_coords(x, y);
|
||||||
let cell_side = self.mouse().cell_side;
|
let cell_side = self.mouse().cell_side;
|
||||||
self.update_selection(Point {
|
self.update_selection(Point { line: point.line, col: point.col }, cell_side);
|
||||||
line: point.line,
|
|
||||||
col: point.col
|
|
||||||
}, cell_side);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,9 +206,7 @@ impl WindowChanges {
|
||||||
|
|
||||||
impl Default for WindowChanges {
|
impl Default for WindowChanges {
|
||||||
fn default() -> WindowChanges {
|
fn default() -> WindowChanges {
|
||||||
WindowChanges {
|
WindowChanges { hide: false }
|
||||||
hide: false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,16 +353,14 @@ impl<N: Notify> Processor<N> {
|
||||||
grid.initialize_all(&Cell::default());
|
grid.initialize_all(&Cell::default());
|
||||||
grid.truncate();
|
grid.truncate();
|
||||||
|
|
||||||
let serialized_grid = json::to_string(&grid)
|
let serialized_grid = json::to_string(&grid).expect("serialize grid");
|
||||||
.expect("serialize grid");
|
|
||||||
|
|
||||||
let serialized_size = json::to_string(processor.ctx.terminal.size_info())
|
let serialized_size =
|
||||||
.expect("serialize size");
|
json::to_string(processor.ctx.terminal.size_info())
|
||||||
|
.expect("serialize size");
|
||||||
|
|
||||||
let serialized_config = format!(
|
let serialized_config =
|
||||||
"{{\"history_size\":{}}}",
|
format!("{{\"history_size\":{}}}", grid.history_size());
|
||||||
grid.history_size()
|
|
||||||
);
|
|
||||||
|
|
||||||
File::create("./grid.json")
|
File::create("./grid.json")
|
||||||
.and_then(|mut f| f.write_all(serialized_grid.as_bytes()))
|
.and_then(|mut f| f.write_all(serialized_grid.as_bytes()))
|
||||||
|
@ -453,7 +446,7 @@ impl<N: Notify> Processor<N> {
|
||||||
},
|
},
|
||||||
Event::Awakened => {
|
Event::Awakened => {
|
||||||
processor.ctx.terminal.dirty = true;
|
processor.ctx.terminal.dirty = true;
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,7 +455,7 @@ impl<N: Notify> Processor<N> {
|
||||||
pub fn process_events<'a>(
|
pub fn process_events<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
term: &'a FairMutex<Term>,
|
term: &'a FairMutex<Term>,
|
||||||
window: &mut Window
|
window: &mut Window,
|
||||||
) -> MutexGuard<'a, Term> {
|
) -> MutexGuard<'a, Term> {
|
||||||
// Terminal is lazily initialized the first time an event is returned
|
// Terminal is lazily initialized the first time an event is returned
|
||||||
// from the blocking WaitEventsIterator. Otherwise, the pty reader would
|
// from the blocking WaitEventsIterator. Otherwise, the pty reader would
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
//! The main event loop which performs I/O on the pseudoterminal
|
//! The main event loop which performs I/O on the pseudoterminal
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::io::{self, ErrorKind, Read, Write};
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::sync::Arc;
|
use std::io::{self, ErrorKind, Read, Write};
|
||||||
use std::marker::Send;
|
use std::marker::Send;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use mio::{self, Events, PollOpt, Ready};
|
use mio::{self, Events, PollOpt, Ready};
|
||||||
use mio_extras::channel::{self, Receiver, Sender};
|
use mio_extras::channel::{self, Receiver, Sender};
|
||||||
|
@ -15,10 +15,10 @@ use mio::unix::UnixReady;
|
||||||
use crate::ansi;
|
use crate::ansi;
|
||||||
use crate::display;
|
use crate::display;
|
||||||
use crate::event;
|
use crate::event;
|
||||||
use crate::tty;
|
|
||||||
use crate::term::Term;
|
|
||||||
use crate::util::thread;
|
|
||||||
use crate::sync::FairMutex;
|
use crate::sync::FairMutex;
|
||||||
|
use crate::term::Term;
|
||||||
|
use crate::tty;
|
||||||
|
use crate::util::thread;
|
||||||
|
|
||||||
/// Messages that may be sent to the `EventLoop`
|
/// Messages that may be sent to the `EventLoop`
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -58,14 +58,14 @@ enum DrainResult {
|
||||||
/// Nothing was available to receive
|
/// Nothing was available to receive
|
||||||
Empty,
|
Empty,
|
||||||
/// A shutdown message was received
|
/// A shutdown message was received
|
||||||
Shutdown
|
Shutdown,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrainResult {
|
impl DrainResult {
|
||||||
pub fn is_shutdown(&self) -> bool {
|
pub fn is_shutdown(&self) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
DrainResult::Shutdown => true,
|
DrainResult::Shutdown => true,
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,12 +84,13 @@ pub struct Notifier(pub Sender<Msg>);
|
||||||
|
|
||||||
impl event::Notify for Notifier {
|
impl event::Notify for Notifier {
|
||||||
fn notify<B>(&mut self, bytes: B)
|
fn notify<B>(&mut self, bytes: B)
|
||||||
where B: Into<Cow<'static, [u8]>>,
|
where
|
||||||
|
B: Into<Cow<'static, [u8]>>,
|
||||||
{
|
{
|
||||||
let bytes = bytes.into();
|
let bytes = bytes.into();
|
||||||
// terminal hangs if we send 0 bytes through.
|
// terminal hangs if we send 0 bytes through.
|
||||||
if bytes.len() == 0 {
|
if bytes.len() == 0 {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
if self.0.send(Msg::Input(bytes)).is_err() {
|
if self.0.send(Msg::Input(bytes)).is_err() {
|
||||||
panic!("expected send event loop msg");
|
panic!("expected send event loop msg");
|
||||||
|
@ -99,11 +100,7 @@ impl event::Notify for Notifier {
|
||||||
|
|
||||||
impl Default for State {
|
impl Default for State {
|
||||||
fn default() -> State {
|
fn default() -> State {
|
||||||
State {
|
State { write_list: VecDeque::new(), parser: ansi::Processor::new(), writing: None }
|
||||||
write_list: VecDeque::new(),
|
|
||||||
parser: ansi::Processor::new(),
|
|
||||||
writing: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,9 +114,7 @@ impl State {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn goto_next(&mut self) {
|
fn goto_next(&mut self) {
|
||||||
self.writing = self.write_list
|
self.writing = self.write_list.pop_front().map(Writing::new);
|
||||||
.pop_front()
|
|
||||||
.map(Writing::new);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -161,8 +156,8 @@ impl Writing {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> EventLoop<T>
|
impl<T> EventLoop<T>
|
||||||
where
|
where
|
||||||
T: tty::EventedPty + Send + 'static,
|
T: tty::EventedPty + Send + 'static,
|
||||||
{
|
{
|
||||||
/// Create a new event loop
|
/// Create a new event loop
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
@ -198,10 +193,10 @@ impl<T> EventLoop<T>
|
||||||
match msg {
|
match msg {
|
||||||
Msg::Input(input) => {
|
Msg::Input(input) => {
|
||||||
state.write_list.push_back(input);
|
state.write_list.push_back(input);
|
||||||
}
|
},
|
||||||
Msg::Shutdown => {
|
Msg::Shutdown => {
|
||||||
return DrainResult::Shutdown;
|
return DrainResult::Shutdown;
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,8 +228,8 @@ impl<T> EventLoop<T>
|
||||||
buf: &mut [u8],
|
buf: &mut [u8],
|
||||||
mut writer: Option<&mut X>,
|
mut writer: Option<&mut X>,
|
||||||
) -> io::Result<()>
|
) -> io::Result<()>
|
||||||
where
|
where
|
||||||
X: Write,
|
X: Write,
|
||||||
{
|
{
|
||||||
const MAX_READ: usize = 0x1_0000;
|
const MAX_READ: usize = 0x1_0000;
|
||||||
let mut processed = 0;
|
let mut processed = 0;
|
||||||
|
@ -271,20 +266,18 @@ impl<T> EventLoop<T>
|
||||||
|
|
||||||
// Run the parser
|
// Run the parser
|
||||||
for byte in &buf[..got] {
|
for byte in &buf[..got] {
|
||||||
state
|
state.parser.advance(&mut **terminal, *byte, &mut self.pty.writer());
|
||||||
.parser
|
|
||||||
.advance(&mut **terminal, *byte, &mut self.pty.writer());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exit if we've processed enough bytes
|
// Exit if we've processed enough bytes
|
||||||
if processed > MAX_READ {
|
if processed > MAX_READ {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
Err(err) => match err.kind() {
|
Err(err) => match err.kind() {
|
||||||
ErrorKind::Interrupted | ErrorKind::WouldBlock => {
|
ErrorKind::Interrupted | ErrorKind::WouldBlock => {
|
||||||
break;
|
break;
|
||||||
}
|
},
|
||||||
_ => return Err(err),
|
_ => return Err(err),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -311,21 +304,21 @@ impl<T> EventLoop<T>
|
||||||
Ok(0) => {
|
Ok(0) => {
|
||||||
state.set_current(Some(current));
|
state.set_current(Some(current));
|
||||||
break 'write_many;
|
break 'write_many;
|
||||||
}
|
},
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
current.advance(n);
|
current.advance(n);
|
||||||
if current.finished() {
|
if current.finished() {
|
||||||
state.goto_next();
|
state.goto_next();
|
||||||
break 'write_one;
|
break 'write_one;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
state.set_current(Some(current));
|
state.set_current(Some(current));
|
||||||
match err.kind() {
|
match err.kind() {
|
||||||
ErrorKind::Interrupted | ErrorKind::WouldBlock => break 'write_many,
|
ErrorKind::Interrupted | ErrorKind::WouldBlock => break 'write_many,
|
||||||
_ => return Err(err),
|
_ => return Err(err),
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -343,14 +336,10 @@ impl<T> EventLoop<T>
|
||||||
let poll_opts = PollOpt::edge() | PollOpt::oneshot();
|
let poll_opts = PollOpt::edge() | PollOpt::oneshot();
|
||||||
|
|
||||||
let channel_token = tokens.next().unwrap();
|
let channel_token = tokens.next().unwrap();
|
||||||
self.poll
|
self.poll.register(&self.rx, channel_token, Ready::readable(), poll_opts).unwrap();
|
||||||
.register(&self.rx, channel_token, Ready::readable(), poll_opts)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Register TTY through EventedRW interface
|
// Register TTY through EventedRW interface
|
||||||
self.pty
|
self.pty.register(&self.poll, &mut tokens, Ready::readable(), poll_opts).unwrap();
|
||||||
.register(&self.poll, &mut tokens, Ready::readable(), poll_opts)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut events = Events::with_capacity(1024);
|
let mut events = Events::with_capacity(1024);
|
||||||
|
|
||||||
|
@ -385,8 +374,12 @@ impl<T> EventLoop<T>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
token if token == self.pty.read_token() || token == self.pty.write_token() => {
|
token
|
||||||
#[cfg(unix)] {
|
if token == self.pty.read_token()
|
||||||
|
|| token == self.pty.write_token() =>
|
||||||
|
{
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
if UnixReady::from(event.readiness()).is_hup() {
|
if UnixReady::from(event.readiness()).is_hup() {
|
||||||
// don't try to do I/O on a dead PTY
|
// don't try to do I/O on a dead PTY
|
||||||
continue;
|
continue;
|
||||||
|
@ -395,7 +388,8 @@ impl<T> EventLoop<T>
|
||||||
|
|
||||||
if event.readiness().is_readable() {
|
if event.readiness().is_readable() {
|
||||||
if let Err(e) = self.pty_read(&mut state, &mut buf, pipe.as_mut()) {
|
if let Err(e) = self.pty_read(&mut state, &mut buf, pipe.as_mut()) {
|
||||||
#[cfg(target_os = "linux")] {
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
// On Linux, a `read` on the master side of a PTY can fail
|
// On Linux, a `read` on the master side of a PTY can fail
|
||||||
// with `EIO` if the client side hangs up. In that case,
|
// with `EIO` if the client side hangs up. In that case,
|
||||||
// just loop back round for the inevitable `Exited` event.
|
// just loop back round for the inevitable `Exited` event.
|
||||||
|
|
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.
|
//! A specialized 2d grid implementation optimized for use in a terminal.
|
||||||
|
|
||||||
use std::cmp::{min, max, Ordering};
|
use std::cmp::{max, min, Ordering};
|
||||||
use std::ops::{Deref, Range, Index, IndexMut, RangeTo, RangeFrom, RangeFull, RangeInclusive};
|
use std::ops::{Deref, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo};
|
||||||
|
|
||||||
use crate::index::{self, Point, Line, Column, IndexRange};
|
use crate::index::{self, Column, IndexRange, Line, Point};
|
||||||
use crate::selection::Selection;
|
use crate::selection::Selection;
|
||||||
|
|
||||||
mod row;
|
mod row;
|
||||||
|
@ -55,13 +55,13 @@ impl<T> Deref for Indexed<T> {
|
||||||
impl<T: PartialEq> ::std::cmp::PartialEq for Grid<T> {
|
impl<T: PartialEq> ::std::cmp::PartialEq for Grid<T> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
// Compare struct fields and check result of grid comparison
|
// Compare struct fields and check result of grid comparison
|
||||||
self.raw.eq(&other.raw) &&
|
self.raw.eq(&other.raw)
|
||||||
self.cols.eq(&other.cols) &&
|
&& self.cols.eq(&other.cols)
|
||||||
self.lines.eq(&other.lines) &&
|
&& self.lines.eq(&other.lines)
|
||||||
self.display_offset.eq(&other.display_offset) &&
|
&& self.display_offset.eq(&other.display_offset)
|
||||||
self.scroll_limit.eq(&other.scroll_limit) &&
|
&& self.scroll_limit.eq(&other.scroll_limit)
|
||||||
self.selection.eq(&other.selection) &&
|
&& self.selection.eq(&other.selection)
|
||||||
self.url_highlight.eq(&other.url_highlight)
|
&& self.url_highlight.eq(&other.url_highlight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,10 +142,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn visible_to_buffer(&self, point: Point) -> Point<usize> {
|
pub fn visible_to_buffer(&self, point: Point) -> Point<usize> {
|
||||||
Point {
|
Point { line: self.visible_line_to_buffer(point.line), col: point.col }
|
||||||
line: self.visible_line_to_buffer(point.line),
|
|
||||||
col: point.col
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn buffer_line_to_visible(&self, line: usize) -> ViewportPosition {
|
pub fn buffer_line_to_visible(&self, line: usize) -> ViewportPosition {
|
||||||
|
@ -164,8 +161,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the size of the scrollback history
|
/// Update the size of the scrollback history
|
||||||
pub fn update_history(&mut self, history_size: usize, template: &T)
|
pub fn update_history(&mut self, history_size: usize, template: &T) {
|
||||||
{
|
|
||||||
self.raw.update_history(history_size, Row::new(self.cols, &template));
|
self.raw.update_history(history_size, Row::new(self.cols, &template));
|
||||||
self.max_scroll_limit = history_size;
|
self.max_scroll_limit = history_size;
|
||||||
self.scroll_limit = min(self.scroll_limit, history_size);
|
self.scroll_limit = min(self.scroll_limit, history_size);
|
||||||
|
@ -177,20 +173,14 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
|
||||||
Scroll::Lines(count) => {
|
Scroll::Lines(count) => {
|
||||||
self.display_offset = min(
|
self.display_offset = min(
|
||||||
max((self.display_offset as isize) + count, 0isize) as usize,
|
max((self.display_offset as isize) + count, 0isize) as usize,
|
||||||
self.scroll_limit
|
self.scroll_limit,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
Scroll::PageUp => {
|
Scroll::PageUp => {
|
||||||
self.display_offset = min(
|
self.display_offset = min(self.display_offset + self.lines.0, self.scroll_limit);
|
||||||
self.display_offset + self.lines.0,
|
|
||||||
self.scroll_limit
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
Scroll::PageDown => {
|
Scroll::PageDown => {
|
||||||
self.display_offset -= min(
|
self.display_offset -= min(self.display_offset, self.lines.0);
|
||||||
self.display_offset,
|
|
||||||
self.lines.0
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
Scroll::Top => self.display_offset = self.scroll_limit,
|
Scroll::Top => self.display_offset = self.scroll_limit,
|
||||||
Scroll::Bottom => self.display_offset = 0,
|
Scroll::Bottom => self.display_offset = 0,
|
||||||
|
@ -222,8 +212,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn increase_scroll_limit(&mut self, count: usize, template: &T)
|
fn increase_scroll_limit(&mut self, count: usize, template: &T) {
|
||||||
{
|
|
||||||
self.scroll_limit = min(self.scroll_limit + count, self.max_scroll_limit);
|
self.scroll_limit = min(self.scroll_limit + count, self.max_scroll_limit);
|
||||||
|
|
||||||
// Initialize new lines when the history buffer is smaller than the scroll limit
|
// Initialize new lines when the history buffer is smaller than the scroll limit
|
||||||
|
@ -246,11 +235,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
|
||||||
/// Alacritty keeps the cursor at the bottom of the terminal as long as there
|
/// Alacritty keeps the cursor at the bottom of the terminal as long as there
|
||||||
/// is scrollback available. Once scrollback is exhausted, new lines are
|
/// is scrollback available. Once scrollback is exhausted, new lines are
|
||||||
/// simply added to the bottom of the screen.
|
/// simply added to the bottom of the screen.
|
||||||
fn grow_lines(
|
fn grow_lines(&mut self, new_line_count: index::Line, template: &T) {
|
||||||
&mut self,
|
|
||||||
new_line_count: index::Line,
|
|
||||||
template: &T,
|
|
||||||
) {
|
|
||||||
let lines_added = new_line_count - self.lines;
|
let lines_added = new_line_count - self.lines;
|
||||||
|
|
||||||
// Need to "resize" before updating buffer
|
// Need to "resize" before updating buffer
|
||||||
|
@ -435,7 +420,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
|
||||||
|
|
||||||
// Now, restore any scroll region lines
|
// Now, restore any scroll region lines
|
||||||
let lines = self.lines;
|
let lines = self.lines;
|
||||||
for i in IndexRange(region.end .. lines) {
|
for i in IndexRange(region.end..lines) {
|
||||||
self.raw.swap_lines(i, i + positions);
|
self.raw.swap_lines(i, i + positions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -449,7 +434,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
|
||||||
self.raw.swap_lines(line, line - positions);
|
self.raw.swap_lines(line, line - positions);
|
||||||
}
|
}
|
||||||
|
|
||||||
for line in IndexRange(region.start .. (region.start + positions)) {
|
for line in IndexRange(region.start..(region.start + positions)) {
|
||||||
self.raw[line].reset(&template);
|
self.raw[line].reset(&template);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -458,19 +443,12 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
|
||||||
/// scroll_up moves lines at the bottom towards the top
|
/// scroll_up moves lines at the bottom towards the top
|
||||||
///
|
///
|
||||||
/// This is the performance-sensitive part of scrolling.
|
/// This is the performance-sensitive part of scrolling.
|
||||||
pub fn scroll_up(
|
pub fn scroll_up(&mut self, region: &Range<index::Line>, positions: index::Line, template: &T) {
|
||||||
&mut self,
|
|
||||||
region: &Range<index::Line>,
|
|
||||||
positions: index::Line,
|
|
||||||
template: &T
|
|
||||||
) {
|
|
||||||
if region.start == Line(0) {
|
if region.start == Line(0) {
|
||||||
// Update display offset when not pinned to active area
|
// Update display offset when not pinned to active area
|
||||||
if self.display_offset != 0 {
|
if self.display_offset != 0 {
|
||||||
self.display_offset = min(
|
self.display_offset =
|
||||||
self.display_offset + *positions,
|
min(self.display_offset + *positions, self.len() - self.num_lines().0);
|
||||||
self.len() - self.num_lines().0,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.increase_scroll_limit(*positions, template);
|
self.increase_scroll_limit(*positions, template);
|
||||||
|
@ -506,7 +484,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear reused lines
|
// Clear reused lines
|
||||||
for line in IndexRange((region.end - positions) .. region.end) {
|
for line in IndexRange((region.end - positions)..region.end) {
|
||||||
self.raw[line].reset(&template);
|
self.raw[line].reset(&template);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -569,7 +547,7 @@ impl<T> Grid<T> {
|
||||||
/// This is used only for initializing after loading ref-tests
|
/// This is used only for initializing after loading ref-tests
|
||||||
pub fn initialize_all(&mut self, template: &T)
|
pub fn initialize_all(&mut self, template: &T)
|
||||||
where
|
where
|
||||||
T: Copy
|
T: Copy,
|
||||||
{
|
{
|
||||||
let history_size = self.raw.len().saturating_sub(*self.lines);
|
let history_size = self.raw.len().saturating_sub(*self.lines);
|
||||||
self.raw.initialize(self.max_scroll_limit - history_size, Row::new(self.cols, template));
|
self.raw.initialize(self.max_scroll_limit - history_size, Row::new(self.cols, template));
|
||||||
|
@ -581,10 +559,7 @@ impl<T> Grid<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter_from(&self, point: Point<usize>) -> GridIterator<'_, T> {
|
pub fn iter_from(&self, point: Point<usize>) -> GridIterator<'_, T> {
|
||||||
GridIterator {
|
GridIterator { grid: self, cur: point }
|
||||||
grid: self,
|
|
||||||
cur: point,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -613,8 +588,7 @@ impl<'a, T> Iterator for GridIterator<'a, T> {
|
||||||
let last_col = self.grid.num_cols() - Column(1);
|
let last_col = self.grid.num_cols() - Column(1);
|
||||||
match self.cur {
|
match self.cur {
|
||||||
Point { line, col } if line == 0 && col == last_col => None,
|
Point { line, col } if line == 0 && col == last_col => None,
|
||||||
Point { col, .. } if
|
Point { col, .. } if (col == last_col) => {
|
||||||
(col == last_col) => {
|
|
||||||
self.cur.line -= 1;
|
self.cur.line -= 1;
|
||||||
self.cur.col = Column(0);
|
self.cur.col = Column(0);
|
||||||
Some(&self.grid[self.cur.line][self.cur.col])
|
Some(&self.grid[self.cur.line][self.cur.col])
|
||||||
|
@ -622,7 +596,7 @@ impl<'a, T> Iterator for GridIterator<'a, T> {
|
||||||
_ => {
|
_ => {
|
||||||
self.cur.col += Column(1);
|
self.cur.col += Column(1);
|
||||||
Some(&self.grid[self.cur.line][self.cur.col])
|
Some(&self.grid[self.cur.line][self.cur.col])
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -641,7 +615,7 @@ impl<'a, T> BidirectionalIterator for GridIterator<'a, T> {
|
||||||
_ => {
|
_ => {
|
||||||
self.cur.col -= Column(1);
|
self.cur.col -= Column(1);
|
||||||
Some(&self.grid[self.cur.line][self.cur.col])
|
Some(&self.grid[self.cur.line][self.cur.col])
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -742,77 +716,48 @@ impl<T> IndexRegion<Range<Line>, T> for Grid<T> {
|
||||||
assert!(index.start < self.num_lines());
|
assert!(index.start < self.num_lines());
|
||||||
assert!(index.end <= self.num_lines());
|
assert!(index.end <= self.num_lines());
|
||||||
assert!(index.start <= index.end);
|
assert!(index.start <= index.end);
|
||||||
Region {
|
Region { start: index.start, end: index.end, raw: &self.raw }
|
||||||
start: index.start,
|
|
||||||
end: index.end,
|
|
||||||
raw: &self.raw
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn region_mut(&mut self, index: Range<Line>) -> RegionMut<'_, T> {
|
fn region_mut(&mut self, index: Range<Line>) -> RegionMut<'_, T> {
|
||||||
assert!(index.start < self.num_lines());
|
assert!(index.start < self.num_lines());
|
||||||
assert!(index.end <= self.num_lines());
|
assert!(index.end <= self.num_lines());
|
||||||
assert!(index.start <= index.end);
|
assert!(index.start <= index.end);
|
||||||
RegionMut {
|
RegionMut { start: index.start, end: index.end, raw: &mut self.raw }
|
||||||
start: index.start,
|
|
||||||
end: index.end,
|
|
||||||
raw: &mut self.raw
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> IndexRegion<RangeTo<Line>, T> for Grid<T> {
|
impl<T> IndexRegion<RangeTo<Line>, T> for Grid<T> {
|
||||||
fn region(&self, index: RangeTo<Line>) -> Region<'_, T> {
|
fn region(&self, index: RangeTo<Line>) -> Region<'_, T> {
|
||||||
assert!(index.end <= self.num_lines());
|
assert!(index.end <= self.num_lines());
|
||||||
Region {
|
Region { start: Line(0), end: index.end, raw: &self.raw }
|
||||||
start: Line(0),
|
|
||||||
end: index.end,
|
|
||||||
raw: &self.raw
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn region_mut(&mut self, index: RangeTo<Line>) -> RegionMut<'_, T> {
|
fn region_mut(&mut self, index: RangeTo<Line>) -> RegionMut<'_, T> {
|
||||||
assert!(index.end <= self.num_lines());
|
assert!(index.end <= self.num_lines());
|
||||||
RegionMut {
|
RegionMut { start: Line(0), end: index.end, raw: &mut self.raw }
|
||||||
start: Line(0),
|
|
||||||
end: index.end,
|
|
||||||
raw: &mut self.raw
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> IndexRegion<RangeFrom<Line>, T> for Grid<T> {
|
impl<T> IndexRegion<RangeFrom<Line>, T> for Grid<T> {
|
||||||
fn region(&self, index: RangeFrom<Line>) -> Region<'_, T> {
|
fn region(&self, index: RangeFrom<Line>) -> Region<'_, T> {
|
||||||
assert!(index.start < self.num_lines());
|
assert!(index.start < self.num_lines());
|
||||||
Region {
|
Region { start: index.start, end: self.num_lines(), raw: &self.raw }
|
||||||
start: index.start,
|
|
||||||
end: self.num_lines(),
|
|
||||||
raw: &self.raw
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn region_mut(&mut self, index: RangeFrom<Line>) -> RegionMut<'_, T> {
|
fn region_mut(&mut self, index: RangeFrom<Line>) -> RegionMut<'_, T> {
|
||||||
assert!(index.start < self.num_lines());
|
assert!(index.start < self.num_lines());
|
||||||
RegionMut {
|
RegionMut { start: index.start, end: self.num_lines(), raw: &mut self.raw }
|
||||||
start: index.start,
|
|
||||||
end: self.num_lines(),
|
|
||||||
raw: &mut self.raw
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> IndexRegion<RangeFull, T> for Grid<T> {
|
impl<T> IndexRegion<RangeFull, T> for Grid<T> {
|
||||||
fn region(&self, _: RangeFull) -> Region<'_, T> {
|
fn region(&self, _: RangeFull) -> Region<'_, T> {
|
||||||
Region {
|
Region { start: Line(0), end: self.num_lines(), raw: &self.raw }
|
||||||
start: Line(0),
|
|
||||||
end: self.num_lines(),
|
|
||||||
raw: &self.raw
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn region_mut(&mut self, _: RangeFull) -> RegionMut<'_, T> {
|
fn region_mut(&mut self, _: RangeFull) -> RegionMut<'_, T> {
|
||||||
RegionMut {
|
RegionMut { start: Line(0), end: self.num_lines(), raw: &mut self.raw }
|
||||||
start: Line(0),
|
|
||||||
end: self.num_lines(),
|
|
||||||
raw: &mut self.raw
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -829,33 +774,26 @@ pub struct RegionIterMut<'a, T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> IntoIterator for Region<'a, T> {
|
impl<'a, T> IntoIterator for Region<'a, T> {
|
||||||
type Item = &'a Row<T>;
|
|
||||||
type IntoIter = RegionIter<'a, T>;
|
type IntoIter = RegionIter<'a, T>;
|
||||||
|
type Item = &'a Row<T>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
RegionIter {
|
RegionIter { end: self.end, cur: self.start, raw: self.raw }
|
||||||
end: self.end,
|
|
||||||
cur: self.start,
|
|
||||||
raw: self.raw
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> IntoIterator for RegionMut<'a, T> {
|
impl<'a, T> IntoIterator for RegionMut<'a, T> {
|
||||||
type Item = &'a mut Row<T>;
|
|
||||||
type IntoIter = RegionIterMut<'a, T>;
|
type IntoIter = RegionIterMut<'a, T>;
|
||||||
|
type Item = &'a mut Row<T>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
RegionIterMut {
|
RegionIterMut { end: self.end, cur: self.start, raw: self.raw }
|
||||||
end: self.end,
|
|
||||||
cur: self.start,
|
|
||||||
raw: self.raw
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> Iterator for RegionIter<'a, T> {
|
impl<'a, T> Iterator for RegionIter<'a, T> {
|
||||||
type Item = &'a Row<T>;
|
type Item = &'a Row<T>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.cur < self.end {
|
if self.cur < self.end {
|
||||||
let index = self.cur;
|
let index = self.cur;
|
||||||
|
@ -869,13 +807,12 @@ impl<'a, T> Iterator for RegionIter<'a, T> {
|
||||||
|
|
||||||
impl<'a, T> Iterator for RegionIterMut<'a, T> {
|
impl<'a, T> Iterator for RegionIterMut<'a, T> {
|
||||||
type Item = &'a mut Row<T>;
|
type Item = &'a mut Row<T>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.cur < self.end {
|
if self.cur < self.end {
|
||||||
let index = self.cur;
|
let index = self.cur;
|
||||||
self.cur += 1;
|
self.cur += 1;
|
||||||
unsafe {
|
unsafe { Some(&mut *(&mut self.raw[index] as *mut _)) }
|
||||||
Some(&mut *(&mut self.raw[index] as *mut _))
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -898,7 +835,7 @@ pub struct DisplayIter<'a, T> {
|
||||||
impl<'a, T: 'a> DisplayIter<'a, T> {
|
impl<'a, T: 'a> DisplayIter<'a, T> {
|
||||||
pub fn new(grid: &'a Grid<T>) -> DisplayIter<'a, T> {
|
pub fn new(grid: &'a Grid<T>) -> DisplayIter<'a, T> {
|
||||||
let offset = grid.display_offset + *grid.num_lines() - 1;
|
let offset = grid.display_offset + *grid.num_lines() - 1;
|
||||||
let limit = grid.display_offset;
|
let limit = grid.display_offset;
|
||||||
let col = Column(0);
|
let col = Column(0);
|
||||||
let line = Line(0);
|
let line = Line(0);
|
||||||
|
|
||||||
|
@ -932,7 +869,7 @@ impl<'a, T: Copy + 'a> Iterator for DisplayIter<'a, T> {
|
||||||
let item = Some(Indexed {
|
let item = Some(Indexed {
|
||||||
inner: self.grid.raw[self.offset][self.col],
|
inner: self.grid.raw[self.offset][self.col],
|
||||||
line: self.line,
|
line: self.line,
|
||||||
column: self.col
|
column: self.col,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update line/col to point to next item
|
// Update line/col to point to next item
|
||||||
|
|
|
@ -14,9 +14,9 @@
|
||||||
|
|
||||||
//! Defines the Row type which makes up lines in the grid
|
//! Defines the Row type which makes up lines in the grid
|
||||||
|
|
||||||
|
use std::cmp::{max, min};
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
use std::ops::{Range, RangeTo, RangeFrom, RangeFull, RangeToInclusive};
|
use std::ops::{Range, RangeFrom, RangeFull, RangeTo, RangeToInclusive};
|
||||||
use std::cmp::{min, max};
|
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
||||||
use crate::grid::GridCell;
|
use crate::grid::GridCell;
|
||||||
|
@ -46,10 +46,7 @@ impl<T: PartialEq> PartialEq for Row<T> {
|
||||||
|
|
||||||
impl<T: Copy> Row<T> {
|
impl<T: Copy> Row<T> {
|
||||||
pub fn new(columns: Column, template: &T) -> Row<T> {
|
pub fn new(columns: Column, template: &T) -> Row<T> {
|
||||||
Row {
|
Row { inner: vec![*template; *columns], occ: 0 }
|
||||||
inner: vec![*template; *columns],
|
|
||||||
occ: 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn grow(&mut self, cols: Column, template: &T) {
|
pub fn grow(&mut self, cols: Column, template: &T) {
|
||||||
|
@ -62,7 +59,7 @@ impl<T: Copy> Row<T> {
|
||||||
|
|
||||||
pub fn shrink(&mut self, cols: Column) -> Option<Vec<T>>
|
pub fn shrink(&mut self, cols: Column) -> Option<Vec<T>>
|
||||||
where
|
where
|
||||||
T: GridCell
|
T: GridCell,
|
||||||
{
|
{
|
||||||
if self.inner.len() <= cols.0 {
|
if self.inner.len() <= cols.0 {
|
||||||
return None;
|
return None;
|
||||||
|
@ -96,10 +93,7 @@ impl<T: Copy> Row<T> {
|
||||||
impl<T> Row<T> {
|
impl<T> Row<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_vec(vec: Vec<T>, occ: usize) -> Row<T> {
|
pub fn from_vec(vec: Vec<T>, occ: usize) -> Row<T> {
|
||||||
Row {
|
Row { inner: vec, occ }
|
||||||
inner: vec,
|
|
||||||
occ,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -121,7 +115,7 @@ impl<T> Row<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn append(&mut self, vec: &mut Vec<T>)
|
pub fn append(&mut self, vec: &mut Vec<T>)
|
||||||
where
|
where
|
||||||
T: GridCell
|
T: GridCell,
|
||||||
{
|
{
|
||||||
self.inner.append(vec);
|
self.inner.append(vec);
|
||||||
self.occ = self.inner.iter().rposition(|c| !c.is_empty()).map(|i| i + 1).unwrap_or(0);
|
self.occ = self.inner.iter().rposition(|c| !c.is_empty()).map(|i| i + 1).unwrap_or(0);
|
||||||
|
@ -137,7 +131,7 @@ impl<T> Row<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_empty(&self) -> bool
|
pub fn is_empty(&self) -> bool
|
||||||
where
|
where
|
||||||
T: GridCell
|
T: GridCell,
|
||||||
{
|
{
|
||||||
self.inner.iter().all(|c| c.is_empty())
|
self.inner.iter().all(|c| c.is_empty())
|
||||||
}
|
}
|
||||||
|
@ -153,8 +147,8 @@ impl<T> Row<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> IntoIterator for &'a mut Row<T> {
|
impl<'a, T> IntoIterator for &'a mut Row<T> {
|
||||||
type Item = &'a mut T;
|
|
||||||
type IntoIter = slice::IterMut<'a, T>;
|
type IntoIter = slice::IterMut<'a, T>;
|
||||||
|
type Item = &'a mut T;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_iter(self) -> slice::IterMut<'a, T> {
|
fn into_iter(self) -> slice::IterMut<'a, T> {
|
||||||
|
|
|
@ -15,9 +15,9 @@ use std::ops::{Index, IndexMut};
|
||||||
|
|
||||||
use static_assertions::assert_eq_size;
|
use static_assertions::assert_eq_size;
|
||||||
|
|
||||||
use crate::index::{Column, Line};
|
|
||||||
use crate::grid::GridCell;
|
|
||||||
use super::Row;
|
use super::Row;
|
||||||
|
use crate::grid::GridCell;
|
||||||
|
use crate::index::{Column, Line};
|
||||||
|
|
||||||
/// Maximum number of invisible lines before buffer is resized
|
/// Maximum number of invisible lines before buffer is resized
|
||||||
const TRUNCATE_STEP: usize = 100;
|
const TRUNCATE_STEP: usize = 100;
|
||||||
|
@ -46,11 +46,8 @@ impl<T: PartialEq> ::std::cmp::PartialEq for Storage<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check which vec has the bigger zero
|
// Check which vec has the bigger zero
|
||||||
let (ref bigger, ref smaller) = if self.zero >= other.zero {
|
let (ref bigger, ref smaller) =
|
||||||
(self, other)
|
if self.zero >= other.zero { (self, other) } else { (other, self) };
|
||||||
} else {
|
|
||||||
(other, self)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Calculate the actual zero offset
|
// Calculate the actual zero offset
|
||||||
let len = self.inner.len();
|
let len = self.inner.len();
|
||||||
|
@ -88,12 +85,7 @@ impl<T> Storage<T> {
|
||||||
// Initialize visible lines, the scrollback buffer is initialized dynamically
|
// Initialize visible lines, the scrollback buffer is initialized dynamically
|
||||||
let inner = vec![template; lines.0];
|
let inner = vec![template; lines.0];
|
||||||
|
|
||||||
Storage {
|
Storage { inner, zero: 0, visible_lines: lines - 1, len: lines.0 }
|
||||||
inner,
|
|
||||||
zero: 0,
|
|
||||||
visible_lines: lines - 1,
|
|
||||||
len: lines.0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the size of the scrollback history
|
/// Update the size of the scrollback history
|
||||||
|
@ -179,7 +171,8 @@ impl<T> Storage<T> {
|
||||||
|
|
||||||
/// Dynamically grow the storage buffer at runtime
|
/// Dynamically grow the storage buffer at runtime
|
||||||
pub fn initialize(&mut self, num_rows: usize, template_row: Row<T>)
|
pub fn initialize(&mut self, num_rows: usize, template_row: Row<T>)
|
||||||
where T: Clone
|
where
|
||||||
|
T: Clone,
|
||||||
{
|
{
|
||||||
let mut new = vec![template_row; num_rows];
|
let mut new = vec![template_row; num_rows];
|
||||||
|
|
||||||
|
@ -297,7 +290,7 @@ impl<T> Storage<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn shrink_hidden(&mut self, cols: Column)
|
pub fn shrink_hidden(&mut self, cols: Column)
|
||||||
where
|
where
|
||||||
T: GridCell + Copy
|
T: GridCell + Copy,
|
||||||
{
|
{
|
||||||
let start = self.zero + self.len;
|
let start = self.zero + self.len;
|
||||||
let end = self.zero + self.inner.len();
|
let end = self.zero + self.inner.len();
|
||||||
|
@ -317,7 +310,7 @@ impl<T> Storage<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn grow_hidden(&mut self, cols: Column, template: &T)
|
pub fn grow_hidden(&mut self, cols: Column, template: &T)
|
||||||
where
|
where
|
||||||
T: Copy + Clone
|
T: Copy + Clone,
|
||||||
{
|
{
|
||||||
let start = self.zero + self.len;
|
let start = self.zero + self.len;
|
||||||
let end = self.zero + self.inner.len();
|
let end = self.zero + self.inner.len();
|
||||||
|
@ -333,6 +326,7 @@ impl<T> Storage<T> {
|
||||||
|
|
||||||
impl<T> Index<usize> for Storage<T> {
|
impl<T> Index<usize> for Storage<T> {
|
||||||
type Output = Row<T>;
|
type Output = Row<T>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn index(&self, index: usize) -> &Self::Output {
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
&self.inner[self.compute_index(index)]
|
&self.inner[self.compute_index(index)]
|
||||||
|
@ -349,6 +343,7 @@ impl<T> IndexMut<usize> for Storage<T> {
|
||||||
|
|
||||||
impl<T> Index<Line> for Storage<T> {
|
impl<T> Index<Line> for Storage<T> {
|
||||||
type Output = Row<T>;
|
type Output = Row<T>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn index(&self, index: Line) -> &Self::Output {
|
fn index(&self, index: Line) -> &Self::Output {
|
||||||
let index = self.visible_lines - index;
|
let index = self.visible_lines - index;
|
||||||
|
@ -379,7 +374,11 @@ impl<T> IndexMut<Line> for Storage<T> {
|
||||||
fn grow_after_zero() {
|
fn grow_after_zero() {
|
||||||
// Setup storage area
|
// Setup storage area
|
||||||
let mut storage = Storage {
|
let mut storage = Storage {
|
||||||
inner: vec![Row::new(Column(1), &'0'), Row::new(Column(1), &'1'), Row::new(Column(1), &'-')],
|
inner: vec![
|
||||||
|
Row::new(Column(1), &'0'),
|
||||||
|
Row::new(Column(1), &'1'),
|
||||||
|
Row::new(Column(1), &'-'),
|
||||||
|
],
|
||||||
zero: 0,
|
zero: 0,
|
||||||
visible_lines: Line(2),
|
visible_lines: Line(2),
|
||||||
len: 3,
|
len: 3,
|
||||||
|
@ -390,7 +389,12 @@ fn grow_after_zero() {
|
||||||
|
|
||||||
// Make sure the result is correct
|
// Make sure the result is correct
|
||||||
let expected = Storage {
|
let expected = Storage {
|
||||||
inner: vec![Row::new(Column(1), &'-'), Row::new(Column(1), &'0'), Row::new(Column(1), &'1'), Row::new(Column(1), &'-')],
|
inner: vec![
|
||||||
|
Row::new(Column(1), &'-'),
|
||||||
|
Row::new(Column(1), &'0'),
|
||||||
|
Row::new(Column(1), &'1'),
|
||||||
|
Row::new(Column(1), &'-'),
|
||||||
|
],
|
||||||
zero: 1,
|
zero: 1,
|
||||||
visible_lines: Line(0),
|
visible_lines: Line(0),
|
||||||
len: 4,
|
len: 4,
|
||||||
|
@ -415,7 +419,11 @@ fn grow_after_zero() {
|
||||||
fn grow_before_zero() {
|
fn grow_before_zero() {
|
||||||
// Setup storage area
|
// Setup storage area
|
||||||
let mut storage = Storage {
|
let mut storage = Storage {
|
||||||
inner: vec![Row::new(Column(1), &'-'), Row::new(Column(1), &'0'), Row::new(Column(1), &'1')],
|
inner: vec![
|
||||||
|
Row::new(Column(1), &'-'),
|
||||||
|
Row::new(Column(1), &'0'),
|
||||||
|
Row::new(Column(1), &'1'),
|
||||||
|
],
|
||||||
zero: 1,
|
zero: 1,
|
||||||
visible_lines: Line(2),
|
visible_lines: Line(2),
|
||||||
len: 3,
|
len: 3,
|
||||||
|
@ -426,7 +434,12 @@ fn grow_before_zero() {
|
||||||
|
|
||||||
// Make sure the result is correct
|
// Make sure the result is correct
|
||||||
let expected = Storage {
|
let expected = Storage {
|
||||||
inner: vec![Row::new(Column(1), &'-'), Row::new(Column(1), &'-'), Row::new(Column(1), &'0'), Row::new(Column(1), &'1')],
|
inner: vec![
|
||||||
|
Row::new(Column(1), &'-'),
|
||||||
|
Row::new(Column(1), &'-'),
|
||||||
|
Row::new(Column(1), &'0'),
|
||||||
|
Row::new(Column(1), &'1'),
|
||||||
|
],
|
||||||
zero: 2,
|
zero: 2,
|
||||||
visible_lines: Line(0),
|
visible_lines: Line(0),
|
||||||
len: 4,
|
len: 4,
|
||||||
|
@ -450,7 +463,11 @@ fn grow_before_zero() {
|
||||||
fn shrink_before_zero() {
|
fn shrink_before_zero() {
|
||||||
// Setup storage area
|
// Setup storage area
|
||||||
let mut storage = Storage {
|
let mut storage = Storage {
|
||||||
inner: vec![Row::new(Column(1), &'2'), Row::new(Column(1), &'0'), Row::new(Column(1), &'1')],
|
inner: vec![
|
||||||
|
Row::new(Column(1), &'2'),
|
||||||
|
Row::new(Column(1), &'0'),
|
||||||
|
Row::new(Column(1), &'1'),
|
||||||
|
],
|
||||||
zero: 1,
|
zero: 1,
|
||||||
visible_lines: Line(2),
|
visible_lines: Line(2),
|
||||||
len: 3,
|
len: 3,
|
||||||
|
@ -461,7 +478,11 @@ fn shrink_before_zero() {
|
||||||
|
|
||||||
// Make sure the result is correct
|
// Make sure the result is correct
|
||||||
let expected = Storage {
|
let expected = Storage {
|
||||||
inner: vec![Row::new(Column(1), &'2'), Row::new(Column(1), &'0'), Row::new(Column(1), &'1')],
|
inner: vec![
|
||||||
|
Row::new(Column(1), &'2'),
|
||||||
|
Row::new(Column(1), &'0'),
|
||||||
|
Row::new(Column(1), &'1'),
|
||||||
|
],
|
||||||
zero: 1,
|
zero: 1,
|
||||||
visible_lines: Line(0),
|
visible_lines: Line(0),
|
||||||
len: 2,
|
len: 2,
|
||||||
|
@ -485,7 +506,11 @@ fn shrink_before_zero() {
|
||||||
fn shrink_after_zero() {
|
fn shrink_after_zero() {
|
||||||
// Setup storage area
|
// Setup storage area
|
||||||
let mut storage = Storage {
|
let mut storage = Storage {
|
||||||
inner: vec![Row::new(Column(1), &'0'), Row::new(Column(1), &'1'), Row::new(Column(1), &'2')],
|
inner: vec![
|
||||||
|
Row::new(Column(1), &'0'),
|
||||||
|
Row::new(Column(1), &'1'),
|
||||||
|
Row::new(Column(1), &'2'),
|
||||||
|
],
|
||||||
zero: 0,
|
zero: 0,
|
||||||
visible_lines: Line(2),
|
visible_lines: Line(2),
|
||||||
len: 3,
|
len: 3,
|
||||||
|
@ -496,7 +521,11 @@ fn shrink_after_zero() {
|
||||||
|
|
||||||
// Make sure the result is correct
|
// Make sure the result is correct
|
||||||
let expected = Storage {
|
let expected = Storage {
|
||||||
inner: vec![Row::new(Column(1), &'0'), Row::new(Column(1), &'1'), Row::new(Column(1), &'2')],
|
inner: vec![
|
||||||
|
Row::new(Column(1), &'0'),
|
||||||
|
Row::new(Column(1), &'1'),
|
||||||
|
Row::new(Column(1), &'2'),
|
||||||
|
],
|
||||||
zero: 0,
|
zero: 0,
|
||||||
visible_lines: Line(0),
|
visible_lines: Line(0),
|
||||||
len: 2,
|
len: 2,
|
||||||
|
@ -526,7 +555,14 @@ fn shrink_after_zero() {
|
||||||
fn shrink_before_and_after_zero() {
|
fn shrink_before_and_after_zero() {
|
||||||
// Setup storage area
|
// Setup storage area
|
||||||
let mut storage = Storage {
|
let mut storage = Storage {
|
||||||
inner: vec![Row::new(Column(1), &'4'), Row::new(Column(1), &'5'), Row::new(Column(1), &'0'), Row::new(Column(1), &'1'), Row::new(Column(1), &'2'), Row::new(Column(1), &'3')],
|
inner: vec![
|
||||||
|
Row::new(Column(1), &'4'),
|
||||||
|
Row::new(Column(1), &'5'),
|
||||||
|
Row::new(Column(1), &'0'),
|
||||||
|
Row::new(Column(1), &'1'),
|
||||||
|
Row::new(Column(1), &'2'),
|
||||||
|
Row::new(Column(1), &'3'),
|
||||||
|
],
|
||||||
zero: 2,
|
zero: 2,
|
||||||
visible_lines: Line(5),
|
visible_lines: Line(5),
|
||||||
len: 6,
|
len: 6,
|
||||||
|
@ -537,7 +573,14 @@ fn shrink_before_and_after_zero() {
|
||||||
|
|
||||||
// Make sure the result is correct
|
// Make sure the result is correct
|
||||||
let expected = Storage {
|
let expected = Storage {
|
||||||
inner: vec![Row::new(Column(1), &'4'), Row::new(Column(1), &'5'), Row::new(Column(1), &'0'), Row::new(Column(1), &'1'), Row::new(Column(1), &'2'), Row::new(Column(1), &'3')],
|
inner: vec![
|
||||||
|
Row::new(Column(1), &'4'),
|
||||||
|
Row::new(Column(1), &'5'),
|
||||||
|
Row::new(Column(1), &'0'),
|
||||||
|
Row::new(Column(1), &'1'),
|
||||||
|
Row::new(Column(1), &'2'),
|
||||||
|
Row::new(Column(1), &'3'),
|
||||||
|
],
|
||||||
zero: 2,
|
zero: 2,
|
||||||
visible_lines: Line(0),
|
visible_lines: Line(0),
|
||||||
len: 2,
|
len: 2,
|
||||||
|
@ -563,7 +606,14 @@ fn shrink_before_and_after_zero() {
|
||||||
fn truncate_invisible_lines() {
|
fn truncate_invisible_lines() {
|
||||||
// Setup storage area
|
// Setup storage area
|
||||||
let mut storage = Storage {
|
let mut storage = Storage {
|
||||||
inner: vec![Row::new(Column(1), &'4'), Row::new(Column(1), &'5'), Row::new(Column(1), &'0'), Row::new(Column(1), &'1'), Row::new(Column(1), &'2'), Row::new(Column(1), &'3')],
|
inner: vec![
|
||||||
|
Row::new(Column(1), &'4'),
|
||||||
|
Row::new(Column(1), &'5'),
|
||||||
|
Row::new(Column(1), &'0'),
|
||||||
|
Row::new(Column(1), &'1'),
|
||||||
|
Row::new(Column(1), &'2'),
|
||||||
|
Row::new(Column(1), &'3'),
|
||||||
|
],
|
||||||
zero: 2,
|
zero: 2,
|
||||||
visible_lines: Line(1),
|
visible_lines: Line(1),
|
||||||
len: 2,
|
len: 2,
|
||||||
|
@ -598,7 +648,11 @@ fn truncate_invisible_lines() {
|
||||||
fn truncate_invisible_lines_beginning() {
|
fn truncate_invisible_lines_beginning() {
|
||||||
// Setup storage area
|
// Setup storage area
|
||||||
let mut storage = Storage {
|
let mut storage = Storage {
|
||||||
inner: vec![Row::new(Column(1), &'1'), Row::new(Column(1), &'2'), Row::new(Column(1), &'0')],
|
inner: vec![
|
||||||
|
Row::new(Column(1), &'1'),
|
||||||
|
Row::new(Column(1), &'2'),
|
||||||
|
Row::new(Column(1), &'0'),
|
||||||
|
],
|
||||||
zero: 2,
|
zero: 2,
|
||||||
visible_lines: Line(1),
|
visible_lines: Line(1),
|
||||||
len: 2,
|
len: 2,
|
||||||
|
|
|
@ -14,10 +14,10 @@
|
||||||
|
|
||||||
//! Tests for the Gird
|
//! Tests for the Gird
|
||||||
|
|
||||||
use super::{Grid, BidirectionalIterator};
|
use super::{BidirectionalIterator, Grid};
|
||||||
use crate::index::{Point, Line, Column};
|
|
||||||
use crate::term::cell::{Cell, Flags};
|
|
||||||
use crate::grid::GridCell;
|
use crate::grid::GridCell;
|
||||||
|
use crate::index::{Column, Line, Point};
|
||||||
|
use crate::term::cell::{Cell, Flags};
|
||||||
|
|
||||||
impl GridCell for usize {
|
impl GridCell for usize {
|
||||||
fn is_empty(&self) -> bool {
|
fn is_empty(&self) -> bool {
|
||||||
|
@ -101,14 +101,11 @@ fn test_iter() {
|
||||||
let mut grid = Grid::new(Line(5), Column(5), 0, 0);
|
let mut grid = Grid::new(Line(5), Column(5), 0, 0);
|
||||||
for i in 0..5 {
|
for i in 0..5 {
|
||||||
for j in 0..5 {
|
for j in 0..5 {
|
||||||
grid[Line(i)][Column(j)] = i*5 + j;
|
grid[Line(i)][Column(j)] = i * 5 + j;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut iter = grid.iter_from(Point {
|
let mut iter = grid.iter_from(Point { line: 4, col: Column(0) });
|
||||||
line: 4,
|
|
||||||
col: Column(0),
|
|
||||||
});
|
|
||||||
|
|
||||||
assert_eq!(None, iter.prev());
|
assert_eq!(None, iter.prev());
|
||||||
assert_eq!(Some(&1), iter.next());
|
assert_eq!(Some(&1), iter.next());
|
||||||
|
@ -128,12 +125,8 @@ fn test_iter() {
|
||||||
assert_eq!(Column(4), iter.cur.col);
|
assert_eq!(Column(4), iter.cur.col);
|
||||||
assert_eq!(4, iter.cur.line);
|
assert_eq!(4, iter.cur.line);
|
||||||
|
|
||||||
|
|
||||||
// test that iter ends at end of grid
|
// test that iter ends at end of grid
|
||||||
let mut final_iter = grid.iter_from(Point {
|
let mut final_iter = grid.iter_from(Point { line: 0, col: Column(4) });
|
||||||
line: 0,
|
|
||||||
col: Column(4),
|
|
||||||
});
|
|
||||||
assert_eq!(None, final_iter.next());
|
assert_eq!(None, final_iter.next());
|
||||||
assert_eq!(Some(&23), final_iter.prev());
|
assert_eq!(Some(&23), final_iter.prev());
|
||||||
}
|
}
|
||||||
|
|
27
src/index.rs
27
src/index.rs
|
@ -17,18 +17,18 @@
|
||||||
/// Indexing types and implementations for Grid and Line
|
/// Indexing types and implementations for Grid and Line
|
||||||
use std::cmp::{Ord, Ordering};
|
use std::cmp::{Ord, Ordering};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::{self, Deref, Range, RangeInclusive, Add, Sub, AddAssign, SubAssign};
|
use std::ops::{self, Add, AddAssign, Deref, Range, RangeInclusive, Sub, SubAssign};
|
||||||
|
|
||||||
/// The side of a cell
|
/// The side of a cell
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub enum Side {
|
pub enum Side {
|
||||||
Left,
|
Left,
|
||||||
Right
|
Right,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Index in the grid using row, column notation
|
/// Index in the grid using row, column notation
|
||||||
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Serialize, Deserialize, PartialOrd)]
|
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Serialize, Deserialize, PartialOrd)]
|
||||||
pub struct Point<L=Line> {
|
pub struct Point<L = Line> {
|
||||||
pub line: L,
|
pub line: L,
|
||||||
pub col: Column,
|
pub col: Column,
|
||||||
}
|
}
|
||||||
|
@ -43,11 +43,10 @@ impl Ord for Point {
|
||||||
fn cmp(&self, other: &Point) -> Ordering {
|
fn cmp(&self, other: &Point) -> Ordering {
|
||||||
use std::cmp::Ordering::*;
|
use std::cmp::Ordering::*;
|
||||||
match (self.line.cmp(&other.line), self.col.cmp(&other.col)) {
|
match (self.line.cmp(&other.line), self.col.cmp(&other.col)) {
|
||||||
(Equal, Equal) => Equal,
|
(Equal, Equal) => Equal,
|
||||||
(Equal, ord) |
|
(Equal, ord) | (ord, Equal) => ord,
|
||||||
(ord, Equal) => ord,
|
(Less, _) => Less,
|
||||||
(Less, _) => Less,
|
(Greater, _) => Greater,
|
||||||
(Greater, _) => Greater,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,7 +155,7 @@ macro_rules! forward_ref_binop {
|
||||||
$imp::$method(*self, *other)
|
$imp::$method(*self, *other)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Macro for deriving deref
|
/// Macro for deriving deref
|
||||||
|
@ -170,7 +169,7 @@ macro_rules! deref {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! add {
|
macro_rules! add {
|
||||||
|
@ -183,7 +182,7 @@ macro_rules! add {
|
||||||
$construct(self.0 + rhs.0)
|
$construct(self.0 + rhs.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! sub {
|
macro_rules! sub {
|
||||||
|
@ -223,7 +222,7 @@ macro_rules! sub {
|
||||||
$construct(self.0 - rhs.0)
|
$construct(self.0 - rhs.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This exists because we can't implement Iterator on Range
|
/// This exists because we can't implement Iterator on Range
|
||||||
|
@ -246,6 +245,7 @@ pub trait Contains {
|
||||||
|
|
||||||
impl<T: PartialOrd<T>> Contains for Range<T> {
|
impl<T: PartialOrd<T>> Contains for Range<T> {
|
||||||
type Content = T;
|
type Content = T;
|
||||||
|
|
||||||
fn contains_(&self, item: Self::Content) -> bool {
|
fn contains_(&self, item: Self::Content) -> bool {
|
||||||
(self.start <= item) && (item < self.end)
|
(self.start <= item) && (item < self.end)
|
||||||
}
|
}
|
||||||
|
@ -253,6 +253,7 @@ impl<T: PartialOrd<T>> Contains for Range<T> {
|
||||||
|
|
||||||
impl<T: PartialOrd<T>> Contains for RangeInclusive<T> {
|
impl<T: PartialOrd<T>> Contains for RangeInclusive<T> {
|
||||||
type Content = T;
|
type Content = T;
|
||||||
|
|
||||||
fn contains_(&self, item: Self::Content) -> bool {
|
fn contains_(&self, item: Self::Content) -> bool {
|
||||||
(self.start() <= &item) && (&item <= self.end())
|
(self.start() <= &item) && (&item <= self.end())
|
||||||
}
|
}
|
||||||
|
@ -383,7 +384,7 @@ ops!(Linear, Linear);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{Line, Column, Point};
|
use super::{Column, Line, Point};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn location_ordering() {
|
fn location_ordering() {
|
||||||
|
|
132
src/input.rs
132
src/input.rs
|
@ -20,27 +20,27 @@
|
||||||
//! determine what to do when a non-modifier key is pressed.
|
//! determine what to do when a non-modifier key is pressed.
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::time::Instant;
|
|
||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
use copypasta::{Clipboard, Load, Buffer as ClipboardBuffer};
|
use copypasta::{Buffer as ClipboardBuffer, Clipboard, Load};
|
||||||
use unicode_width::UnicodeWidthStr;
|
|
||||||
use glutin::{
|
use glutin::{
|
||||||
ElementState, KeyboardInput, ModifiersState, MouseButton, MouseCursor, MouseScrollDelta,
|
ElementState, KeyboardInput, ModifiersState, MouseButton, MouseCursor, MouseScrollDelta,
|
||||||
TouchPhase,
|
TouchPhase,
|
||||||
};
|
};
|
||||||
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
|
use crate::ansi::{ClearMode, Handler};
|
||||||
use crate::config::{self, Key};
|
use crate::config::{self, Key};
|
||||||
use crate::grid::Scroll;
|
|
||||||
use crate::event::{ClickState, Mouse};
|
use crate::event::{ClickState, Mouse};
|
||||||
use crate::index::{Line, Column, Side, Point, Linear};
|
use crate::grid::Scroll;
|
||||||
use crate::term::{Term, SizeInfo, Search};
|
use crate::index::{Column, Line, Linear, Point, Side};
|
||||||
|
use crate::message_bar::{self, Message};
|
||||||
use crate::term::mode::TermMode;
|
use crate::term::mode::TermMode;
|
||||||
|
use crate::term::{Search, SizeInfo, Term};
|
||||||
|
use crate::url::Url;
|
||||||
use crate::util::fmt::Red;
|
use crate::util::fmt::Red;
|
||||||
use crate::util::start_daemon;
|
use crate::util::start_daemon;
|
||||||
use crate::message_bar::{self, Message};
|
|
||||||
use crate::ansi::{Handler, ClearMode};
|
|
||||||
use crate::url::Url;
|
|
||||||
|
|
||||||
pub const FONT_SIZE_STEP: f32 = 0.5;
|
pub const FONT_SIZE_STEP: f32 = 0.5;
|
||||||
|
|
||||||
|
@ -148,10 +148,10 @@ impl<T: Eq> Binding<T> {
|
||||||
// Check input first since bindings are stored in one big list. This is
|
// Check input first since bindings are stored in one big list. This is
|
||||||
// the most likely item to fail so prioritizing it here allows more
|
// the most likely item to fail so prioritizing it here allows more
|
||||||
// checks to be short circuited.
|
// checks to be short circuited.
|
||||||
self.trigger == *input &&
|
self.trigger == *input
|
||||||
self.mode_matches(mode) &&
|
&& self.mode_matches(mode)
|
||||||
self.not_mode_matches(mode) &&
|
&& self.not_mode_matches(mode)
|
||||||
self.mods_match(mods, relaxed)
|
&& self.mods_match(mods, relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -267,8 +267,8 @@ impl Action {
|
||||||
},
|
},
|
||||||
Action::Paste => {
|
Action::Paste => {
|
||||||
Clipboard::new()
|
Clipboard::new()
|
||||||
.and_then(|clipboard| clipboard.load_primary() )
|
.and_then(|clipboard| clipboard.load_primary())
|
||||||
.map(|contents| { self.paste(ctx, &contents) })
|
.map(|contents| self.paste(ctx, &contents))
|
||||||
.unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
error!("Error loading data from clipboard: {}", Red(err));
|
error!("Error loading data from clipboard: {}", Red(err));
|
||||||
});
|
});
|
||||||
|
@ -277,8 +277,8 @@ impl Action {
|
||||||
// Only paste if mouse events are not captured by an application
|
// Only paste if mouse events are not captured by an application
|
||||||
if !mouse_mode {
|
if !mouse_mode {
|
||||||
Clipboard::new()
|
Clipboard::new()
|
||||||
.and_then(|clipboard| clipboard.load_selection() )
|
.and_then(|clipboard| clipboard.load_selection())
|
||||||
.map(|contents| { self.paste(ctx, &contents) })
|
.map(|contents| self.paste(ctx, &contents))
|
||||||
.unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
error!("Error loading data from clipboard: {}", Red(err));
|
error!("Error loading data from clipboard: {}", Red(err));
|
||||||
});
|
});
|
||||||
|
@ -303,13 +303,13 @@ impl Action {
|
||||||
ctx.terminal_mut().exit();
|
ctx.terminal_mut().exit();
|
||||||
},
|
},
|
||||||
Action::IncreaseFontSize => {
|
Action::IncreaseFontSize => {
|
||||||
ctx.terminal_mut().change_font_size(FONT_SIZE_STEP);
|
ctx.terminal_mut().change_font_size(FONT_SIZE_STEP);
|
||||||
},
|
},
|
||||||
Action::DecreaseFontSize => {
|
Action::DecreaseFontSize => {
|
||||||
ctx.terminal_mut().change_font_size(-FONT_SIZE_STEP);
|
ctx.terminal_mut().change_font_size(-FONT_SIZE_STEP);
|
||||||
}
|
},
|
||||||
Action::ResetFontSize => {
|
Action::ResetFontSize => {
|
||||||
ctx.terminal_mut().reset_font_size();
|
ctx.terminal_mut().reset_font_size();
|
||||||
},
|
},
|
||||||
Action::ScrollPageUp => {
|
Action::ScrollPageUp => {
|
||||||
ctx.scroll(Scroll::PageUp);
|
ctx.scroll(Scroll::PageUp);
|
||||||
|
@ -339,7 +339,7 @@ impl Action {
|
||||||
fn paste<A: ActionContext>(&self, ctx: &mut A, contents: &str) {
|
fn paste<A: ActionContext>(&self, ctx: &mut A, contents: &str) {
|
||||||
if ctx.terminal().mode().contains(TermMode::BRACKETED_PASTE) {
|
if ctx.terminal().mode().contains(TermMode::BRACKETED_PASTE) {
|
||||||
ctx.write_to_pty(&b"\x1b[200~"[..]);
|
ctx.write_to_pty(&b"\x1b[200~"[..]);
|
||||||
ctx.write_to_pty(contents.replace("\x1b","").into_bytes());
|
ctx.write_to_pty(contents.replace("\x1b", "").into_bytes());
|
||||||
ctx.write_to_pty(&b"\x1b[201~"[..]);
|
ctx.write_to_pty(&b"\x1b[201~"[..]);
|
||||||
} else {
|
} else {
|
||||||
// In non-bracketed (ie: normal) mode, terminal applications cannot distinguish
|
// In non-bracketed (ie: normal) mode, terminal applications cannot distinguish
|
||||||
|
@ -348,7 +348,7 @@ impl Action {
|
||||||
// pasting... since that's neither practical nor sensible (and probably an impossible
|
// pasting... since that's neither practical nor sensible (and probably an impossible
|
||||||
// task to solve in a general way), we'll just replace line breaks (windows and unix
|
// task to solve in a general way), we'll just replace line breaks (windows and unix
|
||||||
// style) with a singe carriage return (\r, which is what the Enter key produces).
|
// style) with a singe carriage return (\r, which is what the Enter key produces).
|
||||||
ctx.write_to_pty(contents.replace("\r\n","\r").replace("\n","\r").into_bytes());
|
ctx.write_to_pty(contents.replace("\r\n", "\r").replace("\n", "\r").into_bytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -411,13 +411,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
if self.ctx.mouse().left_button_state == ElementState::Pressed
|
if self.ctx.mouse().left_button_state == ElementState::Pressed
|
||||||
&& (modifiers.shift || !self.ctx.terminal().mode().intersects(report_mode))
|
&& (modifiers.shift || !self.ctx.terminal().mode().intersects(report_mode))
|
||||||
{
|
{
|
||||||
self.ctx.update_selection(
|
self.ctx.update_selection(Point { line: point.line, col: point.col }, cell_side);
|
||||||
Point {
|
|
||||||
line: point.line,
|
|
||||||
col: point.col,
|
|
||||||
},
|
|
||||||
cell_side,
|
|
||||||
);
|
|
||||||
} else if self.ctx.terminal().mode().intersects(motion_mode)
|
} else if self.ctx.terminal().mode().intersects(motion_mode)
|
||||||
&& size_info.contains_point(x, y, false)
|
&& size_info.contains_point(x, y, false)
|
||||||
{
|
{
|
||||||
|
@ -443,7 +437,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
&& (!self.ctx.terminal().mode().intersects(mouse_mode) || modifiers.shift)
|
&& (!self.ctx.terminal().mode().intersects(mouse_mode) || modifiers.shift)
|
||||||
&& self.mouse_config.url.launcher.is_some()
|
&& self.mouse_config.url.launcher.is_some()
|
||||||
{
|
{
|
||||||
self.ctx.terminal().url_search(point.into())
|
self.ctx.terminal().url_search(point.into())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -582,14 +576,14 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
self.ctx.mouse_mut().block_url_launcher = true;
|
self.ctx.mouse_mut().block_url_launcher = true;
|
||||||
self.on_mouse_double_click(button, point);
|
self.on_mouse_double_click(button, point);
|
||||||
ClickState::DoubleClick
|
ClickState::DoubleClick
|
||||||
},
|
}
|
||||||
ClickState::DoubleClick
|
ClickState::DoubleClick
|
||||||
if !button_changed && elapsed < self.mouse_config.triple_click.threshold =>
|
if !button_changed && elapsed < self.mouse_config.triple_click.threshold =>
|
||||||
{
|
{
|
||||||
self.ctx.mouse_mut().block_url_launcher = true;
|
self.ctx.mouse_mut().block_url_launcher = true;
|
||||||
self.on_mouse_triple_click(button, point);
|
self.on_mouse_triple_click(button, point);
|
||||||
ClickState::TripleClick
|
ClickState::TripleClick
|
||||||
},
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Don't launch URLs if this click cleared the selection
|
// Don't launch URLs if this click cleared the selection
|
||||||
self.ctx.mouse_mut().block_url_launcher = !self.ctx.selection_is_empty();
|
self.ctx.mouse_mut().block_url_launcher = !self.ctx.selection_is_empty();
|
||||||
|
@ -617,7 +611,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
}
|
}
|
||||||
|
|
||||||
ClickState::Click
|
ClickState::Click
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -690,7 +684,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
},
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -744,11 +738,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
|
|
||||||
pub fn on_focus_change(&mut self, is_focused: bool) {
|
pub fn on_focus_change(&mut self, is_focused: bool) {
|
||||||
if self.ctx.terminal().mode().contains(TermMode::FOCUS_IN_OUT) {
|
if self.ctx.terminal().mode().contains(TermMode::FOCUS_IN_OUT) {
|
||||||
let chr = if is_focused {
|
let chr = if is_focused { "I" } else { "O" };
|
||||||
"I"
|
|
||||||
} else {
|
|
||||||
"O"
|
|
||||||
};
|
|
||||||
|
|
||||||
let msg = format!("\x1b[{}", chr);
|
let msg = format!("\x1b[{}", chr);
|
||||||
self.ctx.write_to_pty(msg.into_bytes());
|
self.ctx.write_to_pty(msg.into_bytes());
|
||||||
|
@ -759,7 +749,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
&mut self,
|
&mut self,
|
||||||
state: ElementState,
|
state: ElementState,
|
||||||
button: MouseButton,
|
button: MouseButton,
|
||||||
modifiers: ModifiersState
|
modifiers: ModifiersState,
|
||||||
) {
|
) {
|
||||||
match button {
|
match button {
|
||||||
MouseButton::Left => self.ctx.mouse_mut().left_button_state = state,
|
MouseButton::Left => self.ctx.mouse_mut().left_button_state = state,
|
||||||
|
@ -849,16 +839,18 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
&Key::Scancode(input.scancode),
|
&Key::Scancode(input.scancode),
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
_ => if let Some(key) = input.virtual_keycode {
|
_ => {
|
||||||
let key = Key::from_glutin_input(key);
|
if let Some(key) = input.virtual_keycode {
|
||||||
binding.is_triggered_by(
|
let key = Key::from_glutin_input(key);
|
||||||
*self.ctx.terminal().mode(),
|
binding.is_triggered_by(
|
||||||
input.modifiers,
|
*self.ctx.terminal().mode(),
|
||||||
&key,
|
input.modifiers,
|
||||||
|
&key,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
false
|
false
|
||||||
)
|
}
|
||||||
} else {
|
|
||||||
false
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -883,11 +875,12 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
for binding in self.mouse_bindings {
|
for binding in self.mouse_bindings {
|
||||||
if binding.is_triggered_by(*self.ctx.terminal().mode(), mods, &button, true) {
|
if binding.is_triggered_by(*self.ctx.terminal().mode(), mods, &button, true) {
|
||||||
// binding was triggered; run the action
|
// binding was triggered; run the action
|
||||||
let mouse_mode = !mods.shift && self.ctx.terminal().mode().intersects(
|
let mouse_mode = !mods.shift
|
||||||
TermMode::MOUSE_REPORT_CLICK
|
&& self.ctx.terminal().mode().intersects(
|
||||||
| TermMode::MOUSE_DRAG
|
TermMode::MOUSE_REPORT_CLICK
|
||||||
| TermMode::MOUSE_MOTION
|
| TermMode::MOUSE_DRAG
|
||||||
);
|
| TermMode::MOUSE_MOTION,
|
||||||
|
);
|
||||||
binding.execute(&mut self.ctx, mouse_mode);
|
binding.execute(&mut self.ctx, mouse_mode);
|
||||||
has_binding = true;
|
has_binding = true;
|
||||||
}
|
}
|
||||||
|
@ -898,10 +891,9 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
|
|
||||||
/// Return the message bar's message if there is some at the specified point
|
/// Return the message bar's message if there is some at the specified point
|
||||||
fn message_at_point(&mut self, point: Option<Point>) -> Option<Message> {
|
fn message_at_point(&mut self, point: Option<Point>) -> Option<Message> {
|
||||||
if let (Some(point), Some(message)) = (
|
if let (Some(point), Some(message)) =
|
||||||
point,
|
(point, self.ctx.terminal_mut().message_buffer_mut().message())
|
||||||
self.ctx.terminal_mut().message_buffer_mut().message(),
|
{
|
||||||
) {
|
|
||||||
let size = self.ctx.size_info();
|
let size = self.ctx.size_info();
|
||||||
if point.line.0 >= size.lines().saturating_sub(message.text(&size).len()) {
|
if point.line.0 >= size.lines().saturating_sub(message.text(&size).len()) {
|
||||||
return Some(message);
|
return Some(message);
|
||||||
|
@ -924,7 +916,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ctx.clear_selection();
|
self.ctx.clear_selection();
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -942,15 +934,15 @@ mod tests {
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use glutin::{VirtualKeyCode, Event, WindowEvent, ElementState, MouseButton, ModifiersState};
|
use glutin::{ElementState, Event, ModifiersState, MouseButton, VirtualKeyCode, WindowEvent};
|
||||||
|
|
||||||
use crate::term::{SizeInfo, Term, TermMode};
|
use crate::config::{self, ClickHandler, Config};
|
||||||
use crate::event::{Mouse, ClickState, WindowChanges};
|
use crate::event::{ClickState, Mouse, WindowChanges};
|
||||||
use crate::config::{self, Config, ClickHandler};
|
|
||||||
use crate::index::{Point, Side};
|
|
||||||
use crate::selection::Selection;
|
|
||||||
use crate::grid::Scroll;
|
use crate::grid::Scroll;
|
||||||
|
use crate::index::{Point, Side};
|
||||||
use crate::message_bar::MessageBuffer;
|
use crate::message_bar::MessageBuffer;
|
||||||
|
use crate::selection::Selection;
|
||||||
|
use crate::term::{SizeInfo, Term, TermMode};
|
||||||
|
|
||||||
use super::{Action, Binding, Processor};
|
use super::{Action, Binding, Processor};
|
||||||
use copypasta::Buffer as ClipboardBuffer;
|
use copypasta::Buffer as ClipboardBuffer;
|
||||||
|
@ -976,13 +968,19 @@ mod tests {
|
||||||
pub window_changes: &'a mut WindowChanges,
|
pub window_changes: &'a mut WindowChanges,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a>super::ActionContext for ActionContext<'a> {
|
impl<'a> super::ActionContext for ActionContext<'a> {
|
||||||
fn write_to_pty<B: Into<Cow<'static, [u8]>>>(&mut self, _val: B) {}
|
fn write_to_pty<B: Into<Cow<'static, [u8]>>>(&mut self, _val: B) {}
|
||||||
|
|
||||||
fn update_selection(&mut self, _point: Point, _side: Side) {}
|
fn update_selection(&mut self, _point: Point, _side: Side) {}
|
||||||
|
|
||||||
fn simple_selection(&mut self, _point: Point, _side: Side) {}
|
fn simple_selection(&mut self, _point: Point, _side: Side) {}
|
||||||
|
|
||||||
fn copy_selection(&self, _buffer: ClipboardBuffer) {}
|
fn copy_selection(&self, _buffer: ClipboardBuffer) {}
|
||||||
|
|
||||||
fn clear_selection(&mut self) {}
|
fn clear_selection(&mut self) {}
|
||||||
|
|
||||||
fn hide_window(&mut self) {}
|
fn hide_window(&mut self) {}
|
||||||
|
|
||||||
fn spawn_new_instance(&mut self) {}
|
fn spawn_new_instance(&mut self) {}
|
||||||
|
|
||||||
fn terminal(&self) -> &Term {
|
fn terminal(&self) -> &Term {
|
||||||
|
|
10
src/lib.rs
10
src/lib.rs
|
@ -17,8 +17,10 @@
|
||||||
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
|
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
|
||||||
#![cfg_attr(all(test, feature = "bench"), feature(test))]
|
#![cfg_attr(all(test, feature = "bench"), feature(test))]
|
||||||
|
|
||||||
#[macro_use] extern crate log;
|
#[macro_use]
|
||||||
#[macro_use] extern crate serde_derive;
|
extern crate log;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -37,6 +39,7 @@ pub mod index;
|
||||||
pub mod input;
|
pub mod input;
|
||||||
pub mod locale;
|
pub mod locale;
|
||||||
pub mod logging;
|
pub mod logging;
|
||||||
|
pub mod message_bar;
|
||||||
pub mod meter;
|
pub mod meter;
|
||||||
pub mod panic;
|
pub mod panic;
|
||||||
pub mod renderer;
|
pub mod renderer;
|
||||||
|
@ -44,10 +47,9 @@ pub mod selection;
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
pub mod term;
|
pub mod term;
|
||||||
pub mod tty;
|
pub mod tty;
|
||||||
|
mod url;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod window;
|
pub mod window;
|
||||||
pub mod message_bar;
|
|
||||||
mod url;
|
|
||||||
|
|
||||||
pub use crate::grid::Grid;
|
pub use crate::grid::Grid;
|
||||||
pub use crate::term::Term;
|
pub use crate::term::Term;
|
||||||
|
|
|
@ -13,13 +13,13 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
#![allow(clippy::let_unit_value)]
|
#![allow(clippy::let_unit_value)]
|
||||||
#![cfg(target_os = "macos")]
|
#![cfg(target_os = "macos")]
|
||||||
use libc::{LC_CTYPE, setlocale};
|
use libc::{setlocale, LC_CTYPE};
|
||||||
use std::ffi::{CString, CStr};
|
use std::env;
|
||||||
|
use std::ffi::{CStr, CString};
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
use std::ptr::null;
|
use std::ptr::null;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::env;
|
|
||||||
|
|
||||||
use objc::runtime::{Class, Object};
|
use objc::runtime::{Class, Object};
|
||||||
|
|
||||||
|
@ -27,30 +27,32 @@ pub fn set_locale_environment() {
|
||||||
let locale_id = unsafe {
|
let locale_id = unsafe {
|
||||||
let locale_class = Class::get("NSLocale").unwrap();
|
let locale_class = Class::get("NSLocale").unwrap();
|
||||||
let locale: *const Object = msg_send![locale_class, currentLocale];
|
let locale: *const Object = msg_send![locale_class, currentLocale];
|
||||||
let _ : () = msg_send![locale_class, release];
|
let _: () = msg_send![locale_class, release];
|
||||||
// `localeIdentifier` returns extra metadata with the locale (including currency and
|
// `localeIdentifier` returns extra metadata with the locale (including currency and
|
||||||
// collator) on newer versions of macOS. This is not a valid locale, so we use
|
// collator) on newer versions of macOS. This is not a valid locale, so we use
|
||||||
// `languageCode` and `countryCode`, if they're available (macOS 10.12+):
|
// `languageCode` and `countryCode`, if they're available (macOS 10.12+):
|
||||||
// https://developer.apple.com/documentation/foundation/nslocale/1416263-localeidentifier?language=objc
|
// https://developer.apple.com/documentation/foundation/nslocale/1416263-localeidentifier?language=objc
|
||||||
// https://developer.apple.com/documentation/foundation/nslocale/1643060-countrycode?language=objc
|
// https://developer.apple.com/documentation/foundation/nslocale/1643060-countrycode?language=objc
|
||||||
// https://developer.apple.com/documentation/foundation/nslocale/1643026-languagecode?language=objc
|
// https://developer.apple.com/documentation/foundation/nslocale/1643026-languagecode?language=objc
|
||||||
let is_language_code_supported: bool = msg_send![locale, respondsToSelector:sel!(languageCode)];
|
let is_language_code_supported: bool =
|
||||||
let is_country_code_supported: bool = msg_send![locale, respondsToSelector:sel!(countryCode)];
|
msg_send![locale, respondsToSelector: sel!(languageCode)];
|
||||||
|
let is_country_code_supported: bool =
|
||||||
|
msg_send![locale, respondsToSelector: sel!(countryCode)];
|
||||||
let locale_id = if is_language_code_supported && is_country_code_supported {
|
let locale_id = if is_language_code_supported && is_country_code_supported {
|
||||||
let language_code: *const Object = msg_send![locale, languageCode];
|
let language_code: *const Object = msg_send![locale, languageCode];
|
||||||
let country_code: *const Object = msg_send![locale, countryCode];
|
let country_code: *const Object = msg_send![locale, countryCode];
|
||||||
let language_code_str = nsstring_as_str(language_code).to_owned();
|
let language_code_str = nsstring_as_str(language_code).to_owned();
|
||||||
let _ : () = msg_send![language_code, release];
|
let _: () = msg_send![language_code, release];
|
||||||
let country_code_str = nsstring_as_str(country_code).to_owned();
|
let country_code_str = nsstring_as_str(country_code).to_owned();
|
||||||
let _ : () = msg_send![country_code, release];
|
let _: () = msg_send![country_code, release];
|
||||||
format!("{}_{}.UTF-8", &language_code_str, &country_code_str)
|
format!("{}_{}.UTF-8", &language_code_str, &country_code_str)
|
||||||
} else {
|
} else {
|
||||||
let identifier: *const Object = msg_send![locale, localeIdentifier];
|
let identifier: *const Object = msg_send![locale, localeIdentifier];
|
||||||
let identifier_str = nsstring_as_str(identifier).to_owned();
|
let identifier_str = nsstring_as_str(identifier).to_owned();
|
||||||
let _ : () = msg_send![identifier, release];
|
let _: () = msg_send![identifier, release];
|
||||||
identifier_str + ".UTF-8"
|
identifier_str + ".UTF-8"
|
||||||
};
|
};
|
||||||
let _ : () = msg_send![locale, release];
|
let _: () = msg_send![locale, release];
|
||||||
locale_id
|
locale_id
|
||||||
};
|
};
|
||||||
// check if locale_id is valid
|
// check if locale_id is valid
|
||||||
|
@ -66,11 +68,7 @@ pub fn set_locale_environment() {
|
||||||
};
|
};
|
||||||
// try setting `locale_id`
|
// try setting `locale_id`
|
||||||
let modified = setlocale(LC_CTYPE, locale_ptr);
|
let modified = setlocale(LC_CTYPE, locale_ptr);
|
||||||
let result = if modified.is_null() {
|
let result = if modified.is_null() { String::new() } else { locale_id };
|
||||||
String::new()
|
|
||||||
} else {
|
|
||||||
locale_id
|
|
||||||
};
|
|
||||||
// restore original setting
|
// restore original setting
|
||||||
setlocale(LC_CTYPE, saved_original.as_ptr());
|
setlocale(LC_CTYPE, saved_original.as_ptr());
|
||||||
result
|
result
|
||||||
|
|
|
@ -68,12 +68,7 @@ impl Logger {
|
||||||
let logfile = Mutex::new(OnDemandLogFile::new());
|
let logfile = Mutex::new(OnDemandLogFile::new());
|
||||||
let stdout = Mutex::new(LineWriter::new(io::stdout()));
|
let stdout = Mutex::new(LineWriter::new(io::stdout()));
|
||||||
|
|
||||||
Logger {
|
Logger { level, logfile, stdout, message_tx }
|
||||||
level,
|
|
||||||
logfile,
|
|
||||||
stdout,
|
|
||||||
message_tx,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file_path(&self) -> Option<PathBuf> {
|
fn file_path(&self) -> Option<PathBuf> {
|
||||||
|
@ -100,10 +95,7 @@ impl log::Log for Logger {
|
||||||
now,
|
now,
|
||||||
record.level(),
|
record.level(),
|
||||||
record.file().unwrap_or("?"),
|
record.file().unwrap_or("?"),
|
||||||
record
|
record.line().map(|l| l.to_string()).unwrap_or_else(|| "?".into()),
|
||||||
.line()
|
|
||||||
.map(|l| l.to_string())
|
|
||||||
.unwrap_or_else(|| "?".into()),
|
|
||||||
record.args()
|
record.args()
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -161,11 +153,7 @@ impl OnDemandLogFile {
|
||||||
// Set log path as an environment variable
|
// Set log path as an environment variable
|
||||||
env::set_var(ALACRITTY_LOG_ENV, path.as_os_str());
|
env::set_var(ALACRITTY_LOG_ENV, path.as_os_str());
|
||||||
|
|
||||||
OnDemandLogFile {
|
OnDemandLogFile { path, file: None, created: Arc::new(AtomicBool::new(false)) }
|
||||||
path,
|
|
||||||
file: None,
|
|
||||||
created: Arc::new(AtomicBool::new(false)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file(&mut self) -> Result<&mut LineWriter<File>, io::Error> {
|
fn file(&mut self) -> Result<&mut LineWriter<File>, io::Error> {
|
||||||
|
@ -176,21 +164,18 @@ impl OnDemandLogFile {
|
||||||
|
|
||||||
// Create the file if it doesn't exist yet
|
// Create the file if it doesn't exist yet
|
||||||
if self.file.is_none() {
|
if self.file.is_none() {
|
||||||
let file = OpenOptions::new()
|
let file = OpenOptions::new().append(true).create(true).open(&self.path);
|
||||||
.append(true)
|
|
||||||
.create(true)
|
|
||||||
.open(&self.path);
|
|
||||||
|
|
||||||
match file {
|
match file {
|
||||||
Ok(file) => {
|
Ok(file) => {
|
||||||
self.file = Some(io::LineWriter::new(file));
|
self.file = Some(io::LineWriter::new(file));
|
||||||
self.created.store(true, Ordering::Relaxed);
|
self.created.store(true, Ordering::Relaxed);
|
||||||
let _ = writeln!(io::stdout(), "Created log file at {:?}", self.path);
|
let _ = writeln!(io::stdout(), "Created log file at {:?}", self.path);
|
||||||
}
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let _ = writeln!(io::stdout(), "Unable to create log file: {}", e);
|
let _ = writeln!(io::stdout(), "Unable to create log file: {}", e);
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
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)]
|
#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use, clippy::wrong_pub_self_convention)]
|
||||||
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
|
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
|
||||||
#![cfg_attr(all(test, feature = "bench"), feature(test))]
|
#![cfg_attr(all(test, feature = "bench"), feature(test))]
|
||||||
|
|
||||||
// With the default subsystem, 'console', windows creates an additional console
|
// With the default subsystem, 'console', windows creates an additional console
|
||||||
// window for the program.
|
// window for the program.
|
||||||
// This is silently ignored on non-windows systems.
|
// This is silently ignored on non-windows systems.
|
||||||
|
@ -29,12 +28,12 @@ use dirs;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use winapi::um::wincon::{AttachConsole, FreeConsole, ATTACH_PARENT_PROCESS};
|
use winapi::um::wincon::{AttachConsole, FreeConsole, ATTACH_PARENT_PROCESS};
|
||||||
|
|
||||||
use log::{info, error};
|
use log::{error, info};
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::sync::Arc;
|
|
||||||
use std::io::{self, Write};
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::io::{self, Write};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use std::env;
|
use std::env;
|
||||||
|
@ -42,19 +41,19 @@ use std::env;
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
use alacritty::locale;
|
|
||||||
use alacritty::{cli, event, die};
|
|
||||||
use alacritty::config::{self, Config, Monitor};
|
use alacritty::config::{self, Config, Monitor};
|
||||||
use alacritty::display::Display;
|
use alacritty::display::Display;
|
||||||
use alacritty::event_loop::{self, EventLoop, Msg};
|
use alacritty::event_loop::{self, EventLoop, Msg};
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use alacritty::locale;
|
||||||
use alacritty::logging;
|
use alacritty::logging;
|
||||||
|
use alacritty::message_bar::MessageBuffer;
|
||||||
use alacritty::panic;
|
use alacritty::panic;
|
||||||
use alacritty::sync::FairMutex;
|
use alacritty::sync::FairMutex;
|
||||||
use alacritty::term::Term;
|
use alacritty::term::Term;
|
||||||
use alacritty::tty;
|
use alacritty::tty;
|
||||||
use alacritty::util::fmt::Red;
|
use alacritty::util::fmt::Red;
|
||||||
use alacritty::message_bar::MessageBuffer;
|
use alacritty::{cli, die, event};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
panic::attach_handler();
|
panic::attach_handler();
|
||||||
|
@ -63,7 +62,9 @@ fn main() {
|
||||||
// to the console of the parent process, so we do it explicitly. This fails
|
// to the console of the parent process, so we do it explicitly. This fails
|
||||||
// silently if the parent has no console.
|
// silently if the parent has no console.
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
unsafe { AttachConsole(ATTACH_PARENT_PROCESS); }
|
unsafe {
|
||||||
|
AttachConsole(ATTACH_PARENT_PROCESS);
|
||||||
|
}
|
||||||
|
|
||||||
// Load command line options
|
// Load command line options
|
||||||
let options = cli::Options::load();
|
let options = cli::Options::load();
|
||||||
|
@ -77,7 +78,8 @@ fn main() {
|
||||||
|
|
||||||
// Load configuration file
|
// Load configuration file
|
||||||
// If the file is a command line argument, we won't write a generated default file
|
// If the file is a command line argument, we won't write a generated default file
|
||||||
let config_path = options.config_path()
|
let config_path = options
|
||||||
|
.config_path()
|
||||||
.or_else(Config::installed_config)
|
.or_else(Config::installed_config)
|
||||||
.or_else(|| Config::write_defaults().ok())
|
.or_else(|| Config::write_defaults().ok())
|
||||||
.map(|path| path.to_path_buf());
|
.map(|path| path.to_path_buf());
|
||||||
|
@ -133,11 +135,7 @@ fn run(
|
||||||
// The display manages a window and can draw the terminal
|
// The display manages a window and can draw the terminal
|
||||||
let mut display = Display::new(&config, options)?;
|
let mut display = Display::new(&config, options)?;
|
||||||
|
|
||||||
info!(
|
info!("PTY Dimensions: {:?} x {:?}", display.size().lines(), display.size().cols());
|
||||||
"PTY Dimensions: {:?} x {:?}",
|
|
||||||
display.size().lines(),
|
|
||||||
display.size().cols()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create the terminal
|
// Create the terminal
|
||||||
//
|
//
|
||||||
|
@ -173,12 +171,8 @@ fn run(
|
||||||
// renderer and input processing. Note that access to the terminal state is
|
// renderer and input processing. Note that access to the terminal state is
|
||||||
// synchronized since the I/O loop updates the state, and the display
|
// synchronized since the I/O loop updates the state, and the display
|
||||||
// consumes it periodically.
|
// consumes it periodically.
|
||||||
let event_loop = EventLoop::new(
|
let event_loop =
|
||||||
Arc::clone(&terminal),
|
EventLoop::new(Arc::clone(&terminal), display.notifier(), pty, options.ref_test);
|
||||||
display.notifier(),
|
|
||||||
pty,
|
|
||||||
options.ref_test,
|
|
||||||
);
|
|
||||||
|
|
||||||
// The event loop channel allows write requests from the event processor
|
// The event loop channel allows write requests from the event processor
|
||||||
// to be sent to the loop and ultimately written to the pty.
|
// to be sent to the loop and ultimately written to the pty.
|
||||||
|
@ -259,16 +253,16 @@ fn run(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loop_tx
|
loop_tx.send(Msg::Shutdown).expect("Error sending shutdown to event loop");
|
||||||
.send(Msg::Shutdown)
|
|
||||||
.expect("Error sending shutdown to event loop");
|
|
||||||
|
|
||||||
// FIXME patch notify library to have a shutdown method
|
// FIXME patch notify library to have a shutdown method
|
||||||
// config_reloader.join().ok();
|
// config_reloader.join().ok();
|
||||||
|
|
||||||
// Without explicitly detaching the console cmd won't redraw it's prompt
|
// Without explicitly detaching the console cmd won't redraw it's prompt
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
unsafe { FreeConsole(); }
|
unsafe {
|
||||||
|
FreeConsole();
|
||||||
|
}
|
||||||
|
|
||||||
info!("Goodbye");
|
info!("Goodbye");
|
||||||
|
|
||||||
|
|
|
@ -33,11 +33,7 @@ pub struct Message {
|
||||||
impl Message {
|
impl Message {
|
||||||
/// Create a new message
|
/// Create a new message
|
||||||
pub fn new(text: String, color: Rgb) -> Message {
|
pub fn new(text: String, color: Rgb) -> Message {
|
||||||
Message {
|
Message { text, color, topic: None }
|
||||||
text,
|
|
||||||
color,
|
|
||||||
topic: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Formatted message text lines
|
/// Formatted message text lines
|
||||||
|
@ -135,11 +131,7 @@ impl MessageBuffer {
|
||||||
/// Create new message buffer
|
/// Create new message buffer
|
||||||
pub fn new() -> MessageBuffer {
|
pub fn new() -> MessageBuffer {
|
||||||
let (tx, messages) = crossbeam_channel::unbounded();
|
let (tx, messages) = crossbeam_channel::unbounded();
|
||||||
MessageBuffer {
|
MessageBuffer { current: None, messages, tx }
|
||||||
current: None,
|
|
||||||
messages,
|
|
||||||
tx,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if there are any messages queued
|
/// Check if there are any messages queued
|
||||||
|
@ -215,10 +207,7 @@ mod test {
|
||||||
fn appends_close_button() {
|
fn appends_close_button() {
|
||||||
let input = "a";
|
let input = "a";
|
||||||
let mut message_buffer = MessageBuffer::new();
|
let mut message_buffer = MessageBuffer::new();
|
||||||
message_buffer
|
message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap();
|
||||||
.tx()
|
|
||||||
.send(Message::new(input.into(), color::RED))
|
|
||||||
.unwrap();
|
|
||||||
let size = SizeInfo {
|
let size = SizeInfo {
|
||||||
width: 7.,
|
width: 7.,
|
||||||
height: 10.,
|
height: 10.,
|
||||||
|
@ -238,10 +227,7 @@ mod test {
|
||||||
fn multiline_close_button_first_line() {
|
fn multiline_close_button_first_line() {
|
||||||
let input = "fo\nbar";
|
let input = "fo\nbar";
|
||||||
let mut message_buffer = MessageBuffer::new();
|
let mut message_buffer = MessageBuffer::new();
|
||||||
message_buffer
|
message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap();
|
||||||
.tx()
|
|
||||||
.send(Message::new(input.into(), color::RED))
|
|
||||||
.unwrap();
|
|
||||||
let size = SizeInfo {
|
let size = SizeInfo {
|
||||||
width: 6.,
|
width: 6.,
|
||||||
height: 10.,
|
height: 10.,
|
||||||
|
@ -261,10 +247,7 @@ mod test {
|
||||||
fn splits_on_newline() {
|
fn splits_on_newline() {
|
||||||
let input = "a\nb";
|
let input = "a\nb";
|
||||||
let mut message_buffer = MessageBuffer::new();
|
let mut message_buffer = MessageBuffer::new();
|
||||||
message_buffer
|
message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap();
|
||||||
.tx()
|
|
||||||
.send(Message::new(input.into(), color::RED))
|
|
||||||
.unwrap();
|
|
||||||
let size = SizeInfo {
|
let size = SizeInfo {
|
||||||
width: 6.,
|
width: 6.,
|
||||||
height: 10.,
|
height: 10.,
|
||||||
|
@ -284,10 +267,7 @@ mod test {
|
||||||
fn splits_on_length() {
|
fn splits_on_length() {
|
||||||
let input = "foobar1";
|
let input = "foobar1";
|
||||||
let mut message_buffer = MessageBuffer::new();
|
let mut message_buffer = MessageBuffer::new();
|
||||||
message_buffer
|
message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap();
|
||||||
.tx()
|
|
||||||
.send(Message::new(input.into(), color::RED))
|
|
||||||
.unwrap();
|
|
||||||
let size = SizeInfo {
|
let size = SizeInfo {
|
||||||
width: 6.,
|
width: 6.,
|
||||||
height: 10.,
|
height: 10.,
|
||||||
|
@ -307,10 +287,7 @@ mod test {
|
||||||
fn empty_with_shortterm() {
|
fn empty_with_shortterm() {
|
||||||
let input = "foobar";
|
let input = "foobar";
|
||||||
let mut message_buffer = MessageBuffer::new();
|
let mut message_buffer = MessageBuffer::new();
|
||||||
message_buffer
|
message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap();
|
||||||
.tx()
|
|
||||||
.send(Message::new(input.into(), color::RED))
|
|
||||||
.unwrap();
|
|
||||||
let size = SizeInfo {
|
let size = SizeInfo {
|
||||||
width: 6.,
|
width: 6.,
|
||||||
height: 0.,
|
height: 0.,
|
||||||
|
@ -330,10 +307,7 @@ mod test {
|
||||||
fn truncates_long_messages() {
|
fn truncates_long_messages() {
|
||||||
let input = "hahahahahahahahahahaha truncate this because it's too long for the term";
|
let input = "hahahahahahahahahahaha truncate this because it's too long for the term";
|
||||||
let mut message_buffer = MessageBuffer::new();
|
let mut message_buffer = MessageBuffer::new();
|
||||||
message_buffer
|
message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap();
|
||||||
.tx()
|
|
||||||
.send(Message::new(input.into(), color::RED))
|
|
||||||
.unwrap();
|
|
||||||
let size = SizeInfo {
|
let size = SizeInfo {
|
||||||
width: 22.,
|
width: 22.,
|
||||||
height: (MIN_FREE_LINES + 2) as f32,
|
height: (MIN_FREE_LINES + 2) as f32,
|
||||||
|
@ -346,23 +320,17 @@ mod test {
|
||||||
|
|
||||||
let lines = message_buffer.message().unwrap().text(&size);
|
let lines = message_buffer.message().unwrap().text(&size);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(lines, vec![
|
||||||
lines,
|
String::from("hahahahahahahahaha [X]"),
|
||||||
vec![
|
String::from("[MESSAGE TRUNCATED] ")
|
||||||
String::from("hahahahahahahahaha [X]"),
|
]);
|
||||||
String::from("[MESSAGE TRUNCATED] ")
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hide_button_when_too_narrow() {
|
fn hide_button_when_too_narrow() {
|
||||||
let input = "ha";
|
let input = "ha";
|
||||||
let mut message_buffer = MessageBuffer::new();
|
let mut message_buffer = MessageBuffer::new();
|
||||||
message_buffer
|
message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap();
|
||||||
.tx()
|
|
||||||
.send(Message::new(input.into(), color::RED))
|
|
||||||
.unwrap();
|
|
||||||
let size = SizeInfo {
|
let size = SizeInfo {
|
||||||
width: 2.,
|
width: 2.,
|
||||||
height: 10.,
|
height: 10.,
|
||||||
|
@ -382,10 +350,7 @@ mod test {
|
||||||
fn hide_truncated_when_too_narrow() {
|
fn hide_truncated_when_too_narrow() {
|
||||||
let input = "hahahahahahahahaha";
|
let input = "hahahahahahahahaha";
|
||||||
let mut message_buffer = MessageBuffer::new();
|
let mut message_buffer = MessageBuffer::new();
|
||||||
message_buffer
|
message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap();
|
||||||
.tx()
|
|
||||||
.send(Message::new(input.into(), color::RED))
|
|
||||||
.unwrap();
|
|
||||||
let size = SizeInfo {
|
let size = SizeInfo {
|
||||||
width: 2.,
|
width: 2.,
|
||||||
height: (MIN_FREE_LINES + 2) as f32,
|
height: (MIN_FREE_LINES + 2) as f32,
|
||||||
|
@ -405,10 +370,7 @@ mod test {
|
||||||
fn add_newline_for_button() {
|
fn add_newline_for_button() {
|
||||||
let input = "test";
|
let input = "test";
|
||||||
let mut message_buffer = MessageBuffer::new();
|
let mut message_buffer = MessageBuffer::new();
|
||||||
message_buffer
|
message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap();
|
||||||
.tx()
|
|
||||||
.send(Message::new(input.into(), color::RED))
|
|
||||||
.unwrap();
|
|
||||||
let size = SizeInfo {
|
let size = SizeInfo {
|
||||||
width: 5.,
|
width: 5.,
|
||||||
height: 10.,
|
height: 10.,
|
||||||
|
@ -466,10 +428,7 @@ mod test {
|
||||||
fn wrap_on_words() {
|
fn wrap_on_words() {
|
||||||
let input = "a\nbc defg";
|
let input = "a\nbc defg";
|
||||||
let mut message_buffer = MessageBuffer::new();
|
let mut message_buffer = MessageBuffer::new();
|
||||||
message_buffer
|
message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap();
|
||||||
.tx()
|
|
||||||
.send(Message::new(input.into(), color::RED))
|
|
||||||
.unwrap();
|
|
||||||
let size = SizeInfo {
|
let size = SizeInfo {
|
||||||
width: 5.,
|
width: 5.,
|
||||||
height: 10.,
|
height: 10.,
|
||||||
|
@ -482,14 +441,11 @@ mod test {
|
||||||
|
|
||||||
let lines = message_buffer.message().unwrap().text(&size);
|
let lines = message_buffer.message().unwrap().text(&size);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(lines, vec![
|
||||||
lines,
|
String::from("a [X]"),
|
||||||
vec![
|
String::from("bc "),
|
||||||
String::from("a [X]"),
|
String::from("defg ")
|
||||||
String::from("bc "),
|
]);
|
||||||
String::from("defg ")
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
//! // the average won't mean much until it's filled up at least once.
|
//! // the average won't mean much until it's filled up at least once.
|
||||||
//! println!("Average time: {}", meter.average());
|
//! println!("Average time: {}", meter.average());
|
||||||
|
|
||||||
use std::time::{Instant, Duration};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
const NUM_SAMPLES: usize = 10;
|
const NUM_SAMPLES: usize = 10;
|
||||||
|
|
||||||
|
@ -61,10 +61,7 @@ pub struct Sampler<'a> {
|
||||||
|
|
||||||
impl<'a> Sampler<'a> {
|
impl<'a> Sampler<'a> {
|
||||||
fn new(meter: &'a mut Meter) -> Sampler<'a> {
|
fn new(meter: &'a mut Meter) -> Sampler<'a> {
|
||||||
Sampler {
|
Sampler { meter, created_at: Instant::now() }
|
||||||
meter,
|
|
||||||
created_at: Instant::now(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -22,17 +22,17 @@ use std::sync::mpsc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use fnv::FnvHasher;
|
use fnv::FnvHasher;
|
||||||
use glutin::dpi::PhysicalSize;
|
|
||||||
use font::{self, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer};
|
use font::{self, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer};
|
||||||
|
use glutin::dpi::PhysicalSize;
|
||||||
use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
|
use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
|
||||||
|
|
||||||
use crate::gl::types::*;
|
|
||||||
use crate::gl;
|
|
||||||
use crate::index::{Column, Line};
|
|
||||||
use crate::term::color::Rgb;
|
|
||||||
use crate::config::{self, Config, Delta};
|
use crate::config::{self, Config, Delta};
|
||||||
use crate::term::{self, cell, RenderableCell};
|
use crate::gl;
|
||||||
|
use crate::gl::types::*;
|
||||||
|
use crate::index::{Column, Line};
|
||||||
use crate::renderer::rects::{Rect, Rects};
|
use crate::renderer::rects::{Rect, Rects};
|
||||||
|
use crate::term::color::Rgb;
|
||||||
|
use crate::term::{self, cell, RenderableCell};
|
||||||
|
|
||||||
pub mod rects;
|
pub mod rects;
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ impl ::std::fmt::Display for Error {
|
||||||
match *self {
|
match *self {
|
||||||
Error::ShaderCreation(ref err) => {
|
Error::ShaderCreation(ref err) => {
|
||||||
write!(f, "There was an error initializing the shaders: {}", err)
|
write!(f, "There was an error initializing the shaders: {}", err)
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,11 +214,7 @@ impl GlyphCache {
|
||||||
fn load_glyphs_for_font<L: LoadGlyph>(&mut self, font: FontKey, loader: &mut L) {
|
fn load_glyphs_for_font<L: LoadGlyph>(&mut self, font: FontKey, loader: &mut L) {
|
||||||
let size = self.font_size;
|
let size = self.font_size;
|
||||||
for i in 32u8..=128u8 {
|
for i in 32u8..=128u8 {
|
||||||
self.get(GlyphKey {
|
self.get(GlyphKey { font_key: font, c: i as char, size }, loader);
|
||||||
font_key: font,
|
|
||||||
c: i as char,
|
|
||||||
size,
|
|
||||||
}, loader);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,7 +226,8 @@ impl GlyphCache {
|
||||||
let size = font.size();
|
let size = font.size();
|
||||||
|
|
||||||
// Load regular font
|
// Load regular font
|
||||||
let regular_desc = Self::make_desc(&font.normal(), font::Slant::Normal, font::Weight::Normal);
|
let regular_desc =
|
||||||
|
Self::make_desc(&font.normal(), font::Slant::Normal, font::Weight::Normal);
|
||||||
|
|
||||||
let regular = rasterizer.load_font(®ular_desc, size)?;
|
let regular = rasterizer.load_font(®ular_desc, size)?;
|
||||||
|
|
||||||
|
@ -239,9 +236,7 @@ impl GlyphCache {
|
||||||
if desc == regular_desc {
|
if desc == regular_desc {
|
||||||
regular
|
regular
|
||||||
} else {
|
} else {
|
||||||
rasterizer
|
rasterizer.load_font(&desc, size).unwrap_or_else(|_| regular)
|
||||||
.load_font(&desc, size)
|
|
||||||
.unwrap_or_else(|_| regular)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -251,7 +246,8 @@ impl GlyphCache {
|
||||||
let bold = load_or_regular(bold_desc);
|
let bold = load_or_regular(bold_desc);
|
||||||
|
|
||||||
// Load italic font
|
// Load italic font
|
||||||
let italic_desc = Self::make_desc(&font.italic(), font::Slant::Italic, font::Weight::Normal);
|
let italic_desc =
|
||||||
|
Self::make_desc(&font.italic(), font::Slant::Italic, font::Weight::Normal);
|
||||||
|
|
||||||
let italic = load_or_regular(italic_desc);
|
let italic = load_or_regular(italic_desc);
|
||||||
|
|
||||||
|
@ -278,30 +274,30 @@ impl GlyphCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get<'a, L>(&'a mut self, glyph_key: GlyphKey, loader: &mut L) -> &'a Glyph
|
pub fn get<'a, L>(&'a mut self, glyph_key: GlyphKey, loader: &mut L) -> &'a Glyph
|
||||||
where L: LoadGlyph
|
where
|
||||||
|
L: LoadGlyph,
|
||||||
{
|
{
|
||||||
let glyph_offset = self.glyph_offset;
|
let glyph_offset = self.glyph_offset;
|
||||||
let rasterizer = &mut self.rasterizer;
|
let rasterizer = &mut self.rasterizer;
|
||||||
let metrics = &self.metrics;
|
let metrics = &self.metrics;
|
||||||
self.cache
|
self.cache.entry(glyph_key).or_insert_with(|| {
|
||||||
.entry(glyph_key)
|
let mut rasterized =
|
||||||
.or_insert_with(|| {
|
rasterizer.get_glyph(glyph_key).unwrap_or_else(|_| Default::default());
|
||||||
let mut rasterized = rasterizer.get_glyph(glyph_key)
|
|
||||||
.unwrap_or_else(|_| Default::default());
|
|
||||||
|
|
||||||
rasterized.left += i32::from(glyph_offset.x);
|
rasterized.left += i32::from(glyph_offset.x);
|
||||||
rasterized.top += i32::from(glyph_offset.y);
|
rasterized.top += i32::from(glyph_offset.y);
|
||||||
rasterized.top -= metrics.descent as i32;
|
rasterized.top -= metrics.descent as i32;
|
||||||
|
|
||||||
loader.load_glyph(&rasterized)
|
loader.load_glyph(&rasterized)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_font_size<L: LoadGlyph>(
|
pub fn update_font_size<L: LoadGlyph>(
|
||||||
&mut self,
|
&mut self,
|
||||||
font: &config::Font,
|
font: &config::Font,
|
||||||
size: font::Size,
|
size: font::Size,
|
||||||
dpr: f64,
|
dpr: f64,
|
||||||
loader: &mut L
|
loader: &mut L,
|
||||||
) -> Result<(), font::Error> {
|
) -> Result<(), font::Error> {
|
||||||
// Clear currently cached data in both GL and the registry
|
// Clear currently cached data in both GL and the registry
|
||||||
loader.clear();
|
loader.clear();
|
||||||
|
@ -410,10 +406,7 @@ pub struct Batch {
|
||||||
impl Batch {
|
impl Batch {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> Batch {
|
pub fn new() -> Batch {
|
||||||
Batch {
|
Batch { tex: 0, instances: Vec::with_capacity(BATCH_MAX) }
|
||||||
tex: 0,
|
|
||||||
instances: Vec::with_capacity(BATCH_MAX),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_item(&mut self, cell: &RenderableCell, glyph: &Glyph) {
|
pub fn add_item(&mut self, cell: &RenderableCell, glyph: &Glyph) {
|
||||||
|
@ -592,16 +585,13 @@ impl QuadRenderer {
|
||||||
gl::GenBuffers(1, &mut rect_vbo);
|
gl::GenBuffers(1, &mut rect_vbo);
|
||||||
gl::GenBuffers(1, &mut rect_ebo);
|
gl::GenBuffers(1, &mut rect_ebo);
|
||||||
gl::BindVertexArray(rect_vao);
|
gl::BindVertexArray(rect_vao);
|
||||||
let indices: [i32; 6] = [
|
let indices: [i32; 6] = [0, 1, 3, 1, 2, 3];
|
||||||
0, 1, 3,
|
|
||||||
1, 2, 3,
|
|
||||||
];
|
|
||||||
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, rect_ebo);
|
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, rect_ebo);
|
||||||
gl::BufferData(
|
gl::BufferData(
|
||||||
gl::ELEMENT_ARRAY_BUFFER,
|
gl::ELEMENT_ARRAY_BUFFER,
|
||||||
(size_of::<i32>() * indices.len()) as _,
|
(size_of::<i32>() * indices.len()) as _,
|
||||||
indices.as_ptr() as *const _,
|
indices.as_ptr() as *const _,
|
||||||
gl::STATIC_DRAW
|
gl::STATIC_DRAW,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
|
@ -629,13 +619,13 @@ impl QuadRenderer {
|
||||||
let event = rx.recv().expect("watcher event");
|
let event = rx.recv().expect("watcher event");
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
DebouncedEvent::Rename(_, _) => continue,
|
DebouncedEvent::Rename(..) => continue,
|
||||||
DebouncedEvent::Create(_)
|
DebouncedEvent::Create(_)
|
||||||
| DebouncedEvent::Write(_)
|
| DebouncedEvent::Write(_)
|
||||||
| DebouncedEvent::Chmod(_) => {
|
| DebouncedEvent::Chmod(_) => {
|
||||||
msg_tx.send(Msg::ShaderReload).expect("msg send ok");
|
msg_tx.send(Msg::ShaderReload).expect("msg send ok");
|
||||||
}
|
},
|
||||||
_ => {}
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -686,7 +676,14 @@ impl QuadRenderer {
|
||||||
gl::BindBuffer(gl::ARRAY_BUFFER, self.rect_vbo);
|
gl::BindBuffer(gl::ARRAY_BUFFER, self.rect_vbo);
|
||||||
|
|
||||||
// Position
|
// Position
|
||||||
gl::VertexAttribPointer(0, 2, gl::FLOAT, gl::FALSE, (size_of::<f32>() * 2) as _, ptr::null());
|
gl::VertexAttribPointer(
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
gl::FLOAT,
|
||||||
|
gl::FALSE,
|
||||||
|
(size_of::<f32>() * 2) as _,
|
||||||
|
ptr::null(),
|
||||||
|
);
|
||||||
gl::EnableVertexAttribArray(0);
|
gl::EnableVertexAttribArray(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -720,12 +717,7 @@ impl QuadRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_api<F, T>(
|
pub fn with_api<F, T>(&mut self, config: &Config, props: &term::SizeInfo, func: F) -> T
|
||||||
&mut self,
|
|
||||||
config: &Config,
|
|
||||||
props: &term::SizeInfo,
|
|
||||||
func: F,
|
|
||||||
) -> T
|
|
||||||
where
|
where
|
||||||
F: FnOnce(RenderApi<'_>) -> T,
|
F: FnOnce(RenderApi<'_>) -> T,
|
||||||
{
|
{
|
||||||
|
@ -788,11 +780,11 @@ impl QuadRenderer {
|
||||||
(Ok(program), Ok(rect_program)) => {
|
(Ok(program), Ok(rect_program)) => {
|
||||||
info!("... successfully reloaded shaders");
|
info!("... successfully reloaded shaders");
|
||||||
(program, rect_program)
|
(program, rect_program)
|
||||||
}
|
},
|
||||||
(Err(err), _) | (_, Err(err)) => {
|
(Err(err), _) | (_, Err(err)) => {
|
||||||
error!("{}", err);
|
error!("{}", err);
|
||||||
return;
|
return;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
self.active_tex = 0;
|
self.active_tex = 0;
|
||||||
|
@ -842,19 +834,14 @@ impl QuadRenderer {
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// Setup vertices
|
// Setup vertices
|
||||||
let vertices: [f32; 8] = [
|
let vertices: [f32; 8] = [x + width, y, x + width, y - height, x, y - height, x, y];
|
||||||
x + width, y ,
|
|
||||||
x + width, y - height,
|
|
||||||
x , y - height,
|
|
||||||
x , y ,
|
|
||||||
];
|
|
||||||
|
|
||||||
// Load vertex data into array buffer
|
// Load vertex data into array buffer
|
||||||
gl::BufferData(
|
gl::BufferData(
|
||||||
gl::ARRAY_BUFFER,
|
gl::ARRAY_BUFFER,
|
||||||
(size_of::<f32>() * vertices.len()) as _,
|
(size_of::<f32>() * vertices.len()) as _,
|
||||||
vertices.as_ptr() as *const _,
|
vertices.as_ptr() as *const _,
|
||||||
gl::STATIC_DRAW
|
gl::STATIC_DRAW,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Color
|
// Color
|
||||||
|
@ -927,7 +914,7 @@ impl<'a> RenderApi<'a> {
|
||||||
string: &str,
|
string: &str,
|
||||||
line: Line,
|
line: Line,
|
||||||
glyph_cache: &mut GlyphCache,
|
glyph_cache: &mut GlyphCache,
|
||||||
color: Option<Rgb>
|
color: Option<Rgb>,
|
||||||
) {
|
) {
|
||||||
let bg_alpha = color.map(|_| 1.0).unwrap_or(0.0);
|
let bg_alpha = color.map(|_| 1.0).unwrap_or(0.0);
|
||||||
let col = Column(0);
|
let col = Column(0);
|
||||||
|
@ -943,7 +930,7 @@ impl<'a> RenderApi<'a> {
|
||||||
chars[0] = c;
|
chars[0] = c;
|
||||||
chars
|
chars
|
||||||
},
|
},
|
||||||
bg: color.unwrap_or(Rgb { r: 0, g: 0, b: 0}),
|
bg: color.unwrap_or(Rgb { r: 0, g: 0, b: 0 }),
|
||||||
fg: Rgb { r: 0, g: 0, b: 0 },
|
fg: Rgb { r: 0, g: 0, b: 0 },
|
||||||
flags: cell::Flags::empty(),
|
flags: cell::Flags::empty(),
|
||||||
bg_alpha,
|
bg_alpha,
|
||||||
|
@ -993,11 +980,7 @@ impl<'a> RenderApi<'a> {
|
||||||
chars[0] = ' ';
|
chars[0] = ' ';
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut glyph_key = GlyphKey {
|
let mut glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, c: chars[0] };
|
||||||
font_key,
|
|
||||||
size: glyph_cache.font_size,
|
|
||||||
c: chars[0],
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add cell to batch
|
// Add cell to batch
|
||||||
let glyph = glyph_cache.get(glyph_key, self);
|
let glyph = glyph_cache.get(glyph_key, self);
|
||||||
|
@ -1028,7 +1011,7 @@ fn load_glyph(
|
||||||
active_tex: &mut GLuint,
|
active_tex: &mut GLuint,
|
||||||
atlas: &mut Vec<Atlas>,
|
atlas: &mut Vec<Atlas>,
|
||||||
current_atlas: &mut usize,
|
current_atlas: &mut usize,
|
||||||
rasterized: &RasterizedGlyph
|
rasterized: &RasterizedGlyph,
|
||||||
) -> Glyph {
|
) -> Glyph {
|
||||||
// At least one atlas is guaranteed to be in the `self.atlas` list; thus
|
// At least one atlas is guaranteed to be in the `self.atlas` list; thus
|
||||||
// the unwrap.
|
// the unwrap.
|
||||||
|
@ -1042,20 +1025,18 @@ fn load_glyph(
|
||||||
atlas.push(new);
|
atlas.push(new);
|
||||||
}
|
}
|
||||||
load_glyph(active_tex, atlas, current_atlas, rasterized)
|
load_glyph(active_tex, atlas, current_atlas, rasterized)
|
||||||
}
|
},
|
||||||
Err(AtlasInsertError::GlyphTooLarge) => {
|
Err(AtlasInsertError::GlyphTooLarge) => Glyph {
|
||||||
Glyph {
|
tex_id: atlas[*current_atlas].id,
|
||||||
tex_id: atlas[*current_atlas].id,
|
top: 0.0,
|
||||||
top: 0.0,
|
left: 0.0,
|
||||||
left: 0.0,
|
width: 0.0,
|
||||||
width: 0.0,
|
height: 0.0,
|
||||||
height: 0.0,
|
uv_bot: 0.0,
|
||||||
uv_bot: 0.0,
|
uv_left: 0.0,
|
||||||
uv_left: 0.0,
|
uv_width: 0.0,
|
||||||
uv_width: 0.0,
|
uv_height: 0.0,
|
||||||
uv_height: 0.0,
|
},
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1113,7 +1094,9 @@ impl TextShaderProgram {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! cptr {
|
macro_rules! cptr {
|
||||||
($thing:expr) => { $thing.as_ptr() as *const _ }
|
($thing:expr) => {
|
||||||
|
$thing.as_ptr() as *const _
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! assert_uniform_valid {
|
macro_rules! assert_uniform_valid {
|
||||||
|
@ -1146,16 +1129,16 @@ impl TextShaderProgram {
|
||||||
|
|
||||||
shader.update_projection(size.width as f32, size.height as f32, 0., 0.);
|
shader.update_projection(size.width as f32, size.height as f32, 0., 0.);
|
||||||
|
|
||||||
unsafe { gl::UseProgram(0); }
|
unsafe {
|
||||||
|
gl::UseProgram(0);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(shader)
|
Ok(shader)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_projection(&self, width: f32, height: f32, padding_x: f32, padding_y: f32) {
|
fn update_projection(&self, width: f32, height: f32, padding_x: f32, padding_y: f32) {
|
||||||
// Bounds check
|
// Bounds check
|
||||||
if (width as u32) < (2 * padding_x as u32) ||
|
if (width as u32) < (2 * padding_x as u32) || (height as u32) < (2 * padding_y as u32) {
|
||||||
(height as u32) < (2 * padding_y as u32)
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1204,16 +1187,8 @@ impl RectShaderProgram {
|
||||||
} else {
|
} else {
|
||||||
(Some(RECT_SHADER_V), Some(RECT_SHADER_F))
|
(Some(RECT_SHADER_V), Some(RECT_SHADER_F))
|
||||||
};
|
};
|
||||||
let vertex_shader = create_shader(
|
let vertex_shader = create_shader(RECT_SHADER_V_PATH, gl::VERTEX_SHADER, vertex_src)?;
|
||||||
RECT_SHADER_V_PATH,
|
let fragment_shader = create_shader(RECT_SHADER_F_PATH, gl::FRAGMENT_SHADER, fragment_src)?;
|
||||||
gl::VERTEX_SHADER,
|
|
||||||
vertex_src
|
|
||||||
)?;
|
|
||||||
let fragment_shader = create_shader(
|
|
||||||
RECT_SHADER_F_PATH,
|
|
||||||
gl::FRAGMENT_SHADER,
|
|
||||||
fragment_src
|
|
||||||
)?;
|
|
||||||
let program = create_program(vertex_shader, fragment_shader)?;
|
let program = create_program(vertex_shader, fragment_shader)?;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -1223,14 +1198,9 @@ impl RectShaderProgram {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get uniform locations
|
// get uniform locations
|
||||||
let u_color = unsafe {
|
let u_color = unsafe { gl::GetUniformLocation(program, b"color\0".as_ptr() as *const _) };
|
||||||
gl::GetUniformLocation(program, b"color\0".as_ptr() as *const _)
|
|
||||||
};
|
|
||||||
|
|
||||||
let shader = RectShaderProgram {
|
let shader = RectShaderProgram { id: program, u_color };
|
||||||
id: program,
|
|
||||||
u_color,
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe { gl::UseProgram(0) }
|
unsafe { gl::UseProgram(0) }
|
||||||
|
|
||||||
|
@ -1276,9 +1246,11 @@ fn create_program(vertex: GLuint, fragment: GLuint) -> Result<GLuint, ShaderCrea
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_shader(path: &str, kind: GLenum, source: Option<&'static str>)
|
fn create_shader(
|
||||||
-> Result<GLuint, ShaderCreationError>
|
path: &str,
|
||||||
{
|
kind: GLenum,
|
||||||
|
source: Option<&'static str>,
|
||||||
|
) -> Result<GLuint, ShaderCreationError> {
|
||||||
let from_disk;
|
let from_disk;
|
||||||
let source = if let Some(src) = source {
|
let source = if let Some(src) = source {
|
||||||
src
|
src
|
||||||
|
@ -1308,7 +1280,9 @@ fn create_shader(path: &str, kind: GLenum, source: Option<&'static str>)
|
||||||
let log = get_shader_info_log(shader);
|
let log = get_shader_info_log(shader);
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
unsafe { gl::DeleteShader(shader); }
|
unsafe {
|
||||||
|
gl::DeleteShader(shader);
|
||||||
|
}
|
||||||
|
|
||||||
Err(ShaderCreationError::Compile(PathBuf::from(path), log))
|
Err(ShaderCreationError::Compile(PathBuf::from(path), log))
|
||||||
}
|
}
|
||||||
|
@ -1325,12 +1299,7 @@ fn get_program_info_log(program: GLuint) -> String {
|
||||||
let mut actual_length: GLint = 0;
|
let mut actual_length: GLint = 0;
|
||||||
let mut buf: Vec<u8> = Vec::with_capacity(max_length as usize);
|
let mut buf: Vec<u8> = Vec::with_capacity(max_length as usize);
|
||||||
unsafe {
|
unsafe {
|
||||||
gl::GetProgramInfoLog(
|
gl::GetProgramInfoLog(program, max_length, &mut actual_length, buf.as_mut_ptr() as *mut _);
|
||||||
program,
|
|
||||||
max_length,
|
|
||||||
&mut actual_length,
|
|
||||||
buf.as_mut_ptr() as *mut _,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build a string
|
// Build a string
|
||||||
|
@ -1353,12 +1322,7 @@ fn get_shader_info_log(shader: GLuint) -> String {
|
||||||
let mut actual_length: GLint = 0;
|
let mut actual_length: GLint = 0;
|
||||||
let mut buf: Vec<u8> = Vec::with_capacity(max_length as usize);
|
let mut buf: Vec<u8> = Vec::with_capacity(max_length as usize);
|
||||||
unsafe {
|
unsafe {
|
||||||
gl::GetShaderInfoLog(
|
gl::GetShaderInfoLog(shader, max_length, &mut actual_length, buf.as_mut_ptr() as *mut _);
|
||||||
shader,
|
|
||||||
max_length,
|
|
||||||
&mut actual_length,
|
|
||||||
buf.as_mut_ptr() as *mut _,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build a string
|
// Build a string
|
||||||
|
@ -1410,15 +1374,11 @@ impl ::std::error::Error for ShaderCreationError {
|
||||||
impl ::std::fmt::Display for ShaderCreationError {
|
impl ::std::fmt::Display for ShaderCreationError {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
ShaderCreationError::Io(ref err) => {
|
ShaderCreationError::Io(ref err) => write!(f, "Couldn't read shader: {}", err),
|
||||||
write!(f, "Couldn't read shader: {}", err)
|
|
||||||
},
|
|
||||||
ShaderCreationError::Compile(ref path, ref log) => {
|
ShaderCreationError::Compile(ref path, ref log) => {
|
||||||
write!(f, "Failed compiling shader at {}: {}", path.display(), log)
|
write!(f, "Failed compiling shader at {}: {}", path.display(), log)
|
||||||
},
|
},
|
||||||
ShaderCreationError::Link(ref log) => {
|
ShaderCreationError::Link(ref log) => write!(f, "Failed linking shader: {}", log),
|
||||||
write!(f, "Failed linking shader: {}", log)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1509,14 +1469,7 @@ impl Atlas {
|
||||||
gl::BindTexture(gl::TEXTURE_2D, 0);
|
gl::BindTexture(gl::TEXTURE_2D, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Atlas {
|
Atlas { id, width: size, height: size, row_extent: 0, row_baseline: 0, row_tallest: 0 }
|
||||||
id,
|
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
row_extent: 0,
|
|
||||||
row_baseline: 0,
|
|
||||||
row_tallest: 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
|
@ -1529,7 +1482,7 @@ impl Atlas {
|
||||||
pub fn insert(
|
pub fn insert(
|
||||||
&mut self,
|
&mut self,
|
||||||
glyph: &RasterizedGlyph,
|
glyph: &RasterizedGlyph,
|
||||||
active_tex: &mut u32
|
active_tex: &mut u32,
|
||||||
) -> Result<Glyph, AtlasInsertError> {
|
) -> Result<Glyph, AtlasInsertError> {
|
||||||
if glyph.width > self.width || glyph.height > self.height {
|
if glyph.width > self.width || glyph.height > self.height {
|
||||||
return Err(AtlasInsertError::GlyphTooLarge);
|
return Err(AtlasInsertError::GlyphTooLarge);
|
||||||
|
|
|
@ -14,8 +14,8 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::term::cell::Flags;
|
use crate::term::cell::Flags;
|
||||||
use crate::term::{RenderableCell, SizeInfo};
|
|
||||||
use crate::term::color::Rgb;
|
use crate::term::color::Rgb;
|
||||||
|
use crate::term::{RenderableCell, SizeInfo};
|
||||||
use font::Metrics;
|
use font::Metrics;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
@ -47,13 +47,7 @@ impl<'a> Rects<'a> {
|
||||||
last_starts.insert(Flags::UNDERLINE, None);
|
last_starts.insert(Flags::UNDERLINE, None);
|
||||||
last_starts.insert(Flags::STRIKEOUT, None);
|
last_starts.insert(Flags::STRIKEOUT, None);
|
||||||
|
|
||||||
Self {
|
Self { inner: Vec::new(), last_cell: None, last_starts, metrics, size }
|
||||||
inner: Vec::new(),
|
|
||||||
last_cell: None,
|
|
||||||
last_starts,
|
|
||||||
metrics,
|
|
||||||
size,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the stored rects to rectangles for the renderer.
|
/// Convert the stored rects to rectangles for the renderer.
|
||||||
|
@ -61,15 +55,13 @@ impl<'a> Rects<'a> {
|
||||||
// If there's still a line pending, draw it until the last cell
|
// If there's still a line pending, draw it until the last cell
|
||||||
for (flag, start_cell) in self.last_starts.iter_mut() {
|
for (flag, start_cell) in self.last_starts.iter_mut() {
|
||||||
if let Some(start) = start_cell {
|
if let Some(start) = start_cell {
|
||||||
self.inner.push(
|
self.inner.push(create_rect(
|
||||||
create_rect(
|
&start,
|
||||||
&start,
|
&self.last_cell.unwrap(),
|
||||||
&self.last_cell.unwrap(),
|
*flag,
|
||||||
*flag,
|
&self.metrics,
|
||||||
&self.metrics,
|
&self.size,
|
||||||
&self.size,
|
));
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,12 +100,14 @@ impl<'a> Rects<'a> {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
// Check for new start of line
|
// Check for new start of line
|
||||||
None => if cell.flags.contains(flag) {
|
None => {
|
||||||
Some(*cell)
|
if cell.flags.contains(flag) {
|
||||||
} else {
|
Some(*cell)
|
||||||
None
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -158,12 +152,8 @@ fn create_rect(
|
||||||
y = max_y;
|
y = max_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
let rect = Rect::new(
|
let rect =
|
||||||
start_x + size.padding_x,
|
Rect::new(start_x + size.padding_x, y.round() + size.padding_y, width, height.round());
|
||||||
y.round() + size.padding_y,
|
|
||||||
width,
|
|
||||||
height.round(),
|
|
||||||
);
|
|
||||||
|
|
||||||
(rect, start.fg)
|
(rect, start.fg)
|
||||||
}
|
}
|
||||||
|
|
129
src/selection.rs
129
src/selection.rs
|
@ -18,10 +18,10 @@
|
||||||
//! finalized when the button is released. The selection should be cleared
|
//! finalized when the button is released. The selection should be cleared
|
||||||
//! when text is added/removed/scrolled on the screen. The selection should
|
//! when text is added/removed/scrolled on the screen. The selection should
|
||||||
//! also be cleared if the user clicks off of the selection.
|
//! also be cleared if the user clicks off of the selection.
|
||||||
use std::cmp::{min, max};
|
use std::cmp::{max, min};
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use crate::index::{Point, Column, Side};
|
use crate::index::{Column, Point, Side};
|
||||||
use crate::term::Search;
|
use crate::term::Search;
|
||||||
|
|
||||||
/// Describes a region of a 2-dimensional area
|
/// Describes a region of a 2-dimensional area
|
||||||
|
@ -55,8 +55,8 @@ pub enum Selection {
|
||||||
|
|
||||||
/// The line under the initial point. This is always selected regardless
|
/// The line under the initial point. This is always selected regardless
|
||||||
/// of which way the cursor is moved.
|
/// of which way the cursor is moved.
|
||||||
initial_line: isize
|
initial_line: isize,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Point and side within that point.
|
/// A Point and side within that point.
|
||||||
|
@ -83,8 +83,8 @@ impl Selection {
|
||||||
Selection::Simple {
|
Selection::Simple {
|
||||||
region: Range {
|
region: Range {
|
||||||
start: Anchor::new(location.into(), side),
|
start: Anchor::new(location.into(), side),
|
||||||
end: Anchor::new(location.into(), side)
|
end: Anchor::new(location.into(), side),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,25 +102,17 @@ impl Selection {
|
||||||
region.start.line += offset;
|
region.start.line += offset;
|
||||||
region.end.line += offset;
|
region.end.line += offset;
|
||||||
*initial_line += offset;
|
*initial_line += offset;
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn semantic(point: Point<usize>) -> Selection {
|
pub fn semantic(point: Point<usize>) -> Selection {
|
||||||
Selection::Semantic {
|
Selection::Semantic { region: Range { start: point.into(), end: point.into() } }
|
||||||
region: Range {
|
|
||||||
start: point.into(),
|
|
||||||
end: point.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lines(point: Point<usize>) -> Selection {
|
pub fn lines(point: Point<usize>) -> Selection {
|
||||||
Selection::Lines {
|
Selection::Lines {
|
||||||
region: Range {
|
region: Range { start: point.into(), end: point.into() },
|
||||||
start: point.into(),
|
|
||||||
end: point.into(),
|
|
||||||
},
|
|
||||||
initial_line: point.line as isize,
|
initial_line: point.line as isize,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,9 +123,7 @@ impl Selection {
|
||||||
Selection::Simple { ref mut region } => {
|
Selection::Simple { ref mut region } => {
|
||||||
region.end = Anchor::new(location.into(), side);
|
region.end = Anchor::new(location.into(), side);
|
||||||
},
|
},
|
||||||
Selection::Semantic { ref mut region } |
|
Selection::Semantic { ref mut region } | Selection::Lines { ref mut region, .. } => {
|
||||||
Selection::Lines { ref mut region, .. } =>
|
|
||||||
{
|
|
||||||
region.end = location.into();
|
region.end = location.into();
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -144,36 +134,28 @@ impl Selection {
|
||||||
G: Search + Dimensions,
|
G: Search + Dimensions,
|
||||||
{
|
{
|
||||||
match *self {
|
match *self {
|
||||||
Selection::Simple { ref region } => {
|
Selection::Simple { ref region } => Selection::span_simple(grid, region, alt_screen),
|
||||||
Selection::span_simple(grid, region, alt_screen)
|
|
||||||
},
|
|
||||||
Selection::Semantic { ref region } => {
|
Selection::Semantic { ref region } => {
|
||||||
Selection::span_semantic(grid, region, alt_screen)
|
Selection::span_semantic(grid, region, alt_screen)
|
||||||
},
|
},
|
||||||
Selection::Lines { ref region, initial_line } => {
|
Selection::Lines { ref region, initial_line } => {
|
||||||
Selection::span_lines(grid, region, initial_line, alt_screen)
|
Selection::span_lines(grid, region, initial_line, alt_screen)
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool
|
pub fn is_empty(&self) -> bool {
|
||||||
{
|
|
||||||
match *self {
|
match *self {
|
||||||
Selection::Simple { ref region } => {
|
Selection::Simple { ref region } => {
|
||||||
region.start == region.end && region.start.side == region.end.side
|
region.start == region.end && region.start.side == region.end.side
|
||||||
},
|
},
|
||||||
Selection::Semantic { .. } | Selection::Lines { .. } => {
|
Selection::Semantic { .. } | Selection::Lines { .. } => false,
|
||||||
false
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn span_semantic<G>(
|
fn span_semantic<G>(grid: &G, region: &Range<Point<isize>>, alt_screen: bool) -> Option<Span>
|
||||||
grid: &G,
|
where
|
||||||
region: &Range<Point<isize>>,
|
G: Search + Dimensions,
|
||||||
alt_screen: bool,
|
|
||||||
) -> Option<Span>
|
|
||||||
where G: Search + Dimensions
|
|
||||||
{
|
{
|
||||||
let cols = grid.dimensions().col;
|
let cols = grid.dimensions().col;
|
||||||
let lines = grid.dimensions().line.0 as isize;
|
let lines = grid.dimensions().line.0 as isize;
|
||||||
|
@ -199,12 +181,7 @@ impl Selection {
|
||||||
::std::mem::swap(&mut start, &mut end);
|
::std::mem::swap(&mut start, &mut end);
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Span {
|
Some(Span { cols, front: start, tail: end, ty: SpanType::Inclusive })
|
||||||
cols,
|
|
||||||
front: start,
|
|
||||||
tail: end,
|
|
||||||
ty: SpanType::Inclusive,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn span_lines<G>(
|
fn span_lines<G>(
|
||||||
|
@ -214,21 +191,15 @@ impl Selection {
|
||||||
alt_screen: bool,
|
alt_screen: bool,
|
||||||
) -> Option<Span>
|
) -> Option<Span>
|
||||||
where
|
where
|
||||||
G: Dimensions
|
G: Dimensions,
|
||||||
{
|
{
|
||||||
let cols = grid.dimensions().col;
|
let cols = grid.dimensions().col;
|
||||||
let lines = grid.dimensions().line.0 as isize;
|
let lines = grid.dimensions().line.0 as isize;
|
||||||
|
|
||||||
// First, create start and end points based on initial line and the grid
|
// First, create start and end points based on initial line and the grid
|
||||||
// dimensions.
|
// dimensions.
|
||||||
let mut start = Point {
|
let mut start = Point { col: cols - 1, line: initial_line };
|
||||||
col: cols - 1,
|
let mut end = Point { col: Column(0), line: initial_line };
|
||||||
line: initial_line
|
|
||||||
};
|
|
||||||
let mut end = Point {
|
|
||||||
col: Column(0),
|
|
||||||
line: initial_line
|
|
||||||
};
|
|
||||||
|
|
||||||
// Now, expand lines based on where cursor started and ended.
|
// Now, expand lines based on where cursor started and ended.
|
||||||
if region.start.line < region.end.line {
|
if region.start.line < region.end.line {
|
||||||
|
@ -245,17 +216,12 @@ impl Selection {
|
||||||
Selection::alt_screen_clamp(&mut start, &mut end, lines, cols)?;
|
Selection::alt_screen_clamp(&mut start, &mut end, lines, cols)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Span {
|
Some(Span { cols, front: start.into(), tail: end.into(), ty: SpanType::Inclusive })
|
||||||
cols,
|
|
||||||
front: start.into(),
|
|
||||||
tail: end.into(),
|
|
||||||
ty: SpanType::Inclusive
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn span_simple<G>(grid: &G, region: &Range<Anchor>, alt_screen: bool) -> Option<Span>
|
fn span_simple<G>(grid: &G, region: &Range<Anchor>, alt_screen: bool) -> Option<Span>
|
||||||
where
|
where
|
||||||
G: Dimensions
|
G: Dimensions,
|
||||||
{
|
{
|
||||||
let start = region.start.point;
|
let start = region.start.point;
|
||||||
let start_side = region.start.side;
|
let start_side = region.start.side;
|
||||||
|
@ -276,7 +242,9 @@ impl Selection {
|
||||||
|
|
||||||
// No selection for single cell with identical sides or two cell with right+left sides
|
// No selection for single cell with identical sides or two cell with right+left sides
|
||||||
if (front == tail && front_side == tail_side)
|
if (front == tail && front_side == tail_side)
|
||||||
|| (tail_side == Side::Right && front_side == Side::Left && front.line == tail.line
|
|| (tail_side == Side::Right
|
||||||
|
&& front_side == Side::Left
|
||||||
|
&& front.line == tail.line
|
||||||
&& front.col == tail.col + 1)
|
&& front.col == tail.col + 1)
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
|
@ -303,12 +271,7 @@ impl Selection {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the selection with all cells inclusive
|
// Return the selection with all cells inclusive
|
||||||
Some(Span {
|
Some(Span { cols, front: front.into(), tail: tail.into(), ty: SpanType::Inclusive })
|
||||||
cols,
|
|
||||||
front: front.into(),
|
|
||||||
tail: tail.into(),
|
|
||||||
ty: SpanType::Inclusive,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clamp selection in the alternate screen to the visible region
|
// Clamp selection in the alternate screen to the visible region
|
||||||
|
@ -387,7 +350,7 @@ impl Span {
|
||||||
(Span::wrap_start(self.front, self.cols), Span::wrap_end(self.tail, self.cols))
|
(Span::wrap_start(self.front, self.cols), Span::wrap_end(self.tail, self.cols))
|
||||||
},
|
},
|
||||||
SpanType::ExcludeFront => (Span::wrap_start(self.front, self.cols), self.tail),
|
SpanType::ExcludeFront => (Span::wrap_start(self.front, self.cols), self.tail),
|
||||||
SpanType::ExcludeTail => (self.front, Span::wrap_end(self.tail, self.cols))
|
SpanType::ExcludeTail => (self.front, Span::wrap_end(self.tail, self.cols)),
|
||||||
};
|
};
|
||||||
|
|
||||||
Locations { start, end }
|
Locations { start, end }
|
||||||
|
@ -395,10 +358,7 @@ impl Span {
|
||||||
|
|
||||||
fn wrap_start(mut start: Point<usize>, cols: Column) -> Point<usize> {
|
fn wrap_start(mut start: Point<usize>, cols: Column) -> Point<usize> {
|
||||||
if start.col == cols - 1 {
|
if start.col == cols - 1 {
|
||||||
Point {
|
Point { line: start.line + 1, col: Column(0) }
|
||||||
line: start.line + 1,
|
|
||||||
col: Column(0),
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
start.col += 1;
|
start.col += 1;
|
||||||
start
|
start
|
||||||
|
@ -407,15 +367,9 @@ impl Span {
|
||||||
|
|
||||||
fn wrap_end(end: Point<usize>, cols: Column) -> Point<usize> {
|
fn wrap_end(end: Point<usize>, cols: Column) -> Point<usize> {
|
||||||
if end.col == Column(0) && end.line != 0 {
|
if end.col == Column(0) && end.line != 0 {
|
||||||
Point {
|
Point { line: end.line - 1, col: cols }
|
||||||
line: end.line - 1,
|
|
||||||
col: cols
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Point {
|
Point { line: end.line, col: end.col - 1 }
|
||||||
line: end.line,
|
|
||||||
col: end.col - 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -431,8 +385,8 @@ impl Span {
|
||||||
/// look like [ B] and [E ].
|
/// look like [ B] and [E ].
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::index::{Line, Column, Side, Point};
|
|
||||||
use super::{Selection, Span, SpanType};
|
use super::{Selection, Span, SpanType};
|
||||||
|
use crate::index::{Column, Line, Point, Side};
|
||||||
use crate::url::Url;
|
use crate::url::Url;
|
||||||
|
|
||||||
struct Dimensions(Point);
|
struct Dimensions(Point);
|
||||||
|
@ -444,17 +398,22 @@ mod test {
|
||||||
|
|
||||||
impl Dimensions {
|
impl Dimensions {
|
||||||
pub fn new(line: usize, col: usize) -> Self {
|
pub fn new(line: usize, col: usize) -> Self {
|
||||||
Dimensions(Point {
|
Dimensions(Point { line: Line(line), col: Column(col) })
|
||||||
line: Line(line),
|
|
||||||
col: Column(col)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::Search for Dimensions {
|
impl super::Search for Dimensions {
|
||||||
fn semantic_search_left(&self, point: Point<usize>) -> Point<usize> { point }
|
fn semantic_search_left(&self, point: Point<usize>) -> Point<usize> {
|
||||||
fn semantic_search_right(&self, point: Point<usize>) -> Point<usize> { point }
|
point
|
||||||
fn url_search(&self, _: Point<usize>) -> Option<Url> { None }
|
}
|
||||||
|
|
||||||
|
fn semantic_search_right(&self, point: Point<usize>) -> Point<usize> {
|
||||||
|
point
|
||||||
|
}
|
||||||
|
|
||||||
|
fn url_search(&self, _: Point<usize>) -> Option<Url> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test case of single cell selection
|
/// Test case of single cell selection
|
||||||
|
|
|
@ -31,10 +31,7 @@ pub struct FairMutex<T> {
|
||||||
impl<T> FairMutex<T> {
|
impl<T> FairMutex<T> {
|
||||||
/// Create a new fair mutex
|
/// Create a new fair mutex
|
||||||
pub fn new(data: T) -> FairMutex<T> {
|
pub fn new(data: T) -> FairMutex<T> {
|
||||||
FairMutex {
|
FairMutex { data: Mutex::new(data), next: Mutex::new(()) }
|
||||||
data: Mutex::new(data),
|
|
||||||
next: Mutex::new(()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lock the mutex
|
/// Lock the mutex
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
|
||||||
use crate::ansi::{NamedColor, Color};
|
use crate::ansi::{Color, NamedColor};
|
||||||
use crate::grid::{self, GridCell};
|
use crate::grid::{self, GridCell};
|
||||||
use crate::index::Column;
|
use crate::index::Column;
|
||||||
|
|
||||||
|
@ -47,19 +47,14 @@ pub struct Cell {
|
||||||
pub fg: Color,
|
pub fg: Color,
|
||||||
pub bg: Color,
|
pub bg: Color,
|
||||||
pub flags: Flags,
|
pub flags: Flags,
|
||||||
#[serde(default="default_extra")]
|
#[serde(default = "default_extra")]
|
||||||
pub extra: [char; MAX_ZEROWIDTH_CHARS],
|
pub extra: [char; MAX_ZEROWIDTH_CHARS],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Cell {
|
impl Default for Cell {
|
||||||
fn default() -> Cell {
|
fn default() -> Cell {
|
||||||
Cell::new(
|
Cell::new(' ', Color::Named(NamedColor::Foreground), Color::Named(NamedColor::Background))
|
||||||
' ',
|
|
||||||
Color::Named(NamedColor::Foreground),
|
|
||||||
Color::Named(NamedColor::Background)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GridCell for Cell {
|
impl GridCell for Cell {
|
||||||
|
@ -130,13 +125,7 @@ impl Cell {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(c: char, fg: Color, bg: Color) -> Cell {
|
pub fn new(c: char, fg: Color, bg: Color) -> Cell {
|
||||||
Cell {
|
Cell { extra: [' '; MAX_ZEROWIDTH_CHARS], c, bg, fg, flags: Flags::empty() }
|
||||||
extra: [' '; MAX_ZEROWIDTH_CHARS],
|
|
||||||
c,
|
|
||||||
bg,
|
|
||||||
fg,
|
|
||||||
flags: Flags::empty(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use std::ops::{Index, IndexMut, Mul};
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::ops::{Index, IndexMut, Mul};
|
||||||
|
|
||||||
use crate::ansi;
|
use crate::ansi;
|
||||||
use crate::config::Colors;
|
use crate::config::Colors;
|
||||||
|
@ -24,7 +24,7 @@ impl Mul<f32> for Rgb {
|
||||||
let result = Rgb {
|
let result = Rgb {
|
||||||
r: (f32::from(self.r) * rhs).max(0.0).min(255.0) as u8,
|
r: (f32::from(self.r) * rhs).max(0.0).min(255.0) as u8,
|
||||||
g: (f32::from(self.g) * rhs).max(0.0).min(255.0) as u8,
|
g: (f32::from(self.g) * rhs).max(0.0).min(255.0) as u8,
|
||||||
b: (f32::from(self.b) * rhs).max(0.0).min(255.0) as u8
|
b: (f32::from(self.b) * rhs).max(0.0).min(255.0) as u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
trace!("Scaling RGB by {} from {:?} to {:?}", rhs, self, result);
|
trace!("Scaling RGB by {} from {:?} to {:?}", rhs, self, result);
|
||||||
|
@ -60,28 +60,26 @@ impl<'a> From<&'a Colors> for List {
|
||||||
impl List {
|
impl List {
|
||||||
pub fn fill_named(&mut self, colors: &Colors) {
|
pub fn fill_named(&mut self, colors: &Colors) {
|
||||||
// Normals
|
// Normals
|
||||||
self[ansi::NamedColor::Black] = colors.normal.black;
|
self[ansi::NamedColor::Black] = colors.normal.black;
|
||||||
self[ansi::NamedColor::Red] = colors.normal.red;
|
self[ansi::NamedColor::Red] = colors.normal.red;
|
||||||
self[ansi::NamedColor::Green] = colors.normal.green;
|
self[ansi::NamedColor::Green] = colors.normal.green;
|
||||||
self[ansi::NamedColor::Yellow] = colors.normal.yellow;
|
self[ansi::NamedColor::Yellow] = colors.normal.yellow;
|
||||||
self[ansi::NamedColor::Blue] = colors.normal.blue;
|
self[ansi::NamedColor::Blue] = colors.normal.blue;
|
||||||
self[ansi::NamedColor::Magenta] = colors.normal.magenta;
|
self[ansi::NamedColor::Magenta] = colors.normal.magenta;
|
||||||
self[ansi::NamedColor::Cyan] = colors.normal.cyan;
|
self[ansi::NamedColor::Cyan] = colors.normal.cyan;
|
||||||
self[ansi::NamedColor::White] = colors.normal.white;
|
self[ansi::NamedColor::White] = colors.normal.white;
|
||||||
|
|
||||||
// Brights
|
// Brights
|
||||||
self[ansi::NamedColor::BrightBlack] = colors.bright.black;
|
self[ansi::NamedColor::BrightBlack] = colors.bright.black;
|
||||||
self[ansi::NamedColor::BrightRed] = colors.bright.red;
|
self[ansi::NamedColor::BrightRed] = colors.bright.red;
|
||||||
self[ansi::NamedColor::BrightGreen] = colors.bright.green;
|
self[ansi::NamedColor::BrightGreen] = colors.bright.green;
|
||||||
self[ansi::NamedColor::BrightYellow] = colors.bright.yellow;
|
self[ansi::NamedColor::BrightYellow] = colors.bright.yellow;
|
||||||
self[ansi::NamedColor::BrightBlue] = colors.bright.blue;
|
self[ansi::NamedColor::BrightBlue] = colors.bright.blue;
|
||||||
self[ansi::NamedColor::BrightMagenta] = colors.bright.magenta;
|
self[ansi::NamedColor::BrightMagenta] = colors.bright.magenta;
|
||||||
self[ansi::NamedColor::BrightCyan] = colors.bright.cyan;
|
self[ansi::NamedColor::BrightCyan] = colors.bright.cyan;
|
||||||
self[ansi::NamedColor::BrightWhite] = colors.bright.white;
|
self[ansi::NamedColor::BrightWhite] = colors.bright.white;
|
||||||
self[ansi::NamedColor::BrightForeground] = colors
|
self[ansi::NamedColor::BrightForeground] =
|
||||||
.primary
|
colors.primary.bright_foreground.unwrap_or(colors.primary.foreground);
|
||||||
.bright_foreground
|
|
||||||
.unwrap_or(colors.primary.foreground);
|
|
||||||
|
|
||||||
// Foreground and background
|
// Foreground and background
|
||||||
self[ansi::NamedColor::Foreground] = colors.primary.foreground;
|
self[ansi::NamedColor::Foreground] = colors.primary.foreground;
|
||||||
|
@ -89,36 +87,34 @@ impl List {
|
||||||
|
|
||||||
// Foreground and background for custom cursor colors
|
// Foreground and background for custom cursor colors
|
||||||
self[ansi::NamedColor::CursorText] = colors.cursor.text.unwrap_or_else(Rgb::default);
|
self[ansi::NamedColor::CursorText] = colors.cursor.text.unwrap_or_else(Rgb::default);
|
||||||
self[ansi::NamedColor::Cursor] = colors.cursor.cursor.unwrap_or_else(Rgb::default);
|
self[ansi::NamedColor::Cursor] = colors.cursor.cursor.unwrap_or_else(Rgb::default);
|
||||||
|
|
||||||
// Dims
|
// Dims
|
||||||
self[ansi::NamedColor::DimForeground] = colors
|
self[ansi::NamedColor::DimForeground] =
|
||||||
.primary
|
colors.primary.dim_foreground.unwrap_or(colors.primary.foreground * 0.66);
|
||||||
.dim_foreground
|
|
||||||
.unwrap_or(colors.primary.foreground * 0.66);
|
|
||||||
match colors.dim {
|
match colors.dim {
|
||||||
Some(ref dim) => {
|
Some(ref dim) => {
|
||||||
trace!("Using config-provided dim colors");
|
trace!("Using config-provided dim colors");
|
||||||
self[ansi::NamedColor::DimBlack] = dim.black;
|
self[ansi::NamedColor::DimBlack] = dim.black;
|
||||||
self[ansi::NamedColor::DimRed] = dim.red;
|
self[ansi::NamedColor::DimRed] = dim.red;
|
||||||
self[ansi::NamedColor::DimGreen] = dim.green;
|
self[ansi::NamedColor::DimGreen] = dim.green;
|
||||||
self[ansi::NamedColor::DimYellow] = dim.yellow;
|
self[ansi::NamedColor::DimYellow] = dim.yellow;
|
||||||
self[ansi::NamedColor::DimBlue] = dim.blue;
|
self[ansi::NamedColor::DimBlue] = dim.blue;
|
||||||
self[ansi::NamedColor::DimMagenta] = dim.magenta;
|
self[ansi::NamedColor::DimMagenta] = dim.magenta;
|
||||||
self[ansi::NamedColor::DimCyan] = dim.cyan;
|
self[ansi::NamedColor::DimCyan] = dim.cyan;
|
||||||
self[ansi::NamedColor::DimWhite] = dim.white;
|
self[ansi::NamedColor::DimWhite] = dim.white;
|
||||||
}
|
},
|
||||||
None => {
|
None => {
|
||||||
trace!("Deriving dim colors from normal colors");
|
trace!("Deriving dim colors from normal colors");
|
||||||
self[ansi::NamedColor::DimBlack] = colors.normal.black * 0.66;
|
self[ansi::NamedColor::DimBlack] = colors.normal.black * 0.66;
|
||||||
self[ansi::NamedColor::DimRed] = colors.normal.red * 0.66;
|
self[ansi::NamedColor::DimRed] = colors.normal.red * 0.66;
|
||||||
self[ansi::NamedColor::DimGreen] = colors.normal.green * 0.66;
|
self[ansi::NamedColor::DimGreen] = colors.normal.green * 0.66;
|
||||||
self[ansi::NamedColor::DimYellow] = colors.normal.yellow * 0.66;
|
self[ansi::NamedColor::DimYellow] = colors.normal.yellow * 0.66;
|
||||||
self[ansi::NamedColor::DimBlue] = colors.normal.blue * 0.66;
|
self[ansi::NamedColor::DimBlue] = colors.normal.blue * 0.66;
|
||||||
self[ansi::NamedColor::DimMagenta] = colors.normal.magenta * 0.66;
|
self[ansi::NamedColor::DimMagenta] = colors.normal.magenta * 0.66;
|
||||||
self[ansi::NamedColor::DimCyan] = colors.normal.cyan * 0.66;
|
self[ansi::NamedColor::DimCyan] = colors.normal.cyan * 0.66;
|
||||||
self[ansi::NamedColor::DimWhite] = colors.normal.white * 0.66;
|
self[ansi::NamedColor::DimWhite] = colors.normal.white * 0.66;
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,14 +125,13 @@ impl List {
|
||||||
for g in 0..6 {
|
for g in 0..6 {
|
||||||
for b in 0..6 {
|
for b in 0..6 {
|
||||||
// Override colors 16..232 with the config (if present)
|
// Override colors 16..232 with the config (if present)
|
||||||
if let Some(indexed_color) = colors
|
if let Some(indexed_color) =
|
||||||
.indexed_colors
|
colors.indexed_colors.iter().find(|ic| ic.index == index as u8)
|
||||||
.iter()
|
|
||||||
.find(|ic| ic.index == index as u8)
|
|
||||||
{
|
{
|
||||||
self[index] = indexed_color.color;
|
self[index] = indexed_color.color;
|
||||||
} else {
|
} else {
|
||||||
self[index] = Rgb { r: if r == 0 { 0 } else { r * 40 + 55 },
|
self[index] = Rgb {
|
||||||
|
r: if r == 0 { 0 } else { r * 40 + 55 },
|
||||||
b: if b == 0 { 0 } else { b * 40 + 55 },
|
b: if b == 0 { 0 } else { b * 40 + 55 },
|
||||||
g: if g == 0 { 0 } else { g * 40 + 55 },
|
g: if g == 0 { 0 } else { g * 40 + 55 },
|
||||||
};
|
};
|
||||||
|
@ -157,10 +152,8 @@ impl List {
|
||||||
let color_index = 16 + 216 + i;
|
let color_index = 16 + 216 + i;
|
||||||
|
|
||||||
// Override colors 232..256 with the config (if present)
|
// Override colors 232..256 with the config (if present)
|
||||||
if let Some(indexed_color) = colors
|
if let Some(indexed_color) =
|
||||||
.indexed_colors
|
colors.indexed_colors.iter().find(|ic| ic.index == color_index)
|
||||||
.iter()
|
|
||||||
.find(|ic| ic.index == color_index)
|
|
||||||
{
|
{
|
||||||
self[index] = indexed_color.color;
|
self[index] = indexed_color.color;
|
||||||
index += 1;
|
index += 1;
|
||||||
|
@ -168,11 +161,7 @@ impl List {
|
||||||
}
|
}
|
||||||
|
|
||||||
let value = i * 10 + 8;
|
let value = i * 10 + 8;
|
||||||
self[index] = Rgb {
|
self[index] = Rgb { r: value, g: value, b: value };
|
||||||
r: value,
|
|
||||||
g: value,
|
|
||||||
b: value
|
|
||||||
};
|
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
378
src/term/mod.rs
378
src/term/mod.rs
|
@ -13,30 +13,32 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
//! Exports the `Term` type which is a high-level API for the Grid
|
//! Exports the `Term` type which is a high-level API for the Grid
|
||||||
use std::ops::{Range, Index, IndexMut, RangeInclusive};
|
use std::cmp::{max, min};
|
||||||
use std::{ptr, io, mem};
|
use std::ops::{Index, IndexMut, Range, RangeInclusive};
|
||||||
use std::cmp::{min, max};
|
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
use std::{io, mem, ptr};
|
||||||
|
|
||||||
use arraydeque::ArrayDeque;
|
use arraydeque::ArrayDeque;
|
||||||
use unicode_width::UnicodeWidthChar;
|
|
||||||
use glutin::MouseCursor;
|
use glutin::MouseCursor;
|
||||||
|
use unicode_width::UnicodeWidthChar;
|
||||||
|
|
||||||
use font::{self, Size};
|
use crate::ansi::{
|
||||||
use crate::ansi::{self, Color, NamedColor, Attr, Handler, CharsetIndex, StandardCharset, CursorStyle};
|
self, Attr, CharsetIndex, Color, CursorStyle, Handler, NamedColor, StandardCharset,
|
||||||
|
};
|
||||||
|
use crate::config::{Config, VisualBellAnimation};
|
||||||
use crate::grid::{
|
use crate::grid::{
|
||||||
BidirectionalIterator, DisplayIter, Grid, GridCell, IndexRegion, Indexed, Scroll,
|
BidirectionalIterator, DisplayIter, Grid, GridCell, IndexRegion, Indexed, Scroll,
|
||||||
ViewportPosition
|
ViewportPosition,
|
||||||
};
|
};
|
||||||
use crate::index::{self, Point, Column, Line, IndexRange, Contains, Linear};
|
use crate::index::{self, Column, Contains, IndexRange, Line, Linear, Point};
|
||||||
use crate::selection::{self, Selection, Locations};
|
|
||||||
use crate::config::{Config, VisualBellAnimation};
|
|
||||||
use copypasta::{Clipboard, Load, Store};
|
|
||||||
use crate::input::FONT_SIZE_STEP;
|
use crate::input::FONT_SIZE_STEP;
|
||||||
use crate::url::{Url, UrlParser};
|
|
||||||
use crate::message_bar::MessageBuffer;
|
use crate::message_bar::MessageBuffer;
|
||||||
|
use crate::selection::{self, Locations, Selection};
|
||||||
|
use crate::term::cell::{Cell, Flags, LineLength};
|
||||||
use crate::term::color::Rgb;
|
use crate::term::color::Rgb;
|
||||||
use crate::term::cell::{LineLength, Cell, Flags};
|
use crate::url::{Url, UrlParser};
|
||||||
|
use copypasta::{Clipboard, Load, Store};
|
||||||
|
use font::{self, Size};
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use crate::tty;
|
use crate::tty;
|
||||||
|
@ -139,10 +141,7 @@ impl Search for Term {
|
||||||
|
|
||||||
impl selection::Dimensions for Term {
|
impl selection::Dimensions for Term {
|
||||||
fn dimensions(&self) -> Point {
|
fn dimensions(&self) -> Point {
|
||||||
Point {
|
Point { col: self.grid.num_cols(), line: self.grid.num_lines() }
|
||||||
col: self.grid.num_cols(),
|
|
||||||
line: self.grid.num_lines()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +199,7 @@ impl<'a> RenderableCellsIter<'a> {
|
||||||
(ViewportPosition::Below, ViewportPosition::Visible(end_line)) => {
|
(ViewportPosition::Below, ViewportPosition::Visible(end_line)) => {
|
||||||
Some((grid.num_lines(), Column(0), end_line, loc.end.col))
|
Some((grid.num_lines(), Column(0), end_line, loc.end.col))
|
||||||
},
|
},
|
||||||
(ViewportPosition::Below, ViewportPosition::Above) => {
|
(ViewportPosition::Below, ViewportPosition::Above) => {
|
||||||
Some((grid.num_lines(), Column(0), Line(0), Column(0)))
|
Some((grid.num_lines(), Column(0), Line(0), Column(0)))
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -209,14 +208,8 @@ impl<'a> RenderableCellsIter<'a> {
|
||||||
if let Some((start_line, start_col, end_line, end_col)) = locations {
|
if let Some((start_line, start_col, end_line, end_col)) = locations {
|
||||||
// start and end *lines* are swapped as we switch from buffer to
|
// start and end *lines* are swapped as we switch from buffer to
|
||||||
// Line coordinates.
|
// Line coordinates.
|
||||||
let mut end = Point {
|
let mut end = Point { line: start_line, col: start_col };
|
||||||
line: start_line,
|
let mut start = Point { line: end_line, col: end_col };
|
||||||
col: start_col,
|
|
||||||
};
|
|
||||||
let mut start = Point {
|
|
||||||
line: end_line,
|
|
||||||
col: end_col,
|
|
||||||
};
|
|
||||||
|
|
||||||
if start > end {
|
if start > end {
|
||||||
::std::mem::swap(&mut start, &mut end);
|
::std::mem::swap(&mut start, &mut end);
|
||||||
|
@ -242,32 +235,31 @@ impl<'a> RenderableCellsIter<'a> {
|
||||||
config,
|
config,
|
||||||
colors: &term.colors,
|
colors: &term.colors,
|
||||||
cursor_cells: ArrayDeque::new(),
|
cursor_cells: ArrayDeque::new(),
|
||||||
}.initialize(cursor_style)
|
}
|
||||||
|
.initialize(cursor_style)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_cursor_cells(&mut self, original: Cell, cursor: Cell, wide: Cell) {
|
fn push_cursor_cells(&mut self, original: Cell, cursor: Cell, wide: Cell) {
|
||||||
// Prints the char under the cell if cursor is situated on a non-empty cell
|
// Prints the char under the cell if cursor is situated on a non-empty cell
|
||||||
self.cursor_cells.push_back(Indexed {
|
self.cursor_cells
|
||||||
line: self.cursor.line,
|
.push_back(Indexed { line: self.cursor.line, column: self.cursor.col, inner: original })
|
||||||
column: self.cursor.col,
|
.expect("won't exceed capacity");
|
||||||
inner: original,
|
|
||||||
}).expect("won't exceed capacity");
|
|
||||||
|
|
||||||
// Prints the cursor
|
// Prints the cursor
|
||||||
self.cursor_cells.push_back(Indexed {
|
self.cursor_cells
|
||||||
line: self.cursor.line,
|
.push_back(Indexed { line: self.cursor.line, column: self.cursor.col, inner: cursor })
|
||||||
column: self.cursor.col,
|
.expect("won't exceed capacity");
|
||||||
inner: cursor,
|
|
||||||
}).expect("won't exceed capacity");
|
|
||||||
|
|
||||||
// If cursor is over a wide (2 cell size) character,
|
// If cursor is over a wide (2 cell size) character,
|
||||||
// print the second cursor cell
|
// print the second cursor cell
|
||||||
if self.is_wide_cursor(&cursor) {
|
if self.is_wide_cursor(&cursor) {
|
||||||
self.cursor_cells.push_back(Indexed {
|
self.cursor_cells
|
||||||
line: self.cursor.line,
|
.push_back(Indexed {
|
||||||
column: self.cursor.col + 1,
|
line: self.cursor.line,
|
||||||
inner: wide,
|
column: self.cursor.col + 1,
|
||||||
}).expect("won't exceed capacity");
|
inner: wide,
|
||||||
|
})
|
||||||
|
.expect("won't exceed capacity");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,11 +313,13 @@ impl<'a> RenderableCellsIter<'a> {
|
||||||
|
|
||||||
/// Populates list of cursor cells with the original cell
|
/// Populates list of cursor cells with the original cell
|
||||||
fn populate_no_cursor(&mut self) {
|
fn populate_no_cursor(&mut self) {
|
||||||
self.cursor_cells.push_back(Indexed {
|
self.cursor_cells
|
||||||
line: self.cursor.line,
|
.push_back(Indexed {
|
||||||
column: self.cursor.col,
|
line: self.cursor.line,
|
||||||
inner: self.grid[self.cursor],
|
column: self.cursor.col,
|
||||||
}).expect("won't exceed capacity");
|
inner: self.grid[self.cursor],
|
||||||
|
})
|
||||||
|
.expect("won't exceed capacity");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize(mut self, cursor_style: CursorStyle) -> Self {
|
fn initialize(mut self, cursor_style: CursorStyle) -> Self {
|
||||||
|
@ -342,7 +336,7 @@ impl<'a> RenderableCellsIter<'a> {
|
||||||
},
|
},
|
||||||
CursorStyle::Underline => {
|
CursorStyle::Underline => {
|
||||||
self.populate_underline_cursor();
|
self.populate_underline_cursor();
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.populate_no_cursor();
|
self.populate_no_cursor();
|
||||||
|
@ -361,37 +355,41 @@ impl<'a> RenderableCellsIter<'a> {
|
||||||
match fg {
|
match fg {
|
||||||
Color::Spec(rgb) => rgb,
|
Color::Spec(rgb) => rgb,
|
||||||
Color::Named(ansi) => {
|
Color::Named(ansi) => {
|
||||||
match (self.config.draw_bold_text_with_bright_colors(), cell.flags & Flags::DIM_BOLD) {
|
match (
|
||||||
|
self.config.draw_bold_text_with_bright_colors(),
|
||||||
|
cell.flags & Flags::DIM_BOLD,
|
||||||
|
) {
|
||||||
// If no bright foreground is set, treat it like the BOLD flag doesn't exist
|
// If no bright foreground is set, treat it like the BOLD flag doesn't exist
|
||||||
(_, self::cell::Flags::DIM_BOLD)
|
(_, self::cell::Flags::DIM_BOLD)
|
||||||
if ansi == NamedColor::Foreground
|
if ansi == NamedColor::Foreground
|
||||||
&& self.config.colors().primary.bright_foreground.is_none() =>
|
&& self.config.colors().primary.bright_foreground.is_none() =>
|
||||||
{
|
{
|
||||||
self.colors[NamedColor::DimForeground]
|
self.colors[NamedColor::DimForeground]
|
||||||
}
|
},
|
||||||
// Draw bold text in bright colors *and* contains bold flag.
|
// Draw bold text in bright colors *and* contains bold flag.
|
||||||
(true, self::cell::Flags::BOLD) => self.colors[ansi.to_bright()],
|
(true, self::cell::Flags::BOLD) => self.colors[ansi.to_bright()],
|
||||||
// Cell is marked as dim and not bold
|
// Cell is marked as dim and not bold
|
||||||
(_, self::cell::Flags::DIM) |
|
(_, self::cell::Flags::DIM) | (false, self::cell::Flags::DIM_BOLD) => {
|
||||||
(false, self::cell::Flags::DIM_BOLD) => self.colors[ansi.to_dim()],
|
self.colors[ansi.to_dim()]
|
||||||
|
},
|
||||||
// None of the above, keep original color.
|
// None of the above, keep original color.
|
||||||
_ => self.colors[ansi]
|
_ => self.colors[ansi],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Color::Indexed(idx) => {
|
Color::Indexed(idx) => {
|
||||||
let idx = match (
|
let idx = match (
|
||||||
self.config.draw_bold_text_with_bright_colors(),
|
self.config.draw_bold_text_with_bright_colors(),
|
||||||
cell.flags & Flags::DIM_BOLD,
|
cell.flags & Flags::DIM_BOLD,
|
||||||
idx
|
idx,
|
||||||
) {
|
) {
|
||||||
(true, self::cell::Flags::BOLD, 0..=7) => idx as usize + 8,
|
(true, self::cell::Flags::BOLD, 0..=7) => idx as usize + 8,
|
||||||
(false, self::cell::Flags::DIM, 8..=15) => idx as usize - 8,
|
(false, self::cell::Flags::DIM, 8..=15) => idx as usize - 8,
|
||||||
(false, self::cell::Flags::DIM, 0..=7) => idx as usize + 260,
|
(false, self::cell::Flags::DIM, 0..=7) => idx as usize + 260,
|
||||||
_ => idx as usize,
|
_ => idx as usize,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.colors[idx]
|
self.colors[idx]
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,7 +397,7 @@ impl<'a> RenderableCellsIter<'a> {
|
||||||
fn compute_bg_alpha(&self, bg: Color) -> f32 {
|
fn compute_bg_alpha(&self, bg: Color) -> f32 {
|
||||||
match bg {
|
match bg {
|
||||||
Color::Named(NamedColor::Background) => 0.0,
|
Color::Named(NamedColor::Background) => 0.0,
|
||||||
_ => 1.0
|
_ => 1.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,8 +433,8 @@ impl<'a> Iterator for RenderableCellsIter<'a> {
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
loop {
|
loop {
|
||||||
// Handle cursor
|
// Handle cursor
|
||||||
let (cell, selected, highlighted) = if self.cursor_offset == self.inner.offset() &&
|
let (cell, selected, highlighted) = if self.cursor_offset == self.inner.offset()
|
||||||
self.inner.column() == self.cursor.col
|
&& self.inner.column() == self.cursor.col
|
||||||
{
|
{
|
||||||
// Cursor cell
|
// Cursor cell
|
||||||
let mut cell = self.cursor_cells.pop_front().unwrap();
|
let mut cell = self.cursor_cells.pop_front().unwrap();
|
||||||
|
@ -454,9 +452,8 @@ impl<'a> Iterator for RenderableCellsIter<'a> {
|
||||||
|
|
||||||
let index = Linear::new(self.grid.num_cols(), cell.column, cell.line);
|
let index = Linear::new(self.grid.num_cols(), cell.column, cell.line);
|
||||||
|
|
||||||
let selected = self.selection.as_ref()
|
let selected =
|
||||||
.map(|range| range.contains_(index))
|
self.selection.as_ref().map(|range| range.contains_(index)).unwrap_or(false);
|
||||||
.unwrap_or(false);
|
|
||||||
|
|
||||||
// Skip empty cells
|
// Skip empty cells
|
||||||
if cell.is_empty() && !selected {
|
if cell.is_empty() && !selected {
|
||||||
|
@ -464,7 +461,9 @@ impl<'a> Iterator for RenderableCellsIter<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Underline URL highlights
|
// Underline URL highlights
|
||||||
let highlighted = self.url_highlight.as_ref()
|
let highlighted = self
|
||||||
|
.url_highlight
|
||||||
|
.as_ref()
|
||||||
.map(|range| range.contains_(index))
|
.map(|range| range.contains_(index))
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
@ -506,10 +505,9 @@ impl<'a> Iterator for RenderableCellsIter<'a> {
|
||||||
bg: bg_rgb,
|
bg: bg_rgb,
|
||||||
bg_alpha,
|
bg_alpha,
|
||||||
flags,
|
flags,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod mode {
|
pub mod mode {
|
||||||
|
@ -558,41 +556,40 @@ impl CharsetMapping for StandardCharset {
|
||||||
fn map(&self, c: char) -> char {
|
fn map(&self, c: char) -> char {
|
||||||
match *self {
|
match *self {
|
||||||
StandardCharset::Ascii => c,
|
StandardCharset::Ascii => c,
|
||||||
StandardCharset::SpecialCharacterAndLineDrawing =>
|
StandardCharset::SpecialCharacterAndLineDrawing => match c {
|
||||||
match c {
|
'`' => '◆',
|
||||||
'`' => '◆',
|
'a' => '▒',
|
||||||
'a' => '▒',
|
'b' => '\t',
|
||||||
'b' => '\t',
|
'c' => '\u{000c}',
|
||||||
'c' => '\u{000c}',
|
'd' => '\r',
|
||||||
'd' => '\r',
|
'e' => '\n',
|
||||||
'e' => '\n',
|
'f' => '°',
|
||||||
'f' => '°',
|
'g' => '±',
|
||||||
'g' => '±',
|
'h' => '\u{2424}',
|
||||||
'h' => '\u{2424}',
|
'i' => '\u{000b}',
|
||||||
'i' => '\u{000b}',
|
'j' => '┘',
|
||||||
'j' => '┘',
|
'k' => '┐',
|
||||||
'k' => '┐',
|
'l' => '┌',
|
||||||
'l' => '┌',
|
'm' => '└',
|
||||||
'm' => '└',
|
'n' => '┼',
|
||||||
'n' => '┼',
|
'o' => '⎺',
|
||||||
'o' => '⎺',
|
'p' => '⎻',
|
||||||
'p' => '⎻',
|
'q' => '─',
|
||||||
'q' => '─',
|
'r' => '⎼',
|
||||||
'r' => '⎼',
|
's' => '⎽',
|
||||||
's' => '⎽',
|
't' => '├',
|
||||||
't' => '├',
|
'u' => '┤',
|
||||||
'u' => '┤',
|
'v' => '┴',
|
||||||
'v' => '┴',
|
'w' => '┬',
|
||||||
'w' => '┬',
|
'x' => '│',
|
||||||
'x' => '│',
|
'y' => '≤',
|
||||||
'y' => '≤',
|
'z' => '≥',
|
||||||
'z' => '≥',
|
'{' => 'π',
|
||||||
'{' => 'π',
|
'|' => '≠',
|
||||||
'|' => '≠',
|
'}' => '£',
|
||||||
'}' => '£',
|
'~' => '·',
|
||||||
'~' => '·',
|
_ => c,
|
||||||
_ => c
|
},
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -602,6 +599,7 @@ struct Charsets([StandardCharset; 4]);
|
||||||
|
|
||||||
impl Index<CharsetIndex> for Charsets {
|
impl Index<CharsetIndex> for Charsets {
|
||||||
type Output = StandardCharset;
|
type Output = StandardCharset;
|
||||||
|
|
||||||
fn index(&self, index: CharsetIndex) -> &StandardCharset {
|
fn index(&self, index: CharsetIndex) -> &StandardCharset {
|
||||||
&self.0[index as usize]
|
&self.0[index as usize]
|
||||||
}
|
}
|
||||||
|
@ -637,10 +635,10 @@ pub struct VisualBell {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cubic_bezier(p0: f64, p1: f64, p2: f64, p3: f64, x: f64) -> f64 {
|
fn cubic_bezier(p0: f64, p1: f64, p2: f64, p3: f64, x: f64) -> f64 {
|
||||||
(1.0 - x).powi(3) * p0 +
|
(1.0 - x).powi(3) * p0
|
||||||
3.0 * (1.0 - x).powi(2) * x * p1 +
|
+ 3.0 * (1.0 - x).powi(2) * x * p1
|
||||||
3.0 * (1.0 - x) * x.powi(2) * p2 +
|
+ 3.0 * (1.0 - x) * x.powi(2) * p2
|
||||||
x.powi(3) * p3
|
+ x.powi(3) * p3
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisualBell {
|
impl VisualBell {
|
||||||
|
@ -675,7 +673,7 @@ impl VisualBell {
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
},
|
},
|
||||||
None => true
|
None => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -703,10 +701,10 @@ impl VisualBell {
|
||||||
}
|
}
|
||||||
|
|
||||||
let elapsed = instant.duration_since(earlier);
|
let elapsed = instant.duration_since(earlier);
|
||||||
let elapsed_f = elapsed.as_secs() as f64 +
|
let elapsed_f =
|
||||||
f64::from(elapsed.subsec_nanos()) / 1e9f64;
|
elapsed.as_secs() as f64 + f64::from(elapsed.subsec_nanos()) / 1e9f64;
|
||||||
let duration_f = self.duration.as_secs() as f64 +
|
let duration_f = self.duration.as_secs() as f64
|
||||||
f64::from(self.duration.subsec_nanos()) / 1e9f64;
|
+ f64::from(self.duration.subsec_nanos()) / 1e9f64;
|
||||||
|
|
||||||
// Otherwise, we compute a value `time` from 0.0 to 1.0
|
// Otherwise, we compute a value `time` from 0.0 to 1.0
|
||||||
// inclusive that represents the ratio of `elapsed` time to the
|
// inclusive that represents the ratio of `elapsed` time to the
|
||||||
|
@ -722,7 +720,9 @@ impl VisualBell {
|
||||||
},
|
},
|
||||||
VisualBellAnimation::EaseOutSine => cubic_bezier(0.39, 0.575, 0.565, 1.0, time),
|
VisualBellAnimation::EaseOutSine => cubic_bezier(0.39, 0.575, 0.565, 1.0, time),
|
||||||
VisualBellAnimation::EaseOutQuad => cubic_bezier(0.25, 0.46, 0.45, 0.94, time),
|
VisualBellAnimation::EaseOutQuad => cubic_bezier(0.25, 0.46, 0.45, 0.94, time),
|
||||||
VisualBellAnimation::EaseOutCubic => cubic_bezier(0.215, 0.61, 0.355, 1.0, time),
|
VisualBellAnimation::EaseOutCubic => {
|
||||||
|
cubic_bezier(0.215, 0.61, 0.355, 1.0, time)
|
||||||
|
},
|
||||||
VisualBellAnimation::EaseOutQuart => cubic_bezier(0.165, 0.84, 0.44, 1.0, time),
|
VisualBellAnimation::EaseOutQuart => cubic_bezier(0.165, 0.84, 0.44, 1.0, time),
|
||||||
VisualBellAnimation::EaseOutQuint => cubic_bezier(0.23, 1.0, 0.32, 1.0, time),
|
VisualBellAnimation::EaseOutQuint => cubic_bezier(0.23, 1.0, 0.32, 1.0, time),
|
||||||
VisualBellAnimation::EaseOutExpo => cubic_bezier(0.19, 1.0, 0.22, 1.0, time),
|
VisualBellAnimation::EaseOutExpo => cubic_bezier(0.19, 1.0, 0.22, 1.0, time),
|
||||||
|
@ -733,7 +733,7 @@ impl VisualBell {
|
||||||
// Since we want the `intensity` of the VisualBell to decay over
|
// Since we want the `intensity` of the VisualBell to decay over
|
||||||
// `time`, we subtract the `inverse_intensity` from 1.0.
|
// `time`, we subtract the `inverse_intensity` from 1.0.
|
||||||
1.0 - inverse_intensity
|
1.0 - inverse_intensity
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -890,12 +890,11 @@ impl SizeInfo {
|
||||||
|
|
||||||
Point {
|
Point {
|
||||||
line: min(line, Line(self.lines().saturating_sub(1))),
|
line: min(line, Line(self.lines().saturating_sub(1))),
|
||||||
col: min(col, Column(self.cols().saturating_sub(1)))
|
col: min(col, Column(self.cols().saturating_sub(1))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Term {
|
impl Term {
|
||||||
pub fn selection(&self) -> &Option<Selection> {
|
pub fn selection(&self) -> &Option<Selection> {
|
||||||
&self.grid.selection
|
&self.grid.selection
|
||||||
|
@ -997,8 +996,7 @@ impl Term {
|
||||||
self.default_cursor_style = config.cursor_style();
|
self.default_cursor_style = config.cursor_style();
|
||||||
self.dynamic_title = config.dynamic_title();
|
self.dynamic_title = config.dynamic_title();
|
||||||
self.auto_scroll = config.scrolling().auto_scroll;
|
self.auto_scroll = config.scrolling().auto_scroll;
|
||||||
self.grid
|
self.grid.update_history(config.scrolling().history as usize, &self.cursor.template);
|
||||||
.update_history(config.scrolling().history as usize, &self.cursor.template);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -1011,7 +1009,9 @@ impl Term {
|
||||||
trait PushChar {
|
trait PushChar {
|
||||||
fn push_char(&mut self, c: char);
|
fn push_char(&mut self, c: char);
|
||||||
fn maybe_newline(&mut self, grid: &Grid<Cell>, line: usize, ending: Column) {
|
fn maybe_newline(&mut self, grid: &Grid<Cell>, line: usize, ending: Column) {
|
||||||
if ending != Column(0) && !grid[line][ending - 1].flags.contains(cell::Flags::WRAPLINE) {
|
if ending != Column(0)
|
||||||
|
&& !grid[line][ending - 1].flags.contains(cell::Flags::WRAPLINE)
|
||||||
|
{
|
||||||
self.push_char('\n');
|
self.push_char('\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1026,7 +1026,7 @@ impl Term {
|
||||||
|
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
trait Append : PushChar {
|
trait Append: PushChar {
|
||||||
fn append(
|
fn append(
|
||||||
&mut self,
|
&mut self,
|
||||||
grid: &Grid<Cell>,
|
grid: &Grid<Cell>,
|
||||||
|
@ -1042,7 +1042,7 @@ impl Term {
|
||||||
grid: &Grid<Cell>,
|
grid: &Grid<Cell>,
|
||||||
tabs: &TabStops,
|
tabs: &TabStops,
|
||||||
mut line: usize,
|
mut line: usize,
|
||||||
cols: Range<Column>
|
cols: Range<Column>,
|
||||||
) {
|
) {
|
||||||
// Select until last line still within the buffer
|
// Select until last line still within the buffer
|
||||||
line = min(line, grid.len() - 1);
|
line = min(line, grid.len() - 1);
|
||||||
|
@ -1115,7 +1115,6 @@ impl Term {
|
||||||
|
|
||||||
// Starting line
|
// Starting line
|
||||||
res.append(&self.grid, &self.tabs, start.line, Column(0)..start.col);
|
res.append(&self.grid, &self.tabs, start.line, Column(0)..start.col);
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Multi line selection
|
// Multi line selection
|
||||||
|
@ -1130,7 +1129,7 @@ impl Term {
|
||||||
|
|
||||||
// Starting line
|
// Starting line
|
||||||
res.append(&self.grid, &self.tabs, start.line, Column(0)..start.col);
|
res.append(&self.grid, &self.tabs, start.line, Column(0)..start.col);
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(res)
|
Some(res)
|
||||||
|
@ -1180,11 +1179,12 @@ impl Term {
|
||||||
window_focused: bool,
|
window_focused: bool,
|
||||||
) -> RenderableCellsIter<'_> {
|
) -> RenderableCellsIter<'_> {
|
||||||
let alt_screen = self.mode.contains(TermMode::ALT_SCREEN);
|
let alt_screen = self.mode.contains(TermMode::ALT_SCREEN);
|
||||||
let selection = self.grid.selection.as_ref()
|
let selection = self
|
||||||
|
.grid
|
||||||
|
.selection
|
||||||
|
.as_ref()
|
||||||
.and_then(|s| s.to_span(self, alt_screen))
|
.and_then(|s| s.to_span(self, alt_screen))
|
||||||
.map(|span| {
|
.map(|span| span.to_locations());
|
||||||
span.to_locations()
|
|
||||||
});
|
|
||||||
|
|
||||||
let cursor = if window_focused || !config.unfocused_hollow_cursor() {
|
let cursor = if window_focused || !config.unfocused_hollow_cursor() {
|
||||||
self.cursor_style.unwrap_or(self.default_cursor_style)
|
self.cursor_style.unwrap_or(self.default_cursor_style)
|
||||||
|
@ -1192,12 +1192,7 @@ impl Term {
|
||||||
CursorStyle::HollowBlock
|
CursorStyle::HollowBlock
|
||||||
};
|
};
|
||||||
|
|
||||||
RenderableCellsIter::new(
|
RenderableCellsIter::new(&self, config, selection, cursor)
|
||||||
&self,
|
|
||||||
config,
|
|
||||||
selection,
|
|
||||||
cursor,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resize terminal to new dimensions
|
/// Resize terminal to new dimensions
|
||||||
|
@ -1205,8 +1200,8 @@ impl Term {
|
||||||
debug!("Resizing terminal");
|
debug!("Resizing terminal");
|
||||||
|
|
||||||
// Bounds check; lots of math assumes width and height are > 0
|
// Bounds check; lots of math assumes width and height are > 0
|
||||||
if size.width as usize <= 2 * self.size_info.padding_x as usize ||
|
if size.width as usize <= 2 * self.size_info.padding_x as usize
|
||||||
size.height as usize <= 2 * self.size_info.padding_y as usize
|
|| size.height as usize <= 2 * self.size_info.padding_y as usize
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1455,10 +1450,7 @@ impl ansi::Handler for Term {
|
||||||
trace!("Wrapping input");
|
trace!("Wrapping input");
|
||||||
|
|
||||||
{
|
{
|
||||||
let location = Point {
|
let location = Point { line: self.cursor.point.line, col: self.cursor.point.col };
|
||||||
line: self.cursor.point.line,
|
|
||||||
col: self.cursor.point.col
|
|
||||||
};
|
|
||||||
|
|
||||||
let cell = &mut self.grid[&location];
|
let cell = &mut self.grid[&location];
|
||||||
cell.flags.insert(cell::Flags::WRAPLINE);
|
cell.flags.insert(cell::Flags::WRAPLINE);
|
||||||
|
@ -1498,8 +1490,7 @@ impl ansi::Handler for Term {
|
||||||
if width == 0 {
|
if width == 0 {
|
||||||
let col = self.cursor.point.col.0.saturating_sub(1);
|
let col = self.cursor.point.col.0.saturating_sub(1);
|
||||||
let line = self.cursor.point.line;
|
let line = self.cursor.point.line;
|
||||||
if self.grid[line][Column(col)].flags.contains(cell::Flags::WIDE_CHAR_SPACER)
|
if self.grid[line][Column(col)].flags.contains(cell::Flags::WIDE_CHAR_SPACER) {
|
||||||
{
|
|
||||||
col.saturating_sub(1);
|
col.saturating_sub(1);
|
||||||
}
|
}
|
||||||
self.grid[line][Column(col)].push_extra(c);
|
self.grid[line][Column(col)].push_extra(c);
|
||||||
|
@ -1536,8 +1527,7 @@ impl ansi::Handler for Term {
|
||||||
let mut template = self.cursor.template;
|
let mut template = self.cursor.template;
|
||||||
template.c = 'E';
|
template.c = 'E';
|
||||||
|
|
||||||
self.grid.region_mut(..)
|
self.grid.region_mut(..).each(|c| c.reset(&template));
|
||||||
.each(|c| c.reset(&template));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -1858,11 +1848,7 @@ impl ansi::Handler for Term {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn save_cursor_position(&mut self) {
|
fn save_cursor_position(&mut self) {
|
||||||
trace!("Saving cursor position");
|
trace!("Saving cursor position");
|
||||||
let cursor = if self.alt {
|
let cursor = if self.alt { &mut self.cursor_save_alt } else { &mut self.cursor_save };
|
||||||
&mut self.cursor_save_alt
|
|
||||||
} else {
|
|
||||||
&mut self.cursor_save
|
|
||||||
};
|
|
||||||
|
|
||||||
*cursor = self.cursor;
|
*cursor = self.cursor;
|
||||||
}
|
}
|
||||||
|
@ -1870,11 +1856,7 @@ impl ansi::Handler for Term {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn restore_cursor_position(&mut self) {
|
fn restore_cursor_position(&mut self) {
|
||||||
trace!("Restoring cursor position");
|
trace!("Restoring cursor position");
|
||||||
let source = if self.alt {
|
let source = if self.alt { &self.cursor_save_alt } else { &self.cursor_save };
|
||||||
&self.cursor_save_alt
|
|
||||||
} else {
|
|
||||||
&self.cursor_save
|
|
||||||
};
|
|
||||||
|
|
||||||
self.cursor = *source;
|
self.cursor = *source;
|
||||||
self.cursor.point.line = min(self.cursor.point.line, self.grid.num_lines() - 1);
|
self.cursor.point.line = min(self.cursor.point.line, self.grid.num_lines() - 1);
|
||||||
|
@ -1887,7 +1869,7 @@ impl ansi::Handler for Term {
|
||||||
let mut template = self.cursor.template;
|
let mut template = self.cursor.template;
|
||||||
template.flags ^= template.flags;
|
template.flags ^= template.flags;
|
||||||
|
|
||||||
let col = self.cursor.point.col;
|
let col = self.cursor.point.col;
|
||||||
|
|
||||||
match mode {
|
match mode {
|
||||||
ansi::LineClearMode::Right => {
|
ansi::LineClearMode::Right => {
|
||||||
|
@ -1929,13 +1911,12 @@ impl ansi::Handler for Term {
|
||||||
|
|
||||||
/// Set the clipboard
|
/// Set the clipboard
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_clipboard(&mut self, string: &str)
|
fn set_clipboard(&mut self, string: &str) {
|
||||||
{
|
Clipboard::new().and_then(|mut clipboard| clipboard.store_primary(string)).unwrap_or_else(
|
||||||
Clipboard::new()
|
|err| {
|
||||||
.and_then(|mut clipboard| clipboard.store_primary(string))
|
|
||||||
.unwrap_or_else(|err| {
|
|
||||||
warn!("Error storing selection to clipboard: {}", err);
|
warn!("Error storing selection to clipboard: {}", err);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -1954,7 +1935,8 @@ impl ansi::Handler for Term {
|
||||||
cell.reset(&template);
|
cell.reset(&template);
|
||||||
}
|
}
|
||||||
if self.cursor.point.line < self.grid.num_lines() - 1 {
|
if self.cursor.point.line < self.grid.num_lines() - 1 {
|
||||||
self.grid.region_mut((self.cursor.point.line + 1)..)
|
self.grid
|
||||||
|
.region_mut((self.cursor.point.line + 1)..)
|
||||||
.each(|cell| cell.reset(&template));
|
.each(|cell| cell.reset(&template));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1963,7 +1945,8 @@ impl ansi::Handler for Term {
|
||||||
// If clearing more than one line
|
// If clearing more than one line
|
||||||
if self.cursor.point.line > Line(1) {
|
if self.cursor.point.line > Line(1) {
|
||||||
// Fully clear all lines before the current line
|
// Fully clear all lines before the current line
|
||||||
self.grid.region_mut(..self.cursor.point.line)
|
self.grid
|
||||||
|
.region_mut(..self.cursor.point.line)
|
||||||
.each(|cell| cell.reset(&template));
|
.each(|cell| cell.reset(&template));
|
||||||
}
|
}
|
||||||
// Clear up to the current column in the current line
|
// Clear up to the current column in the current line
|
||||||
|
@ -1987,7 +1970,7 @@ impl ansi::Handler for Term {
|
||||||
},
|
},
|
||||||
ansi::TabulationClearMode::All => {
|
ansi::TabulationClearMode::All => {
|
||||||
self.tabs.clear_all();
|
self.tabs.clear_all();
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2042,7 +2025,9 @@ impl ansi::Handler for Term {
|
||||||
Attr::Bold => self.cursor.template.flags.insert(cell::Flags::BOLD),
|
Attr::Bold => self.cursor.template.flags.insert(cell::Flags::BOLD),
|
||||||
Attr::CancelBold => self.cursor.template.flags.remove(cell::Flags::BOLD),
|
Attr::CancelBold => self.cursor.template.flags.remove(cell::Flags::BOLD),
|
||||||
Attr::Dim => self.cursor.template.flags.insert(cell::Flags::DIM),
|
Attr::Dim => self.cursor.template.flags.insert(cell::Flags::DIM),
|
||||||
Attr::CancelBoldDim => self.cursor.template.flags.remove(cell::Flags::BOLD | cell::Flags::DIM),
|
Attr::CancelBoldDim => {
|
||||||
|
self.cursor.template.flags.remove(cell::Flags::BOLD | cell::Flags::DIM)
|
||||||
|
},
|
||||||
Attr::Italic => self.cursor.template.flags.insert(cell::Flags::ITALIC),
|
Attr::Italic => self.cursor.template.flags.insert(cell::Flags::ITALIC),
|
||||||
Attr::CancelItalic => self.cursor.template.flags.remove(cell::Flags::ITALIC),
|
Attr::CancelItalic => self.cursor.template.flags.remove(cell::Flags::ITALIC),
|
||||||
Attr::Underscore => self.cursor.template.flags.insert(cell::Flags::UNDERLINE),
|
Attr::Underscore => self.cursor.template.flags.insert(cell::Flags::UNDERLINE),
|
||||||
|
@ -2053,7 +2038,7 @@ impl ansi::Handler for Term {
|
||||||
Attr::CancelStrike => self.cursor.template.flags.remove(cell::Flags::STRIKEOUT),
|
Attr::CancelStrike => self.cursor.template.flags.remove(cell::Flags::STRIKEOUT),
|
||||||
_ => {
|
_ => {
|
||||||
debug!("Term got unhandled attr: {:?}", attr);
|
debug!("Term got unhandled attr: {:?}", attr);
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2093,12 +2078,12 @@ impl ansi::Handler for Term {
|
||||||
ansi::Mode::Insert => self.mode.insert(mode::TermMode::INSERT), // heh
|
ansi::Mode::Insert => self.mode.insert(mode::TermMode::INSERT), // heh
|
||||||
ansi::Mode::BlinkingCursor => {
|
ansi::Mode::BlinkingCursor => {
|
||||||
trace!("... unimplemented mode");
|
trace!("... unimplemented mode");
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn unset_mode(&mut self,mode: ansi::Mode) {
|
fn unset_mode(&mut self, mode: ansi::Mode) {
|
||||||
trace!("Unsetting mode: {:?}", mode);
|
trace!("Unsetting mode: {:?}", mode);
|
||||||
match mode {
|
match mode {
|
||||||
ansi::Mode::SwapScreenAndSetRestoreCursor => {
|
ansi::Mode::SwapScreenAndSetRestoreCursor => {
|
||||||
|
@ -2133,7 +2118,7 @@ impl ansi::Handler for Term {
|
||||||
ansi::Mode::Insert => self.mode.remove(mode::TermMode::INSERT),
|
ansi::Mode::Insert => self.mode.remove(mode::TermMode::INSERT),
|
||||||
ansi::Mode::BlinkingCursor => {
|
ansi::Mode::BlinkingCursor => {
|
||||||
trace!("... unimplemented mode");
|
trace!("... unimplemented mode");
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2177,7 +2162,7 @@ impl ansi::Handler for Term {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TabStops {
|
struct TabStops {
|
||||||
tabs: Vec<bool>
|
tabs: Vec<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TabStops {
|
impl TabStops {
|
||||||
|
@ -2185,7 +2170,7 @@ impl TabStops {
|
||||||
TabStops {
|
TabStops {
|
||||||
tabs: IndexRange::from(Column(0)..num_cols)
|
tabs: IndexRange::from(Column(0)..num_cols)
|
||||||
.map(|i| (*i as usize) % tabspaces == 0)
|
.map(|i| (*i as usize) % tabspaces == 0)
|
||||||
.collect::<Vec<bool>>()
|
.collect::<Vec<bool>>(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2214,18 +2199,18 @@ impl IndexMut<Column> for TabStops {
|
||||||
mod tests {
|
mod tests {
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
|
||||||
use super::{Cell, Term, SizeInfo};
|
use super::{Cell, SizeInfo, Term};
|
||||||
use crate::term::cell;
|
use crate::term::cell;
|
||||||
|
|
||||||
use crate::grid::{Grid, Scroll};
|
use crate::ansi::{self, CharsetIndex, Handler, StandardCharset};
|
||||||
use crate::index::{Point, Line, Column, Side};
|
|
||||||
use crate::ansi::{self, Handler, CharsetIndex, StandardCharset};
|
|
||||||
use crate::selection::Selection;
|
|
||||||
use std::mem;
|
|
||||||
use crate::input::FONT_SIZE_STEP;
|
|
||||||
use font::Size;
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
use crate::grid::{Grid, Scroll};
|
||||||
|
use crate::index::{Column, Line, Point, Side};
|
||||||
|
use crate::input::FONT_SIZE_STEP;
|
||||||
use crate::message_bar::MessageBuffer;
|
use crate::message_bar::MessageBuffer;
|
||||||
|
use crate::selection::Selection;
|
||||||
|
use font::Size;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn semantic_selection_works() {
|
fn semantic_selection_works() {
|
||||||
|
@ -2290,7 +2275,6 @@ mod tests {
|
||||||
grid[Line(0)][Column(0)].c = '"';
|
grid[Line(0)][Column(0)].c = '"';
|
||||||
grid[Line(0)][Column(3)].c = '"';
|
grid[Line(0)][Column(3)].c = '"';
|
||||||
|
|
||||||
|
|
||||||
mem::swap(&mut term.grid, &mut grid);
|
mem::swap(&mut term.grid, &mut grid);
|
||||||
|
|
||||||
*term.selection_mut() = Some(Selection::lines(Point { line: 0, col: Column(3) }));
|
*term.selection_mut() = Some(Selection::lines(Point { line: 0, col: Column(3) }));
|
||||||
|
@ -2336,8 +2320,7 @@ mod tests {
|
||||||
|
|
||||||
let grid: Grid<Cell> = Grid::new(Line(24), Column(80), 0, template);
|
let grid: Grid<Cell> = Grid::new(Line(24), Column(80), 0, template);
|
||||||
let serialized = serde_json::to_string(&grid).expect("ser");
|
let serialized = serde_json::to_string(&grid).expect("ser");
|
||||||
let deserialized = serde_json::from_str::<Grid<Cell>>(&serialized)
|
let deserialized = serde_json::from_str::<Grid<Cell>>(&serialized).expect("de");
|
||||||
.expect("de");
|
|
||||||
|
|
||||||
assert_eq!(deserialized, grid);
|
assert_eq!(deserialized, grid);
|
||||||
}
|
}
|
||||||
|
@ -2355,8 +2338,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
let mut term = Term::new(&Default::default(), size, MessageBuffer::new());
|
let mut term = Term::new(&Default::default(), size, MessageBuffer::new());
|
||||||
let cursor = Point::new(Line(0), Column(0));
|
let cursor = Point::new(Line(0), Column(0));
|
||||||
term.configure_charset(CharsetIndex::G0,
|
term.configure_charset(CharsetIndex::G0, StandardCharset::SpecialCharacterAndLineDrawing);
|
||||||
StandardCharset::SpecialCharacterAndLineDrawing);
|
|
||||||
term.input('a');
|
term.input('a');
|
||||||
|
|
||||||
assert_eq!(term.grid()[&cursor].c, '▒');
|
assert_eq!(term.grid()[&cursor].c, '▒');
|
||||||
|
@ -2440,7 +2422,7 @@ mod tests {
|
||||||
cell_height: 3.0,
|
cell_height: 3.0,
|
||||||
padding_x: 0.0,
|
padding_x: 0.0,
|
||||||
padding_y: 0.0,
|
padding_y: 0.0,
|
||||||
dpr: 1.0
|
dpr: 1.0,
|
||||||
};
|
};
|
||||||
let config: Config = Default::default();
|
let config: Config = Default::default();
|
||||||
let mut term: Term = Term::new(&config, size, MessageBuffer::new());
|
let mut term: Term = Term::new(&config, size, MessageBuffer::new());
|
||||||
|
@ -2460,27 +2442,27 @@ mod tests {
|
||||||
|
|
||||||
#[cfg(all(test, feature = "bench"))]
|
#[cfg(all(test, feature = "bench"))]
|
||||||
mod benches {
|
mod benches {
|
||||||
extern crate test;
|
|
||||||
extern crate serde_json as json;
|
extern crate serde_json as json;
|
||||||
|
extern crate test;
|
||||||
|
|
||||||
use std::io::Read;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::grid::Grid;
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
use crate::grid::Grid;
|
||||||
use crate::message_bar::MessageBuffer;
|
use crate::message_bar::MessageBuffer;
|
||||||
|
|
||||||
use super::{SizeInfo, Term};
|
|
||||||
use super::cell::Cell;
|
use super::cell::Cell;
|
||||||
|
use super::{SizeInfo, Term};
|
||||||
|
|
||||||
fn read_string<P>(path: P) -> String
|
fn read_string<P>(path: P) -> String
|
||||||
where P: AsRef<Path>
|
where
|
||||||
|
P: AsRef<Path>,
|
||||||
{
|
{
|
||||||
let mut res = String::new();
|
let mut res = String::new();
|
||||||
File::open(path.as_ref()).unwrap()
|
File::open(path.as_ref()).unwrap().read_to_string(&mut res).unwrap();
|
||||||
.read_to_string(&mut res).unwrap();
|
|
||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
@ -2497,12 +2479,14 @@ mod benches {
|
||||||
#[bench]
|
#[bench]
|
||||||
fn render_iter(b: &mut test::Bencher) {
|
fn render_iter(b: &mut test::Bencher) {
|
||||||
// Need some realistic grid state; using one of the ref files.
|
// Need some realistic grid state; using one of the ref files.
|
||||||
let serialized_grid = read_string(
|
let serialized_grid = read_string(concat!(
|
||||||
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/ref/vim_large_window_scroll/grid.json")
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
);
|
"/tests/ref/vim_large_window_scroll/grid.json"
|
||||||
let serialized_size = read_string(
|
));
|
||||||
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/ref/vim_large_window_scroll/size.json")
|
let serialized_size = read_string(concat!(
|
||||||
);
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
|
"/tests/ref/vim_large_window_scroll/size.json"
|
||||||
|
));
|
||||||
|
|
||||||
let mut grid: Grid<Cell> = json::from_str(&serialized_grid).unwrap();
|
let mut grid: Grid<Cell> = json::from_str(&serialized_grid).unwrap();
|
||||||
let size: SizeInfo = json::from_str(&serialized_size).unwrap();
|
let size: SizeInfo = json::from_str(&serialized_size).unwrap();
|
||||||
|
|
|
@ -57,7 +57,7 @@ pub trait EventedReadWrite {
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub enum ChildEvent {
|
pub enum ChildEvent {
|
||||||
/// Indicates the child has exited
|
/// Indicates the child has exited
|
||||||
Exited
|
Exited,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A pseudoterminal (or PTY)
|
/// A pseudoterminal (or PTY)
|
||||||
|
@ -65,7 +65,7 @@ pub enum ChildEvent {
|
||||||
/// This is a refinement of EventedReadWrite that also provides a channel through which we can be
|
/// This is a refinement of EventedReadWrite that also provides a channel through which we can be
|
||||||
/// notified if the PTY child process does something we care about (other than writing to the TTY).
|
/// notified if the PTY child process does something we care about (other than writing to the TTY).
|
||||||
/// In particular, this allows for race-free child exit notification on UNIX (cf. `SIGCHLD`).
|
/// In particular, this allows for race-free child exit notification on UNIX (cf. `SIGCHLD`).
|
||||||
pub trait EventedPty : EventedReadWrite {
|
pub trait EventedPty: EventedReadWrite {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn child_event_token(&self) -> mio::Token;
|
fn child_event_token(&self) -> mio::Token;
|
||||||
|
|
||||||
|
@ -83,11 +83,7 @@ pub fn setup_env(config: &Config) {
|
||||||
// below.
|
// below.
|
||||||
env::set_var(
|
env::set_var(
|
||||||
"TERM",
|
"TERM",
|
||||||
if Database::from_name("alacritty").is_ok() {
|
if Database::from_name("alacritty").is_ok() { "alacritty" } else { "xterm-256color" },
|
||||||
"alacritty"
|
|
||||||
} else {
|
|
||||||
"xterm-256color"
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Advertise 24-bit color support
|
// Advertise 24-bit color support
|
||||||
|
|
|
@ -15,24 +15,27 @@
|
||||||
//! tty related functionality
|
//! tty related functionality
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use crate::tty::{EventedReadWrite, EventedPty, ChildEvent};
|
|
||||||
use crate::term::SizeInfo;
|
|
||||||
use crate::display::OnResize;
|
|
||||||
use crate::config::{Config, Shell};
|
|
||||||
use crate::cli::Options;
|
use crate::cli::Options;
|
||||||
|
use crate::config::{Config, Shell};
|
||||||
|
use crate::display::OnResize;
|
||||||
|
use crate::term::SizeInfo;
|
||||||
|
use crate::tty::{ChildEvent, EventedPty, EventedReadWrite};
|
||||||
use mio;
|
use mio;
|
||||||
|
|
||||||
use libc::{self, c_int, pid_t, winsize, TIOCSCTTY};
|
use libc::{self, c_int, pid_t, winsize, TIOCSCTTY};
|
||||||
use nix::pty::openpty;
|
use nix::pty::openpty;
|
||||||
use signal_hook::{self as sighook, iterator::Signals};
|
use signal_hook::{self as sighook, iterator::Signals};
|
||||||
|
|
||||||
use std::os::unix::{process::CommandExt, io::{FromRawFd, AsRawFd, RawFd}};
|
|
||||||
use std::fs::File;
|
|
||||||
use std::process::{Command, Stdio, Child};
|
|
||||||
use std::ffi::CStr;
|
|
||||||
use std::ptr;
|
|
||||||
use mio::unix::EventedFd;
|
use mio::unix::EventedFd;
|
||||||
|
use std::ffi::CStr;
|
||||||
|
use std::fs::File;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use std::os::unix::{
|
||||||
|
io::{AsRawFd, FromRawFd, RawFd},
|
||||||
|
process::CommandExt,
|
||||||
|
};
|
||||||
|
use std::process::{Child, Command, Stdio};
|
||||||
|
use std::ptr;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
/// Process ID of child process
|
/// Process ID of child process
|
||||||
|
@ -55,8 +58,7 @@ fn make_pty(size: winsize) -> (RawFd, RawFd) {
|
||||||
win_size.ws_xpixel = 0;
|
win_size.ws_xpixel = 0;
|
||||||
win_size.ws_ypixel = 0;
|
win_size.ws_ypixel = 0;
|
||||||
|
|
||||||
let ends = openpty(Some(&win_size), None)
|
let ends = openpty(Some(&win_size), None).expect("openpty failed");
|
||||||
.expect("openpty failed");
|
|
||||||
|
|
||||||
(ends.master, ends.slave)
|
(ends.master, ends.slave)
|
||||||
}
|
}
|
||||||
|
@ -134,7 +136,6 @@ pub struct Pty {
|
||||||
token: mio::Token,
|
token: mio::Token,
|
||||||
signals: Signals,
|
signals: Signals,
|
||||||
signals_token: mio::Token,
|
signals_token: mio::Token,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pty {
|
impl Pty {
|
||||||
|
@ -145,9 +146,7 @@ impl Pty {
|
||||||
pub fn resize<T: ToWinsize>(&self, size: &T) {
|
pub fn resize<T: ToWinsize>(&self, size: &T) {
|
||||||
let win = size.to_winsize();
|
let win = size.to_winsize();
|
||||||
|
|
||||||
let res = unsafe {
|
let res = unsafe { libc::ioctl(self.fd.as_raw_fd(), libc::TIOCSWINSZ, &win as *const _) };
|
||||||
libc::ioctl(self.fd.as_raw_fd(), libc::TIOCSWINSZ, &win as *const _)
|
|
||||||
};
|
|
||||||
|
|
||||||
if res < 0 {
|
if res < 0 {
|
||||||
die!("ioctl TIOCSWINSZ failed: {}", errno());
|
die!("ioctl TIOCSWINSZ failed: {}", errno());
|
||||||
|
@ -170,10 +169,7 @@ pub fn new<T: ToWinsize>(
|
||||||
|
|
||||||
let default_shell = if cfg!(target_os = "macos") {
|
let default_shell = if cfg!(target_os = "macos") {
|
||||||
let shell_name = pw.shell.rsplit('/').next().unwrap();
|
let shell_name = pw.shell.rsplit('/').next().unwrap();
|
||||||
let argv = vec![
|
let argv = vec![String::from("-c"), format!("exec -a -{} {}", shell_name, pw.shell)];
|
||||||
String::from("-c"),
|
|
||||||
format!("exec -a -{} {}", shell_name, pw.shell),
|
|
||||||
];
|
|
||||||
|
|
||||||
Shell::new_with_args("/bin/bash", argv)
|
Shell::new_with_args("/bin/bash", argv)
|
||||||
} else {
|
} else {
|
||||||
|
@ -265,7 +261,7 @@ pub fn new<T: ToWinsize>(
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
die!("Failed to spawn command: {}", err);
|
die!("Failed to spawn command: {}", err);
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,19 +278,14 @@ impl EventedReadWrite for Pty {
|
||||||
poll_opts: mio::PollOpt,
|
poll_opts: mio::PollOpt,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
self.token = token.next().unwrap();
|
self.token = token.next().unwrap();
|
||||||
poll.register(
|
poll.register(&EventedFd(&self.fd.as_raw_fd()), self.token, interest, poll_opts)?;
|
||||||
&EventedFd(&self.fd.as_raw_fd()),
|
|
||||||
self.token,
|
|
||||||
interest,
|
|
||||||
poll_opts
|
|
||||||
)?;
|
|
||||||
|
|
||||||
self.signals_token = token.next().unwrap();
|
self.signals_token = token.next().unwrap();
|
||||||
poll.register(
|
poll.register(
|
||||||
&self.signals,
|
&self.signals,
|
||||||
self.signals_token,
|
self.signals_token,
|
||||||
mio::Ready::readable(),
|
mio::Ready::readable(),
|
||||||
mio::PollOpt::level()
|
mio::PollOpt::level(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,20 +294,15 @@ impl EventedReadWrite for Pty {
|
||||||
&mut self,
|
&mut self,
|
||||||
poll: &mio::Poll,
|
poll: &mio::Poll,
|
||||||
interest: mio::Ready,
|
interest: mio::Ready,
|
||||||
poll_opts: mio::PollOpt
|
poll_opts: mio::PollOpt,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
poll.reregister(
|
poll.reregister(&EventedFd(&self.fd.as_raw_fd()), self.token, interest, poll_opts)?;
|
||||||
&EventedFd(&self.fd.as_raw_fd()),
|
|
||||||
self.token,
|
|
||||||
interest,
|
|
||||||
poll_opts
|
|
||||||
)?;
|
|
||||||
|
|
||||||
poll.reregister(
|
poll.reregister(
|
||||||
&self.signals,
|
&self.signals,
|
||||||
self.signals_token,
|
self.signals_token,
|
||||||
mio::Ready::readable(),
|
mio::Ready::readable(),
|
||||||
mio::PollOpt::level()
|
mio::PollOpt::level(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,23 +336,20 @@ impl EventedReadWrite for Pty {
|
||||||
impl EventedPty for Pty {
|
impl EventedPty for Pty {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next_child_event(&mut self) -> Option<ChildEvent> {
|
fn next_child_event(&mut self) -> Option<ChildEvent> {
|
||||||
self.signals
|
self.signals.pending().next().and_then(|signal| {
|
||||||
.pending()
|
if signal != sighook::SIGCHLD {
|
||||||
.next()
|
return None;
|
||||||
.and_then(|signal| {
|
}
|
||||||
if signal != sighook::SIGCHLD {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.child.try_wait() {
|
match self.child.try_wait() {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Error checking child process termination: {}", e);
|
error!("Error checking child process termination: {}", e);
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
Ok(None) => None,
|
Ok(None) => None,
|
||||||
Ok(_) => Some(ChildEvent::Exited),
|
Ok(_) => Some(ChildEvent::Exited),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -400,9 +383,7 @@ impl OnResize for i32 {
|
||||||
fn on_resize(&mut self, size: &SizeInfo) {
|
fn on_resize(&mut self, size: &SizeInfo) {
|
||||||
let win = size.to_winsize();
|
let win = size.to_winsize();
|
||||||
|
|
||||||
let res = unsafe {
|
let res = unsafe { libc::ioctl(*self, libc::TIOCSWINSZ, &win as *const _) };
|
||||||
libc::ioctl(*self, libc::TIOCSWINSZ, &win as *const _)
|
|
||||||
};
|
|
||||||
|
|
||||||
if res < 0 {
|
if res < 0 {
|
||||||
die!("ioctl TIOCSWINSZ failed: {}", errno());
|
die!("ioctl TIOCSWINSZ failed: {}", errno());
|
||||||
|
|
|
@ -214,10 +214,7 @@ pub fn new<'a>(
|
||||||
cmdline.insert(0, initial_command.program().into());
|
cmdline.insert(0, initial_command.program().into());
|
||||||
|
|
||||||
// Warning, here be borrow hell
|
// Warning, here be borrow hell
|
||||||
let cwd = options
|
let cwd = options.working_dir.as_ref().map(|dir| canonicalize(dir).unwrap());
|
||||||
.working_dir
|
|
||||||
.as_ref()
|
|
||||||
.map(|dir| canonicalize(dir).unwrap());
|
|
||||||
let cwd = cwd.as_ref().map(|dir| dir.to_str().unwrap());
|
let cwd = cwd.as_ref().map(|dir| dir.to_str().unwrap());
|
||||||
|
|
||||||
// Create the client application, using startup info containing ConPTY info
|
// Create the client application, using startup info containing ConPTY info
|
||||||
|
@ -250,10 +247,7 @@ pub fn new<'a>(
|
||||||
let conin = EventedAnonWrite::new(conin);
|
let conin = EventedAnonWrite::new(conin);
|
||||||
let conout = EventedAnonRead::new(conout);
|
let conout = EventedAnonRead::new(conout);
|
||||||
|
|
||||||
let agent = Conpty {
|
let agent = Conpty { handle: pty_handle, api };
|
||||||
handle: pty_handle,
|
|
||||||
api,
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(Pty {
|
Some(Pty {
|
||||||
handle: super::PtyHandle::Conpty(ConptyHandle::new(agent)),
|
handle: super::PtyHandle::Conpty(ConptyHandle::new(agent)),
|
||||||
|
@ -279,10 +273,7 @@ fn coord_from_sizeinfo(sizeinfo: &SizeInfo) -> Option<COORD> {
|
||||||
let lines = sizeinfo.lines().0;
|
let lines = sizeinfo.lines().0;
|
||||||
|
|
||||||
if cols <= i16::MAX as usize && lines <= i16::MAX as usize {
|
if cols <= i16::MAX as usize && lines <= i16::MAX as usize {
|
||||||
Some(COORD {
|
Some(COORD { X: sizeinfo.cols().0 as i16, Y: sizeinfo.lines().0 as i16 })
|
||||||
X: sizeinfo.cols().0 as i16,
|
|
||||||
Y: sizeinfo.lines().0 as i16,
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ use crate::cli::Options;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::display::OnResize;
|
use crate::display::OnResize;
|
||||||
use crate::term::SizeInfo;
|
use crate::term::SizeInfo;
|
||||||
use crate::tty::{EventedReadWrite, EventedPty};
|
use crate::tty::{EventedPty, EventedReadWrite};
|
||||||
|
|
||||||
mod conpty;
|
mod conpty;
|
||||||
mod winpty;
|
mod winpty;
|
||||||
|
@ -44,14 +44,14 @@ pub fn process_should_exit() -> bool {
|
||||||
WAIT_OBJECT_0 => {
|
WAIT_OBJECT_0 => {
|
||||||
info!("wait_object_0");
|
info!("wait_object_0");
|
||||||
true
|
true
|
||||||
}
|
},
|
||||||
// Reached timeout of 0, process has not exited
|
// Reached timeout of 0, process has not exited
|
||||||
WAIT_TIMEOUT => false,
|
WAIT_TIMEOUT => false,
|
||||||
// Error checking process, winpty gave us a bad agent handle?
|
// Error checking process, winpty gave us a bad agent handle?
|
||||||
_ => {
|
_ => {
|
||||||
info!("Bad exit: {}", ::std::io::Error::last_os_error());
|
info!("Bad exit: {}", ::std::io::Error::last_os_error());
|
||||||
true
|
true
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,7 +219,7 @@ impl<'a> OnResize for PtyHandle<'a> {
|
||||||
PtyHandle::Conpty(c) => {
|
PtyHandle::Conpty(c) => {
|
||||||
let mut handle = c.clone();
|
let mut handle = c.clone();
|
||||||
handle.on_resize(sizeinfo)
|
handle.on_resize(sizeinfo)
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -240,34 +240,14 @@ impl<'a> EventedReadWrite for Pty<'a> {
|
||||||
self.write_token = token.next().unwrap();
|
self.write_token = token.next().unwrap();
|
||||||
|
|
||||||
if interest.is_readable() {
|
if interest.is_readable() {
|
||||||
poll.register(
|
poll.register(&self.conout, self.read_token, mio::Ready::readable(), poll_opts)?
|
||||||
&self.conout,
|
|
||||||
self.read_token,
|
|
||||||
mio::Ready::readable(),
|
|
||||||
poll_opts,
|
|
||||||
)?
|
|
||||||
} else {
|
} else {
|
||||||
poll.register(
|
poll.register(&self.conout, self.read_token, mio::Ready::empty(), poll_opts)?
|
||||||
&self.conout,
|
|
||||||
self.read_token,
|
|
||||||
mio::Ready::empty(),
|
|
||||||
poll_opts,
|
|
||||||
)?
|
|
||||||
}
|
}
|
||||||
if interest.is_writable() {
|
if interest.is_writable() {
|
||||||
poll.register(
|
poll.register(&self.conin, self.write_token, mio::Ready::writable(), poll_opts)?
|
||||||
&self.conin,
|
|
||||||
self.write_token,
|
|
||||||
mio::Ready::writable(),
|
|
||||||
poll_opts,
|
|
||||||
)?
|
|
||||||
} else {
|
} else {
|
||||||
poll.register(
|
poll.register(&self.conin, self.write_token, mio::Ready::empty(), poll_opts)?
|
||||||
&self.conin,
|
|
||||||
self.write_token,
|
|
||||||
mio::Ready::empty(),
|
|
||||||
poll_opts,
|
|
||||||
)?
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -280,34 +260,14 @@ impl<'a> EventedReadWrite for Pty<'a> {
|
||||||
poll_opts: mio::PollOpt,
|
poll_opts: mio::PollOpt,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
if interest.is_readable() {
|
if interest.is_readable() {
|
||||||
poll.reregister(
|
poll.reregister(&self.conout, self.read_token, mio::Ready::readable(), poll_opts)?;
|
||||||
&self.conout,
|
|
||||||
self.read_token,
|
|
||||||
mio::Ready::readable(),
|
|
||||||
poll_opts,
|
|
||||||
)?;
|
|
||||||
} else {
|
} else {
|
||||||
poll.reregister(
|
poll.reregister(&self.conout, self.read_token, mio::Ready::empty(), poll_opts)?;
|
||||||
&self.conout,
|
|
||||||
self.read_token,
|
|
||||||
mio::Ready::empty(),
|
|
||||||
poll_opts,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
if interest.is_writable() {
|
if interest.is_writable() {
|
||||||
poll.reregister(
|
poll.reregister(&self.conin, self.write_token, mio::Ready::writable(), poll_opts)?;
|
||||||
&self.conin,
|
|
||||||
self.write_token,
|
|
||||||
mio::Ready::writable(),
|
|
||||||
poll_opts,
|
|
||||||
)?;
|
|
||||||
} else {
|
} else {
|
||||||
poll.reregister(
|
poll.reregister(&self.conin, self.write_token, mio::Ready::empty(), poll_opts)?;
|
||||||
&self.conin,
|
|
||||||
self.write_token,
|
|
||||||
mio::Ready::empty(),
|
|
||||||
poll_opts,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -340,4 +300,4 @@ impl<'a> EventedReadWrite for Pty<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> EventedPty for Pty<'a> { }
|
impl<'a> EventedPty for Pty<'a> {}
|
||||||
|
|
|
@ -14,29 +14,29 @@
|
||||||
|
|
||||||
use super::{Pty, HANDLE};
|
use super::{Pty, HANDLE};
|
||||||
|
|
||||||
use std::io;
|
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::os::windows::io::{FromRawHandle, IntoRawHandle};
|
use std::io;
|
||||||
use std::os::windows::fs::OpenOptionsExt;
|
use std::os::windows::fs::OpenOptionsExt;
|
||||||
|
use std::os::windows::io::{FromRawHandle, IntoRawHandle};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::u16;
|
use std::u16;
|
||||||
|
|
||||||
use dunce::canonicalize;
|
use dunce::canonicalize;
|
||||||
use mio_named_pipes::NamedPipe;
|
use mio_named_pipes::NamedPipe;
|
||||||
use winapi::um::winbase::FILE_FLAG_OVERLAPPED;
|
use winapi::um::winbase::FILE_FLAG_OVERLAPPED;
|
||||||
use winpty::{ConfigFlags, MouseMode, SpawnConfig, SpawnFlags, Winpty};
|
|
||||||
use winpty::Config as WinptyConfig;
|
use winpty::Config as WinptyConfig;
|
||||||
|
use winpty::{ConfigFlags, MouseMode, SpawnConfig, SpawnFlags, Winpty};
|
||||||
|
|
||||||
|
use crate::cli::Options;
|
||||||
use crate::config::{Config, Shell};
|
use crate::config::{Config, Shell};
|
||||||
use crate::display::OnResize;
|
use crate::display::OnResize;
|
||||||
use crate::cli::Options;
|
|
||||||
use crate::term::SizeInfo;
|
use crate::term::SizeInfo;
|
||||||
|
|
||||||
// We store a raw pointer because we need mutable access to call
|
// We store a raw pointer because we need mutable access to call
|
||||||
// on_resize from a separate thread. Winpty internally uses a mutex
|
// on_resize from a separate thread. Winpty internally uses a mutex
|
||||||
// so this is safe, despite outwards appearance.
|
// so this is safe, despite outwards appearance.
|
||||||
pub struct Agent<'a> {
|
pub struct Agent<'a> {
|
||||||
winpty: *mut Winpty<'a>
|
winpty: *mut Winpty<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle can be cloned freely and moved between threads.
|
/// Handle can be cloned freely and moved between threads.
|
||||||
|
@ -48,9 +48,7 @@ unsafe impl<'a> Sync for Agent<'a> {}
|
||||||
|
|
||||||
impl<'a> Agent<'a> {
|
impl<'a> Agent<'a> {
|
||||||
pub fn new(winpty: Winpty<'a>) -> Self {
|
pub fn new(winpty: Winpty<'a>) -> Self {
|
||||||
Self {
|
Self { winpty: Box::into_raw(Box::new(winpty)) }
|
||||||
winpty: Box::into_raw(Box::new(winpty))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get immutable access to Winpty.
|
/// Get immutable access to Winpty.
|
||||||
|
@ -68,11 +66,12 @@ impl<'a> Agent<'a> {
|
||||||
|
|
||||||
impl<'a> Drop for Agent<'a> {
|
impl<'a> Drop for Agent<'a> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe { Box::from_raw(self.winpty); }
|
unsafe {
|
||||||
|
Box::from_raw(self.winpty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// How long the winpty agent should wait for any RPC request
|
/// How long the winpty agent should wait for any RPC request
|
||||||
/// This is a placeholder value until we see how often long responses happen
|
/// This is a placeholder value until we see how often long responses happen
|
||||||
const AGENT_TIMEOUT: u32 = 10000;
|
const AGENT_TIMEOUT: u32 = 10000;
|
||||||
|
@ -112,30 +111,19 @@ pub fn new<'a>(
|
||||||
Some(&cmdline.join(" ")),
|
Some(&cmdline.join(" ")),
|
||||||
cwd,
|
cwd,
|
||||||
None, // Env
|
None, // Env
|
||||||
).unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let default_opts = &mut OpenOptions::new();
|
let default_opts = &mut OpenOptions::new();
|
||||||
default_opts
|
default_opts.share_mode(0).custom_flags(FILE_FLAG_OVERLAPPED);
|
||||||
.share_mode(0)
|
|
||||||
.custom_flags(FILE_FLAG_OVERLAPPED);
|
|
||||||
|
|
||||||
let (conout_pipe, conin_pipe);
|
let (conout_pipe, conin_pipe);
|
||||||
unsafe {
|
unsafe {
|
||||||
conout_pipe = NamedPipe::from_raw_handle(
|
conout_pipe = NamedPipe::from_raw_handle(
|
||||||
default_opts
|
default_opts.clone().read(true).open(conout).unwrap().into_raw_handle(),
|
||||||
.clone()
|
|
||||||
.read(true)
|
|
||||||
.open(conout)
|
|
||||||
.unwrap()
|
|
||||||
.into_raw_handle(),
|
|
||||||
);
|
);
|
||||||
conin_pipe = NamedPipe::from_raw_handle(
|
conin_pipe = NamedPipe::from_raw_handle(
|
||||||
default_opts
|
default_opts.clone().write(true).open(conin).unwrap().into_raw_handle(),
|
||||||
.clone()
|
|
||||||
.write(true)
|
|
||||||
.open(conin)
|
|
||||||
.unwrap()
|
|
||||||
.into_raw_handle(),
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -166,7 +154,7 @@ pub fn new<'a>(
|
||||||
conout: super::EventedReadablePipe::Named(conout_pipe),
|
conout: super::EventedReadablePipe::Named(conout_pipe),
|
||||||
conin: super::EventedWritablePipe::Named(conin_pipe),
|
conin: super::EventedWritablePipe::Named(conin_pipe),
|
||||||
read_token: 0.into(),
|
read_token: 0.into(),
|
||||||
write_token: 0.into()
|
write_token: 0.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
36
src/url.rs
36
src/url.rs
|
@ -12,17 +12,15 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use url;
|
|
||||||
use unicode_width::UnicodeWidthChar;
|
use unicode_width::UnicodeWidthChar;
|
||||||
|
use url;
|
||||||
|
|
||||||
use crate::term::cell::{Cell, Flags};
|
use crate::term::cell::{Cell, Flags};
|
||||||
|
|
||||||
// See https://tools.ietf.org/html/rfc3987#page-13
|
// See https://tools.ietf.org/html/rfc3987#page-13
|
||||||
const URL_SEPARATOR_CHARS: [char; 10] = ['<', '>', '"', ' ', '{', '}', '|', '\\', '^', '`'];
|
const URL_SEPARATOR_CHARS: [char; 10] = ['<', '>', '"', ' ', '{', '}', '|', '\\', '^', '`'];
|
||||||
const URL_DENY_END_CHARS: [char; 8] = ['.', ',', ';', ':', '?', '!', '/', '('];
|
const URL_DENY_END_CHARS: [char; 8] = ['.', ',', ';', ':', '?', '!', '/', '('];
|
||||||
const URL_SCHEMES: [&str; 8] = [
|
const URL_SCHEMES: [&str; 8] = ["http", "https", "mailto", "news", "file", "git", "ssh", "ftp"];
|
||||||
"http", "https", "mailto", "news", "file", "git", "ssh", "ftp",
|
|
||||||
];
|
|
||||||
|
|
||||||
/// URL text and origin of the original click position.
|
/// URL text and origin of the original click position.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
@ -39,10 +37,7 @@ pub struct UrlParser {
|
||||||
|
|
||||||
impl UrlParser {
|
impl UrlParser {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
UrlParser {
|
UrlParser { state: String::new(), origin: 0 }
|
||||||
state: String::new(),
|
|
||||||
origin: 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Advance the parser one character to the left.
|
/// Advance the parser one character to the left.
|
||||||
|
@ -74,19 +69,17 @@ impl UrlParser {
|
||||||
// Remove non-alphabetical characters before the scheme
|
// Remove non-alphabetical characters before the scheme
|
||||||
// https://tools.ietf.org/html/rfc3986#section-3.1
|
// https://tools.ietf.org/html/rfc3986#section-3.1
|
||||||
if let Some(index) = self.state.find("://") {
|
if let Some(index) = self.state.find("://") {
|
||||||
let iter = self
|
let iter =
|
||||||
.state
|
self.state.char_indices().rev().skip_while(|(byte_index, _)| *byte_index >= index);
|
||||||
.char_indices()
|
|
||||||
.rev()
|
|
||||||
.skip_while(|(byte_index, _)| *byte_index >= index);
|
|
||||||
for (byte_index, c) in iter {
|
for (byte_index, c) in iter {
|
||||||
match c {
|
match c {
|
||||||
'a'...'z' | 'A'...'Z' => (),
|
'a'...'z' | 'A'...'Z' => (),
|
||||||
_ => {
|
_ => {
|
||||||
self.origin = self.origin.saturating_sub(byte_index + c.width().unwrap_or(1));
|
self.origin =
|
||||||
|
self.origin.saturating_sub(byte_index + c.width().unwrap_or(1));
|
||||||
self.state = self.state.split_off(byte_index + c.len_utf8());
|
self.state = self.state.split_off(byte_index + c.len_utf8());
|
||||||
break;
|
break;
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +96,7 @@ impl UrlParser {
|
||||||
')' | ']' => {
|
')' | ']' => {
|
||||||
self.state.truncate(i);
|
self.state.truncate(i);
|
||||||
break;
|
break;
|
||||||
}
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,14 +120,11 @@ impl UrlParser {
|
||||||
match url::Url::parse(&self.state) {
|
match url::Url::parse(&self.state) {
|
||||||
Ok(url) => {
|
Ok(url) => {
|
||||||
if URL_SCHEMES.contains(&url.scheme()) && self.origin > 0 {
|
if URL_SCHEMES.contains(&url.scheme()) && self.origin > 0 {
|
||||||
Some(Url {
|
Some(Url { origin: self.origin - 1, text: self.state })
|
||||||
origin: self.origin - 1,
|
|
||||||
text: self.state,
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,9 +150,9 @@ mod tests {
|
||||||
|
|
||||||
use crate::grid::Grid;
|
use crate::grid::Grid;
|
||||||
use crate::index::{Column, Line, Point};
|
use crate::index::{Column, Line, Point};
|
||||||
use crate::term::{Search, SizeInfo, Term};
|
|
||||||
use crate::term::cell::{Cell, Flags};
|
|
||||||
use crate::message_bar::MessageBuffer;
|
use crate::message_bar::MessageBuffer;
|
||||||
|
use crate::term::cell::{Cell, Flags};
|
||||||
|
use crate::term::{Search, SizeInfo, Term};
|
||||||
|
|
||||||
fn url_create_term(input: &str) -> Term {
|
fn url_create_term(input: &str) -> Term {
|
||||||
let size = SizeInfo {
|
let size = SizeInfo {
|
||||||
|
|
19
src/util.rs
19
src/util.rs
|
@ -12,10 +12,10 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::{cmp, io};
|
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
|
use std::{cmp, io};
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
use std::os::unix::process::CommandExt;
|
use std::os::unix::process::CommandExt;
|
||||||
|
@ -35,10 +35,7 @@ pub mod thread {
|
||||||
T: Send + 'static,
|
T: Send + 'static,
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
::std::thread::Builder::new()
|
::std::thread::Builder::new().name(name.into()).spawn(f).expect("thread spawn works")
|
||||||
.name(name.into())
|
|
||||||
.spawn(f)
|
|
||||||
.expect("thread spawn works")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use std::thread::*;
|
pub use std::thread::*;
|
||||||
|
@ -87,9 +84,9 @@ pub mod fmt {
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
pub fn start_daemon<I, S>(program: &str, args: I) -> io::Result<()>
|
pub fn start_daemon<I, S>(program: &str, args: I) -> io::Result<()>
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = S>,
|
I: IntoIterator<Item = S>,
|
||||||
S: AsRef<OsStr>,
|
S: AsRef<OsStr>,
|
||||||
{
|
{
|
||||||
Command::new(program)
|
Command::new(program)
|
||||||
.args(args)
|
.args(args)
|
||||||
|
@ -109,9 +106,9 @@ pub fn start_daemon<I, S>(program: &str, args: I) -> io::Result<()>
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub fn start_daemon<I, S>(program: &str, args: I) -> io::Result<()>
|
pub fn start_daemon<I, S>(program: &str, args: I) -> io::Result<()>
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = S>,
|
I: IntoIterator<Item = S>,
|
||||||
S: AsRef<OsStr>,
|
S: AsRef<OsStr>,
|
||||||
{
|
{
|
||||||
// Setting all the I/O handles to null and setting the
|
// Setting all the I/O handles to null and setting the
|
||||||
// CREATE_NEW_PROCESS_GROUP and CREATE_NO_WINDOW has the effect
|
// CREATE_NEW_PROCESS_GROUP and CREATE_NO_WINDOW has the effect
|
||||||
|
|
|
@ -15,17 +15,16 @@ use std::convert::From;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use crate::gl;
|
use crate::gl;
|
||||||
#[cfg(windows)]
|
|
||||||
use glutin::Icon;
|
|
||||||
#[cfg(windows)]
|
|
||||||
use image::ImageFormat;
|
|
||||||
use glutin::{
|
|
||||||
self, ContextBuilder, ControlFlow, Event, EventsLoop,
|
|
||||||
MouseCursor, WindowBuilder, ContextTrait
|
|
||||||
};
|
|
||||||
use glutin::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize};
|
use glutin::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize};
|
||||||
#[cfg(not(any(target_os = "macos", windows)))]
|
#[cfg(not(any(target_os = "macos", windows)))]
|
||||||
use glutin::os::unix::EventsLoopExt;
|
use glutin::os::unix::EventsLoopExt;
|
||||||
|
#[cfg(windows)]
|
||||||
|
use glutin::Icon;
|
||||||
|
use glutin::{
|
||||||
|
self, ContextBuilder, ContextTrait, ControlFlow, Event, EventsLoop, MouseCursor, WindowBuilder,
|
||||||
|
};
|
||||||
|
#[cfg(windows)]
|
||||||
|
use image::ImageFormat;
|
||||||
|
|
||||||
use crate::cli::Options;
|
use crate::cli::Options;
|
||||||
use crate::config::{Decorations, WindowConfig};
|
use crate::config::{Decorations, WindowConfig};
|
||||||
|
@ -160,12 +159,7 @@ impl Window {
|
||||||
// Set OpenGL symbol loader. This call MUST be after window.make_current on windows.
|
// Set OpenGL symbol loader. This call MUST be after window.make_current on windows.
|
||||||
gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
|
gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
|
||||||
|
|
||||||
let window = Window {
|
let window = Window { event_loop, window, mouse_visible: true, is_focused: false };
|
||||||
event_loop,
|
|
||||||
window,
|
|
||||||
mouse_visible: true,
|
|
||||||
is_focused: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
window.run_os_extensions();
|
window.run_os_extensions();
|
||||||
|
|
||||||
|
@ -177,9 +171,7 @@ impl Window {
|
||||||
/// Some window properties are provided since subsystems like font
|
/// Some window properties are provided since subsystems like font
|
||||||
/// rasterization depend on DPI and scale factor.
|
/// rasterization depend on DPI and scale factor.
|
||||||
pub fn device_properties(&self) -> DeviceProperties {
|
pub fn device_properties(&self) -> DeviceProperties {
|
||||||
DeviceProperties {
|
DeviceProperties { scale_factor: self.window.get_hidpi_factor() }
|
||||||
scale_factor: self.window.get_hidpi_factor(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inner_size_pixels(&self) -> Option<LogicalSize> {
|
pub fn inner_size_pixels(&self) -> Option<LogicalSize> {
|
||||||
|
@ -204,9 +196,7 @@ impl Window {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn create_window_proxy(&self) -> Proxy {
|
pub fn create_window_proxy(&self) -> Proxy {
|
||||||
Proxy {
|
Proxy { inner: self.event_loop.create_proxy() }
|
||||||
inner: self.event_loop.create_proxy(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -260,7 +250,7 @@ impl Window {
|
||||||
pub fn get_platform_window(
|
pub fn get_platform_window(
|
||||||
title: &str,
|
title: &str,
|
||||||
class: &str,
|
class: &str,
|
||||||
window_config: &WindowConfig
|
window_config: &WindowConfig,
|
||||||
) -> WindowBuilder {
|
) -> WindowBuilder {
|
||||||
use glutin::os::unix::WindowBuilderExt;
|
use glutin::os::unix::WindowBuilderExt;
|
||||||
|
|
||||||
|
@ -285,7 +275,7 @@ impl Window {
|
||||||
pub fn get_platform_window(
|
pub fn get_platform_window(
|
||||||
title: &str,
|
title: &str,
|
||||||
_class: &str,
|
_class: &str,
|
||||||
window_config: &WindowConfig
|
window_config: &WindowConfig,
|
||||||
) -> WindowBuilder {
|
) -> WindowBuilder {
|
||||||
let icon = Icon::from_bytes_with_format(WINDOW_ICON, ImageFormat::ICO).unwrap();
|
let icon = Icon::from_bytes_with_format(WINDOW_ICON, ImageFormat::ICO).unwrap();
|
||||||
|
|
||||||
|
@ -307,7 +297,7 @@ impl Window {
|
||||||
pub fn get_platform_window(
|
pub fn get_platform_window(
|
||||||
title: &str,
|
title: &str,
|
||||||
_class: &str,
|
_class: &str,
|
||||||
window_config: &WindowConfig
|
window_config: &WindowConfig,
|
||||||
) -> WindowBuilder {
|
) -> WindowBuilder {
|
||||||
use glutin::os::macos::WindowBuilderExt;
|
use glutin::os::macos::WindowBuilderExt;
|
||||||
|
|
||||||
|
@ -328,19 +318,16 @@ impl Window {
|
||||||
.with_titlebar_buttons_hidden(true)
|
.with_titlebar_buttons_hidden(true)
|
||||||
.with_titlebar_transparent(true)
|
.with_titlebar_transparent(true)
|
||||||
.with_fullsize_content_view(true),
|
.with_fullsize_content_view(true),
|
||||||
Decorations::None => window
|
Decorations::None => window.with_titlebar_hidden(true),
|
||||||
.with_titlebar_hidden(true),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(
|
#[cfg(any(
|
||||||
any(
|
target_os = "linux",
|
||||||
target_os = "linux",
|
target_os = "freebsd",
|
||||||
target_os = "freebsd",
|
target_os = "dragonfly",
|
||||||
target_os = "dragonfly",
|
target_os = "openbsd"
|
||||||
target_os = "openbsd"
|
))]
|
||||||
)
|
|
||||||
)]
|
|
||||||
pub fn set_urgent(&self, is_urgent: bool) {
|
pub fn set_urgent(&self, is_urgent: bool) {
|
||||||
use glutin::os::unix::WindowExt;
|
use glutin::os::unix::WindowExt;
|
||||||
self.window.set_urgent(is_urgent);
|
self.window.set_urgent(is_urgent);
|
||||||
|
@ -384,21 +371,20 @@ pub trait OsExtensions {
|
||||||
fn run_os_extensions(&self) {}
|
fn run_os_extensions(&self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(
|
#[cfg(not(any(
|
||||||
not(
|
target_os = "linux",
|
||||||
any(
|
target_os = "freebsd",
|
||||||
target_os = "linux",
|
target_os = "dragonfly",
|
||||||
target_os = "freebsd",
|
target_os = "openbsd"
|
||||||
target_os = "dragonfly",
|
)))]
|
||||||
target_os = "openbsd"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)]
|
|
||||||
impl OsExtensions for Window {}
|
impl OsExtensions for Window {}
|
||||||
|
|
||||||
#[cfg(
|
#[cfg(any(
|
||||||
any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd")
|
target_os = "linux",
|
||||||
)]
|
target_os = "freebsd",
|
||||||
|
target_os = "dragonfly",
|
||||||
|
target_os = "openbsd"
|
||||||
|
))]
|
||||||
impl OsExtensions for Window {
|
impl OsExtensions for Window {
|
||||||
fn run_os_extensions(&self) {
|
fn run_os_extensions(&self) {
|
||||||
use glutin::os::unix::WindowExt;
|
use glutin::os::unix::WindowExt;
|
||||||
|
|
28
tests/ref.rs
28
tests/ref.rs
|
@ -6,15 +6,15 @@ use std::fs::File;
|
||||||
use std::io::{self, Read};
|
use std::io::{self, Read};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use alacritty::Grid;
|
|
||||||
use alacritty::Term;
|
|
||||||
use alacritty::ansi;
|
use alacritty::ansi;
|
||||||
|
use alacritty::config::Config;
|
||||||
use alacritty::index::Column;
|
use alacritty::index::Column;
|
||||||
|
use alacritty::message_bar::MessageBuffer;
|
||||||
use alacritty::term::cell::Cell;
|
use alacritty::term::cell::Cell;
|
||||||
use alacritty::term::SizeInfo;
|
use alacritty::term::SizeInfo;
|
||||||
use alacritty::util::fmt::{Red, Green};
|
use alacritty::util::fmt::{Green, Red};
|
||||||
use alacritty::config::Config;
|
use alacritty::Grid;
|
||||||
use alacritty::message_bar::MessageBuffer;
|
use alacritty::Term;
|
||||||
|
|
||||||
macro_rules! ref_tests {
|
macro_rules! ref_tests {
|
||||||
($($name:ident)*) => {
|
($($name:ident)*) => {
|
||||||
|
@ -55,17 +55,18 @@ ref_tests! {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_u8<P>(path: P) -> Vec<u8>
|
fn read_u8<P>(path: P) -> Vec<u8>
|
||||||
where P: AsRef<Path>
|
where
|
||||||
|
P: AsRef<Path>,
|
||||||
{
|
{
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
File::open(path.as_ref()).unwrap()
|
File::open(path.as_ref()).unwrap().read_to_end(&mut res).unwrap();
|
||||||
.read_to_end(&mut res).unwrap();
|
|
||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_string<P>(path: P) -> Result<String, ::std::io::Error>
|
fn read_string<P>(path: P) -> Result<String, ::std::io::Error>
|
||||||
where P: AsRef<Path>
|
where
|
||||||
|
P: AsRef<Path>,
|
||||||
{
|
{
|
||||||
let mut res = String::new();
|
let mut res = String::new();
|
||||||
File::open(path.as_ref()).and_then(|mut f| f.read_to_string(&mut res))?;
|
File::open(path.as_ref()).and_then(|mut f| f.read_to_string(&mut res))?;
|
||||||
|
@ -109,8 +110,13 @@ fn ref_test(dir: &Path) {
|
||||||
let cell = term_grid[i][Column(j)];
|
let cell = term_grid[i][Column(j)];
|
||||||
let original_cell = grid[i][Column(j)];
|
let original_cell = grid[i][Column(j)];
|
||||||
if original_cell != cell {
|
if original_cell != cell {
|
||||||
println!("[{i}][{j}] {original:?} => {now:?}",
|
println!(
|
||||||
i=i, j=j, original=Green(original_cell), now=Red(cell));
|
"[{i}][{j}] {original:?} => {now:?}",
|
||||||
|
i = i,
|
||||||
|
j = j,
|
||||||
|
original = Green(original_cell),
|
||||||
|
now = Red(cell)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,10 @@ fn main() {
|
||||||
// The working directory for `cargo test` is in the deps folder, not the debug/release root
|
// The working directory for `cargo test` is in the deps folder, not the debug/release root
|
||||||
if cfg!(test) && Path::new("target").exists() {
|
if cfg!(test) && Path::new("target").exists() {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
copy("../assets/windows/x86_64/winpty-agent.exe", "target/debug/deps/winpty-agent.exe").unwrap();
|
copy("../assets/windows/x86_64/winpty-agent.exe", "target/debug/deps/winpty-agent.exe")
|
||||||
|
.unwrap();
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
copy("../assets/windows/x86_64/winpty-agent.exe", "target/release/deps/winpty-agent.exe").unwrap();
|
copy("../assets/windows/x86_64/winpty-agent.exe", "target/release/deps/winpty-agent.exe")
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::result::Result;
|
|
||||||
use std::os::windows::io::RawHandle;
|
use std::os::windows::io::RawHandle;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::ptr::{null, null_mut};
|
use std::ptr::{null, null_mut};
|
||||||
|
use std::result::Result;
|
||||||
|
|
||||||
use winpty_sys::*;
|
use winpty_sys::*;
|
||||||
|
|
||||||
|
@ -166,10 +166,7 @@ impl<'a, 'b> Winpty<'a> {
|
||||||
pub fn conin_name(&mut self) -> PathBuf {
|
pub fn conin_name(&mut self) -> PathBuf {
|
||||||
unsafe {
|
unsafe {
|
||||||
let raw = winpty_conin_name(self.0);
|
let raw = winpty_conin_name(self.0);
|
||||||
PathBuf::from(&String::from_utf16_lossy(std::slice::from_raw_parts(
|
PathBuf::from(&String::from_utf16_lossy(std::slice::from_raw_parts(raw, wcslen(raw))))
|
||||||
raw,
|
|
||||||
wcslen(raw),
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,10 +175,7 @@ impl<'a, 'b> Winpty<'a> {
|
||||||
pub fn conout_name(&mut self) -> PathBuf {
|
pub fn conout_name(&mut self) -> PathBuf {
|
||||||
unsafe {
|
unsafe {
|
||||||
let raw = winpty_conout_name(self.0);
|
let raw = winpty_conout_name(self.0);
|
||||||
PathBuf::from(&String::from_utf16_lossy(std::slice::from_raw_parts(
|
PathBuf::from(&String::from_utf16_lossy(std::slice::from_raw_parts(raw, wcslen(raw))))
|
||||||
raw,
|
|
||||||
wcslen(raw),
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,10 +185,7 @@ impl<'a, 'b> Winpty<'a> {
|
||||||
pub fn conerr_name(&mut self) -> PathBuf {
|
pub fn conerr_name(&mut self) -> PathBuf {
|
||||||
unsafe {
|
unsafe {
|
||||||
let raw = winpty_conerr_name(self.0);
|
let raw = winpty_conerr_name(self.0);
|
||||||
PathBuf::from(&String::from_utf16_lossy(std::slice::from_raw_parts(
|
PathBuf::from(&String::from_utf16_lossy(std::slice::from_raw_parts(raw, wcslen(raw))))
|
||||||
raw,
|
|
||||||
wcslen(raw),
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +218,12 @@ impl<'a, 'b> Winpty<'a> {
|
||||||
let mut process_list = Vec::with_capacity(count);
|
let mut process_list = Vec::with_capacity(count);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let len = winpty_get_console_process_list(self.0, process_list.as_mut_ptr(), count as i32, &mut err) as usize;
|
let len = winpty_get_console_process_list(
|
||||||
|
self.0,
|
||||||
|
process_list.as_mut_ptr(),
|
||||||
|
count as i32,
|
||||||
|
&mut err,
|
||||||
|
) as usize;
|
||||||
process_list.set_len(len);
|
process_list.set_len(len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,10 +242,7 @@ impl<'a, 'b> Winpty<'a> {
|
||||||
/// (https://blogs.msdn.microsoft.com/oldnewthing/20110107-00/?p=11803)
|
/// (https://blogs.msdn.microsoft.com/oldnewthing/20110107-00/?p=11803)
|
||||||
// TODO: Support getting the process and thread handle of the spawned process (Not the agent)
|
// TODO: Support getting the process and thread handle of the spawned process (Not the agent)
|
||||||
// TODO: Support returning the error from CreateProcess
|
// TODO: Support returning the error from CreateProcess
|
||||||
pub fn spawn(
|
pub fn spawn(&mut self, cfg: &SpawnConfig) -> Result<(), Err> {
|
||||||
&mut self,
|
|
||||||
cfg: &SpawnConfig,
|
|
||||||
) -> Result<(), Err> {
|
|
||||||
let mut err = null_mut() as *mut winpty_error_t;
|
let mut err = null_mut() as *mut winpty_error_t;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -261,7 +254,9 @@ impl<'a, 'b> Winpty<'a> {
|
||||||
null_mut(), // Create process error
|
null_mut(), // Create process error
|
||||||
&mut err,
|
&mut err,
|
||||||
);
|
);
|
||||||
if ok == 0 { return Ok(());}
|
if ok == 0 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(err) = check_err(err) {
|
if let Some(err) = check_err(err) {
|
||||||
|
@ -354,80 +349,70 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
// Test that we can start a process in winpty
|
// Test that we can start a process in winpty
|
||||||
fn spawn_process() {
|
fn spawn_process() {
|
||||||
let mut winpty = Winpty::open(
|
let mut winpty =
|
||||||
&Config::new(ConfigFlags::empty()).expect("failed to create config")
|
Winpty::open(&Config::new(ConfigFlags::empty()).expect("failed to create config"))
|
||||||
).expect("failed to create winpty instance");
|
.expect("failed to create winpty instance");
|
||||||
|
|
||||||
winpty.spawn(
|
winpty
|
||||||
&SpawnConfig::new(
|
.spawn(
|
||||||
SpawnFlags::empty(),
|
&SpawnConfig::new(SpawnFlags::empty(), None, Some("cmd"), None, None)
|
||||||
None,
|
.expect("failed to create spawn config"),
|
||||||
Some("cmd"),
|
)
|
||||||
None,
|
.unwrap();
|
||||||
None
|
|
||||||
).expect("failed to create spawn config")
|
|
||||||
).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
// Test that pipes connected before winpty is spawned can be connected to
|
// Test that pipes connected before winpty is spawned can be connected to
|
||||||
fn valid_pipe_connect_before() {
|
fn valid_pipe_connect_before() {
|
||||||
let mut winpty = Winpty::open(
|
let mut winpty =
|
||||||
&Config::new(ConfigFlags::empty()).expect("failed to create config")
|
Winpty::open(&Config::new(ConfigFlags::empty()).expect("failed to create config"))
|
||||||
).expect("failed to create winpty instance");
|
.expect("failed to create winpty instance");
|
||||||
|
|
||||||
// Check we can connect to both pipes
|
// Check we can connect to both pipes
|
||||||
PipeClient::connect_ms(winpty.conout_name(), 1000).expect("failed to connect to conout pipe");
|
PipeClient::connect_ms(winpty.conout_name(), 1000)
|
||||||
|
.expect("failed to connect to conout pipe");
|
||||||
PipeClient::connect_ms(winpty.conin_name(), 1000).expect("failed to connect to conin pipe");
|
PipeClient::connect_ms(winpty.conin_name(), 1000).expect("failed to connect to conin pipe");
|
||||||
|
|
||||||
winpty.spawn(
|
winpty
|
||||||
&SpawnConfig::new(
|
.spawn(
|
||||||
SpawnFlags::empty(),
|
&SpawnConfig::new(SpawnFlags::empty(), None, Some("cmd"), None, None)
|
||||||
None,
|
.expect("failed to create spawn config"),
|
||||||
Some("cmd"),
|
)
|
||||||
None,
|
.unwrap();
|
||||||
None
|
|
||||||
).expect("failed to create spawn config")
|
|
||||||
).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
// Test that pipes connected after winpty is spawned can be connected to
|
// Test that pipes connected after winpty is spawned can be connected to
|
||||||
fn valid_pipe_connect_after() {
|
fn valid_pipe_connect_after() {
|
||||||
let mut winpty = Winpty::open(
|
let mut winpty =
|
||||||
&Config::new(ConfigFlags::empty()).expect("failed to create config")
|
Winpty::open(&Config::new(ConfigFlags::empty()).expect("failed to create config"))
|
||||||
).expect("failed to create winpty instance");
|
.expect("failed to create winpty instance");
|
||||||
|
|
||||||
winpty.spawn(
|
winpty
|
||||||
&SpawnConfig::new(
|
.spawn(
|
||||||
SpawnFlags::empty(),
|
&SpawnConfig::new(SpawnFlags::empty(), None, Some("cmd"), None, None)
|
||||||
None,
|
.expect("failed to create spawn config"),
|
||||||
Some("cmd"),
|
)
|
||||||
None,
|
.unwrap();
|
||||||
None
|
|
||||||
).expect("failed to create spawn config")
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
// Check we can connect to both pipes
|
// Check we can connect to both pipes
|
||||||
PipeClient::connect_ms(winpty.conout_name(), 1000).expect("failed to connect to conout pipe");
|
PipeClient::connect_ms(winpty.conout_name(), 1000)
|
||||||
|
.expect("failed to connect to conout pipe");
|
||||||
PipeClient::connect_ms(winpty.conin_name(), 1000).expect("failed to connect to conin pipe");
|
PipeClient::connect_ms(winpty.conin_name(), 1000).expect("failed to connect to conin pipe");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn resize() {
|
fn resize() {
|
||||||
let mut winpty = Winpty::open(
|
let mut winpty =
|
||||||
&Config::new(ConfigFlags::empty()).expect("failed to create config")
|
Winpty::open(&Config::new(ConfigFlags::empty()).expect("failed to create config"))
|
||||||
).expect("failed to create winpty instance");
|
.expect("failed to create winpty instance");
|
||||||
|
|
||||||
winpty.spawn(
|
winpty
|
||||||
&SpawnConfig::new(
|
.spawn(
|
||||||
SpawnFlags::empty(),
|
&SpawnConfig::new(SpawnFlags::empty(), None, Some("cmd"), None, None)
|
||||||
None,
|
.expect("failed to create spawn config"),
|
||||||
Some("cmd"),
|
)
|
||||||
None,
|
.unwrap();
|
||||||
None
|
|
||||||
).expect("failed to create spawn config")
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
winpty.set_size(1, 1).unwrap();
|
winpty.set_size(1, 1).unwrap();
|
||||||
}
|
}
|
||||||
|
@ -436,21 +421,19 @@ mod tests {
|
||||||
#[ignore]
|
#[ignore]
|
||||||
// Test that each id returned by cosole_process_list points to an actual process
|
// Test that each id returned by cosole_process_list points to an actual process
|
||||||
fn console_process_list_valid() {
|
fn console_process_list_valid() {
|
||||||
let mut winpty = Winpty::open(
|
let mut winpty =
|
||||||
&Config::new(ConfigFlags::empty()).expect("failed to create config")
|
Winpty::open(&Config::new(ConfigFlags::empty()).expect("failed to create config"))
|
||||||
).expect("failed to create winpty instance");
|
.expect("failed to create winpty instance");
|
||||||
|
|
||||||
winpty.spawn(
|
winpty
|
||||||
&SpawnConfig::new(
|
.spawn(
|
||||||
SpawnFlags::empty(),
|
&SpawnConfig::new(SpawnFlags::empty(), None, Some("cmd"), None, None)
|
||||||
None,
|
.expect("failed to create spawn config"),
|
||||||
Some("cmd"),
|
)
|
||||||
None,
|
.unwrap();
|
||||||
None
|
|
||||||
).expect("failed to create spawn config")
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
let processes = winpty.console_process_list(1000).expect("failed to get console process list");
|
let processes =
|
||||||
|
winpty.console_process_list(1000).expect("failed to get console process list");
|
||||||
|
|
||||||
// Check that each id is valid
|
// Check that each id is valid
|
||||||
processes.iter().for_each(|id| {
|
processes.iter().for_each(|id| {
|
||||||
|
@ -458,7 +441,7 @@ mod tests {
|
||||||
OpenProcess(
|
OpenProcess(
|
||||||
READ_CONTROL, // permissions
|
READ_CONTROL, // permissions
|
||||||
false as i32, // inheret
|
false as i32, // inheret
|
||||||
*id as u32
|
*id as u32,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
assert!(!handle.is_null());
|
assert!(!handle.is_null());
|
||||||
|
|
Loading…
Reference in New Issue