line wrap kinda working

This commit is contained in:
Aram Drevekenin 2020-07-13 15:04:45 +02:00
commit 22f109e8cf
4 changed files with 780 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

164
Cargo.lock generated Normal file
View 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
View 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
View 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();
// }
}