fix(atomicity): block commands before executing others (#59)
This commit is contained in:
parent
cf43736656
commit
429e415ecc
6 changed files with 217 additions and 121 deletions
52
src/command_is_executing.rs
Normal file
52
src/command_is_executing.rs
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
use std::sync::{Arc, Condvar, Mutex};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct CommandIsExecuting {
|
||||||
|
opening_new_pane: Arc<(Mutex<bool>, Condvar)>,
|
||||||
|
closing_pane: Arc<(Mutex<bool>, Condvar)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandIsExecuting {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
CommandIsExecuting {
|
||||||
|
opening_new_pane: Arc::new((Mutex::new(false), Condvar::new())),
|
||||||
|
closing_pane: Arc::new((Mutex::new(false), Condvar::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn closing_pane(&mut self) {
|
||||||
|
let (lock, _cvar) = &*self.closing_pane;
|
||||||
|
let mut closing_pane = lock.lock().unwrap();
|
||||||
|
*closing_pane = true;
|
||||||
|
}
|
||||||
|
pub fn done_closing_pane(&mut self) {
|
||||||
|
let (lock, cvar) = &*self.closing_pane;
|
||||||
|
let mut closing_pane = lock.lock().unwrap();
|
||||||
|
*closing_pane = false;
|
||||||
|
cvar.notify_one();
|
||||||
|
}
|
||||||
|
pub fn opening_new_pane(&mut self) {
|
||||||
|
let (lock, _cvar) = &*self.opening_new_pane;
|
||||||
|
let mut opening_new_pane = lock.lock().unwrap();
|
||||||
|
*opening_new_pane = true;
|
||||||
|
}
|
||||||
|
pub fn done_opening_new_pane(&mut self) {
|
||||||
|
let (lock, cvar) = &*self.opening_new_pane;
|
||||||
|
let mut opening_new_pane = lock.lock().unwrap();
|
||||||
|
*opening_new_pane = false;
|
||||||
|
cvar.notify_one();
|
||||||
|
}
|
||||||
|
pub fn wait_until_pane_is_closed(&self) {
|
||||||
|
let (lock, cvar) = &*self.closing_pane;
|
||||||
|
let mut closing_pane = lock.lock().unwrap();
|
||||||
|
while *closing_pane {
|
||||||
|
closing_pane = cvar.wait(closing_pane).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn wait_until_new_pane_is_opened(&self) {
|
||||||
|
let (lock, cvar) = &*self.opening_new_pane;
|
||||||
|
let mut opening_new_pane = lock.lock().unwrap();
|
||||||
|
while *opening_new_pane {
|
||||||
|
opening_new_pane = cvar.wait(opening_new_pane).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -87,18 +87,18 @@ fn split_space(space_to_split: &PositionAndSize, layout: &Layout) -> Vec<Positio
|
||||||
pane_positions
|
pane_positions
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub enum Direction {
|
pub enum Direction {
|
||||||
Horizontal,
|
Horizontal,
|
||||||
Vertical,
|
Vertical,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub enum SplitSize {
|
pub enum SplitSize {
|
||||||
Percent(u8), // 1 to 100
|
Percent(u8), // 1 to 100
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Layout {
|
pub struct Layout {
|
||||||
pub direction: Direction,
|
pub direction: Direction,
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
|
|
||||||
230
src/main.rs
230
src/main.rs
|
|
@ -1,11 +1,13 @@
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
mod boundaries;
|
mod boundaries;
|
||||||
|
mod command_is_executing;
|
||||||
mod layout;
|
mod layout;
|
||||||
mod os_input_output;
|
mod os_input_output;
|
||||||
mod pty_bus;
|
mod pty_bus;
|
||||||
mod screen;
|
mod screen;
|
||||||
mod terminal_pane;
|
mod terminal_pane;
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
|
@ -18,6 +20,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use serde_yaml;
|
use serde_yaml;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
use crate::command_is_executing::CommandIsExecuting;
|
||||||
use crate::layout::Layout;
|
use crate::layout::Layout;
|
||||||
use crate::os_input_output::{get_os_input, OsApi};
|
use crate::os_input_output::{get_os_input, OsApi};
|
||||||
use crate::pty_bus::{PtyBus, PtyInstruction, VteEvent};
|
use crate::pty_bus::{PtyBus, PtyInstruction, VteEvent};
|
||||||
|
|
@ -92,6 +95,8 @@ pub enum AppInstruction {
|
||||||
pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
|
pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
|
||||||
let mut active_threads = vec![];
|
let mut active_threads = vec![];
|
||||||
|
|
||||||
|
let command_is_executing = CommandIsExecuting::new();
|
||||||
|
|
||||||
delete_log_dir().unwrap();
|
delete_log_dir().unwrap();
|
||||||
delete_log_file().unwrap();
|
delete_log_file().unwrap();
|
||||||
|
|
||||||
|
|
@ -128,6 +133,7 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name("pty".to_string())
|
.name("pty".to_string())
|
||||||
.spawn({
|
.spawn({
|
||||||
|
let mut command_is_executing = command_is_executing.clone();
|
||||||
move || {
|
move || {
|
||||||
match opts.layout {
|
match opts.layout {
|
||||||
Some(layout_path) => {
|
Some(layout_path) => {
|
||||||
|
|
@ -166,6 +172,7 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
|
||||||
}
|
}
|
||||||
PtyInstruction::ClosePane(id) => {
|
PtyInstruction::ClosePane(id) => {
|
||||||
pty_bus.close_pane(id);
|
pty_bus.close_pane(id);
|
||||||
|
command_is_executing.done_closing_pane();
|
||||||
}
|
}
|
||||||
PtyInstruction::Quit => {
|
PtyInstruction::Quit => {
|
||||||
break;
|
break;
|
||||||
|
|
@ -181,6 +188,7 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name("screen".to_string())
|
.name("screen".to_string())
|
||||||
.spawn({
|
.spawn({
|
||||||
|
let mut command_is_executing = command_is_executing.clone();
|
||||||
move || loop {
|
move || loop {
|
||||||
let event = screen
|
let event = screen
|
||||||
.receiver
|
.receiver
|
||||||
|
|
@ -195,12 +203,15 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
|
||||||
}
|
}
|
||||||
ScreenInstruction::NewPane(pid) => {
|
ScreenInstruction::NewPane(pid) => {
|
||||||
screen.new_pane(pid);
|
screen.new_pane(pid);
|
||||||
|
command_is_executing.done_opening_new_pane();
|
||||||
}
|
}
|
||||||
ScreenInstruction::HorizontalSplit(pid) => {
|
ScreenInstruction::HorizontalSplit(pid) => {
|
||||||
screen.horizontal_split(pid);
|
screen.horizontal_split(pid);
|
||||||
|
command_is_executing.done_opening_new_pane();
|
||||||
}
|
}
|
||||||
ScreenInstruction::VerticalSplit(pid) => {
|
ScreenInstruction::VerticalSplit(pid) => {
|
||||||
screen.vertical_split(pid);
|
screen.vertical_split(pid);
|
||||||
|
command_is_executing.done_opening_new_pane();
|
||||||
}
|
}
|
||||||
ScreenInstruction::WriteCharacter(bytes) => {
|
ScreenInstruction::WriteCharacter(bytes) => {
|
||||||
screen.write_to_active_terminal(bytes);
|
screen.write_to_active_terminal(bytes);
|
||||||
|
|
@ -306,113 +317,120 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let _stdin_thread = thread::Builder::new()
|
let _stdin_thread = thread::Builder::new().name("stdin".to_string()).spawn({
|
||||||
.name("ipc_server".to_string())
|
let send_screen_instructions = send_screen_instructions.clone();
|
||||||
.spawn({
|
let send_pty_instructions = send_pty_instructions.clone();
|
||||||
let send_screen_instructions = send_screen_instructions.clone();
|
let send_app_instructions = send_app_instructions.clone();
|
||||||
let send_pty_instructions = send_pty_instructions.clone();
|
let os_input = os_input.clone();
|
||||||
let send_app_instructions = send_app_instructions.clone();
|
|
||||||
let os_input = os_input.clone();
|
let mut command_is_executing = command_is_executing.clone();
|
||||||
move || {
|
move || {
|
||||||
let mut stdin = os_input.get_stdin_reader();
|
let mut stdin = os_input.get_stdin_reader();
|
||||||
loop {
|
loop {
|
||||||
let mut buffer = [0; 10]; // TODO: more accurately
|
let mut buffer = [0; 10]; // TODO: more accurately
|
||||||
stdin.read(&mut buffer).expect("failed to read stdin");
|
stdin.read(&mut buffer).expect("failed to read stdin");
|
||||||
// uncomment this to print the entered character to a log file (/tmp/mosaic-log.txt) for debugging
|
// uncomment this to print the entered character to a log file (/tmp/mosaic-log.txt) for debugging
|
||||||
//crate::utils::logging::debug_log_to_file(format!("buffer {:?}", buffer));
|
//crate::utils::logging::debug_log_to_file(format!("buffer {:?}", buffer));
|
||||||
match buffer {
|
match buffer {
|
||||||
[10, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
[10, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
||||||
// ctrl-j
|
// ctrl-j
|
||||||
send_screen_instructions
|
send_screen_instructions
|
||||||
.send(ScreenInstruction::ResizeDown)
|
.send(ScreenInstruction::ResizeDown)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
[11, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
[11, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
||||||
// ctrl-k
|
// ctrl-k
|
||||||
send_screen_instructions
|
send_screen_instructions
|
||||||
.send(ScreenInstruction::ResizeUp)
|
.send(ScreenInstruction::ResizeUp)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
[16, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
[16, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
||||||
// ctrl-p
|
// ctrl-p
|
||||||
send_screen_instructions
|
send_screen_instructions
|
||||||
.send(ScreenInstruction::MoveFocus)
|
.send(ScreenInstruction::MoveFocus)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
[8, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
[8, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
||||||
// ctrl-h
|
// ctrl-h
|
||||||
send_screen_instructions
|
send_screen_instructions
|
||||||
.send(ScreenInstruction::ResizeLeft)
|
.send(ScreenInstruction::ResizeLeft)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
[12, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
[12, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
||||||
// ctrl-l
|
// ctrl-l
|
||||||
send_screen_instructions
|
send_screen_instructions
|
||||||
.send(ScreenInstruction::ResizeRight)
|
.send(ScreenInstruction::ResizeRight)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
[26, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
[26, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
||||||
// ctrl-z
|
// ctrl-z
|
||||||
send_pty_instructions
|
command_is_executing.opening_new_pane();
|
||||||
.send(PtyInstruction::SpawnTerminal(None))
|
send_pty_instructions
|
||||||
.unwrap();
|
.send(PtyInstruction::SpawnTerminal(None))
|
||||||
}
|
.unwrap();
|
||||||
[14, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
command_is_executing.wait_until_new_pane_is_opened();
|
||||||
// ctrl-n
|
}
|
||||||
send_pty_instructions
|
[14, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
||||||
.send(PtyInstruction::SpawnTerminalVertically(None))
|
// ctrl-n
|
||||||
.unwrap();
|
command_is_executing.opening_new_pane();
|
||||||
}
|
send_pty_instructions
|
||||||
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
.send(PtyInstruction::SpawnTerminalVertically(None))
|
||||||
// ctrl-b
|
.unwrap();
|
||||||
send_pty_instructions
|
command_is_executing.wait_until_new_pane_is_opened();
|
||||||
.send(PtyInstruction::SpawnTerminalHorizontally(None))
|
}
|
||||||
.unwrap();
|
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
||||||
}
|
// ctrl-b
|
||||||
[17, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
command_is_executing.opening_new_pane();
|
||||||
// ctrl-q
|
send_pty_instructions
|
||||||
let _ = send_screen_instructions.send(ScreenInstruction::Quit);
|
.send(PtyInstruction::SpawnTerminalHorizontally(None))
|
||||||
let _ = send_pty_instructions.send(PtyInstruction::Quit);
|
.unwrap();
|
||||||
let _ = send_app_instructions.send(AppInstruction::Exit);
|
command_is_executing.wait_until_new_pane_is_opened();
|
||||||
break;
|
}
|
||||||
}
|
[17, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
||||||
[27, 91, 53, 94, 0, 0, 0, 0, 0, 0] => {
|
// ctrl-q
|
||||||
// ctrl-PgUp
|
let _ = send_screen_instructions.send(ScreenInstruction::Quit);
|
||||||
send_screen_instructions
|
let _ = send_pty_instructions.send(PtyInstruction::Quit);
|
||||||
.send(ScreenInstruction::ScrollUp)
|
let _ = send_app_instructions.send(AppInstruction::Exit);
|
||||||
.unwrap();
|
break;
|
||||||
}
|
}
|
||||||
[27, 91, 54, 94, 0, 0, 0, 0, 0, 0] => {
|
[27, 91, 53, 94, 0, 0, 0, 0, 0, 0] => {
|
||||||
// ctrl-PgDown
|
// ctrl-PgUp
|
||||||
send_screen_instructions
|
send_screen_instructions
|
||||||
.send(ScreenInstruction::ScrollDown)
|
.send(ScreenInstruction::ScrollUp)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
[24, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
[27, 91, 54, 94, 0, 0, 0, 0, 0, 0] => {
|
||||||
// ctrl-x
|
// ctrl-PgDown
|
||||||
send_screen_instructions
|
send_screen_instructions
|
||||||
.send(ScreenInstruction::CloseFocusedPane)
|
.send(ScreenInstruction::ScrollDown)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// ::std::thread::sleep(::std::time::Duration::from_millis(10));
|
}
|
||||||
}
|
[24, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
||||||
[5, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
// ctrl-x
|
||||||
// ctrl-e
|
command_is_executing.closing_pane();
|
||||||
send_screen_instructions
|
send_screen_instructions
|
||||||
.send(ScreenInstruction::ToggleActiveTerminalFullscreen)
|
.send(ScreenInstruction::CloseFocusedPane)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
command_is_executing.wait_until_pane_is_closed();
|
||||||
_ => {
|
}
|
||||||
send_screen_instructions
|
[5, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
|
||||||
.send(ScreenInstruction::ClearScroll)
|
// ctrl-e
|
||||||
.unwrap();
|
send_screen_instructions
|
||||||
send_screen_instructions
|
.send(ScreenInstruction::ToggleActiveTerminalFullscreen)
|
||||||
.send(ScreenInstruction::WriteCharacter(buffer))
|
.unwrap();
|
||||||
.unwrap();
|
}
|
||||||
}
|
_ => {
|
||||||
|
send_screen_instructions
|
||||||
|
.send(ScreenInstruction::ClearScroll)
|
||||||
|
.unwrap();
|
||||||
|
send_screen_instructions
|
||||||
|
.send(ScreenInstruction::WriteCharacter(buffer))
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let app_instruction = receive_app_instructions
|
let app_instruction = receive_app_instructions
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ impl Stream for ReadFromPid {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum VteEvent {
|
pub enum VteEvent {
|
||||||
// TODO: try not to allocate Vecs
|
// TODO: try not to allocate Vecs
|
||||||
Print(char),
|
Print(char),
|
||||||
|
|
@ -141,6 +141,7 @@ impl vte::Perform for VteEventSender {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub enum PtyInstruction {
|
pub enum PtyInstruction {
|
||||||
SpawnTerminal(Option<PathBuf>),
|
SpawnTerminal(Option<PathBuf>),
|
||||||
SpawnTerminalVertically(Option<PathBuf>),
|
SpawnTerminalVertically(Option<PathBuf>),
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ fn split_horizontally_with_gap(rect: &PositionAndSize) -> (PositionAndSize, Posi
|
||||||
(first_rect, second_rect)
|
(first_rect, second_rect)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ScreenInstruction {
|
pub enum ScreenInstruction {
|
||||||
Pty(RawFd, VteEvent),
|
Pty(RawFd, VteEvent),
|
||||||
Render,
|
Render,
|
||||||
|
|
@ -1469,10 +1469,10 @@ impl Screen {
|
||||||
}
|
}
|
||||||
pub fn close_focused_pane(&mut self) {
|
pub fn close_focused_pane(&mut self) {
|
||||||
if let Some(active_terminal_id) = self.get_active_terminal_id() {
|
if let Some(active_terminal_id) = self.get_active_terminal_id() {
|
||||||
|
self.close_pane(active_terminal_id);
|
||||||
self.send_pty_instructions
|
self.send_pty_instructions
|
||||||
.send(PtyInstruction::ClosePane(active_terminal_id))
|
.send(PtyInstruction::ClosePane(active_terminal_id))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.close_pane(active_terminal_id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn scroll_active_terminal_up(&mut self) {
|
pub fn scroll_active_terminal_up(&mut self) {
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,13 @@ use ::std::io::{Read, Write};
|
||||||
use ::std::os::unix::io::RawFd;
|
use ::std::os::unix::io::RawFd;
|
||||||
use ::std::path::PathBuf;
|
use ::std::path::PathBuf;
|
||||||
use ::std::sync::{Arc, Mutex};
|
use ::std::sync::{Arc, Mutex};
|
||||||
use ::std::time::Duration;
|
use ::std::time::{Duration, Instant};
|
||||||
|
|
||||||
use crate::os_input_output::OsApi;
|
use crate::os_input_output::OsApi;
|
||||||
use crate::tests::possible_tty_inputs::{get_possible_tty_inputs, Bytes};
|
use crate::tests::possible_tty_inputs::{get_possible_tty_inputs, Bytes};
|
||||||
|
|
||||||
|
const MIN_TIME_BETWEEN_SNAPSHOTS: Duration = Duration::from_millis(50);
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum IoEvent {
|
pub enum IoEvent {
|
||||||
Kill(RawFd),
|
Kill(RawFd),
|
||||||
|
|
@ -21,23 +23,29 @@ pub enum IoEvent {
|
||||||
pub struct FakeStdinReader {
|
pub struct FakeStdinReader {
|
||||||
pub input_chars: Vec<[u8; 10]>,
|
pub input_chars: Vec<[u8; 10]>,
|
||||||
pub read_position: usize,
|
pub read_position: usize,
|
||||||
|
last_snapshot_time: Arc<Mutex<Instant>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FakeStdinReader {
|
impl FakeStdinReader {
|
||||||
pub fn new(input_chars: Vec<[u8; 10]>) -> Self {
|
pub fn new(input_chars: Vec<[u8; 10]>, last_snapshot_time: Arc<Mutex<Instant>>) -> Self {
|
||||||
FakeStdinReader {
|
FakeStdinReader {
|
||||||
input_chars,
|
input_chars,
|
||||||
read_position: 0,
|
read_position: 0,
|
||||||
|
last_snapshot_time,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Read for FakeStdinReader {
|
impl Read for FakeStdinReader {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
|
||||||
// ideally, we shouldn't have to sleep here
|
loop {
|
||||||
// stdin should be buffered and handled in the app itself
|
let last_snapshot_time = { *self.last_snapshot_time.lock().unwrap() };
|
||||||
::std::thread::sleep(Duration::from_millis(50));
|
if last_snapshot_time.elapsed() > MIN_TIME_BETWEEN_SNAPSHOTS {
|
||||||
// ::std::thread::sleep(Duration::from_millis(100));
|
break;
|
||||||
|
} else {
|
||||||
|
::std::thread::sleep(MIN_TIME_BETWEEN_SNAPSHOTS - last_snapshot_time.elapsed());
|
||||||
|
}
|
||||||
|
}
|
||||||
let read_position = self.read_position;
|
let read_position = self.read_position;
|
||||||
let bytes_to_read = self.input_chars.get(read_position).unwrap();
|
let bytes_to_read = self.input_chars.get(read_position).unwrap();
|
||||||
for (i, byte) in bytes_to_read.iter().enumerate() {
|
for (i, byte) in bytes_to_read.iter().enumerate() {
|
||||||
|
|
@ -48,10 +56,21 @@ impl Read for FakeStdinReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone)]
|
||||||
pub struct FakeStdoutWriter {
|
pub struct FakeStdoutWriter {
|
||||||
output_buffer: Arc<Mutex<Vec<u8>>>,
|
output_buffer: Arc<Mutex<Vec<u8>>>,
|
||||||
pub output_frames: Arc<Mutex<Vec<Vec<u8>>>>,
|
pub output_frames: Arc<Mutex<Vec<Vec<u8>>>>,
|
||||||
|
last_snapshot_time: Arc<Mutex<Instant>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FakeStdoutWriter {
|
||||||
|
pub fn new(last_snapshot_time: Arc<Mutex<Instant>>) -> 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 {
|
impl Write for FakeStdoutWriter {
|
||||||
|
|
@ -69,6 +88,8 @@ impl Write for FakeStdoutWriter {
|
||||||
let mut output_frames = self.output_frames.lock().unwrap();
|
let mut output_frames = self.output_frames.lock().unwrap();
|
||||||
let new_frame = output_buffer.drain(..).collect();
|
let new_frame = output_buffer.drain(..).collect();
|
||||||
output_frames.push(new_frame);
|
output_frames.push(new_frame);
|
||||||
|
let mut last_snapshot_time = self.last_snapshot_time.lock().unwrap();
|
||||||
|
*last_snapshot_time = Instant::now();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -82,17 +103,21 @@ pub struct FakeInputOutput {
|
||||||
io_events: Arc<Mutex<Vec<IoEvent>>>,
|
io_events: Arc<Mutex<Vec<IoEvent>>>,
|
||||||
win_sizes: Arc<Mutex<HashMap<RawFd, PositionAndSize>>>,
|
win_sizes: Arc<Mutex<HashMap<RawFd, PositionAndSize>>>,
|
||||||
possible_tty_inputs: HashMap<u16, Bytes>,
|
possible_tty_inputs: HashMap<u16, Bytes>,
|
||||||
|
last_snapshot_time: Arc<Mutex<Instant>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FakeInputOutput {
|
impl FakeInputOutput {
|
||||||
pub fn new(winsize: PositionAndSize) -> Self {
|
pub fn new(winsize: PositionAndSize) -> Self {
|
||||||
let mut win_sizes = HashMap::new();
|
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());
|
||||||
win_sizes.insert(0, winsize); // 0 is the current terminal
|
win_sizes.insert(0, winsize); // 0 is the current terminal
|
||||||
FakeInputOutput {
|
FakeInputOutput {
|
||||||
read_buffers: Arc::new(Mutex::new(HashMap::new())),
|
read_buffers: Arc::new(Mutex::new(HashMap::new())),
|
||||||
stdin_writes: Arc::new(Mutex::new(HashMap::new())),
|
stdin_writes: Arc::new(Mutex::new(HashMap::new())),
|
||||||
input_to_add: Arc::new(Mutex::new(None)),
|
input_to_add: Arc::new(Mutex::new(None)),
|
||||||
stdout_writer: FakeStdoutWriter::default(),
|
stdout_writer,
|
||||||
|
last_snapshot_time,
|
||||||
io_events: Arc::new(Mutex::new(vec![])),
|
io_events: Arc::new(Mutex::new(vec![])),
|
||||||
win_sizes: Arc::new(Mutex::new(win_sizes)),
|
win_sizes: Arc::new(Mutex::new(win_sizes)),
|
||||||
possible_tty_inputs: get_possible_tty_inputs(),
|
possible_tty_inputs: get_possible_tty_inputs(),
|
||||||
|
|
@ -205,7 +230,7 @@ impl OsApi for FakeInputOutput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
input_chars.push([17, 0, 0, 0, 0, 0, 0, 0, 0, 0]); // ctrl-q (quit)
|
input_chars.push([17, 0, 0, 0, 0, 0, 0, 0, 0, 0]); // ctrl-q (quit)
|
||||||
let reader = FakeStdinReader::new(input_chars);
|
let reader = FakeStdinReader::new(input_chars, self.last_snapshot_time.clone());
|
||||||
Box::new(reader)
|
Box::new(reader)
|
||||||
}
|
}
|
||||||
fn get_stdout_writer(&self) -> Box<dyn Write> {
|
fn get_stdout_writer(&self) -> Box<dyn Write> {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue