fix(compatibility): some fixes for htop (#53)

* Moved all log to file functions to one module.

* Moved logging to file function to utils module.

* Housekeeping.

* Housekeeping.

* Started investigation into htop CSIs.

* Used better function for scrolling.

* Minor cleanup.

* Cleanup and start of scroll region rotation.

* Implemented scroll region rotation.

* Improved performace.

* New SCI found.

* Typo.

* Typo.

* Removed logging function.

* Added scroll rotation functions.

* Typo.

* Moved all logging function to one module.

* Attempt at making htop work.

* Reverted unneeded changes.

* Improved log file name creation.

* Ran rust fmt.

* PR review edits.

* PR review edits.

* Used mark_for_rerender function.

* Removed _ prefix from logging functions.
This commit is contained in:
Roee Shapira 2020-11-19 12:15:59 +02:00 committed by GitHub
parent 6f168c5ef2
commit 5ddf95a16c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 188 additions and 161 deletions

View File

@ -1,17 +1,5 @@
use std::collections::HashMap;
fn _debug_log_to_file(message: String) {
use std::fs::OpenOptions;
use std::io::prelude::*;
let mut file = OpenOptions::new()
.append(true)
.create(true)
.open("/tmp/mosaic-log.txt")
.unwrap();
file.write_all(message.as_bytes()).unwrap();
file.write_all("\n".as_bytes()).unwrap();
}
pub mod boundary_type {
pub const TOP_RIGHT: &str = "";
pub const VERTICAL: &str = "";

View File

@ -1,18 +1,6 @@
use crate::terminal_pane::PositionAndSize;
use serde::{Deserialize, Serialize};
fn _debug_log_to_file(message: String) {
use std::fs::OpenOptions;
use std::io::prelude::*;
let mut file = OpenOptions::new()
.append(true)
.create(true)
.open("/tmp/mosaic-log.txt")
.unwrap();
file.write_all(message.as_bytes()).unwrap();
file.write_all("\n".as_bytes()).unwrap();
}
fn split_space_to_parts_vertically(
space_to_split: &PositionAndSize,
percentages: Vec<u8>,

View File

@ -1,12 +1,12 @@
#[cfg(test)]
mod tests;
mod boundaries;
mod layout;
mod os_input_output;
mod pty_bus;
mod screen;
mod terminal_pane;
#[cfg(test)]
mod tests;
mod utils;
use std::io::{Read, Write};
use std::os::unix::net::UnixStream;
@ -22,6 +22,7 @@ use crate::layout::Layout;
use crate::os_input_output::{get_os_input, OsApi};
use crate::pty_bus::{PtyBus, PtyInstruction, VteEvent};
use crate::screen::{Screen, ScreenInstruction};
use crate::utils::{consts::MOSAIC_TMP_FOLDER, logging::*};
#[derive(Serialize, Deserialize, Debug)]
enum ApiCommand {
@ -53,46 +54,28 @@ pub struct Opt {
debug: bool,
}
fn _debug_log_to_file(message: String) {
use std::fs::OpenOptions;
use std::io::prelude::*;
let mut file = OpenOptions::new()
.append(true)
.create(true)
.open("/tmp/mosaic-log.txt")
.unwrap();
file.write_all(message.as_bytes()).unwrap();
file.write_all("\n".as_bytes()).unwrap();
}
fn delete_log_files() -> std::io::Result<()> {
std::fs::remove_dir_all("/tmp/mosaic-logs").ok();
std::fs::create_dir_all("/tmp/mosaic-logs").ok();
Ok(())
}
pub fn main() {
let opts = Opt::from_args();
if opts.split.is_some() {
match opts.split {
Some('h') => {
let mut stream = UnixStream::connect("/tmp/mosaic").unwrap();
let mut stream = UnixStream::connect(MOSAIC_TMP_FOLDER).unwrap();
let api_command = bincode::serialize(&ApiCommand::SplitHorizontally).unwrap();
stream.write_all(&api_command).unwrap();
}
Some('v') => {
let mut stream = UnixStream::connect("/tmp/mosaic").unwrap();
let mut stream = UnixStream::connect(MOSAIC_TMP_FOLDER).unwrap();
let api_command = bincode::serialize(&ApiCommand::SplitVertically).unwrap();
stream.write_all(&api_command).unwrap();
}
_ => {}
};
} else if opts.move_focus {
let mut stream = UnixStream::connect("/tmp/mosaic").unwrap();
let mut stream = UnixStream::connect(MOSAIC_TMP_FOLDER).unwrap();
let api_command = bincode::serialize(&ApiCommand::MoveFocus).unwrap();
stream.write_all(&api_command).unwrap();
} else if opts.open_file.is_some() {
let mut stream = UnixStream::connect("/tmp/mosaic").unwrap();
let mut stream = UnixStream::connect(MOSAIC_TMP_FOLDER).unwrap();
let file_to_open = opts.open_file.unwrap();
let api_command = bincode::serialize(&ApiCommand::OpenFile(file_to_open)).unwrap();
stream.write_all(&api_command).unwrap();
@ -109,7 +92,8 @@ pub enum AppInstruction {
pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
let mut active_threads = vec![];
delete_log_files().unwrap();
delete_log_dir().unwrap();
delete_log_file().unwrap();
let full_screen_ws = os_input.get_terminal_size_using_fd(0);
os_input.into_raw_mode(0);
@ -335,7 +319,7 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
let mut buffer = [0; 10]; // TODO: more accurately
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
// _debug_log_to_file(format!("buffer {:?}", buffer));
//crate::utils::logging::debug_log_to_file(format!("buffer {:?}", buffer));
match buffer {
[10, 0, 0, 0, 0, 0, 0, 0, 0, 0] => {
// ctrl-j

View File

@ -58,18 +58,6 @@ pub fn set_terminal_size_using_fd(fd: RawFd, columns: u16, rows: u16) {
unsafe { ioctl(fd, TIOCSWINSZ.into(), &winsize) };
}
fn _debug_log_to_file(message: String) {
use std::fs::OpenOptions;
use std::io::prelude::*;
let mut file = OpenOptions::new()
.append(true)
.create(true)
.open("/tmp/mosaic-log.txt")
.unwrap();
file.write_all(message.as_bytes()).unwrap();
file.write_all("\n".as_bytes()).unwrap();
}
fn handle_command_exit(mut child: Child) {
let signals = ::signal_hook::iterator::Signals::new(&[::signal_hook::SIGINT]).unwrap();
'handle_exit: loop {

View File

@ -11,6 +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::ScreenInstruction;
pub struct ReadFromPid {
@ -27,38 +28,6 @@ impl ReadFromPid {
}
}
fn _debug_log_to_file(message: String) {
use std::fs::OpenOptions;
use std::io::prelude::*;
let mut file = OpenOptions::new()
.append(true)
.create(true)
.open("/tmp/mosaic-log.txt")
.unwrap();
file.write_all(message.as_bytes()).unwrap();
file.write_all("\n".as_bytes()).unwrap();
}
fn debug_to_file(message: u8, pid: RawFd) {
use std::fs::OpenOptions;
use std::io::prelude::*;
let mut path = PathBuf::new();
path.push(
[
String::from("/tmp/mosaic-logs/mosaic-"),
pid.to_string(),
String::from(".log"),
]
.concat(),
);
let mut file = OpenOptions::new()
.append(true)
.create(true)
.open(path)
.unwrap();
file.write_all(&[message]).unwrap();
}
impl Stream for ReadFromPid {
type Item = Vec<u8>;
fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
@ -208,7 +177,7 @@ fn stream_terminal_bytes(
let bytes_is_empty = bytes.is_empty();
for byte in bytes {
if debug {
debug_to_file(byte, pid);
debug_to_file(byte, pid).unwrap();
}
vte_parser.advance(&mut vte_event_sender, byte);
}

View File

@ -19,19 +19,7 @@ use crate::AppInstruction;
*
*/
fn _debug_log_to_file(message: String) {
use std::fs::OpenOptions;
use std::io::prelude::*;
let mut file = OpenOptions::new()
.append(true)
.create(true)
.open("/tmp/mosaic-log.txt")
.unwrap();
file.write_all(message.as_bytes()).unwrap();
file.write_all("\n".as_bytes()).unwrap();
}
const CURSOR_HEIGHT_WIDGH_RATIO: usize = 4; // this is not accurate and kind of a magic number, TODO: look into this
const CURSOR_HEIGHT_WIDTH_RATIO: usize = 4; // this is not accurate and kind of a magic number, TODO: look into this
type BorderAndPaneIds = (usize, Vec<RawFd>);
@ -206,7 +194,7 @@ impl Screen {
(0, 0),
|(current_longest_edge, current_terminal_id_to_split), id_and_terminal_to_check| {
let (id_of_terminal_to_check, terminal_to_check) = id_and_terminal_to_check;
let terminal_size = (terminal_to_check.get_rows() * CURSOR_HEIGHT_WIDGH_RATIO)
let terminal_size = (terminal_to_check.get_rows() * CURSOR_HEIGHT_WIDTH_RATIO)
* terminal_to_check.get_columns();
if terminal_size > current_longest_edge {
(terminal_size, *id_of_terminal_to_check)
@ -222,7 +210,7 @@ impl Screen {
ws_xpixel: terminal_to_split.get_x() as u16,
ws_ypixel: terminal_to_split.get_y() as u16,
};
if terminal_to_split.get_rows() * CURSOR_HEIGHT_WIDGH_RATIO
if terminal_to_split.get_rows() * CURSOR_HEIGHT_WIDTH_RATIO
> terminal_to_split.get_columns()
{
let (top_winsize, bottom_winsize) = split_horizontally_with_gap(&terminal_ws);

View File

@ -26,18 +26,6 @@ pub struct CanonicalLine {
pub wrapped_fragments: Vec<WrappedFragment>,
}
fn debug_log_to_file(message: String) {
use std::fs::OpenOptions;
use std::io::prelude::*;
let mut file = OpenOptions::new()
.append(true)
.create(true)
.open("/tmp/mosaic-log.txt")
.unwrap();
file.write_all(message.as_bytes()).unwrap();
file.write_all("\n".as_bytes()).unwrap();
}
impl CanonicalLine {
pub fn new() -> Self {
CanonicalLine {
@ -163,6 +151,9 @@ impl CursorPosition {
pub fn move_to_next_canonical_line(&mut self) {
self.line_index.0 += 1;
}
pub fn move_to_prev_canonical_line(&mut self) {
self.line_index.0 -= 1;
}
pub fn move_to_beginning_of_linewrap(&mut self) {
self.column_index = 0;
}
@ -307,8 +298,7 @@ impl Scroll {
}
}
if current_canonical_line_index == self.canonical_lines.len() - 1 {
let new_canonical_line = CanonicalLine::new();
self.canonical_lines.push(new_canonical_line);
self.canonical_lines.push(CanonicalLine::new());
self.cursor_position.move_to_next_canonical_line();
self.cursor_position.move_to_beginning_of_canonical_line();
} else if current_canonical_line_index < self.canonical_lines.len() - 1 {
@ -517,6 +507,46 @@ impl Scroll {
}
}
}
/// [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) {
if let Some((_, scroll_region_bottom)) = self.scroll_region {
if self.show_cursor {
let scroll_region_bottom_index = scroll_region_bottom - 1;
self.cursor_position
.move_to_canonical_line(scroll_region_bottom_index);
let new_empty_lines = vec![CanonicalLine::new(); count];
self.canonical_lines.splice(
scroll_region_bottom_index..scroll_region_bottom_index + 1,
new_empty_lines,
);
self.cursor_position
.move_to_canonical_line(scroll_region_bottom_index + count);
}
}
}
/// [scroll_down](https://github.com/alacritty/alacritty/blob/ec42b42ce601808070462111c0c28edb0e89babb/alacritty_terminal/src/grid/mod.rs#L221)
/// This function takes the last line of the scroll region and moves it to the top (count times)
pub fn rotate_scroll_region_down(&mut self, count: usize) {
if let Some((scroll_region_top, _)) = self.scroll_region {
if self.show_cursor {
let scroll_region_top_index = scroll_region_top - 1;
self.cursor_position
.move_to_canonical_line(scroll_region_top_index);
let new_empty_lines = vec![CanonicalLine::new(); count];
self.canonical_lines.splice(
scroll_region_top_index..scroll_region_top_index,
new_empty_lines,
);
self.cursor_position
.move_to_canonical_line(scroll_region_top_index + count);
}
}
}
pub fn move_viewport_up(&mut self, count: usize) {
if let Some(current_offset) = self.viewport_bottom_offset.as_mut() {
*current_offset += count;

View File

@ -7,6 +7,7 @@ use crate::terminal_pane::terminal_character::{
AnsiCode, CharacterStyles, NamedColor, TerminalCharacter,
};
use crate::terminal_pane::Scroll;
use crate::utils::logging::{debug_log_to_file, debug_log_to_file_pid_0};
use crate::VteEvent;
#[derive(Clone, Copy, Debug)]
@ -61,11 +62,14 @@ impl TerminalPane {
position_and_size_override: None,
}
}
pub fn mark_for_rerender(&mut self) {
self.should_render = true;
}
pub fn handle_event(&mut self, event: VteEvent) {
match event {
VteEvent::Print(c) => {
self.print(c);
self.should_render = true;
self.mark_for_rerender();
}
VteEvent::Execute(byte) => {
self.execute(byte);
@ -95,57 +99,57 @@ impl TerminalPane {
self.position_and_size.x += count;
self.position_and_size.columns -= count;
self.reflow_lines();
self.should_render = true;
self.mark_for_rerender();
}
pub fn reduce_width_left(&mut self, count: usize) {
self.position_and_size.columns -= count;
self.reflow_lines();
self.should_render = true;
self.mark_for_rerender();
}
pub fn increase_width_left(&mut self, count: usize) {
self.position_and_size.x -= count;
self.position_and_size.columns += count;
self.reflow_lines();
self.should_render = true;
self.mark_for_rerender();
}
pub fn increase_width_right(&mut self, count: usize) {
self.position_and_size.columns += count;
self.reflow_lines();
self.should_render = true;
self.mark_for_rerender();
}
pub fn reduce_height_down(&mut self, count: usize) {
self.position_and_size.y += count;
self.position_and_size.rows -= count;
self.reflow_lines();
self.should_render = true;
self.mark_for_rerender();
}
pub fn increase_height_down(&mut self, count: usize) {
self.position_and_size.rows += count;
self.reflow_lines();
self.should_render = true;
self.mark_for_rerender();
}
pub fn increase_height_up(&mut self, count: usize) {
self.position_and_size.y -= count;
self.position_and_size.rows += count;
self.reflow_lines();
self.should_render = true;
self.mark_for_rerender();
}
pub fn reduce_height_up(&mut self, count: usize) {
self.position_and_size.rows -= count;
self.reflow_lines();
self.should_render = true;
self.mark_for_rerender();
}
pub fn change_size_p(&mut self, position_and_size: &PositionAndSize) {
self.position_and_size = *position_and_size;
self.reflow_lines();
self.should_render = true;
self.mark_for_rerender();
}
// TODO: merge these two methods
pub fn change_size(&mut self, ws: &Winsize) {
self.position_and_size.columns = ws.ws_col as usize;
self.position_and_size.rows = ws.ws_row as usize;
self.reflow_lines();
self.should_render = true;
self.mark_for_rerender();
}
pub fn get_x(&self) -> usize {
match self.position_and_size_override {
@ -210,7 +214,7 @@ impl TerminalPane {
}
character_styles.clear();
}
self.should_render = false;
self.mark_for_rerender();
Some(vte_output)
} else {
None
@ -225,15 +229,23 @@ impl TerminalPane {
}
pub fn scroll_up(&mut self, count: usize) {
self.scroll.move_viewport_up(count);
self.should_render = true;
self.mark_for_rerender();
}
pub fn scroll_down(&mut self, count: usize) {
self.scroll.move_viewport_down(count);
self.should_render = true;
self.mark_for_rerender();
}
pub fn rotate_scroll_region_up(&mut self, count: usize) {
self.scroll.rotate_scroll_region_up(count);
self.mark_for_rerender();
}
pub fn rotate_scroll_region_down(&mut self, count: usize) {
self.scroll.rotate_scroll_region_down(count);
self.mark_for_rerender();
}
pub fn clear_scroll(&mut self) {
self.scroll.reset_viewport();
self.should_render = true;
self.mark_for_rerender();
}
pub fn override_size_and_position(&mut self, x: usize, y: usize, size: &Winsize) {
let position_and_size_override = PositionAndSize {
@ -244,17 +256,17 @@ impl TerminalPane {
};
self.position_and_size_override = Some(position_and_size_override);
self.reflow_lines();
self.should_render = true;
self.mark_for_rerender();
}
pub fn reset_size_and_position_override(&mut self) {
self.position_and_size_override = None;
self.reflow_lines();
self.should_render = true;
self.mark_for_rerender();
}
fn add_newline(&mut self) {
self.scroll.add_canonical_line();
// self.reset_all_ansi_codes(); // TODO: find out if we should be resetting here or not
self.should_render = true;
self.mark_for_rerender();
}
fn move_to_beginning_of_line(&mut self) {
self.scroll.move_cursor_to_beginning_of_linewrap();
@ -267,20 +279,6 @@ impl TerminalPane {
}
}
fn debug_log_to_file(message: String, pid: RawFd) {
if pid == 3 {
use std::fs::OpenOptions;
use std::io::prelude::*;
let mut file = OpenOptions::new()
.append(true)
.create(true)
.open("/tmp/mosaic-log.txt")
.unwrap();
file.write_all(message.as_bytes()).unwrap();
file.write_all("\n".as_bytes()).unwrap();
}
}
impl vte::Perform for TerminalPane {
fn print(&mut self, c: char) {
// apparently, building TerminalCharacter like this without a "new" method
@ -632,7 +630,8 @@ impl vte::Perform for TerminalPane {
.pending_styles
.background(Some(AnsiCode::NamedColor(NamedColor::White)));
} else {
debug_log_to_file(format!("unhandled csi m code {:?}", params), self.pid);
debug_log_to_file_pid_0(format!("unhandled csi m code {:?}", params), self.pid)
.unwrap();
}
} else if c == 'C' {
// move cursor forward
@ -685,7 +684,7 @@ impl vte::Perform for TerminalPane {
match params.get(0) {
Some(25) => {
self.scroll.hide_cursor();
self.should_render = true;
self.mark_for_rerender();
}
_ => {}
};
@ -700,7 +699,7 @@ impl vte::Perform for TerminalPane {
match params.get(0) {
Some(25) => {
self.scroll.show_cursor();
self.should_render = true;
self.mark_for_rerender();
}
_ => {}
};
@ -741,8 +740,38 @@ impl vte::Perform for TerminalPane {
.add_empty_lines_in_scroll_region(line_count_to_add);
} else if c == 'q' || c == 'd' || c == 'X' || c == 'G' {
// ignore for now to run on mac
} else if c == 'T' {
/*
* 124 54 T SD
* Scroll down, new lines inserted at top of screen
* [4T = Scroll down 4, bring previous lines back into view
*/
debug_log_to_file(format!(
"htop (only?) linux csi: {}->{:?} ({:?} - ignore: {})",
c, params, _intermediates, _ignore
))
.unwrap();
let line_count: i64 = *params.get(0).expect("A number of lines was expected.");
if line_count >= 0 {
self.rotate_scroll_region_up(line_count as usize);
} else {
self.rotate_scroll_region_down(line_count.abs() as usize);
}
} else if c == 'P' {
/*
* 120 50 P * DCH
* Delete Character, from current position to end of field
* [4P = Delete 4 characters, VT102 series
*/
debug_log_to_file(format!(
"htop (only?) linux csi: {}->{:?} (intermediates: {:?}, ignore: {})",
c, params, _intermediates, _ignore
))
.unwrap();
} else {
panic!("unhandled csi: {:?}->{:?}", c, params);
debug_log_to_file(format!("Unhandled csi: {}->{:?}", c, params)).unwrap();
panic!("unhandled csi: {}->{:?}", c, params);
}
}

3
src/utils/consts.rs Normal file
View File

@ -0,0 +1,3 @@
pub const MOSAIC_TMP_LOG_FILE: &str = "/tmp/mosaic-log.txt";
pub const MOSAIC_TMP_LOG_DIR: &str = "/tmp/mosaic-log";
pub const MOSAIC_TMP_FOLDER: &str = "/tmp/mosaic";

58
src/utils/logging.rs Normal file
View File

@ -0,0 +1,58 @@
use std::{
fs,
io::{self, prelude::*},
os::unix::io::RawFd,
path::PathBuf,
};
use crate::utils::consts::{MOSAIC_TMP_LOG_DIR, MOSAIC_TMP_LOG_FILE};
pub fn debug_log_to_file(message: String) -> io::Result<()> {
let mut file = fs::OpenOptions::new()
.append(true)
.create(true)
.open(MOSAIC_TMP_LOG_FILE)?;
file.write_all(message.as_bytes())?;
file.write_all("\n".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)?;
}
Ok(())
}
pub fn delete_log_file() -> io::Result<()> {
if fs::metadata(MOSAIC_TMP_LOG_FILE).is_ok() {
fs::remove_file(MOSAIC_TMP_LOG_FILE)?;
}
Ok(())
}
pub fn delete_log_dir() -> io::Result<()> {
if fs::metadata(MOSAIC_TMP_LOG_DIR).is_ok() {
fs::remove_dir_all(MOSAIC_TMP_LOG_DIR)?;
}
fs::create_dir_all(MOSAIC_TMP_LOG_DIR)?;
Ok(())
}
pub fn debug_to_file(message: u8, pid: RawFd) -> io::Result<()> {
let mut path = PathBuf::new();
path.push(MOSAIC_TMP_LOG_DIR);
path.push(format!("mosaic-{}.log", pid.to_string()));
let mut file = fs::OpenOptions::new()
.append(true)
.create(true)
.open(path)?;
file.write_all(&[message])?;
Ok(())
}

2
src/utils/mod.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod consts;
pub mod logging;