use crate::panes::PositionAndSize; use interprocess::local_socket::LocalSocketStream; use std::collections::{HashMap, VecDeque}; use std::io::Write; use std::os::unix::io::RawFd; use std::path::PathBuf; use std::sync::{mpsc, Arc, Condvar, Mutex}; use std::time::{Duration, Instant}; use crate::client::ClientInstruction; use crate::common::{ChannelWithContext, SenderType, SenderWithContext}; use crate::errors::ErrorContext; use crate::os_input_output::{ClientOsApi, ServerOsApi}; use crate::server::ServerInstruction; use crate::tests::possible_tty_inputs::{get_possible_tty_inputs, Bytes}; use crate::tests::utils::commands::{QUIT, SLEEP}; const MIN_TIME_BETWEEN_SNAPSHOTS: Duration = Duration::from_millis(150); #[derive(Clone)] pub enum IoEvent { Kill(RawFd), SetTerminalSizeUsingFd(RawFd, u16, u16), IntoRawMode(RawFd), UnsetRawMode(RawFd), TcDrain(RawFd), } #[derive(Clone)] pub struct FakeStdoutWriter { output_buffer: Arc>>, pub output_frames: Arc>>>, last_snapshot_time: Arc>, } impl FakeStdoutWriter { pub fn new(last_snapshot_time: Arc>) -> Self { FakeStdoutWriter { output_buffer: Arc::new(Mutex::new(Vec::new())), output_frames: Arc::new(Mutex::new(Vec::new())), last_snapshot_time, } } } impl Write for FakeStdoutWriter { fn write(&mut self, buf: &[u8]) -> Result { let mut bytes_written = 0; let mut output_buffer = self.output_buffer.lock().unwrap(); for byte in buf { bytes_written += 1; output_buffer.push(*byte); } Ok(bytes_written) } fn flush(&mut self) -> Result<(), std::io::Error> { let mut output_buffer = self.output_buffer.lock().unwrap(); let mut output_frames = self.output_frames.lock().unwrap(); let new_frame = output_buffer.drain(..).collect(); output_frames.push(new_frame); let mut last_snapshot_time = self.last_snapshot_time.lock().unwrap(); *last_snapshot_time = Instant::now(); Ok(()) } } #[derive(Clone)] pub struct FakeInputOutput { read_buffers: Arc>>, input_to_add: Arc>>>, stdin_commands: Arc>>>, stdin_writes: Arc>>>, pub stdout_writer: FakeStdoutWriter, // stdout_writer.output is already an arc/mutex io_events: Arc>>, win_sizes: Arc>>, possible_tty_inputs: HashMap, last_snapshot_time: Arc>, client_sender: SenderWithContext, client_receiver: Arc>>, server_sender: SenderWithContext, server_receiver: Arc>>, should_trigger_sigwinch: Arc<(Mutex, Condvar)>, sigwinch_event: Option, } impl FakeInputOutput { pub fn new(winsize: PositionAndSize) -> Self { let mut win_sizes = HashMap::new(); let last_snapshot_time = Arc::new(Mutex::new(Instant::now())); let stdout_writer = FakeStdoutWriter::new(last_snapshot_time.clone()); let (client_sender, client_receiver): ChannelWithContext = mpsc::channel(); let client_sender = SenderWithContext::new(SenderType::Sender(client_sender)); let (server_sender, server_receiver): ChannelWithContext = mpsc::channel(); let server_sender = SenderWithContext::new(SenderType::Sender(server_sender)); win_sizes.insert(0, winsize); // 0 is the current terminal FakeInputOutput { read_buffers: Arc::new(Mutex::new(HashMap::new())), stdin_writes: Arc::new(Mutex::new(HashMap::new())), input_to_add: Arc::new(Mutex::new(None)), stdin_commands: Arc::new(Mutex::new(VecDeque::new())), stdout_writer, last_snapshot_time, io_events: Arc::new(Mutex::new(vec![])), win_sizes: Arc::new(Mutex::new(win_sizes)), possible_tty_inputs: get_possible_tty_inputs(), server_receiver: Arc::new(Mutex::new(server_receiver)), server_sender, client_receiver: Arc::new(Mutex::new(client_receiver)), client_sender, should_trigger_sigwinch: Arc::new((Mutex::new(false), Condvar::new())), sigwinch_event: None, } } pub fn with_tty_inputs(mut self, tty_inputs: HashMap) -> Self { self.possible_tty_inputs = tty_inputs; self } pub fn add_terminal_input(&mut self, input: &[&[u8]]) { let mut stdin_commands: VecDeque> = VecDeque::new(); for command in input.iter() { stdin_commands.push_back(command.iter().copied().collect()) } self.stdin_commands = Arc::new(Mutex::new(stdin_commands)); } pub fn add_terminal(&mut self, fd: RawFd) { self.stdin_writes.lock().unwrap().insert(fd, vec![]); } pub fn add_sigwinch_event(&mut self, new_position_and_size: PositionAndSize) { self.sigwinch_event = Some(new_position_and_size); } } impl ClientOsApi for FakeInputOutput { fn get_terminal_size_using_fd(&self, pid: RawFd) -> PositionAndSize { if let Some(new_position_and_size) = self.sigwinch_event { let (lock, _cvar) = &*self.should_trigger_sigwinch; let should_trigger_sigwinch = lock.lock().unwrap(); if *should_trigger_sigwinch && pid == 0 { return new_position_and_size; } } let win_sizes = self.win_sizes.lock().unwrap(); let winsize = win_sizes.get(&pid).unwrap(); *winsize } fn set_raw_mode(&mut self, pid: RawFd) { self.io_events .lock() .unwrap() .push(IoEvent::IntoRawMode(pid)); } fn unset_raw_mode(&mut self, pid: RawFd) { self.io_events .lock() .unwrap() .push(IoEvent::UnsetRawMode(pid)); } fn box_clone(&self) -> Box { Box::new((*self).clone()) } fn read_from_stdin(&self) -> Vec { loop { let last_snapshot_time = { *self.last_snapshot_time.lock().unwrap() }; if last_snapshot_time.elapsed() > MIN_TIME_BETWEEN_SNAPSHOTS { break; } else { ::std::thread::sleep(MIN_TIME_BETWEEN_SNAPSHOTS - last_snapshot_time.elapsed()); } } let command = self .stdin_commands .lock() .unwrap() .pop_front() .unwrap_or(vec![]); if command == SLEEP { std::thread::sleep(std::time::Duration::from_millis(200)); } else if command == QUIT && self.sigwinch_event.is_some() { let (lock, cvar) = &*self.should_trigger_sigwinch; { let mut should_trigger_sigwinch = lock.lock().unwrap(); *should_trigger_sigwinch = true; } cvar.notify_one(); ::std::thread::sleep(MIN_TIME_BETWEEN_SNAPSHOTS); // give some time for the app to resize before quitting } else if command == QUIT { ::std::thread::sleep(MIN_TIME_BETWEEN_SNAPSHOTS); } command } fn get_stdout_writer(&self) -> Box { Box::new(self.stdout_writer.clone()) } fn send_to_server(&self, msg: ServerInstruction) { self.server_sender.send(msg).unwrap(); } fn client_recv(&self) -> (ClientInstruction, ErrorContext) { self.client_receiver.lock().unwrap().recv().unwrap() } fn receive_sigwinch(&self, cb: Box) { if self.sigwinch_event.is_some() { let (lock, cvar) = &*self.should_trigger_sigwinch; { let mut should_trigger_sigwinch = lock.lock().unwrap(); while !*should_trigger_sigwinch { should_trigger_sigwinch = cvar.wait(should_trigger_sigwinch).unwrap(); } } cb(); } } fn connect_to_server(&self) {} } impl ServerOsApi for FakeInputOutput { fn set_terminal_size_using_fd(&mut self, pid: RawFd, cols: u16, rows: u16) { let terminal_input = self .possible_tty_inputs .get(&cols) .expect(&format!("could not find input for size {:?}", cols)); self.read_buffers .lock() .unwrap() .insert(pid, terminal_input.clone()); self.io_events .lock() .unwrap() .push(IoEvent::SetTerminalSizeUsingFd(pid, cols, rows)); } fn spawn_terminal(&mut self, _file_to_open: Option) -> (RawFd, RawFd) { let next_terminal_id = self.stdin_writes.lock().unwrap().keys().len() as RawFd + 1; self.add_terminal(next_terminal_id); (next_terminal_id as i32, next_terminal_id + 1000) // secondary number is arbitrary here } fn write_to_tty_stdin(&mut self, pid: RawFd, buf: &mut [u8]) -> Result { let mut stdin_writes = self.stdin_writes.lock().unwrap(); let write_buffer = stdin_writes.get_mut(&pid).unwrap(); let mut bytes_written = 0; for byte in buf { bytes_written += 1; write_buffer.push(*byte); } Ok(bytes_written) } fn read_from_tty_stdout(&mut self, pid: RawFd, buf: &mut [u8]) -> Result { let mut read_buffers = self.read_buffers.lock().unwrap(); let mut bytes_read = 0; match read_buffers.get_mut(&pid) { Some(bytes) => { for i in bytes.read_position..bytes.content.len() { bytes_read += 1; buf[i] = bytes.content[i]; } if bytes_read > bytes.read_position { bytes.set_read_position(bytes_read); } return Ok(bytes_read); } None => Err(nix::Error::Sys(nix::errno::Errno::EAGAIN)), } } fn tcdrain(&mut self, pid: RawFd) -> Result<(), nix::Error> { self.io_events.lock().unwrap().push(IoEvent::TcDrain(pid)); Ok(()) } fn box_clone(&self) -> Box { Box::new((*self).clone()) } fn kill(&mut self, fd: RawFd) -> Result<(), nix::Error> { self.io_events.lock().unwrap().push(IoEvent::Kill(fd)); Ok(()) } fn server_recv(&self) -> (ServerInstruction, ErrorContext) { self.server_receiver.lock().unwrap().recv().unwrap() } fn send_to_client(&self, msg: ClientInstruction) { self.client_sender.send(msg).unwrap(); } fn add_client_sender(&mut self) {} fn update_receiver(&mut self, stream: LocalSocketStream) {} }