fix(scroll): support show/hide cursor and fix vim scrolling behaviour (#27)
This commit is contained in:
parent
cc347ab24a
commit
13af16b336
8 changed files with 92 additions and 38 deletions
|
|
@ -269,7 +269,7 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
|
||||||
send_screen_instructions.send(ScreenInstruction::ScrollDown).unwrap();
|
send_screen_instructions.send(ScreenInstruction::ScrollDown).unwrap();
|
||||||
} else if buffer[0] == 24 { // ctrl-x
|
} else if buffer[0] == 24 { // ctrl-x
|
||||||
send_screen_instructions.send(ScreenInstruction::CloseFocusedPane).unwrap();
|
send_screen_instructions.send(ScreenInstruction::CloseFocusedPane).unwrap();
|
||||||
} else if buffer[0] == 6 { // ctrl-f
|
} else if buffer[0] == 5 { // ctrl-e
|
||||||
send_screen_instructions.send(ScreenInstruction::ToggleActiveTerminalFullscreen).unwrap();
|
send_screen_instructions.send(ScreenInstruction::ToggleActiveTerminalFullscreen).unwrap();
|
||||||
} else {
|
} else {
|
||||||
// println!("\r buffer {:?} ", buffer[0]);
|
// println!("\r buffer {:?} ", buffer[0]);
|
||||||
|
|
|
||||||
|
|
@ -264,13 +264,13 @@ impl Screen {
|
||||||
self.os_api.tcdrain(*active_terminal_id).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, usize) { // (x, y)
|
fn get_active_terminal_cursor_position(&self) -> Option<(usize, usize)> { // (x, y)
|
||||||
let active_terminal = &self.get_active_terminal().unwrap();
|
let active_terminal = &self.get_active_terminal().unwrap();
|
||||||
let (x_in_terminal, y_in_terminal) = active_terminal.cursor_coordinates();
|
active_terminal.cursor_coordinates().and_then(|(x_in_terminal, y_in_terminal)| {
|
||||||
|
let x = active_terminal.get_x() + x_in_terminal;
|
||||||
let x = active_terminal.get_x() + x_in_terminal;
|
let y = active_terminal.get_y() + y_in_terminal;
|
||||||
let y = active_terminal.get_y() + y_in_terminal;
|
Some((x, y))
|
||||||
(x, y)
|
})
|
||||||
}
|
}
|
||||||
pub fn toggle_active_terminal_fullscreen(&mut self) {
|
pub fn toggle_active_terminal_fullscreen(&mut self) {
|
||||||
if let Some(active_terminal_id) = self.get_active_terminal_id() {
|
if let Some(active_terminal_id) = self.get_active_terminal_id() {
|
||||||
|
|
@ -312,10 +312,20 @@ impl Screen {
|
||||||
let vte_output = boundaries.vte_output();
|
let vte_output = boundaries.vte_output();
|
||||||
stdout.write_all(&vte_output.as_bytes()).expect("cannot write to stdout");
|
stdout.write_all(&vte_output.as_bytes()).expect("cannot write to stdout");
|
||||||
|
|
||||||
let (cursor_position_x, cursor_position_y) = self.get_active_terminal_cursor_position();
|
match self.get_active_terminal_cursor_position() {
|
||||||
let goto_cursor_position = format!("\u{1b}[{};{}H\u{1b}[m", cursor_position_y + 1, cursor_position_x + 1); // goto row/col
|
Some((cursor_position_x, cursor_position_y)) => {
|
||||||
stdout.write_all(&goto_cursor_position.as_bytes()).expect("cannot write to stdout");
|
let show_cursor = "\u{1b}[?25h";
|
||||||
stdout.flush().expect("could not flush");
|
let goto_cursor_position = format!("\u{1b}[{};{}H\u{1b}[m", cursor_position_y + 1, cursor_position_x + 1); // goto row/col
|
||||||
|
stdout.write_all(&show_cursor.as_bytes()).expect("cannot write to stdout");
|
||||||
|
stdout.write_all(&goto_cursor_position.as_bytes()).expect("cannot write to stdout");
|
||||||
|
stdout.flush().expect("could not flush");
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
let hide_cursor = "\u{1b}[?25l";
|
||||||
|
stdout.write_all(&hide_cursor.as_bytes()).expect("cannot write to stdout");
|
||||||
|
stdout.flush().expect("could not flush");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn terminal_ids_directly_left_of(&self, id: &RawFd) -> Option<Vec<RawFd>> {
|
fn terminal_ids_directly_left_of(&self, id: &RawFd) -> Option<Vec<RawFd>> {
|
||||||
let mut ids = vec![];
|
let mut ids = vec![];
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,8 @@ pub struct Scroll {
|
||||||
total_columns: usize,
|
total_columns: usize,
|
||||||
lines_in_view: usize,
|
lines_in_view: usize,
|
||||||
viewport_bottom_offset: Option<usize>,
|
viewport_bottom_offset: Option<usize>,
|
||||||
scroll_region: Option<(usize, usize)> // start line, end line (if set, this is the area the will scroll)
|
scroll_region: Option<(usize, usize)>, // start line, end line (if set, this is the area the will scroll)
|
||||||
|
show_cursor: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scroll {
|
impl Scroll {
|
||||||
|
|
@ -187,6 +188,7 @@ impl Scroll {
|
||||||
cursor_position,
|
cursor_position,
|
||||||
viewport_bottom_offset: None,
|
viewport_bottom_offset: None,
|
||||||
scroll_region: None,
|
scroll_region: None,
|
||||||
|
show_cursor: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn as_character_lines(&self) -> Vec<Vec<TerminalCharacter>> {
|
pub fn as_character_lines(&self) -> Vec<Vec<TerminalCharacter>> {
|
||||||
|
|
@ -243,21 +245,29 @@ impl Scroll {
|
||||||
self.cursor_position.move_to_beginning_of_linewrap();
|
self.cursor_position.move_to_beginning_of_linewrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn show_cursor (&mut self) {
|
||||||
|
self.show_cursor = true;
|
||||||
|
}
|
||||||
|
pub fn hide_cursor (&mut self) {
|
||||||
|
self.show_cursor = false;
|
||||||
|
}
|
||||||
pub fn add_canonical_line(&mut self) {
|
pub fn add_canonical_line(&mut self) {
|
||||||
let current_canonical_line_index = self.cursor_position.line_index.0;
|
let current_canonical_line_index = self.cursor_position.line_index.0;
|
||||||
if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
|
if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
|
||||||
// the scroll region indices start at 1, so we need to adjust them
|
// the scroll region indices start at 1, so we need to adjust them
|
||||||
let scroll_region_top_index = scroll_region_top - 1;
|
if self.show_cursor { // scroll region should be ignored if the cursor is hidden
|
||||||
let scroll_region_bottom_index = scroll_region_bottom - 1;
|
let scroll_region_top_index = scroll_region_top - 1;
|
||||||
if current_canonical_line_index == scroll_region_bottom_index { // end of scroll region
|
let scroll_region_bottom_index = scroll_region_bottom - 1;
|
||||||
// when we have a scroll region set and we're at its bottom
|
if current_canonical_line_index == scroll_region_bottom_index { // end of scroll region
|
||||||
// we need to delete its first line, thus shifting all lines in it upwards
|
// when we have a scroll region set and we're at its bottom
|
||||||
// then we add an empty line at its end which will be filled by the application
|
// we need to delete its first line, thus shifting all lines in it upwards
|
||||||
// controlling the scroll region (presumably filled by whatever comes next in the
|
// then we add an empty line at its end which will be filled by the application
|
||||||
// scroll buffer, but that's not something we control)
|
// controlling the scroll region (presumably filled by whatever comes next in the
|
||||||
self.canonical_lines.remove(scroll_region_top_index);
|
// scroll buffer, but that's not something we control)
|
||||||
self.canonical_lines.insert(scroll_region_bottom_index, CanonicalLine::new());
|
self.canonical_lines.remove(scroll_region_top_index);
|
||||||
return;
|
self.canonical_lines.insert(scroll_region_bottom_index, CanonicalLine::new());
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if current_canonical_line_index == self.canonical_lines.len() - 1 {
|
if current_canonical_line_index == self.canonical_lines.len() - 1 {
|
||||||
|
|
@ -272,7 +282,10 @@ impl Scroll {
|
||||||
panic!("cursor out of bounds, cannot add_canonical_line");
|
panic!("cursor out of bounds, cannot add_canonical_line");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn cursor_coordinates_on_screen(&self) -> (usize, usize) { // (x, y)
|
pub fn cursor_coordinates_on_screen(&self) -> Option<(usize, usize)> { // (x, y)
|
||||||
|
if !self.show_cursor {
|
||||||
|
return None
|
||||||
|
}
|
||||||
let (canonical_line_cursor_position, line_wrap_cursor_position) = self.cursor_position.line_index;
|
let (canonical_line_cursor_position, line_wrap_cursor_position) = self.cursor_position.line_index;
|
||||||
let x = self.cursor_position.column_index;
|
let x = self.cursor_position.column_index;
|
||||||
let mut y = 0;
|
let mut y = 0;
|
||||||
|
|
@ -294,10 +307,12 @@ impl Scroll {
|
||||||
let total_lines = self.canonical_lines.iter().fold(0, |total_lines, current_line| total_lines + current_line.wrapped_fragments.len()); // TODO: is this performant enough? should it be cached or kept track of?
|
let total_lines = self.canonical_lines.iter().fold(0, |total_lines, current_line| total_lines + current_line.wrapped_fragments.len()); // TODO: is this performant enough? should it be cached or kept track of?
|
||||||
let y = if total_lines < self.lines_in_view {
|
let y = if total_lines < self.lines_in_view {
|
||||||
total_lines - y
|
total_lines - y
|
||||||
|
} else if y > self.lines_in_view {
|
||||||
|
self.lines_in_view
|
||||||
} else {
|
} else {
|
||||||
self.lines_in_view - y
|
self.lines_in_view - y
|
||||||
};
|
};
|
||||||
(x, y)
|
Some((x, y))
|
||||||
}
|
}
|
||||||
pub fn move_cursor_forward(&mut self, count: usize) {
|
pub fn move_cursor_forward(&mut self, count: usize) {
|
||||||
let (current_canonical_line_index, current_line_wrap_position) = self.cursor_position.line_index;
|
let (current_canonical_line_index, current_line_wrap_position) = self.cursor_position.line_index;
|
||||||
|
|
|
||||||
|
|
@ -223,7 +223,7 @@ impl TerminalPane {
|
||||||
pub fn read_buffer_as_lines (&self) -> Vec<Vec<TerminalCharacter>> {
|
pub fn read_buffer_as_lines (&self) -> Vec<Vec<TerminalCharacter>> {
|
||||||
self.scroll.as_character_lines()
|
self.scroll.as_character_lines()
|
||||||
}
|
}
|
||||||
pub fn cursor_coordinates (&self) -> (usize, usize) { // (x, y)
|
pub fn cursor_coordinates (&self) -> Option<(usize, usize)> { // (x, y)
|
||||||
self.scroll.cursor_coordinates_on_screen()
|
self.scroll.cursor_coordinates_on_screen()
|
||||||
}
|
}
|
||||||
pub fn scroll_up(&mut self, count: usize) {
|
pub fn scroll_up(&mut self, count: usize) {
|
||||||
|
|
@ -255,8 +255,8 @@ impl TerminalPane {
|
||||||
self.should_render = true;
|
self.should_render = true;
|
||||||
}
|
}
|
||||||
fn add_newline (&mut self) {
|
fn add_newline (&mut self) {
|
||||||
self.scroll.add_canonical_line(); // TODO: handle scroll region
|
self.scroll.add_canonical_line();
|
||||||
self.reset_all_ansi_codes();
|
// self.reset_all_ansi_codes(); // TODO: find out if we should be resetting here or not
|
||||||
self.should_render = true;
|
self.should_render = true;
|
||||||
}
|
}
|
||||||
fn move_to_beginning_of_line (&mut self) {
|
fn move_to_beginning_of_line (&mut self) {
|
||||||
|
|
@ -557,14 +557,41 @@ impl vte::Perform for TerminalPane {
|
||||||
let move_back_count = if params[0] == 0 { 1 } else { params[0] as usize };
|
let move_back_count = if params[0] == 0 { 1 } else { params[0] as usize };
|
||||||
self.scroll.move_cursor_back(move_back_count);
|
self.scroll.move_cursor_back(move_back_count);
|
||||||
} else if c == 'l' {
|
} else if c == 'l' {
|
||||||
// TBD
|
let first_intermediate_is_questionmark = match _intermediates.get(0) {
|
||||||
|
Some(b'?') => true,
|
||||||
|
None => false,
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
if first_intermediate_is_questionmark {
|
||||||
|
match params.get(0) {
|
||||||
|
Some(25) => {
|
||||||
|
self.scroll.hide_cursor();
|
||||||
|
self.should_render = true;
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
} else if c == 'h' {
|
} else if c == 'h' {
|
||||||
// TBD
|
let first_intermediate_is_questionmark = match _intermediates.get(0) {
|
||||||
|
Some(b'?') => true,
|
||||||
|
None => false,
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
if first_intermediate_is_questionmark {
|
||||||
|
match params.get(0) {
|
||||||
|
Some(25) => {
|
||||||
|
self.scroll.show_cursor();
|
||||||
|
self.should_render = true;
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
} else if c == 'r' {
|
} else if c == 'r' {
|
||||||
if params.len() > 1 {
|
if params.len() > 1 {
|
||||||
let top_line_index = params[0] as usize;
|
let top_line_index = params[0] as usize;
|
||||||
let bottom_line_index = params[1] as usize;
|
let bottom_line_index = params[1] as usize;
|
||||||
self.scroll.set_scroll_region(top_line_index, bottom_line_index);
|
self.scroll.set_scroll_region(top_line_index, bottom_line_index);
|
||||||
|
self.scroll.show_cursor();
|
||||||
} else {
|
} else {
|
||||||
self.scroll.clear_scroll_region();
|
self.scroll.clear_scroll_region();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,7 @@ pub fn toggle_focused_pane_fullscreen () {
|
||||||
// split-largest_terminal * 3, toggle-fullscreen * 2, ctrl-p, toggle-fullscreen * 2, ctrl-p, toggle-fullscreen * 2, ctrl-p, toggle-fullscreen * 2 and quit
|
// split-largest_terminal * 3, toggle-fullscreen * 2, ctrl-p, toggle-fullscreen * 2, ctrl-p, toggle-fullscreen * 2, ctrl-p, toggle-fullscreen * 2 and quit
|
||||||
// (ctrl-z + ctrl-z + ctrl-z + ctrl-z + ctrl-f, ctrl-f, ctrl-p, ctrl-f, ctrl-f, ctrl-p, ctrl-f,
|
// (ctrl-z + ctrl-z + ctrl-z + ctrl-z + ctrl-f, ctrl-f, ctrl-p, ctrl-f, ctrl-f, ctrl-p, ctrl-f,
|
||||||
// ctrl-f, ctrl-p, ctrl-f, ctrl-f, ctrl-q)
|
// ctrl-f, ctrl-p, ctrl-f, ctrl-f, ctrl-q)
|
||||||
fake_input_output.add_terminal_input(&[26, 26, 26, 6, 6, 16, 6, 6, 16, 6, 6, 16, 6, 6, 17]);
|
fake_input_output.add_terminal_input(&[26, 26, 26, 5, 5, 16, 5, 5, 16, 5, 5, 16, 5, 5, 17]);
|
||||||
let mut opts = Opt::default();
|
let mut opts = Opt::default();
|
||||||
opts.max_panes = Some(4);
|
opts.max_panes = Some(4);
|
||||||
start(Box::new(fake_input_output.clone()), opts);
|
start(Box::new(fake_input_output.clone()), opts);
|
||||||
|
|
|
||||||
|
|
@ -29,4 +29,4 @@ expression: snapshot
|
||||||
│ │
|
│ │
|
||||||
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||||
Press <SPACE> to pause. Use <TAB> to rearrange tables. (DNS queries hidden).
|
Press <SPACE> to pause. Use <TAB> to rearrange tables. (DNS queries hidden).
|
||||||
Bye from Mosaic!█
|
Bye from Mosaic!
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ expression: snapshot
|
||||||
┌Utilization by process name───────────────────────────────────────────────────────────────────────────────────────┐
|
┌Utilization by process name───────────────────────────────────────────────────────────────────────────────────────┐
|
||||||
│Process Connections Up / Down │
|
│Process Connections Up / Down │
|
||||||
│ │
|
│ │
|
||||||
│firefox 3 46Bps / 5█Bps │
|
│firefox 3 46Bps / 57Bps │
|
||||||
│ │
|
│ │
|
||||||
│ │
|
│ │
|
||||||
│ │
|
│ │
|
||||||
|
|
|
||||||
|
|
@ -14,15 +14,17 @@ pub fn get_output_frame_snapshots(output_frames: &[Vec<u8>], win_size: &Winsize)
|
||||||
vte_parser.advance(&mut terminal_output, *byte);
|
vte_parser.advance(&mut terminal_output, *byte);
|
||||||
}
|
}
|
||||||
let output_lines = terminal_output.read_buffer_as_lines();
|
let output_lines = terminal_output.read_buffer_as_lines();
|
||||||
let (cursor_x, cursor_y) = terminal_output.cursor_coordinates();
|
let cursor_coordinates = terminal_output.cursor_coordinates();
|
||||||
let mut snapshot = String::new();
|
let mut snapshot = String::new();
|
||||||
for (line_index, line) in output_lines.iter().enumerate() {
|
for (line_index, line) in output_lines.iter().enumerate() {
|
||||||
for (character_index, terminal_character) in line.iter().enumerate() {
|
for (character_index, terminal_character) in line.iter().enumerate() {
|
||||||
if line_index == cursor_y && character_index == cursor_x {
|
if let Some((cursor_x, cursor_y)) = cursor_coordinates {
|
||||||
snapshot.push('█');
|
if line_index == cursor_y && character_index == cursor_x {
|
||||||
} else {
|
snapshot.push('█');
|
||||||
snapshot.push(terminal_character.character);
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
snapshot.push(terminal_character.character);
|
||||||
}
|
}
|
||||||
if line_index != output_lines.len() - 1 {
|
if line_index != output_lines.len() - 1 {
|
||||||
snapshot.push('\n');
|
snapshot.push('\n');
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue