line wrap kinda working
This commit is contained in:
commit
22f109e8cf
4 changed files with 780 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/target
|
||||
164
Cargo.lock
generated
Normal file
164
Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fde55d2a2bfaa4c9668bbc63f531fbdeee3ffe188f4662511ce2c22b3eedebe"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
|
||||
|
||||
[[package]]
|
||||
name = "mosaic"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"nix",
|
||||
"signal-hook",
|
||||
"termios",
|
||||
"unicode-truncate",
|
||||
"unicode-width",
|
||||
"vte",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"void",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "604508c1418b99dfe1925ca9224829bb2a8a9a04dda655cc01fcad46f4ab05ed"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termios"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f0fcee7b24a25675de40d5bb4de6e41b0df07bc9856295e7e2b3a3600c400c2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-truncate"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ac822301b0f9f2c2e60509da419b2401a5dc480c784e951ac6bebeee16a7beb"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
|
||||
[[package]]
|
||||
name = "vte"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96cc8a191608603611e78c6ec11dafef37e3cca0775aeef1931824753e81711d"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"utf8parse",
|
||||
"vte_generate_state_changes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vte_generate_state_changes"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
16
Cargo.toml
Normal file
16
Cargo.toml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "mosaic"
|
||||
version = "0.1.0"
|
||||
authors = ["Aram Drevekenin <aram@poor.dev>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
termios = "0.3"
|
||||
libc = "0.2"
|
||||
nix = "0.17.0"
|
||||
signal-hook = "0.1.10"
|
||||
unicode-width = "0.1.8"
|
||||
unicode-truncate = "0.1.1"
|
||||
vte = "0.8.0"
|
||||
599
src/main.rs
Normal file
599
src/main.rs
Normal file
|
|
@ -0,0 +1,599 @@
|
|||
use std::{mem, io};
|
||||
use std::io::{stdin, stdout, Read, Write};
|
||||
use std::collections::VecDeque;
|
||||
use nix::unistd::{read, write, ForkResult};
|
||||
use nix::fcntl::{fcntl, FcntlArg, OFlag};
|
||||
use nix::sys::termios::SpecialCharacterIndices::{VMIN, VTIME};
|
||||
use nix::sys::termios::{
|
||||
tcgetattr,
|
||||
cfmakeraw,
|
||||
tcsetattr,
|
||||
SetArg,
|
||||
tcdrain,
|
||||
tcflush,
|
||||
FlushArg,
|
||||
cfsetispeed,
|
||||
cfsetospeed,
|
||||
BaudRate,
|
||||
InputFlags,
|
||||
};
|
||||
use nix::pty::{forkpty, Winsize};
|
||||
use std::os::unix::io::{RawFd, FromRawFd};
|
||||
use std::process::Command;
|
||||
use ::std::{thread, time};
|
||||
use ::std::fs::File;
|
||||
use ::std::io::prelude::*;
|
||||
use ::std::sync::{Arc, Mutex};
|
||||
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
use unicode_truncate::UnicodeTruncateStr;
|
||||
use vte;
|
||||
|
||||
// fn read_from_pid (pid: RawFd) -> (usize, [u8; 115200]) {
|
||||
fn read_from_pid (pid: RawFd) -> Option<Vec<u8>> {
|
||||
let mut read_buffer = [0; 115200];
|
||||
let read_result = read(pid, &mut read_buffer);
|
||||
match read_result {
|
||||
Ok(res) => {
|
||||
Some(read_buffer[..=res].to_vec())
|
||||
// (res, read_buffer)
|
||||
},
|
||||
Err(e) => {
|
||||
match e {
|
||||
nix::Error::Sys(errno) => {
|
||||
if errno == nix::errno::Errno::EAGAIN {
|
||||
None
|
||||
// (0, read_buffer)
|
||||
} else {
|
||||
panic!("error {:?}", e);
|
||||
}
|
||||
},
|
||||
_ => panic!("error {:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn into_raw_mode(pid: RawFd) {
|
||||
let mut tio = tcgetattr(pid).expect("could not get terminal attribute");
|
||||
cfmakeraw(&mut tio);
|
||||
match tcsetattr(pid, SetArg::TCSANOW, &mut tio) {
|
||||
Ok(_) => {},
|
||||
Err(e) => panic!("error {:?}", e)
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
fn change_vmin_and_vtime(pid: RawFd) {
|
||||
let mut tio = tcgetattr(pid).expect("could not get terminal attribute");
|
||||
// tio.control["VMIN"] = 1;
|
||||
tio.control_chars[VMIN as usize] = 0;
|
||||
tio.control_chars[VTIME as usize] = 0;
|
||||
match tcsetattr(pid, SetArg::TCSANOW, &mut tio) {
|
||||
Ok(_) => {},
|
||||
Err(e) => panic!("error {:?}", e)
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
fn set_baud_rate(pid: RawFd) {
|
||||
let mut tio = tcgetattr(pid).expect("could not get terminal attribute");
|
||||
cfsetospeed(&mut tio, BaudRate::B115200).expect("could not set baud rate");
|
||||
cfsetispeed(&mut tio, BaudRate::B115200).expect("could not set baud rate");
|
||||
tcsetattr(pid, SetArg::TCSANOW, &mut tio).expect("could not set attributes");
|
||||
}
|
||||
|
||||
pub fn get_terminal_size_using_fd(fd: RawFd) -> Winsize {
|
||||
// TODO: do this with the nix ioctl
|
||||
use libc::ioctl;
|
||||
use libc::TIOCGWINSZ;
|
||||
|
||||
let mut winsize = Winsize {
|
||||
ws_row: 0,
|
||||
ws_col: 0,
|
||||
ws_xpixel: 0,
|
||||
ws_ypixel: 0,
|
||||
};
|
||||
|
||||
unsafe { ioctl(fd, TIOCGWINSZ.into(), &mut winsize) };
|
||||
winsize
|
||||
}
|
||||
|
||||
pub fn set_terminal_size_using_fd(fd: RawFd, ws: &Winsize) {
|
||||
// TODO: do this with the nix ioctl
|
||||
use libc::ioctl;
|
||||
use libc::TIOCSWINSZ;
|
||||
|
||||
let mut winsize = Winsize {
|
||||
ws_row: ws.ws_row,
|
||||
ws_col: ws.ws_col,
|
||||
ws_xpixel: 0,
|
||||
ws_ypixel: 0,
|
||||
};
|
||||
unsafe { ioctl(fd, TIOCSWINSZ.into(), &winsize) };
|
||||
}
|
||||
|
||||
|
||||
fn spawn_terminal () -> (RawFd, RawFd, Winsize) {
|
||||
// let ws = Winsize { ws_row: 11, ws_col: 116, ws_xpixel: 0, ws_ypixel: 0 };
|
||||
let ws = get_terminal_size_using_fd(0);
|
||||
let (pid_primary, pid_secondary): (RawFd, RawFd) = {
|
||||
match forkpty(Some(&ws), None) {
|
||||
Ok(fork_pty_res) => {
|
||||
let pid_primary = fork_pty_res.master;
|
||||
let pid_secondary = match fork_pty_res.fork_result {
|
||||
ForkResult::Parent { child } => {
|
||||
fcntl(pid_primary, FcntlArg::F_SETFL(OFlag::O_NONBLOCK)).expect("could not fcntl");
|
||||
child
|
||||
},
|
||||
ForkResult::Child => {
|
||||
// TODO: why does $SHELL not work?
|
||||
// Command::new("$SHELL").spawn().expect("failed to spawn");
|
||||
set_baud_rate(0);
|
||||
Command::new("/usr/bin/fish").spawn().expect("failed to spawn");
|
||||
::std::thread::sleep(std::time::Duration::from_millis(300000));
|
||||
panic!("I am secondary, why?!");
|
||||
},
|
||||
};
|
||||
(pid_primary, pid_secondary.as_raw())
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("failed to fork {:?}", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
(pid_primary, pid_secondary, ws)
|
||||
}
|
||||
|
||||
fn to_utf8_lines(buf: &[u8]) -> Vec<String> {
|
||||
let buf_utf8 = String::from_utf8(buf.to_vec()).unwrap();
|
||||
let mut lines: Vec<String> = buf_utf8.lines().map(|l| l.to_string()).collect();
|
||||
for i in 0..lines.len() - 1 {
|
||||
// lines[i].push('\r');
|
||||
// lines[i].push('\n'); // TODO: remove these?
|
||||
}
|
||||
lines
|
||||
}
|
||||
|
||||
|
||||
/// A type implementing Perform that just logs actions
|
||||
struct CharacterCounter {
|
||||
pub characters: u16
|
||||
}
|
||||
|
||||
impl CharacterCounter {
|
||||
pub fn new() -> CharacterCounter {
|
||||
CharacterCounter {
|
||||
characters: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl vte::Perform for CharacterCounter {
|
||||
fn print(&mut self, c: char) {
|
||||
self.characters += 1;
|
||||
// println!("[print] {:?}", c);
|
||||
}
|
||||
|
||||
fn execute(&mut self, byte: u8) {
|
||||
// println!("[execute] {:02x}", byte);
|
||||
}
|
||||
|
||||
fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) {
|
||||
// println!(
|
||||
// "[hook] params={:?}, intermediates={:?}, ignore={:?}, char={:?}",
|
||||
// params, intermediates, ignore, c
|
||||
// );
|
||||
}
|
||||
|
||||
fn put(&mut self, byte: u8) {
|
||||
// println!("[put] {:02x}", byte);
|
||||
}
|
||||
|
||||
fn unhook(&mut self) {
|
||||
// println!("[unhook]");
|
||||
}
|
||||
|
||||
fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) {
|
||||
// println!("[osc_dispatch] params={:?} bell_terminated={}", params, bell_terminated);
|
||||
}
|
||||
|
||||
fn csi_dispatch(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) {
|
||||
// println!(
|
||||
// "[csi_dispatch] params={:?}, intermediates={:?}, ignore={:?}, char={:?}",
|
||||
// params, intermediates, ignore, c
|
||||
// );
|
||||
}
|
||||
|
||||
fn esc_dispatch(&mut self, intermediates: &[u8], ignore: bool, byte: u8) {
|
||||
// println!(
|
||||
// "[esc_dispatch] intermediates={:?}, ignore={:?}, byte={:02x}",
|
||||
// intermediates, ignore, byte
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn wrap_row (row: &str, columns: u16) -> Vec<String> {
|
||||
// TODO:
|
||||
// * create a new character_counter
|
||||
// * loop through characters in row
|
||||
// * add each character to the character_counter
|
||||
// * once the counter reaches columns, push line into lines
|
||||
let row = row.as_bytes();
|
||||
let mut wrapped_lines = vec![];
|
||||
let mut vte_parser = vte::Parser::new();
|
||||
let mut character_counter = CharacterCounter::new();
|
||||
let mut index_in_row = 0;
|
||||
let mut line = vec![];
|
||||
loop {
|
||||
let character = row[index_in_row];
|
||||
line.push(character);
|
||||
vte_parser.advance(&mut character_counter, character);
|
||||
if character_counter.characters == columns * (wrapped_lines.len() as u16 + 1) {
|
||||
let mut string_line = String::from_utf8(line.clone()).expect("could not create utf8 string");
|
||||
// string_line.push('\r');
|
||||
// string_line.push('\n');
|
||||
wrapped_lines.push(string_line);
|
||||
line.clear();
|
||||
}
|
||||
if index_in_row == row.len() - 1 {
|
||||
if line.len() > 0 {
|
||||
let mut string_line = String::from_utf8(line.clone()).expect("could not create utf8 string");
|
||||
// string_line.push('\n');
|
||||
// string_line.push('\r');
|
||||
wrapped_lines.push(string_line);
|
||||
line.clear(); // TODO: we don't need this?
|
||||
}
|
||||
break;
|
||||
}
|
||||
index_in_row += 1;
|
||||
}
|
||||
// println!("\rwrapped_lines {:?}", wrapped_lines);
|
||||
wrapped_lines
|
||||
|
||||
|
||||
|
||||
|
||||
// let mut wrapped_lines = vec![];
|
||||
// let mut line = String::new();
|
||||
// let mut index_in_row = 0;
|
||||
// loop {
|
||||
// let (line, w) = &row.get(index_in_row..).unwrap().unicode_truncate(columns as usize);
|
||||
// // let rest_of_line = &row.get(line.chars().count()..).unwrap();
|
||||
// index_in_row += line.chars().count();
|
||||
// let mut wrapped_line = String::from(*line);
|
||||
// if index_in_row < row.chars().count() {
|
||||
// // this is not the last row, so add newline
|
||||
// wrapped_line.push('\r');
|
||||
// wrapped_line.push('\n');
|
||||
// }
|
||||
// // wrapped_lines.push(String::from(*line));
|
||||
// wrapped_lines.push(wrapped_line);
|
||||
// if index_in_row >= row.chars().count() {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// wrapped_lines
|
||||
}
|
||||
|
||||
fn lines_in_buffer(buffer: &Vec<String>, ws: &Winsize) -> Vec<u8> {
|
||||
let column_count = ws.ws_col;
|
||||
let row_count = ws.ws_row;
|
||||
let mut rows = VecDeque::new();
|
||||
|
||||
if buffer.is_empty() {
|
||||
return vec![]
|
||||
};
|
||||
let carriage_return = String::from("\r");
|
||||
let mut index_in_buffer = buffer.len() - 1;
|
||||
loop {
|
||||
if rows.len() >= row_count as usize {
|
||||
break;
|
||||
}
|
||||
let current_row = &buffer[index_in_buffer];
|
||||
let mut current_row_wrapped = wrap_row(current_row, column_count);
|
||||
current_row_wrapped.reverse();
|
||||
for mut line in current_row_wrapped {
|
||||
if rows.len() < row_count as usize {
|
||||
line.push('\r');
|
||||
line.push('\n');
|
||||
rows.push_front(line);
|
||||
}
|
||||
}
|
||||
index_in_buffer -= 1;
|
||||
if index_in_buffer == 0 {
|
||||
break;
|
||||
}
|
||||
// rows.push_front(String::from("\r\n"));
|
||||
}
|
||||
let rows_length = rows.len();
|
||||
rows[rows_length - 1].pop(); // remove last \n (ugly hack, TODO better)
|
||||
// println!("\rrow_count, rows.len {:?}, {:?}", row_count, rows.len());
|
||||
// for row in rows {
|
||||
// println!("\rrow: {:?}", row);
|
||||
// }
|
||||
// ::std::process::exit(2);
|
||||
|
||||
rows.push_front(carriage_return); // TODO: ??
|
||||
let bytes: Vec<u8> = rows.iter().fold(vec![], |mut acc, l| {
|
||||
for byte in l.as_bytes() {
|
||||
acc.push(*byte)
|
||||
}
|
||||
acc
|
||||
});
|
||||
bytes
|
||||
}
|
||||
|
||||
fn create_empty_lines(ws: &Winsize) -> Vec<u8> {
|
||||
let columns = ws.ws_col;
|
||||
let rows = ws.ws_row;
|
||||
let mut lines = vec![];
|
||||
let carriage_return = String::from("\r");
|
||||
lines.append(carriage_return.as_bytes().to_vec().as_mut());
|
||||
let mut empty_line = String::new();
|
||||
let empty_char = ' ';
|
||||
// for _i in 0..columns - 1 {
|
||||
for _i in 0..columns {
|
||||
empty_line.push(empty_char);
|
||||
}
|
||||
empty_line.push('\n');
|
||||
for _i in 0..rows {
|
||||
let mut line = vec![];
|
||||
let carriage_return = String::from("\r");
|
||||
line.append(carriage_return.as_bytes().to_vec().as_mut());
|
||||
line.append(empty_line.as_bytes().to_vec().as_mut());
|
||||
lines.append(&mut line);
|
||||
}
|
||||
lines
|
||||
}
|
||||
|
||||
// sigwinch stuff
|
||||
use ::signal_hook::iterator::Signals;
|
||||
|
||||
pub type OnSigWinch = dyn Fn(Box<dyn Fn()>) + Send;
|
||||
pub type SigCleanup = dyn Fn() + Send;
|
||||
|
||||
pub fn sigwinch() -> (Box<OnSigWinch>, Box<SigCleanup>) {
|
||||
let signals = Signals::new(&[signal_hook::SIGWINCH]).unwrap();
|
||||
let on_winch = {
|
||||
let signals = signals.clone();
|
||||
move |cb: Box<dyn Fn()>| {
|
||||
for signal in signals.forever() {
|
||||
match signal {
|
||||
signal_hook::SIGWINCH => cb(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let cleanup = move || {
|
||||
signals.close();
|
||||
};
|
||||
(Box::new(on_winch), Box::new(cleanup))
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut active_threads = vec![];
|
||||
|
||||
let (first_terminal_pid, pid_secondary, first_terminal_ws): (RawFd, RawFd, Winsize) = spawn_terminal();
|
||||
let (second_terminal_pid, pid_secondary, second_terminal_ws): (RawFd, RawFd, Winsize) = spawn_terminal();
|
||||
let stdin = io::stdin();
|
||||
into_raw_mode(0);
|
||||
set_baud_rate(0);
|
||||
::std::thread::sleep(std::time::Duration::from_millis(2000));
|
||||
let active_terminal = Arc::new(Mutex::new(first_terminal_pid));
|
||||
let terminal1_buffer: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
|
||||
let terminal2_buffer: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
|
||||
let first_terminal_ws = Arc::new(Mutex::new(first_terminal_ws));
|
||||
let second_terminal_ws = Arc::new(Mutex::new(second_terminal_ws));
|
||||
active_threads.push(
|
||||
thread::Builder::new()
|
||||
.name("terminal_stdout_handler".to_string())
|
||||
.spawn({
|
||||
let active_terminal = active_terminal.clone();
|
||||
let terminal1_buffer = terminal1_buffer.clone();
|
||||
move || {
|
||||
let mut read_buffer = vec![];
|
||||
loop {
|
||||
match read_from_pid(first_terminal_pid) {
|
||||
Some(mut read_bytes) => {
|
||||
read_buffer.append(&mut read_bytes);
|
||||
},
|
||||
None => {
|
||||
if read_buffer.len() > 0 {
|
||||
{
|
||||
let mut terminal1_buffer = terminal1_buffer.lock().unwrap();
|
||||
let mut lines = to_utf8_lines(&read_buffer);
|
||||
terminal1_buffer.append(&mut lines);
|
||||
}
|
||||
{
|
||||
let active_terminal = active_terminal.lock().unwrap();
|
||||
if *active_terminal == first_terminal_pid {
|
||||
::std::io::stdout().write_all(&read_buffer).expect("cannot write to stdout");
|
||||
::std::io::stdout().flush().expect("could not flush");
|
||||
}
|
||||
}
|
||||
read_buffer.clear();
|
||||
}
|
||||
::std::thread::sleep(std::time::Duration::from_millis(50)); // TODO: adjust this
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.unwrap(),
|
||||
);
|
||||
active_threads.push(
|
||||
thread::Builder::new()
|
||||
.name("terminal_stdout_handler2".to_string())
|
||||
.spawn({
|
||||
let active_terminal = active_terminal.clone();
|
||||
let terminal2_buffer = terminal2_buffer.clone();
|
||||
move || {
|
||||
let mut read_buffer = vec![];
|
||||
loop {
|
||||
match read_from_pid(second_terminal_pid) {
|
||||
Some(mut read_bytes) => {
|
||||
read_buffer.append(&mut read_bytes);
|
||||
},
|
||||
None => {
|
||||
if read_buffer.len() > 0 {
|
||||
{
|
||||
let mut terminal2_buffer = terminal2_buffer.lock().unwrap();
|
||||
let mut lines = to_utf8_lines(&read_buffer);
|
||||
terminal2_buffer.append(&mut lines);
|
||||
}
|
||||
{
|
||||
let active_terminal = active_terminal.lock().unwrap();
|
||||
if *active_terminal == second_terminal_pid {
|
||||
::std::io::stdout().write_all(&read_buffer).expect("cannot write to stdout");
|
||||
::std::io::stdout().flush().expect("could not flush");
|
||||
}
|
||||
}
|
||||
read_buffer.clear();
|
||||
}
|
||||
::std::thread::sleep(std::time::Duration::from_millis(50)); // TODO: adjust this
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.unwrap(),
|
||||
);
|
||||
let (on_sigwinch, cleanup) = sigwinch();
|
||||
active_threads.push(
|
||||
thread::Builder::new()
|
||||
.name("resize_handler".to_string())
|
||||
.spawn({
|
||||
let active_terminal = active_terminal.clone();
|
||||
let first_terminal_ws = first_terminal_ws.clone();
|
||||
let second_terminal_ws = second_terminal_ws.clone();
|
||||
let terminal1_buffer = terminal1_buffer.clone();
|
||||
let terminal2_buffer = terminal2_buffer.clone();
|
||||
move || {
|
||||
on_sigwinch(Box::new(move || {
|
||||
let active_terminal = active_terminal.lock().unwrap();
|
||||
let ws = get_terminal_size_using_fd(0);
|
||||
|
||||
let empty_lines = create_empty_lines(&ws);
|
||||
|
||||
|
||||
set_terminal_size_using_fd(*active_terminal, &ws);
|
||||
if *active_terminal == first_terminal_pid {
|
||||
let mut first_terminal_ws = first_terminal_ws.lock().unwrap();
|
||||
*first_terminal_ws = ws;
|
||||
|
||||
let terminal1_buffer = terminal1_buffer.lock().unwrap();
|
||||
let new_lines = lines_in_buffer(&*terminal1_buffer, &ws);
|
||||
|
||||
::std::io::stdout().write_all(&empty_lines).expect("cannot write to stdout");
|
||||
::std::io::stdout().write_all(&new_lines).expect("cannot write to stdout");
|
||||
::std::io::stdout().flush().expect("could not flush");
|
||||
} else {
|
||||
let mut second_terminal_ws = second_terminal_ws.lock().unwrap();
|
||||
*second_terminal_ws = ws;
|
||||
|
||||
let terminal2_buffer = terminal2_buffer.lock().unwrap();
|
||||
let new_lines = lines_in_buffer(&*terminal2_buffer, &ws);
|
||||
|
||||
::std::io::stdout().write_all(&empty_lines).expect("cannot write to stdout");
|
||||
::std::io::stdout().write_all(&new_lines).expect("cannot write to stdout");
|
||||
::std::io::stdout().flush().expect("could not flush");
|
||||
}
|
||||
}));
|
||||
}
|
||||
})
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let mut temp_ws = get_terminal_size_using_fd(0);
|
||||
loop {
|
||||
let mut buffer = [0; 1];
|
||||
{
|
||||
let mut handle = stdin.lock();
|
||||
handle.read(&mut buffer).expect("failed to read stdin");
|
||||
if buffer[0] == 10 { // ctrl-j
|
||||
let mut active_terminal = active_terminal.lock().unwrap();
|
||||
temp_ws.ws_col -= 10;
|
||||
let empty_lines = create_empty_lines(&temp_ws);
|
||||
if *active_terminal == first_terminal_pid {
|
||||
let mut first_terminal_ws = first_terminal_ws.lock().unwrap();
|
||||
*first_terminal_ws = temp_ws;
|
||||
|
||||
let terminal1_buffer = terminal1_buffer.lock().unwrap();
|
||||
let new_lines = lines_in_buffer(&*terminal1_buffer, &temp_ws);
|
||||
|
||||
::std::io::stdout().write_all(&empty_lines).expect("cannot write to stdout");
|
||||
::std::io::stdout().write_all(&new_lines).expect("cannot write to stdout");
|
||||
::std::io::stdout().flush().expect("could not flush");
|
||||
|
||||
set_terminal_size_using_fd(*active_terminal, &temp_ws);
|
||||
} else {
|
||||
panic!("not terminal 1");
|
||||
}
|
||||
continue;
|
||||
} else if buffer[0] == 11 { // ctrl-k
|
||||
let mut active_terminal = active_terminal.lock().unwrap();
|
||||
temp_ws.ws_col += 10;
|
||||
let empty_lines = create_empty_lines(&temp_ws);
|
||||
if *active_terminal == first_terminal_pid {
|
||||
let mut first_terminal_ws = first_terminal_ws.lock().unwrap();
|
||||
*first_terminal_ws = temp_ws;
|
||||
|
||||
let terminal1_buffer = terminal1_buffer.lock().unwrap();
|
||||
let new_lines = lines_in_buffer(&*terminal1_buffer, &temp_ws);
|
||||
|
||||
::std::io::stdout().write_all(&empty_lines).expect("cannot write to stdout");
|
||||
::std::io::stdout().write_all(&new_lines).expect("cannot write to stdout");
|
||||
::std::io::stdout().flush().expect("could not flush");
|
||||
} else {
|
||||
panic!("not terminal 1");
|
||||
}
|
||||
continue;
|
||||
} else if buffer[0] == 16 { // ctrl-p
|
||||
let mut active_terminal = active_terminal.lock().unwrap();
|
||||
if *active_terminal == first_terminal_pid {
|
||||
*active_terminal = second_terminal_pid;
|
||||
// TODO: this is actually not correct: we need to use the first terminal width to
|
||||
// clear and the second terminal width to write
|
||||
let first_terminal_ws = first_terminal_ws.lock().unwrap();
|
||||
|
||||
let empty_lines = create_empty_lines(&*first_terminal_ws);
|
||||
|
||||
let second_terminal_ws = second_terminal_ws.lock().unwrap();
|
||||
let terminal2_buffer = terminal2_buffer.lock().unwrap();
|
||||
let new_lines = lines_in_buffer(&*terminal2_buffer, &*second_terminal_ws);
|
||||
|
||||
::std::io::stdout().write_all(&empty_lines).expect("cannot write to stdout");
|
||||
::std::io::stdout().write_all(&new_lines).expect("cannot write to stdout");
|
||||
|
||||
::std::io::stdout().flush().expect("could not flush");
|
||||
} else {
|
||||
*active_terminal = first_terminal_pid;
|
||||
let second_terminal_ws = second_terminal_ws.lock().unwrap();
|
||||
let empty_lines = create_empty_lines(&*second_terminal_ws);
|
||||
|
||||
let first_terminal_ws = first_terminal_ws.lock().unwrap();
|
||||
let terminal1_buffer = terminal1_buffer.lock().unwrap();
|
||||
let lines = lines_in_buffer(&*terminal1_buffer, &*first_terminal_ws);
|
||||
|
||||
::std::io::stdout().write_all(&empty_lines).expect("cannot write to stdout");
|
||||
::std::io::stdout().write_all(&lines).expect("cannot write to stdout");
|
||||
|
||||
::std::io::stdout().flush().expect("could not flush");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let active_terminal = active_terminal.lock().unwrap();
|
||||
write(*active_terminal, &mut buffer).expect("failed to write to terminal");
|
||||
tcdrain(*active_terminal).expect("failed to drain terminal");
|
||||
};
|
||||
// cleanup();
|
||||
|
||||
// for thread_handler in active_threads {
|
||||
// thread_handler.join().unwrap();
|
||||
// }
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue