This commit is contained in:
Michael Zhang 2023-01-07 18:31:39 -06:00
parent 2997304edc
commit 9108e7d28d
9 changed files with 377 additions and 359 deletions

158
Cargo.lock generated
View file

@ -13,9 +13,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.57"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
[[package]]
name = "asciinema"
@ -43,7 +43,7 @@ version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"hermit-abi 0.1.19",
"libc",
"winapi",
]
@ -68,9 +68,9 @@ checksum = "e0dcbc35f504eb6fc275a6d20e4ebcda18cf50d40ba6fabff8c711fa16cb3b16"
[[package]]
name = "cc"
version = "1.0.73"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
[[package]]
name = "cfg-if"
@ -95,9 +95,9 @@ dependencies = [
[[package]]
name = "futures"
version = "0.3.21"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
dependencies = [
"futures-channel",
"futures-core",
@ -110,9 +110,9 @@ dependencies = [
[[package]]
name = "futures-channel"
version = "0.3.21"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
dependencies = [
"futures-core",
"futures-sink",
@ -120,15 +120,15 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.21"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
[[package]]
name = "futures-executor"
version = "0.3.21"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
dependencies = [
"futures-core",
"futures-task",
@ -137,15 +137,15 @@ dependencies = [
[[package]]
name = "futures-io"
version = "0.3.21"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
[[package]]
name = "futures-macro"
version = "0.3.21"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
dependencies = [
"proc-macro2",
"quote",
@ -154,21 +154,21 @@ dependencies = [
[[package]]
name = "futures-sink"
version = "0.3.21"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
[[package]]
name = "futures-task"
version = "0.3.21"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
[[package]]
name = "futures-util"
version = "0.3.21"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
dependencies = [
"futures-channel",
"futures-core",
@ -200,6 +200,15 @@ dependencies = [
"libc",
]
[[package]]
name = "hermit-abi"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
dependencies = [
"libc",
]
[[package]]
name = "instant"
version = "0.1.12"
@ -211,9 +220,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.1"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[package]]
name = "lazy_static"
@ -223,15 +232,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.125"
version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "lock_api"
version = "0.4.7"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
dependencies = [
"autocfg",
"scopeguard",
@ -297,19 +306,19 @@ dependencies = [
[[package]]
name = "num_cpus"
version = "1.13.1"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [
"hermit-abi",
"hermit-abi 0.2.6",
"libc",
]
[[package]]
name = "once_cell"
version = "1.10.0"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
[[package]]
name = "parking_lot"
@ -324,9 +333,9 @@ dependencies = [
[[package]]
name = "parking_lot_core"
version = "0.8.5"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
dependencies = [
"cfg-if",
"instant",
@ -374,36 +383,36 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.38"
version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa"
checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
dependencies = [
"unicode-xid",
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.18"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.2.13"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags",
]
[[package]]
name = "ryu"
version = "1.0.9"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
[[package]]
name = "scopeguard"
@ -413,15 +422,15 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.137"
version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
[[package]]
name = "serde_derive"
version = "1.0.137"
version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
dependencies = [
"proc-macro2",
"quote",
@ -430,9 +439,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.81"
version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
dependencies = [
"itoa",
"ryu",
@ -460,15 +469,18 @@ dependencies = [
[[package]]
name = "slab"
version = "0.4.6"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
dependencies = [
"autocfg",
]
[[package]]
name = "smallvec"
version = "1.8.0"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "strsim"
@ -502,13 +514,13 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.92"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
"unicode-ident",
]
[[package]]
@ -531,18 +543,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.31"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.31"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2",
"quote",
@ -597,22 +609,22 @@ dependencies = [
]
[[package]]
name = "unicode-segmentation"
version = "1.9.0"
name = "unicode-ident"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "unicode-segmentation"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
[[package]]
name = "unicode-width"
version = "0.1.9"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "vec_map"

View file

@ -2,9 +2,7 @@
"nodes": {
"fenix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"nixpkgs": "nixpkgs",
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
@ -36,6 +34,22 @@
}
},
"nixpkgs": {
"locked": {
"lastModified": 1672953546,
"narHash": "sha256-oz757DnJ1ITvwyTovuwG3l9cX6j9j6/DH9eH+cXFJmc=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "a518c77148585023ff56022f09c4b2c418a51ef5",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1667629849,
"narHash": "sha256-P+v+nDOFWicM4wziFK9S/ajF2lc0N2Rg9p6Y35uMoZI=",
@ -53,7 +67,7 @@
"inputs": {
"fenix": "fenix",
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
"nixpkgs": "nixpkgs_2"
}
},
"rust-analyzer-src": {

View file

@ -1,8 +1,5 @@
{
inputs = {
fenix.url = "github:nix-community/fenix";
fenix.inputs.nixpkgs.follows = "nixpkgs";
};
inputs = { fenix.url = "github:nix-community/fenix"; };
outputs = { self, nixpkgs, flake-utils, fenix }:
flake-utils.lib.eachDefaultSystem (system:
@ -12,7 +9,7 @@
overlays = [ fenix.overlays.default ];
};
toolchain = pkgs.fenix.default;
toolchain = pkgs.fenix.stable;
flakePkgs = {
asciinema = pkgs.callPackage ./. { inherit toolchain; };
@ -23,9 +20,9 @@
devShell = pkgs.mkShell {
inputsFrom = with packages; [ asciinema ];
packages =
(with pkgs; [ cargo-watch cargo-deny cargo-edit sqlx-cli sqlite ])
++ (with toolchain; [ cargo rustc ]);
packages = (with pkgs; [ cargo-watch cargo-deny cargo-edit ])
++ (with toolchain; [ cargo rustc rustfmt ]);
CARGO_UNSTABLE_SPARSE_REGISTRY = "true";
};
});
}

View file

@ -4,66 +4,66 @@ use serde::ser::{Serialize, SerializeSeq, Serializer};
#[derive(Serialize)]
pub struct Header {
version: u32,
width: u32,
height: u32,
version: u32,
width: u32,
height: u32,
timestamp: Option<u32>,
duration: Option<f64>,
idle_time_limit: Option<f64>,
command: Option<String>,
title: Option<String>,
env: Option<HashMap<String, String>>,
theme: Option<Theme>,
timestamp: Option<u32>,
duration: Option<f64>,
idle_time_limit: Option<f64>,
command: Option<String>,
title: Option<String>,
env: Option<HashMap<String, String>>,
theme: Option<Theme>,
}
#[derive(Serialize)]
pub struct Theme {
fg: String,
bg: String,
palette: String,
fg: String,
bg: String,
palette: String,
}
pub struct Event<'a>(pub f64, pub EventKind<'a>);
pub enum EventKind<'a> {
Output(&'a [u8]),
Input(&'a [u8]),
Output(&'a [u8]),
Input(&'a [u8]),
}
impl<'a> Serialize for Event<'a> {
fn serialize<S: Serializer>(
&self,
s: S,
) -> std::result::Result<S::Ok, S::Error> {
let mut seq = s.serialize_seq(Some(3))?;
seq.serialize_element(&self.0)?;
match &self.1 {
EventKind::Output(s) => {
seq.serialize_element("o")?;
seq.serialize_element(std::str::from_utf8(s).unwrap())?;
}
EventKind::Input(s) => {
seq.serialize_element("i")?;
seq.serialize_element(std::str::from_utf8(s).unwrap())?;
}
}
seq.end()
fn serialize<S: Serializer>(
&self,
s: S,
) -> std::result::Result<S::Ok, S::Error> {
let mut seq = s.serialize_seq(Some(3))?;
seq.serialize_element(&self.0)?;
match &self.1 {
EventKind::Output(s) => {
seq.serialize_element("o")?;
seq.serialize_element(std::str::from_utf8(s).unwrap())?;
}
EventKind::Input(s) => {
seq.serialize_element("i")?;
seq.serialize_element(std::str::from_utf8(s).unwrap())?;
}
}
seq.end()
}
}
fn build_header(width: u32, height: u32) -> Header {
Header {
version: 2,
width,
height,
Header {
version: 2,
width,
height,
timestamp: None,
duration: None,
idle_time_limit: None,
command: None,
title: None,
env: None,
theme: None,
}
timestamp: None,
duration: None,
idle_time_limit: None,
command: None,
title: None,
env: None,
theme: None,
}
}

View file

@ -2,15 +2,15 @@ pub type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug, Error)]
pub enum Error {
#[error("generic nix error: {0}")]
Nix(#[from] nix::Error),
#[error("generic nix error: {0}")]
Nix(#[from] nix::Error),
#[error("write(3) error (fd={1}, data={2:?}): {0}")]
NixWrite(nix::Error, i32, Vec<u8>),
#[error("write(3) error (fd={1}, data={2:?}): {0}")]
NixWrite(nix::Error, i32, Vec<u8>),
#[error("generic io error: {0}")]
Io(#[from] std::io::Error),
#[error("generic io error: {0}")]
Io(#[from] std::io::Error),
#[error("generic serde_json error: {0}")]
SerdeJson(#[from] serde_json::Error),
#[error("generic serde_json error: {0}")]
SerdeJson(#[from] serde_json::Error),
}

View file

@ -19,22 +19,22 @@ use crate::writer::FileWriter;
#[derive(StructOpt)]
enum Command {
/// Record terminal session.
#[structopt(name = "rec")]
Record,
/// Record terminal session.
#[structopt(name = "rec")]
Record,
}
fn record() -> Result<()> {
let file = File::create("output.cast")?;
pty::record(&["sh", "-c", "/usr/bin/zsh"], FileWriter::new(file))?;
Ok(())
let file = File::create("output.cast")?;
pty::record(&["sh", "-c", "/usr/bin/zsh"], FileWriter::new(file))?;
Ok(())
}
fn main() -> Result<()> {
match Command::from_args() {
Command::Record => {
record()?;
}
match Command::from_args() {
Command::Record => {
record()?;
}
Ok(())
}
Ok(())
}

View file

@ -5,15 +5,15 @@ use std::os::unix::io::RawFd;
use std::time::Instant;
use nix::{
fcntl::{fcntl, FcntlArg, OFlag},
ioctl_write_buf,
pty::{forkpty, Winsize},
sys::{
select::{select, FdSet},
termios::{tcsetattr, SetArg, Termios},
wait::waitpid,
},
unistd::{execvpe, isatty, pipe, read, write, ForkResult},
fcntl::{fcntl, FcntlArg, OFlag},
ioctl_write_buf,
pty::{forkpty, Winsize},
sys::{
select::{select, FdSet},
termios::{tcsetattr, SetArg, Termios},
wait::waitpid,
},
unistd::{execvpe, isatty, pipe, read, write, ForkResult},
};
use signal_hook::SigId;
@ -24,181 +24,180 @@ const STDIN_FILENO: RawFd = 0;
const STDOUT_FILENO: RawFd = 1;
pub fn record(args: &[&str], writer: impl Writer + Send) -> Result<()> {
let forkpty_result = forkpty(None, None)?;
let master_fd = forkpty_result.master;
let start_time = Instant::now();
let forkpty_result = forkpty(None, None)?;
let master_fd = forkpty_result.master;
let start_time = Instant::now();
let set_pty_size = || -> Result<()> {
ioctl_write_buf!(helper_write, libc::TIOCGWINSZ, 104, Winsize);
let winsize = Winsize {
ws_row: 24,
ws_col: 80,
ws_xpixel: 0,
ws_ypixel: 0,
};
if isatty(STDOUT_FILENO)? {}
unsafe { helper_write(master_fd, &[winsize]) }?;
Ok(())
let _set_pty_size = || -> Result<()> {
ioctl_write_buf!(helper_write, libc::TIOCGWINSZ, 104, Winsize);
let winsize = Winsize {
ws_row: 24,
ws_col: 80,
ws_xpixel: 0,
ws_ypixel: 0,
};
let write_stdout = |data: &[u8]| -> Result<()> {
write(STDOUT_FILENO, &data)?;
Ok(())
};
let mut master_writer = writer.clone();
let mut handle_master_read = move |data: &[u8]| -> Result<()> {
let elapsed = (Instant::now() - start_time).as_secs_f64();
master_writer.write_stdout(elapsed, data)?;
write_stdout(data)?;
Ok(())
};
let write_master = |data: &[u8]| -> Result<()> {
let mut offset = 0;
while offset < data.len() {
let len = write(master_fd, data).map_err(|err| {
Error::NixWrite(err, master_fd, data.to_vec())
})?;
offset += len;
}
Ok(())
};
let mut stdin_writer = writer.clone();
let mut handle_stdin_read = |data: &[u8]| -> Result<()> {
write_master(data)?;
let elapsed = (Instant::now() - start_time).as_secs_f64();
stdin_writer.write_stdin(elapsed, data)?;
Ok(())
};
let mut copy = |signal_fd: RawFd| -> Result<()> {
let mut fdset = FdSet::new();
let mut buf = [0; 1024];
loop {
fdset.clear();
fdset.insert(master_fd);
fdset.insert(STDIN_FILENO);
fdset.insert(signal_fd);
select(None, &mut fdset, None, None, None)?;
if fdset.contains(master_fd) {
let len = read(master_fd, &mut buf)?;
if len == 0 {
fdset.remove(master_fd);
} else {
handle_master_read(&buf[..len])?;
}
} else if fdset.contains(STDIN_FILENO) {
let len = read(STDIN_FILENO, &mut buf)?;
if len == 0 {
fdset.remove(STDIN_FILENO);
} else {
handle_stdin_read(&buf[..len])?;
}
} else if fdset.contains(signal_fd) {
// TODO
}
}
};
let child_pid = match forkpty_result.fork_result {
ForkResult::Parent { child } => child,
ForkResult::Child => {
let cstr_args: Vec<_> = args
.into_iter()
.map(|s| CString::new(*s).unwrap())
.collect();
let args: Vec<_> = cstr_args.iter().map(|s| s.as_ref()).collect();
let cstr_env: Vec<_> = env::vars()
.map(|(k, v)| CString::new(format!("{}={}", k, v)).unwrap())
.collect();
let env: Vec<_> = cstr_env.iter().map(|s| s.as_ref()).collect();
execvpe(&args[0], &args, &env)?;
unreachable!();
}
};
let (pipe_r, pipe_w) = pipe()?;
let mut flags = fcntl(pipe_w, FcntlArg::F_GETFL)?;
flags |= libc::O_NONBLOCK;
fcntl(pipe_w, FcntlArg::F_SETFL(OFlag::from_bits(flags).unwrap()))?;
let old_handlers = set_signals(
[
signal_hook::SIGWINCH,
signal_hook::SIGCHLD,
signal_hook::SIGHUP,
signal_hook::SIGTERM,
signal_hook::SIGQUIT,
]
.iter()
.map(|sig| (*sig, move || eprintln!("HIT SIGNAL {:?}", *sig))),
)?;
// set_pty_size()?;
{
let _term = RawTerm::init(STDIN_FILENO)?;
copy(pipe_r)?;
}
unset_signals(old_handlers)?;
waitpid(child_pid, None)?;
if isatty(STDOUT_FILENO)? {}
unsafe { helper_write(master_fd, &[winsize]) }?;
Ok(())
};
let write_stdout = |data: &[u8]| -> Result<()> {
write(STDOUT_FILENO, &data)?;
Ok(())
};
let mut master_writer = writer.clone();
let mut handle_master_read = move |data: &[u8]| -> Result<()> {
let elapsed = (Instant::now() - start_time).as_secs_f64();
master_writer.write_stdout(elapsed, data)?;
write_stdout(data)?;
Ok(())
};
let write_master = |data: &[u8]| -> Result<()> {
let mut offset = 0;
while offset < data.len() {
let len = write(master_fd, data)
.map_err(|err| Error::NixWrite(err, master_fd, data.to_vec()))?;
offset += len;
}
Ok(())
};
let mut stdin_writer = writer.clone();
let mut handle_stdin_read = |data: &[u8]| -> Result<()> {
write_master(data)?;
let elapsed = (Instant::now() - start_time).as_secs_f64();
stdin_writer.write_stdin(elapsed, data)?;
Ok(())
};
let mut copy = |signal_fd: RawFd| -> Result<()> {
let mut fdset = FdSet::new();
let mut buf = [0; 1024];
loop {
fdset.clear();
fdset.insert(master_fd);
fdset.insert(STDIN_FILENO);
fdset.insert(signal_fd);
select(None, &mut fdset, None, None, None)?;
if fdset.contains(master_fd) {
let len = read(master_fd, &mut buf)?;
if len == 0 {
fdset.remove(master_fd);
} else {
handle_master_read(&buf[..len])?;
}
} else if fdset.contains(STDIN_FILENO) {
let len = read(STDIN_FILENO, &mut buf)?;
if len == 0 {
fdset.remove(STDIN_FILENO);
} else {
handle_stdin_read(&buf[..len])?;
}
} else if fdset.contains(signal_fd) {
// TODO
}
}
};
let child_pid = match forkpty_result.fork_result {
ForkResult::Parent { child } => child,
ForkResult::Child => {
let cstr_args: Vec<_> = args
.into_iter()
.map(|s| CString::new(*s).unwrap())
.collect();
let args: Vec<_> = cstr_args.iter().map(|s| s.as_ref()).collect();
let cstr_env: Vec<_> = env::vars()
.map(|(k, v)| CString::new(format!("{}={}", k, v)).unwrap())
.collect();
let env: Vec<_> = cstr_env.iter().map(|s| s.as_ref()).collect();
execvpe(&args[0], &args, &env)?;
unreachable!();
}
};
let (pipe_r, pipe_w) = pipe()?;
let mut flags = fcntl(pipe_w, FcntlArg::F_GETFL)?;
flags |= libc::O_NONBLOCK;
fcntl(pipe_w, FcntlArg::F_SETFL(OFlag::from_bits(flags).unwrap()))?;
let old_handlers = set_signals(
[
signal_hook::SIGWINCH,
signal_hook::SIGCHLD,
signal_hook::SIGHUP,
signal_hook::SIGTERM,
signal_hook::SIGQUIT,
]
.iter()
.map(|sig| (*sig, move || eprintln!("HIT SIGNAL {:?}", *sig))),
)?;
// set_pty_size()?;
{
let _term = RawTerm::init(STDIN_FILENO)?;
copy(pipe_r)?;
}
unset_signals(old_handlers)?;
waitpid(child_pid, None)?;
Ok(())
}
fn set_signals<I, F>(signals_list: I) -> Result<Vec<SigId>, io::Error>
where
I: Iterator<Item = (i32, F)>,
F: Fn() + Sync + Send + 'static,
I: Iterator<Item = (i32, F)>,
F: Fn() + Sync + Send + 'static,
{
let mut old_handlers = Vec::new();
for (sig, handler) in signals_list {
old_handlers.push(unsafe { signal_hook::register(sig, handler) }?);
}
Ok(old_handlers)
let mut old_handlers = Vec::new();
for (sig, handler) in signals_list {
old_handlers.push(unsafe { signal_hook::register(sig, handler) }?);
}
Ok(old_handlers)
}
fn unset_signals(handlers_list: Vec<SigId>) -> Result<(), io::Error> {
for handler in handlers_list {
signal_hook::unregister(handler);
}
Ok(())
for handler in handlers_list {
signal_hook::unregister(handler);
}
Ok(())
}
struct RawTerm(RawFd, Termios);
impl RawTerm {
pub fn init(fd: RawFd) -> Result<Self> {
use nix::sys::termios::*;
let saved_mode = tcgetattr(fd)?;
let mut mode = saved_mode.clone();
mode.input_flags &= !(InputFlags::BRKINT
| InputFlags::ICRNL
| InputFlags::INPCK
| InputFlags::ISTRIP
| InputFlags::IXON);
mode.output_flags &= !OutputFlags::OPOST;
mode.control_flags &= !(ControlFlags::CSIZE | ControlFlags::PARENB);
mode.control_flags |= ControlFlags::CS8;
mode.local_flags &= !(LocalFlags::ECHO
| LocalFlags::ICANON
| LocalFlags::IEXTEN
| LocalFlags::ISIG);
mode.control_chars[libc::VMIN] = 1;
mode.control_chars[libc::VTIME] = 0;
tcsetattr(fd, SetArg::TCSAFLUSH, &mode)?;
Ok(RawTerm(fd, saved_mode))
}
pub fn init(fd: RawFd) -> Result<Self> {
use nix::sys::termios::*;
let saved_mode = tcgetattr(fd)?;
let mut mode = saved_mode.clone();
mode.input_flags &= !(InputFlags::BRKINT
| InputFlags::ICRNL
| InputFlags::INPCK
| InputFlags::ISTRIP
| InputFlags::IXON);
mode.output_flags &= !OutputFlags::OPOST;
mode.control_flags &= !(ControlFlags::CSIZE | ControlFlags::PARENB);
mode.control_flags |= ControlFlags::CS8;
mode.local_flags &= !(LocalFlags::ECHO
| LocalFlags::ICANON
| LocalFlags::IEXTEN
| LocalFlags::ISIG);
mode.control_chars[libc::VMIN] = 1;
mode.control_chars[libc::VTIME] = 0;
tcsetattr(fd, SetArg::TCSAFLUSH, &mode)?;
Ok(RawTerm(fd, saved_mode))
}
}
impl Drop for RawTerm {
fn drop(&mut self) {
tcsetattr(self.0, SetArg::TCSAFLUSH, &self.1).unwrap();
}
fn drop(&mut self) {
tcsetattr(self.0, SetArg::TCSAFLUSH, &self.1).unwrap();
}
}

View file

@ -1,34 +1,30 @@
use std::collections::HashMap;
use std::env;
use std::path::PathBuf;
use crate::asciicast::Header;
use crate::term;
pub struct RecordOptions {
pub path: PathBuf,
pub append: Option<bool>,
pub command: Option<String>,
pub capture_env: Option<Vec<String>>,
pub title: Option<String>,
pub path: PathBuf,
pub append: Option<bool>,
pub command: Option<String>,
pub capture_env: Option<Vec<String>>,
pub title: Option<String>,
}
pub fn record(options: RecordOptions) {
let command = options
.command
.unwrap_or_else(|| env::var("SHELL").unwrap_or_else(|_| "sh".to_string()));
let _command = options
.command
.unwrap_or_else(|| env::var("SHELL").unwrap_or_else(|_| "sh".to_string()));
let command_env = env::vars();
let _command_env = env::vars();
// let header_env = HashMap::new();
// let header_env = HashMap::new();
// let (width, height) = term::get_size();
// let (width, height) = term::get_size();
// let header = Header {
// version: 2,
// width,
// height,
// let header = Header {
// version: 2,
// width,
// height,
// title: options.title,
// };
// title: options.title,
// };
}

View file

@ -8,38 +8,38 @@ use crate::asciicast::{Event, EventKind};
use crate::errors::Result;
pub trait Writer: Clone {
fn write_line(&mut self, line: String) -> Result<(), io::Error>;
fn write_line(&mut self, line: String) -> Result<(), io::Error>;
fn write_stdin(&mut self, time: f64, data: &[u8]) -> Result<()> {
let output = EventKind::Input(data);
let event = Event(time, output);
self.write_line(serde_json::to_string(&event)?)?;
Ok(())
}
fn write_stdin(&mut self, time: f64, data: &[u8]) -> Result<()> {
let output = EventKind::Input(data);
let event = Event(time, output);
self.write_line(serde_json::to_string(&event)?)?;
Ok(())
}
fn write_stdout(&mut self, time: f64, data: &[u8]) -> Result<()> {
let output = EventKind::Output(data);
let event = Event(time, output);
self.write_line(serde_json::to_string(&event)?)?;
Ok(())
}
fn write_stdout(&mut self, time: f64, data: &[u8]) -> Result<()> {
let output = EventKind::Output(data);
let event = Event(time, output);
self.write_line(serde_json::to_string(&event)?)?;
Ok(())
}
}
#[derive(Clone)]
pub struct FileWriter(Arc<Mutex<File>>);
impl FileWriter {
pub fn new(file: File) -> Self {
FileWriter(Arc::new(Mutex::new(file)))
}
pub fn new(file: File) -> Self {
FileWriter(Arc::new(Mutex::new(file)))
}
}
impl Writer for FileWriter {
fn write_line(&mut self, line: String) -> Result<(), io::Error> {
let mut writer = self.0.lock();
writer.write_all(line.as_bytes())?;
writer.write(b"\n")?;
writer.flush()?;
Ok(())
}
fn write_line(&mut self, line: String) -> Result<(), io::Error> {
let mut writer = self.0.lock();
writer.write_all(line.as_bytes())?;
writer.write(b"\n")?;
writer.flush()?;
Ok(())
}
}