buggy recursive vertical splitting
This commit is contained in:
parent
1a3e74be47
commit
9c63dab376
2 changed files with 314 additions and 123 deletions
417
src/main.rs
417
src/main.rs
|
|
@ -5,7 +5,7 @@ use futures::future::join_all;
|
||||||
use ::std::fmt::{self, Display, Formatter};
|
use ::std::fmt::{self, Display, Formatter};
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::collections::{VecDeque};
|
use std::collections::{VecDeque, HashMap};
|
||||||
use nix::pty::Winsize;
|
use nix::pty::Winsize;
|
||||||
use std::os::unix::io::RawFd;
|
use std::os::unix::io::RawFd;
|
||||||
use ::std::thread;
|
use ::std::thread;
|
||||||
|
|
@ -133,7 +133,7 @@ impl TerminalOutput {
|
||||||
linebreak_indices: Vec::new(),
|
linebreak_indices: Vec::new(),
|
||||||
display_rows: ws.ws_row,
|
display_rows: ws.ws_row,
|
||||||
display_cols: ws.ws_col,
|
display_cols: ws.ws_col,
|
||||||
should_render: false,
|
should_render: true,
|
||||||
pending_ansi_code: None,
|
pending_ansi_code: None,
|
||||||
x_coords,
|
x_coords,
|
||||||
}
|
}
|
||||||
|
|
@ -189,6 +189,12 @@ impl TerminalOutput {
|
||||||
self.reflow_lines();
|
self.reflow_lines();
|
||||||
self.should_render = true;
|
self.should_render = true;
|
||||||
}
|
}
|
||||||
|
pub fn change_size(&mut self, ws: &Winsize) {
|
||||||
|
self.display_cols = ws.ws_col;
|
||||||
|
self.display_rows = ws.ws_row;
|
||||||
|
self.reflow_lines();
|
||||||
|
self.should_render = true;
|
||||||
|
}
|
||||||
fn reflow_lines (&mut self) {
|
fn reflow_lines (&mut self) {
|
||||||
self.linebreak_indices.clear();
|
self.linebreak_indices.clear();
|
||||||
|
|
||||||
|
|
@ -235,7 +241,15 @@ impl TerminalOutput {
|
||||||
return vec![];
|
return vec![];
|
||||||
}
|
}
|
||||||
if self.characters.len() == 0 {
|
if self.characters.len() == 0 {
|
||||||
return vec![];
|
let mut output = vec![];
|
||||||
|
let mut empty_line = vec![];
|
||||||
|
for _ in 0..self.display_cols {
|
||||||
|
empty_line.push(&EMPTY_TERMINAL_CHARACTER);
|
||||||
|
}
|
||||||
|
for _ in 0..self.display_rows as usize {
|
||||||
|
output.push(Vec::from(empty_line.clone()));
|
||||||
|
}
|
||||||
|
return output
|
||||||
}
|
}
|
||||||
let mut output: VecDeque<Vec<&TerminalCharacter>> = VecDeque::new();
|
let mut output: VecDeque<Vec<&TerminalCharacter>> = VecDeque::new();
|
||||||
let mut i = self.characters.len();
|
let mut i = self.characters.len();
|
||||||
|
|
@ -247,14 +261,10 @@ impl TerminalOutput {
|
||||||
let mut next_newline_index = newline_indices.next();
|
let mut next_newline_index = newline_indices.next();
|
||||||
let mut next_linebreak_index = linebreak_indices.next();
|
let mut next_linebreak_index = linebreak_indices.next();
|
||||||
|
|
||||||
let mut debug_messages = String::new();
|
|
||||||
loop {
|
loop {
|
||||||
debug_messages.push_str(&format!("\r\n*** i {:?}", i));
|
|
||||||
i -= 1;
|
i -= 1;
|
||||||
if let Some(newline_index) = next_newline_index {
|
if let Some(newline_index) = next_newline_index {
|
||||||
debug_messages.push_str(&format!("Some(newline_index): {:?} == {:?}", newline_index, i));
|
|
||||||
if *newline_index == i + 1 {
|
if *newline_index == i + 1 {
|
||||||
debug_messages.push_str(&format!("equals i"));
|
|
||||||
// pad line
|
// pad line
|
||||||
for _ in current_line.len()..self.display_cols as usize {
|
for _ in current_line.len()..self.display_cols as usize {
|
||||||
current_line.push_back(&EMPTY_TERMINAL_CHARACTER);
|
current_line.push_back(&EMPTY_TERMINAL_CHARACTER);
|
||||||
|
|
@ -264,9 +274,7 @@ impl TerminalOutput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(linebreak_index) = next_linebreak_index {
|
if let Some(linebreak_index) = next_linebreak_index {
|
||||||
debug_messages.push_str(&format!("Some(linebreak_index): {:?}", linebreak_index));
|
|
||||||
if *linebreak_index == i + 1 {
|
if *linebreak_index == i + 1 {
|
||||||
debug_messages.push_str(&format!("equals i"));
|
|
||||||
// pad line
|
// pad line
|
||||||
for _ in current_line.len()..self.display_cols as usize {
|
for _ in current_line.len()..self.display_cols as usize {
|
||||||
current_line.push_back(&EMPTY_TERMINAL_CHARACTER);
|
current_line.push_back(&EMPTY_TERMINAL_CHARACTER);
|
||||||
|
|
@ -295,18 +303,15 @@ impl TerminalOutput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut debug_output = String::new();
|
|
||||||
for line in output.iter() {
|
|
||||||
for t_char in line.iter() {
|
|
||||||
debug_output.push(t_char.character);
|
|
||||||
}
|
|
||||||
debug_output.push('\n');
|
|
||||||
}
|
|
||||||
Vec::from(output)
|
Vec::from(output)
|
||||||
}
|
}
|
||||||
pub fn cursor_position_in_last_line (&self) -> usize {
|
pub fn cursor_position_in_last_line (&self) -> usize {
|
||||||
if self.cursor_position < self.characters.len() {
|
if self.cursor_position < self.characters.len() {
|
||||||
let start_of_last_line = self.index_of_beginning_of_last_line();
|
let start_of_last_line = self.index_of_beginning_of_last_line();
|
||||||
|
if self.cursor_position < start_of_last_line {
|
||||||
|
// TODO: why does this happen?
|
||||||
|
return self.display_cols as usize
|
||||||
|
};
|
||||||
let difference_from_last_newline = self.cursor_position - start_of_last_line;
|
let difference_from_last_newline = self.cursor_position - start_of_last_line;
|
||||||
difference_from_last_newline
|
difference_from_last_newline
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -644,7 +649,7 @@ fn split_horizontally_with_gap (rect: &Winsize) -> (Winsize, Winsize) {
|
||||||
enum ScreenInstruction {
|
enum ScreenInstruction {
|
||||||
Pty(RawFd, VteEvent),
|
Pty(RawFd, VteEvent),
|
||||||
Render,
|
Render,
|
||||||
AddTerminal(RawFd, Winsize),
|
AddTerminal(RawFd),
|
||||||
WriteCharacter(u8),
|
WriteCharacter(u8),
|
||||||
ResizeLeft,
|
ResizeLeft,
|
||||||
ResizeRight,
|
ResizeRight,
|
||||||
|
|
@ -653,12 +658,11 @@ enum ScreenInstruction {
|
||||||
|
|
||||||
struct Screen {
|
struct Screen {
|
||||||
pub receiver: Receiver<ScreenInstruction>,
|
pub receiver: Receiver<ScreenInstruction>,
|
||||||
pub sender: Sender<ScreenInstruction>,
|
pub send_screen_instructions: Sender<ScreenInstruction>,
|
||||||
full_screen_ws: Winsize,
|
full_screen_ws: Winsize,
|
||||||
vertical_separator: TerminalCharacter, // TODO: better
|
vertical_separator: TerminalCharacter, // TODO: better
|
||||||
|
terminals: HashMap<RawFd, TerminalOutput>,
|
||||||
active_terminal: Option<RawFd>,
|
active_terminal: Option<RawFd>,
|
||||||
terminal1_output: Option<TerminalOutput>,
|
|
||||||
terminal2_output: Option<TerminalOutput>,
|
|
||||||
os_api: Box<dyn OsApi>,
|
os_api: Box<dyn OsApi>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -667,130 +671,311 @@ impl Screen {
|
||||||
let (sender, receiver): (Sender<ScreenInstruction>, Receiver<ScreenInstruction>) = channel();
|
let (sender, receiver): (Sender<ScreenInstruction>, Receiver<ScreenInstruction>) = channel();
|
||||||
Screen {
|
Screen {
|
||||||
receiver,
|
receiver,
|
||||||
sender,
|
send_screen_instructions: sender,
|
||||||
full_screen_ws: full_screen_ws.clone(),
|
full_screen_ws: full_screen_ws.clone(),
|
||||||
vertical_separator: TerminalCharacter::new('|').ansi_code(String::from("\u{1b}[m")), // TODO: better
|
vertical_separator: TerminalCharacter::new('│').ansi_code(String::from("\u{1b}[m")), // TODO: better
|
||||||
terminal1_output: None,
|
terminals: HashMap::new(),
|
||||||
terminal2_output: None,
|
|
||||||
active_terminal: None,
|
active_terminal: None,
|
||||||
os_api,
|
os_api,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn add_terminal(&mut self, pid: RawFd, ws: Winsize) {
|
pub fn add_terminal(&mut self, pid: RawFd) {
|
||||||
if self.terminal1_output.is_none() {
|
if self.terminals.is_empty() {
|
||||||
let terminal1_x = 0;
|
let x = 0;
|
||||||
self.terminal1_output = Some(TerminalOutput::new(pid, ws, terminal1_x));
|
let new_terminal = TerminalOutput::new(pid, self.full_screen_ws.clone(), x);
|
||||||
|
self.os_api.set_terminal_size_using_fd(new_terminal.pid, new_terminal.display_cols, new_terminal.display_rows);
|
||||||
|
self.terminals.insert(pid, new_terminal);
|
||||||
self.active_terminal = Some(pid);
|
self.active_terminal = Some(pid);
|
||||||
} else if self.terminal2_output.is_none() {
|
|
||||||
let terminal2_x = self.terminal1_output.as_ref().unwrap().display_cols + 1;
|
|
||||||
self.terminal2_output = Some(TerminalOutput::new(pid, ws, terminal2_x));
|
|
||||||
} else {
|
} else {
|
||||||
panic!("cannot support more than 2 terminals atm");
|
// TODO: check minimum size of active terminal
|
||||||
|
let (active_terminal_ws, active_terminal_x_coords) = {
|
||||||
|
let active_terminal = &self.get_active_terminal().unwrap();
|
||||||
|
(
|
||||||
|
Winsize {
|
||||||
|
ws_row: active_terminal.display_rows,
|
||||||
|
ws_col: active_terminal.display_cols,
|
||||||
|
ws_xpixel: 0,
|
||||||
|
ws_ypixel: 0,
|
||||||
|
},
|
||||||
|
active_terminal.x_coords
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let (left_winszie, right_winsize) = split_horizontally_with_gap(&active_terminal_ws);
|
||||||
|
let right_side_x = active_terminal_x_coords + left_winszie.ws_col + 1;
|
||||||
|
let new_terminal = TerminalOutput::new(pid, right_winsize, right_side_x);
|
||||||
|
self.os_api.set_terminal_size_using_fd(new_terminal.pid, right_winsize.ws_col, right_winsize.ws_row);
|
||||||
|
|
||||||
|
{
|
||||||
|
let active_terminal_id = &self.get_active_terminal_id().unwrap();
|
||||||
|
let active_terminal = &mut self.terminals.get_mut(&active_terminal_id).unwrap();
|
||||||
|
active_terminal.change_size(&left_winszie);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.terminals.insert(pid, new_terminal);
|
||||||
|
let active_terminal_pid = self.get_active_terminal_id().unwrap();
|
||||||
|
self.os_api.set_terminal_size_using_fd(active_terminal_pid, left_winszie.ws_col, left_winszie.ws_row);
|
||||||
|
self.active_terminal = Some(pid);
|
||||||
|
self.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn get_active_terminal (&self) -> Option<&TerminalOutput> {
|
||||||
|
match self.active_terminal {
|
||||||
|
Some(active_terminal) => self.terminals.get(&active_terminal),
|
||||||
|
None => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn get_active_terminal_id (&self) -> Option<RawFd> {
|
||||||
|
match self.active_terminal {
|
||||||
|
Some(active_terminal) => Some(self.terminals.get(&active_terminal).unwrap().pid),
|
||||||
|
None => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn handle_pty_event(&mut self, pid: RawFd, event: VteEvent) {
|
pub fn handle_pty_event(&mut self, pid: RawFd, event: VteEvent) {
|
||||||
if let Some(terminal_output) = self.terminal1_output.as_mut() {
|
let terminal_output = self.terminals.get_mut(&pid).unwrap();
|
||||||
if terminal_output.pid == pid {
|
|
||||||
terminal_output.handle_event(event);
|
terminal_output.handle_event(event);
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(terminal_output) = self.terminal2_output.as_mut() {
|
|
||||||
if terminal_output.pid == pid {
|
|
||||||
terminal_output.handle_event(event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub fn write_to_active_terminal(&self, byte: u8) {
|
pub fn write_to_active_terminal(&self, byte: u8) {
|
||||||
if let Some(active_terminal) = &self.active_terminal {
|
if let Some(active_terminal_id) = &self.get_active_terminal_id() {
|
||||||
let mut buffer = [byte];
|
let mut buffer = [byte];
|
||||||
self.os_api.write(*active_terminal, &mut buffer).expect("failed to write to terminal");
|
self.os_api.write(*active_terminal_id, &mut buffer).expect("failed to write to terminal");
|
||||||
self.os_api.tcdrain(*active_terminal).expect("failed to drain terminal");
|
self.os_api.tcdrain(*active_terminal_id).expect("failed to drain terminal");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn get_active_terminal_cursor_position(&self) -> usize {
|
||||||
|
let active_terminal = &self.get_active_terminal().unwrap();
|
||||||
|
active_terminal.x_coords as usize + active_terminal.cursor_position_in_last_line()
|
||||||
|
}
|
||||||
pub fn render (&mut self) {
|
pub fn render (&mut self) {
|
||||||
let vte_output_terminal1 = self.terminal1_output.as_mut().unwrap().buffer_as_vte_output();
|
for (_pid, terminal) in self.terminals.iter_mut() {
|
||||||
let vte_output_terminal2 = self.terminal2_output.as_mut().unwrap().buffer_as_vte_output();
|
if let Some(vte_output) = terminal.buffer_as_vte_output() {
|
||||||
|
if terminal.x_coords + terminal.display_cols < self.full_screen_ws.ws_col {
|
||||||
|
let boundary_x_coords = terminal.x_coords + terminal.display_cols;
|
||||||
let mut vte_output_boundaries = String::new();
|
let mut vte_output_boundaries = String::new();
|
||||||
for row in 0..self.full_screen_ws.ws_row {
|
for row in 0..self.full_screen_ws.ws_row {
|
||||||
vte_output_boundaries.push_str(&format!("\u{1b}[{};{}H\u{1b}[m", row + 1, self.terminal1_output.as_ref().unwrap().display_cols + 1)); // goto row/col
|
vte_output_boundaries.push_str(&format!("\u{1b}[{};{}H\u{1b}[m", row + 1, boundary_x_coords + 1)); // goto row/col
|
||||||
vte_output_boundaries.push_str(&self.vertical_separator.to_string());
|
vte_output_boundaries.push_str(&self.vertical_separator.to_string());
|
||||||
}
|
}
|
||||||
let left_terminal_cursor_position = self.terminal1_output.as_ref().unwrap().cursor_position_in_last_line();
|
|
||||||
let right_terminal_cursor_position = self.terminal2_output.as_ref().unwrap().cursor_position_in_last_line();
|
|
||||||
|
|
||||||
let active_terminal = self.active_terminal.unwrap();
|
|
||||||
if active_terminal == self.terminal1_output.as_ref().unwrap().pid {
|
|
||||||
let goto_cursor_position = format!("\r\u{1b}[{}C", left_terminal_cursor_position);
|
|
||||||
vte_output_boundaries.push_str(&goto_cursor_position);
|
|
||||||
} else {
|
|
||||||
let goto_cursor_position = format!("\r\u{1b}[{}C", right_terminal_cursor_position + (self.terminal1_output.as_ref().unwrap().display_cols + 1) as usize);
|
|
||||||
vte_output_boundaries.push_str(&goto_cursor_position);
|
|
||||||
}
|
|
||||||
if let Some(vte_output_terminal1) = vte_output_terminal1 {
|
|
||||||
::std::io::stdout().write_all(&vte_output_terminal1.as_bytes()).expect("cannot write to stdout");
|
|
||||||
}
|
|
||||||
if let Some(vte_output_terminal2) = vte_output_terminal2 {
|
|
||||||
::std::io::stdout().write_all(&vte_output_terminal2.as_bytes()).expect("cannot write to stdout");
|
|
||||||
}
|
|
||||||
// ::std::io::stdout().write_all(&vte_output_terminal2.as_bytes()).expect("cannot write to stdout");
|
|
||||||
::std::io::stdout().write_all(&vte_output_boundaries.as_bytes()).expect("cannot write to stdout");
|
::std::io::stdout().write_all(&vte_output_boundaries.as_bytes()).expect("cannot write to stdout");
|
||||||
|
}
|
||||||
|
::std::io::stdout().write_all(&vte_output.as_bytes()).expect("cannot write to stdout");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let active_terminal_cursor_position = self.get_active_terminal_cursor_position();
|
||||||
|
let goto_cursor_position = format!("\r\u{1b}[{}C", active_terminal_cursor_position);
|
||||||
|
::std::io::stdout().write_all(&goto_cursor_position.as_bytes()).expect("cannot write to stdout");
|
||||||
::std::io::stdout().flush().expect("could not flush");
|
::std::io::stdout().flush().expect("could not flush");
|
||||||
}
|
}
|
||||||
|
fn terminal_ids_directly_left_of(&self, id: &RawFd) -> Option<Vec<RawFd>> {
|
||||||
|
let mut ids = vec![];
|
||||||
|
let terminal_to_check = self.terminals.get(id).unwrap();
|
||||||
|
if terminal_to_check.x_coords == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
for (pid, terminal) in self.terminals.iter() {
|
||||||
|
if terminal.x_coords + terminal.display_cols == terminal_to_check.x_coords - 1 {
|
||||||
|
ids.push(*pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ids.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(ids)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn terminal_ids_directly_right_of(&self, id: &RawFd) -> Option<Vec<RawFd>> {
|
||||||
|
let mut ids = vec![];
|
||||||
|
let terminal_to_check = self.terminals.get(id).unwrap();
|
||||||
|
for (pid, terminal) in self.terminals.iter() {
|
||||||
|
if terminal.x_coords == terminal_to_check.x_coords + terminal_to_check.display_cols + 1 {
|
||||||
|
ids.push(*pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ids.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(ids)
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn resize_left (&mut self) {
|
pub fn resize_left (&mut self) {
|
||||||
let terminal1_output = self.terminal1_output.as_mut().unwrap();
|
// TODO: find out by how much we actually reduced and only reduce by that much
|
||||||
let terminal2_output = self.terminal2_output.as_mut().unwrap();
|
let count = 10;
|
||||||
terminal1_output.reduce_width_left(10);
|
if let Some(active_terminal_id) = self.get_active_terminal_id() {
|
||||||
terminal2_output.increase_width_left(10);
|
let terminals_to_the_left = self.terminal_ids_directly_left_of(&active_terminal_id);
|
||||||
self.os_api.set_terminal_size_using_fd(terminal1_output.pid, terminal1_output.display_cols, terminal1_output.display_rows);
|
let terminals_to_the_right = self.terminal_ids_directly_right_of(&active_terminal_id);
|
||||||
self.os_api.set_terminal_size_using_fd(terminal2_output.pid, terminal2_output.display_cols, terminal2_output.display_rows);
|
match (terminals_to_the_left, terminals_to_the_right) {
|
||||||
|
(Some(terminals_to_the_left), Some(_terminals_to_the_right)) => {
|
||||||
|
let active_terminal = self.terminals.get_mut(&active_terminal_id).unwrap();
|
||||||
|
active_terminal.increase_width_left(count);
|
||||||
|
self.os_api.set_terminal_size_using_fd(
|
||||||
|
active_terminal.pid,
|
||||||
|
active_terminal.display_cols,
|
||||||
|
active_terminal.display_rows
|
||||||
|
);
|
||||||
|
for terminal_id in terminals_to_the_left {
|
||||||
|
let terminal = self.terminals.get_mut(&terminal_id).unwrap();
|
||||||
|
terminal.reduce_width_left(count);
|
||||||
|
self.os_api.set_terminal_size_using_fd(
|
||||||
|
terminal.pid,
|
||||||
|
terminal.display_cols,
|
||||||
|
terminal.display_rows
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(Some(terminals_to_the_left), None) => {
|
||||||
|
let active_terminal = self.terminals.get_mut(&active_terminal_id).unwrap();
|
||||||
|
active_terminal.increase_width_left(count);
|
||||||
|
self.os_api.set_terminal_size_using_fd(
|
||||||
|
active_terminal.pid,
|
||||||
|
active_terminal.display_cols,
|
||||||
|
active_terminal.display_rows
|
||||||
|
);
|
||||||
|
for terminal_id in terminals_to_the_left {
|
||||||
|
let terminal = self.terminals.get_mut(&terminal_id).unwrap();
|
||||||
|
terminal.reduce_width_left(count);
|
||||||
|
self.os_api.set_terminal_size_using_fd(
|
||||||
|
terminal.pid,
|
||||||
|
terminal.display_cols,
|
||||||
|
terminal.display_rows
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(None, Some(terminals_to_the_right)) => {
|
||||||
|
let active_terminal = self.terminals.get_mut(&active_terminal_id).unwrap();
|
||||||
|
active_terminal.reduce_width_left(count);
|
||||||
|
self.os_api.set_terminal_size_using_fd(
|
||||||
|
active_terminal.pid,
|
||||||
|
active_terminal.display_cols,
|
||||||
|
active_terminal.display_rows
|
||||||
|
);
|
||||||
|
for terminal_id in terminals_to_the_right {
|
||||||
|
let terminal = self.terminals.get_mut(&terminal_id).unwrap();
|
||||||
|
terminal.increase_width_left(count);
|
||||||
|
self.os_api.set_terminal_size_using_fd(
|
||||||
|
terminal.pid,
|
||||||
|
terminal.display_cols,
|
||||||
|
terminal.display_rows
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn resize_right (&mut self) {
|
pub fn resize_right (&mut self) {
|
||||||
let terminal1_output = self.terminal1_output.as_mut().unwrap();
|
let count = 10;
|
||||||
let terminal2_output = self.terminal2_output.as_mut().unwrap();
|
if let Some(active_terminal_id) = self.get_active_terminal_id() {
|
||||||
terminal1_output.increase_width_right(10);
|
let terminals_to_the_left = self.terminal_ids_directly_left_of(&active_terminal_id);
|
||||||
terminal2_output.reduce_width_right(10);
|
let terminals_to_the_right = self.terminal_ids_directly_right_of(&active_terminal_id);
|
||||||
self.os_api.set_terminal_size_using_fd(terminal1_output.pid, terminal1_output.display_cols, terminal1_output.display_rows);
|
match (terminals_to_the_left, terminals_to_the_right) {
|
||||||
self.os_api.set_terminal_size_using_fd(terminal2_output.pid, terminal2_output.display_cols, terminal2_output.display_rows);
|
(Some(_terminals_to_the_left), Some(terminals_to_the_right)) => {
|
||||||
|
let active_terminal = self.terminals.get_mut(&active_terminal_id).unwrap();
|
||||||
|
active_terminal.increase_width_right(count);
|
||||||
|
self.os_api.set_terminal_size_using_fd(
|
||||||
|
active_terminal.pid,
|
||||||
|
active_terminal.display_cols,
|
||||||
|
active_terminal.display_rows
|
||||||
|
);
|
||||||
|
for terminal_id in terminals_to_the_right {
|
||||||
|
let terminal = self.terminals.get_mut(&terminal_id).unwrap();
|
||||||
|
terminal.reduce_width_right(count);
|
||||||
|
self.os_api.set_terminal_size_using_fd(
|
||||||
|
terminal.pid,
|
||||||
|
terminal.display_cols,
|
||||||
|
terminal.display_rows
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(Some(terminals_to_the_left), None) => {
|
||||||
|
let active_terminal = self.terminals.get_mut(&active_terminal_id).unwrap();
|
||||||
|
active_terminal.reduce_width_right(count);
|
||||||
|
self.os_api.set_terminal_size_using_fd(
|
||||||
|
active_terminal.pid,
|
||||||
|
active_terminal.display_cols,
|
||||||
|
active_terminal.display_rows
|
||||||
|
);
|
||||||
|
for terminal_id in terminals_to_the_left {
|
||||||
|
let terminal = self.terminals.get_mut(&terminal_id).unwrap();
|
||||||
|
terminal.increase_width_right(count);
|
||||||
|
self.os_api.set_terminal_size_using_fd(
|
||||||
|
terminal.pid,
|
||||||
|
terminal.display_cols,
|
||||||
|
terminal.display_rows
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(None, Some(terminals_to_the_right)) => {
|
||||||
|
let active_terminal = self.terminals.get_mut(&active_terminal_id).unwrap();
|
||||||
|
active_terminal.increase_width_right(count);
|
||||||
|
self.os_api.set_terminal_size_using_fd(
|
||||||
|
active_terminal.pid,
|
||||||
|
active_terminal.display_cols,
|
||||||
|
active_terminal.display_rows
|
||||||
|
);
|
||||||
|
for terminal_id in terminals_to_the_right {
|
||||||
|
let terminal = self.terminals.get_mut(&terminal_id).unwrap();
|
||||||
|
terminal.reduce_width_right(count);
|
||||||
|
self.os_api.set_terminal_size_using_fd(
|
||||||
|
terminal.pid,
|
||||||
|
terminal.display_cols,
|
||||||
|
terminal.display_rows
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn move_focus(&mut self) {
|
pub fn move_focus(&mut self) {
|
||||||
let terminal1_output = self.terminal1_output.as_ref().unwrap();
|
if self.terminals.is_empty() {
|
||||||
let terminal2_output = self.terminal2_output.as_ref().unwrap();
|
return;
|
||||||
let active_terminal = self.active_terminal.unwrap();
|
|
||||||
if active_terminal == terminal1_output.pid {
|
|
||||||
self.active_terminal = Some(terminal2_output.pid);
|
|
||||||
} else {
|
|
||||||
self.active_terminal = Some(terminal1_output.pid);
|
|
||||||
}
|
}
|
||||||
|
let active_terminal = self.get_active_terminal().unwrap();
|
||||||
|
let mut first_terminal = active_terminal.pid;
|
||||||
|
for (terminal_pid, terminal_output) in self.terminals.iter() {
|
||||||
|
if terminal_output.x_coords == 0 {
|
||||||
|
first_terminal = terminal_output.pid
|
||||||
|
} else if active_terminal.x_coords + active_terminal.display_cols == terminal_output.x_coords - 1 {
|
||||||
|
self.active_terminal = Some(*terminal_pid);
|
||||||
|
self.render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.active_terminal = Some(first_terminal);
|
||||||
self.render();
|
self.render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum PtyInstruction {
|
||||||
|
SpawnTerminal
|
||||||
|
}
|
||||||
|
|
||||||
struct PtyBus {
|
struct PtyBus {
|
||||||
sender: Sender<ScreenInstruction>,
|
receive_pty_instructions: Receiver<PtyInstruction>,
|
||||||
|
send_pty_instructions: Sender<PtyInstruction>,
|
||||||
|
send_screen_instructions: Sender<ScreenInstruction>,
|
||||||
active_ptys: Vec<JoinHandle<()>>,
|
active_ptys: Vec<JoinHandle<()>>,
|
||||||
os_input: Box<dyn OsApi>,
|
os_input: Box<dyn OsApi>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PtyBus {
|
impl PtyBus {
|
||||||
pub fn new (sender: Sender<ScreenInstruction>, os_input: Box<dyn OsApi>) -> Self {
|
pub fn new (send_screen_instructions: Sender<ScreenInstruction>, os_input: Box<dyn OsApi>) -> Self {
|
||||||
|
let (send_pty_instructions, receive_pty_instructions): (Sender<PtyInstruction>, Receiver<PtyInstruction>) = channel();
|
||||||
PtyBus {
|
PtyBus {
|
||||||
sender,
|
send_pty_instructions,
|
||||||
|
send_screen_instructions,
|
||||||
|
receive_pty_instructions,
|
||||||
active_ptys: Vec::new(),
|
active_ptys: Vec::new(),
|
||||||
os_input,
|
os_input,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn spawn_terminal(&mut self, ws: &Winsize) {
|
pub fn spawn_terminal(&mut self) {
|
||||||
let ws = *ws;
|
let (pid_primary, _pid_secondary): (RawFd, RawFd) = self.os_input.spawn_terminal();
|
||||||
let (pid_primary, _pid_secondary): (RawFd, RawFd) = self.os_input.spawn_terminal(&ws);
|
|
||||||
let task_handle = task::spawn({
|
let task_handle = task::spawn({
|
||||||
let sender = self.sender.clone();
|
let send_screen_instructions = self.send_screen_instructions.clone();
|
||||||
let os_input = self.os_input.clone();
|
let os_input = self.os_input.clone();
|
||||||
async move {
|
async move {
|
||||||
let mut vte_parser = vte::Parser::new();
|
let mut vte_parser = vte::Parser::new();
|
||||||
let mut vte_event_sender = VteEventSender::new(pid_primary, sender.clone());
|
let mut vte_event_sender = VteEventSender::new(pid_primary, send_screen_instructions.clone());
|
||||||
let mut first_terminal_bytes = ReadFromPid::new(&pid_primary, os_input);
|
let mut first_terminal_bytes = ReadFromPid::new(&pid_primary, os_input);
|
||||||
while let Some(bytes) = first_terminal_bytes.next().await {
|
while let Some(bytes) = first_terminal_bytes.next().await {
|
||||||
let bytes_is_empty = bytes.is_empty();
|
let bytes_is_empty = bytes.is_empty();
|
||||||
|
|
@ -798,15 +983,15 @@ impl PtyBus {
|
||||||
vte_parser.advance(&mut vte_event_sender, byte);
|
vte_parser.advance(&mut vte_event_sender, byte);
|
||||||
}
|
}
|
||||||
if !bytes_is_empty {
|
if !bytes_is_empty {
|
||||||
sender.send(ScreenInstruction::Render).unwrap();
|
send_screen_instructions.send(ScreenInstruction::Render).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
self.sender.send(ScreenInstruction::AddTerminal(pid_primary, ws)).unwrap();
|
self.send_screen_instructions.send(ScreenInstruction::AddTerminal(pid_primary)).unwrap();
|
||||||
self.active_ptys.push(task_handle);
|
self.active_ptys.push(task_handle);
|
||||||
}
|
}
|
||||||
pub async fn wait_for_tasks(&mut self) {
|
pub async fn _wait_for_tasks(&mut self) {
|
||||||
// let task1 = self.active_ptys.get_mut(0).unwrap();
|
// let task1 = self.active_ptys.get_mut(0).unwrap();
|
||||||
// task1.await;
|
// task1.await;
|
||||||
let mut v = vec![];
|
let mut v = vec![];
|
||||||
|
|
@ -831,25 +1016,28 @@ fn start(os_input: OsInputOutput) {
|
||||||
let full_screen_ws = os_input.get_terminal_size_using_fd(0);
|
let full_screen_ws = os_input.get_terminal_size_using_fd(0);
|
||||||
os_input.into_raw_mode(0);
|
os_input.into_raw_mode(0);
|
||||||
let mut screen = Screen::new(&full_screen_ws, Box::new(os_input.clone()));
|
let mut screen = Screen::new(&full_screen_ws, Box::new(os_input.clone()));
|
||||||
let send_screen_instructions = screen.sender.clone();
|
let send_screen_instructions = screen.send_screen_instructions.clone();
|
||||||
|
let mut pty_bus = PtyBus::new(send_screen_instructions.clone(), Box::new(os_input.clone()));
|
||||||
|
let send_pty_instructions = pty_bus.send_pty_instructions.clone();
|
||||||
|
|
||||||
active_threads.push(
|
active_threads.push(
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name("pty".to_string())
|
.name("pty".to_string())
|
||||||
.spawn({
|
.spawn({
|
||||||
let (first_terminal_ws, second_terminal_ws) = split_horizontally_with_gap(&full_screen_ws);
|
|
||||||
let first_terminal_ws = first_terminal_ws.clone();
|
|
||||||
let second_terminal_ws = second_terminal_ws.clone();
|
|
||||||
let send_screen_instructions = send_screen_instructions.clone();
|
|
||||||
let os_input = os_input.clone();
|
|
||||||
move || {
|
move || {
|
||||||
let mut pty_bus = PtyBus::new(send_screen_instructions, Box::new(os_input));
|
pty_bus.spawn_terminal();
|
||||||
// this is done here so that we can add terminals dynamically on a different
|
loop {
|
||||||
// thread later
|
let event = pty_bus.receive_pty_instructions
|
||||||
pty_bus.spawn_terminal(&first_terminal_ws);
|
.recv()
|
||||||
pty_bus.spawn_terminal(&second_terminal_ws);
|
.expect("failed to receive event on channel");
|
||||||
task::block_on(pty_bus.wait_for_tasks());
|
match event {
|
||||||
|
PtyInstruction::SpawnTerminal => {
|
||||||
|
pty_bus.spawn_terminal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: do this when we exit
|
||||||
|
// task::block_on(pty_bus.wait_for_tasks());
|
||||||
}
|
}
|
||||||
}).unwrap()
|
}).unwrap()
|
||||||
);
|
);
|
||||||
|
|
@ -870,8 +1058,8 @@ fn start(os_input: OsInputOutput) {
|
||||||
ScreenInstruction::Render => {
|
ScreenInstruction::Render => {
|
||||||
screen.render();
|
screen.render();
|
||||||
},
|
},
|
||||||
ScreenInstruction::AddTerminal(pid, ws) => {
|
ScreenInstruction::AddTerminal(pid) => {
|
||||||
screen.add_terminal(pid, ws);
|
screen.add_terminal(pid);
|
||||||
}
|
}
|
||||||
ScreenInstruction::WriteCharacter(byte) => {
|
ScreenInstruction::WriteCharacter(byte) => {
|
||||||
screen.write_to_active_terminal(byte);
|
screen.write_to_active_terminal(byte);
|
||||||
|
|
@ -905,6 +1093,9 @@ fn start(os_input: OsInputOutput) {
|
||||||
} else if buffer[0] == 16 { // ctrl-p
|
} else if buffer[0] == 16 { // ctrl-p
|
||||||
send_screen_instructions.send(ScreenInstruction::MoveFocus).unwrap();
|
send_screen_instructions.send(ScreenInstruction::MoveFocus).unwrap();
|
||||||
continue;
|
continue;
|
||||||
|
} else if buffer[0] == 14 { // ctrl-n
|
||||||
|
send_pty_instructions.send(PtyInstruction::SpawnTerminal).unwrap();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
send_screen_instructions.send(ScreenInstruction::WriteCharacter(buffer[0])).unwrap();
|
send_screen_instructions.send(ScreenInstruction::WriteCharacter(buffer[0])).unwrap();
|
||||||
|
|
|
||||||
|
|
@ -53,9 +53,9 @@ pub fn set_terminal_size_using_fd(fd: RawFd, columns: u16, rows: u16) {
|
||||||
unsafe { ioctl(fd, TIOCSWINSZ.into(), &winsize) };
|
unsafe { ioctl(fd, TIOCSWINSZ.into(), &winsize) };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_terminal (ws: &Winsize) -> (RawFd, RawFd) {
|
fn spawn_terminal () -> (RawFd, RawFd) {
|
||||||
let (pid_primary, pid_secondary): (RawFd, RawFd) = {
|
let (pid_primary, pid_secondary): (RawFd, RawFd) = {
|
||||||
match forkpty(Some(ws), None) {
|
match forkpty(None, None) {
|
||||||
Ok(fork_pty_res) => {
|
Ok(fork_pty_res) => {
|
||||||
let pid_primary = fork_pty_res.master;
|
let pid_primary = fork_pty_res.master;
|
||||||
let pid_secondary = match fork_pty_res.fork_result {
|
let pid_secondary = match fork_pty_res.fork_result {
|
||||||
|
|
@ -92,7 +92,7 @@ pub trait OsApi: Send + Sync {
|
||||||
fn get_terminal_size_using_fd(&self, pid: RawFd) -> Winsize;
|
fn get_terminal_size_using_fd(&self, pid: RawFd) -> Winsize;
|
||||||
fn set_terminal_size_using_fd(&self, pid: RawFd, cols: u16, rows: u16);
|
fn set_terminal_size_using_fd(&self, pid: RawFd, cols: u16, rows: u16);
|
||||||
fn into_raw_mode(&self, pid: RawFd);
|
fn into_raw_mode(&self, pid: RawFd);
|
||||||
fn spawn_terminal(&self, ws: &Winsize) -> (RawFd, RawFd);
|
fn spawn_terminal(&self) -> (RawFd, RawFd);
|
||||||
fn read(&self, pid: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error>;
|
fn read(&self, pid: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error>;
|
||||||
fn write(&self, pid: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error>;
|
fn write(&self, pid: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error>;
|
||||||
fn tcdrain(&self, pid: RawFd) -> Result<(), nix::Error>;
|
fn tcdrain(&self, pid: RawFd) -> Result<(), nix::Error>;
|
||||||
|
|
@ -110,8 +110,8 @@ impl OsApi for OsInputOutput {
|
||||||
fn into_raw_mode(&self, pid: RawFd) {
|
fn into_raw_mode(&self, pid: RawFd) {
|
||||||
into_raw_mode(pid);
|
into_raw_mode(pid);
|
||||||
}
|
}
|
||||||
fn spawn_terminal(&self, ws: &Winsize) -> (RawFd, RawFd) {
|
fn spawn_terminal(&self) -> (RawFd, RawFd) {
|
||||||
spawn_terminal(ws)
|
spawn_terminal()
|
||||||
}
|
}
|
||||||
fn read(&self, pid: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error> {
|
fn read(&self, pid: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error> {
|
||||||
read(pid, buf)
|
read(pid, buf)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue