fic(compatibility): htop scrolling issues (#77)

* fix(compatibility): scrolling inside scroll region with 'M' esc dispatch

* style(format): make rustfmt happy

* fix(tests): send proper data

* fix(logs): do not crash if log file doesn't exist
This commit is contained in:
Aram Drevekenin 2020-11-30 09:40:24 +01:00 committed by GitHub
parent ea549f151e
commit c47fed927e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 179 additions and 16 deletions

Binary file not shown.

BIN
htop-debugging/mosaic-3.log Normal file

Binary file not shown.

View File

@ -450,10 +450,11 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
}
// cleanup();
let reset_style = "\u{1b}[m";
let show_cursor = "\u{1b}[?25h";
let goto_start_of_last_line = format!("\u{1b}[{};{}H", full_screen_ws.rows, 1);
let goodbye_message = format!(
"{}\n{}Bye from Mosaic!",
goto_start_of_last_line, reset_style
"{}\n{}{}Bye from Mosaic!",
goto_start_of_last_line, reset_style, show_cursor
);
os_input.unset_raw_mode(0);

View File

@ -11,7 +11,7 @@ use std::path::PathBuf;
use crate::layout::Layout;
use crate::os_input_output::OsApi;
use crate::utils::logging::debug_to_file;
use crate::utils::logging::{debug_log_to_file, debug_to_file};
use crate::ScreenInstruction;
pub struct ReadFromPid {

View File

@ -135,6 +135,28 @@ impl CanonicalLine {
self.wrapped_fragments.truncate(fragment_index + 1);
}
pub fn replace_with_empty_chars_before_cursor(
&mut self,
fragment_index: usize,
until_col: usize,
style_of_empty_space: CharacterStyles,
) {
let mut empty_char_character = EMPTY_TERMINAL_CHARACTER;
empty_char_character.styles = style_of_empty_space;
if fragment_index > 0 {
for i in 0..(fragment_index - 1) {
let fragment = self.wrapped_fragments.get_mut(i).unwrap();
for i in 0..fragment.characters.len() {
fragment.add_character(empty_char_character, i);
}
}
}
let current_fragment = self.wrapped_fragments.get_mut(fragment_index).unwrap();
for i in 0..until_col {
current_fragment.add_character(empty_char_character, i);
}
}
}
impl Debug for CanonicalLine {
@ -331,21 +353,18 @@ impl Scroll {
pub fn add_canonical_line(&mut self) {
let current_canonical_line_index = self.cursor_position.line_index.0;
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
if self.show_cursor {
// scroll region should be ignored if the cursor is hidden
let scroll_region_top_index = scroll_region_top - 1;
let scroll_region_bottom_index = scroll_region_bottom - 1;
if current_canonical_line_index == scroll_region_bottom_index + 1 {
if current_canonical_line_index == scroll_region_bottom {
// end of scroll region
// when we have a scroll region set and we're at its bottom
// we need to delete its first line, thus shifting all lines in it upwards
// then we add an empty line at its end which will be filled by the application
// controlling the scroll region (presumably filled by whatever comes next in the
// scroll buffer, but that's not something we control)
self.canonical_lines.remove(scroll_region_top_index);
self.canonical_lines.remove(scroll_region_top);
self.canonical_lines
.insert(scroll_region_bottom_index + 1, CanonicalLine::new());
.insert(scroll_region_bottom, CanonicalLine::new());
return;
}
}
@ -473,6 +492,20 @@ impl Scroll {
style_of_empty_space,
);
}
pub fn clear_canonical_line_left_of_cursor(&mut self, style_of_empty_space: CharacterStyles) {
let (current_canonical_line_index, current_line_wrap_position) =
self.cursor_position.line_index;
let current_cursor_column_position = self.cursor_position.column_index;
let current_canonical_line = self
.canonical_lines
.get_mut(current_canonical_line_index)
.expect("cursor out of bounds");
current_canonical_line.replace_with_empty_chars_before_cursor(
current_line_wrap_position,
current_cursor_column_position,
style_of_empty_space,
);
}
pub fn clear_all_after_cursor(&mut self) {
let (current_canonical_line_index, current_line_wrap_position) =
self.cursor_position.line_index;
@ -592,6 +625,25 @@ impl Scroll {
}
}
}
pub fn move_cursor_up_in_scroll_region(&mut self, count: usize) {
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
for _ in 0..count {
let current_canonical_line_index = self.cursor_position.line_index.0;
if current_canonical_line_index == scroll_region_top {
// if we're at the top line, we create a new line and remove the last line that
// would otherwise overflow
self.canonical_lines.remove(scroll_region_bottom);
self.canonical_lines
.insert(current_canonical_line_index, CanonicalLine::new());
} else if current_canonical_line_index > scroll_region_top
&& current_canonical_line_index <= scroll_region_bottom
{
self.move_cursor_up(count);
}
}
}
}
/// [scroll_up](https://github.com/alacritty/alacritty/blob/ec42b42ce601808070462111c0c28edb0e89babb/alacritty_terminal/src/grid/mod.rs#L261)
/// This function takes the first line of the scroll region and moves it to the bottom (count times)
pub fn rotate_scroll_region_up(&mut self, count: usize) {

View File

@ -687,8 +687,11 @@ impl vte::Perform for TerminalPane {
if params[0] == 0 {
self.scroll
.clear_canonical_line_right_of_cursor(self.pending_styles);
} else if params[0] == 1 {
self.scroll
.clear_canonical_line_left_of_cursor(self.pending_styles);
}
// TODO: implement 1 and 2
// TODO: implement 2
} else if c == 'J' {
// clear all (0 => below, 1 => above, 2 => all, 3 => saved)
if params[0] == 0 {
@ -826,12 +829,21 @@ impl vte::Perform for TerminalPane {
self.scroll.delete_lines_in_scroll_region(count);
self.scroll.add_empty_lines_in_scroll_region(count);
} else {
debug_log_to_file(format!("Unhandled csi: {}->{:?}", c, params)).unwrap();
panic!("unhandled csi: {}->{:?}", c, params);
let _ = debug_log_to_file(format!("Unhandled csi: {}->{:?}", c, params));
}
}
fn esc_dispatch(&mut self, _intermediates: &[u8], _ignore: bool, _byte: u8) {
// TBD
fn esc_dispatch(&mut self, intermediates: &[u8], _ignore: bool, byte: u8) {
match (byte, intermediates.get(0)) {
(b'M', None) => {
self.scroll.move_cursor_up_in_scroll_region(1);
}
_ => {
let _ = debug_log_to_file(format!(
"Unhandled esc_dispatch: {}->{:?}",
byte, intermediates
));
}
}
}
}

BIN
src/tests/fixtures/htop_scrolling vendored Normal file

Binary file not shown.

View File

@ -214,3 +214,26 @@ pub fn htop() {
assert_snapshot!(snapshot);
}
}
#[test]
pub fn htop_scrolling() {
let fake_win_size = PositionAndSize {
columns: 116,
rows: 28,
x: 0,
y: 0,
};
let fixture_name = "htop_scrolling";
let mut fake_input_output = get_fake_os_input(&fake_win_size, fixture_name);
fake_input_output.add_terminal_input(&[&COMMAND_TOGGLE, &COMMAND_TOGGLE, &QUIT]);
start(Box::new(fake_input_output.clone()), Opt::default());
let output_frames = fake_input_output
.stdout_writer
.output_frames
.lock()
.unwrap();
let snapshots = get_output_frame_snapshots(&output_frames, &fake_win_size);
for snapshot in snapshots {
assert_snapshot!(snapshot);
}
}

View File

@ -29,4 +29,4 @@ expression: snapshot
99272 aram 20 0 8456 4348 3320 R 1.3 0.0 0:00.13 htop
8611 aram 20 0 2944M 318M 130M S 1.3 2.0 8:17.90 /usr/lib/firefox/firefox -contentproc -childID 6 -i
F1Help F2Setup F3SearchF4FilterF5Tree F6SortByF7Nice -F8Nice +F9Kill F10Quit
Bye from Mosaic!
Bye from Mosaic!

View File

@ -0,0 +1,32 @@
---
source: src/tests/integration/compatibility.rs
expression: snapshot
---
1 [||||||||||||||||||||||||||||||||||||||||||100.0%] Tasks: 79, 382 thr; 1 running
2 [ 0.0%] Load average: 1.40 1.43 1.38
3 [ 0.0%] Uptime: 2 days, 07:33:50
4 [ 0.0%]
Mem[|||||||||||||||||||||||||||||||||||||3.64G/15.3G]
Swp[ 0K/16.0G]
PID USER PRI NI VIRT RES SHR S CPU% MEM% TIME+ Command
123934 aram 20 0 8444 4384 3364 R 66.7 0.0 0:00.05 htop --delay=100000000000
1 root 20 0 171M 11616 8608 S 0.0 0.1 0:56.91 /sbin/init
268 root 20 0 93324 34340 33072 S 0.0 0.2 0:01.05 /usr/lib/systemd/systemd-journald
276 root 20 0 32648 10192 7240 S 0.0 0.1 0:01.13 /usr/lib/systemd/systemd-udevd
286 root 20 0 78060 1132 996 S 0.0 0.0 0:00.00 /usr/bin/lvmetad -f
343 dbus 20 0 6952 4384 3520 S 0.0 0.0 0:13.85 /usr/bin/dbus-daemon --system --address=systemd: --
344 root 20 0 14588 7512 6176 S 0.0 0.0 0:03.21 /usr/bin/connmand -n --nodnsproxy
345 root 20 0 17696 8372 7128 S 0.0 0.1 0:00.67 /usr/lib/systemd/systemd-logind
395 root 20 0 1635M 83324 44976 S 0.0 0.5 0:32.43 /usr/bin/dockerd -H fd://
396 root 20 0 1635M 83324 44976 S 0.0 0.5 0:00.01 /usr/bin/dockerd -H fd://
397 root 20 0 1635M 83324 44976 S 0.0 0.5 0:00.01 /usr/bin/dockerd -H fd://
398 root 20 0 1635M 83324 44976 S 0.0 0.5 0:00.00 /usr/bin/dockerd -H fd://
399 root 20 0 1635M 83324 44976 S 0.0 0.5 0:00.00 /usr/bin/dockerd -H fd://
412 root 20 0 1635M 83324 44976 S 0.0 0.5 0:00.00 /usr/bin/dockerd -H fd://
441 root 20 0 1635M 83324 44976 S 0.0 0.5 0:41.35 /usr/bin/dockerd -H fd://
442 root 20 0 1635M 83324 44976 S 0.0 0.5 0:33.61 /usr/bin/dockerd -H fd://
444 root 20 0 1635M 83324 44976 S 0.0 0.5 0:33.13 /usr/bin/dockerd -H fd://
449 root 20 0 1635M 83324 44976 S 0.0 0.5 0:41.39 /usr/bin/dockerd -H fd://
F1Help F2Setup F3SearchF4FilterF5Tree F6SortByF7Nice -F8Nice +F9Kill F10Quit
Bye from Mosaic!█

View File

@ -0,0 +1,32 @@
---
source: src/tests/integration/compatibility.rs
expression: snapshot
---
1 [||||||||||||||||||||||||||||||||||||||||||100.0%] Tasks: 79, 382 thr; 1 running
2 [ 0.0%] Load average: 1.40 1.43 1.38
3 [ 0.0%] Uptime: 2 days, 07:33:50
4 [ 0.0%]
Mem[|||||||||||||||||||||||||||||||||||||3.64G/15.3G]
Swp[ 0K/16.0G]
PID USER PRI NI VIRT RES SHR S CPU% MEM% TIME+ Command
123934 aram 20 0 8444 4384 3364 R 66.7 0.0 0:00.05 htop --delay=100000000000
1 root 20 0 171M 11616 8608 S 0.0 0.1 0:56.91 /sbin/init
268 root 20 0 93324 34340 33072 S 0.0 0.2 0:01.05 /usr/lib/systemd/systemd-journald
276 root 20 0 32648 10192 7240 S 0.0 0.1 0:01.13 /usr/lib/systemd/systemd-udevd
286 root 20 0 78060 1132 996 S 0.0 0.0 0:00.00 /usr/bin/lvmetad -f
343 dbus 20 0 6952 4384 3520 S 0.0 0.0 0:13.85 /usr/bin/dbus-daemon --system --address=systemd: --
344 root 20 0 14588 7512 6176 S 0.0 0.0 0:03.21 /usr/bin/connmand -n --nodnsproxy
345 root 20 0 17696 8372 7128 S 0.0 0.1 0:00.67 /usr/lib/systemd/systemd-logind
395 root 20 0 1635M 83324 44976 S 0.0 0.5 0:32.43 /usr/bin/dockerd -H fd://
396 root 20 0 1635M 83324 44976 S 0.0 0.5 0:00.01 /usr/bin/dockerd -H fd://
397 root 20 0 1635M 83324 44976 S 0.0 0.5 0:00.01 /usr/bin/dockerd -H fd://
398 root 20 0 1635M 83324 44976 S 0.0 0.5 0:00.00 /usr/bin/dockerd -H fd://
399 root 20 0 1635M 83324 44976 S 0.0 0.5 0:00.00 /usr/bin/dockerd -H fd://
412 root 20 0 1635M 83324 44976 S 0.0 0.5 0:00.00 /usr/bin/dockerd -H fd://
441 root 20 0 1635M 83324 44976 S 0.0 0.5 0:41.35 /usr/bin/dockerd -H fd://
442 root 20 0 1635M 83324 44976 S 0.0 0.5 0:33.61 /usr/bin/dockerd -H fd://
444 root 20 0 1635M 83324 44976 S 0.0 0.5 0:33.13 /usr/bin/dockerd -H fd://
449 root 20 0 1635M 83324 44976 S 0.0 0.5 0:41.39 /usr/bin/dockerd -H fd://
F1Help F2Setup F3SearchF4FilterF5Tree F6SortByF7Nice -F8Nice +F9Kill F10Quit

View File

@ -29,4 +29,4 @@ expression: snapshot
│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Press <SPACE> to pause. Use <TAB> to rearrange tables. (DNS queries hidden).
Bye from Mosaic!
Bye from Mosaic!

View File

@ -33,6 +33,17 @@ pub fn debug_log_to_file(message: String) -> io::Result<()> {
file.write_all(b"\n")
}
pub fn debug_log_to_file_without_newline(message: String) -> io::Result<()> {
atomic_create_file(MOSAIC_TMP_LOG_FILE);
let mut file = fs::OpenOptions::new()
.append(true)
.create(true)
.open(MOSAIC_TMP_LOG_FILE)?;
file.write_all(message.as_bytes())?;
Ok(())
}
pub fn debug_log_to_file_pid_0(message: String, pid: RawFd) -> io::Result<()> {
if pid == 0 {
debug_log_to_file(message)