From 606d8bf67f80488a7023586fd8bf9db848183950 Mon Sep 17 00:00:00 2001 From: stjet <49297268+stjet@users.noreply.github.com> Date: Fri, 20 Dec 2024 08:39:58 +0000 Subject: [PATCH] file explorer mnvp, malvim fixes also terminal ctrl+p and ctrl+n, error logging for the ipc windows, other fixes --- src/bin/file_explorer.rs | 100 +++++++++++++++++++++++++++++++++++---- src/bin/malvim.rs | 22 +++++++-- src/bin/terminal.rs | 13 +++++ src/bin/test.rs | 10 ++-- src/framebuffer.rs | 8 ++-- src/ipc.rs | 15 ++++++ src/window_manager.rs | 10 ++-- 7 files changed, 151 insertions(+), 27 deletions(-) diff --git a/src/bin/file_explorer.rs b/src/bin/file_explorer.rs index abcb8b9..5cc620b 100644 --- a/src/bin/file_explorer.rs +++ b/src/bin/file_explorer.rs @@ -1,6 +1,7 @@ use std::vec::Vec; use std::vec; use std::fs::read_dir; +use std::path::PathBuf; use ming_wm::window_manager::{ DrawInstructions, WindowLike, WindowLikeType }; use ming_wm::messages::{ WindowMessage, WindowMessageResponse }; @@ -8,19 +9,32 @@ use ming_wm::framebuffer::Dimensions; use ming_wm::themes::ThemeInfo; use ming_wm::ipc::listen; +struct DirectoryChild { + //if some, use instead of file/dir name + override_name: Option, + path: PathBuf, + is_file: bool, + //can only be true if dir + //if true, means the contents of this dir should be visible too, even though it isn't the current path. like a tree + tree_open: bool, +} + #[derive(Default)] pub struct FileExplorer { dimensions: Dimensions, - current_path: String, - //current_dir_contents: + current_path: PathBuf, + current_dir_contents: Vec, + //for scrolling and selecting dirs + position: usize, } impl WindowLike for FileExplorer { fn handle_message(&mut self, message: WindowMessage) -> WindowMessageResponse { match message { WindowMessage::Init(dimensions) => { - self.current_path = "/".to_string(); + self.current_path = PathBuf::from("/"); self.dimensions = dimensions; + self.current_dir_contents = self.get_current_dir_contents(); WindowMessageResponse::JustRerender }, WindowMessage::ChangeDimensions(dimensions) => { @@ -28,16 +42,68 @@ impl WindowLike for FileExplorer { WindowMessageResponse::JustRerender }, WindowMessage::KeyPress(key_press) => { - // - WindowMessageResponse::DoNothing + if key_press.key == '𐘂' { //the enter key + if self.current_dir_contents.len() > 0 { + let selected_entry = &self.current_dir_contents[self.position]; + if !selected_entry.is_file { + self.current_path = selected_entry.path.clone(); + self.current_dir_contents = self.get_current_dir_contents(); + self.position = 0; + return WindowMessageResponse::JustRerender; + } + } + WindowMessageResponse::DoNothing + } else if key_press.key == 'j' { + //down + if self.position == self.current_dir_contents.len() - 1 { + self.position = 0; + } else { + self.position += 1; + } + WindowMessageResponse::JustRerender + } else if key_press.key == 'k' { + //up + if self.position == 0 { + self.position = self.current_dir_contents.len() - 1; + } else { + self.position -= 1; + } + WindowMessageResponse::JustRerender + } else { + WindowMessageResponse::DoNothing + } }, _ => WindowMessageResponse::DoNothing, } } fn draw(&self, theme_info: &ThemeInfo) -> Vec { + let mut instructions = Vec::new(); //top bar with path name and editing - vec![] + // + //the actual files and directories + let mut start_y = 0; + let mut i = 0; + for entry in &self.current_dir_contents { + if start_y > self.dimensions[1] { + break; + } + let is_selected = i == self.position; + if is_selected { + instructions.push(DrawInstructions::Rect([0, start_y], [self.dimensions[0], 20], theme_info.top)); + } + //unwrap_or not used because "Arguments passed to unwrap_or are eagerly evaluated", apparently + let name = entry.override_name.clone(); + let name = if name.is_none() { + entry.path.file_name().unwrap().to_os_string().into_string().unwrap() + } else { + name.unwrap() + }; + instructions.push(DrawInstructions::Text([5, start_y], vec!["times-new-roman".to_string(), "shippori-mincho".to_string()], name, if is_selected { theme_info.top_text } else { theme_info.text }, if is_selected { theme_info.top } else { theme_info.background }, None, None)); + start_y += 20; + i += 1; + } + instructions } fn title(&self) -> String { @@ -63,8 +129,26 @@ impl FileExplorer { } //should include .. if not / - fn read_current_dir_contents(&self) { - // + fn get_current_dir_contents(&self) -> Vec { + let mut contents = Vec::new(); + if self.current_path != PathBuf::from("/") { + contents.push(DirectoryChild { + override_name: Some("..".to_string()), + is_file: false, + tree_open: false, + path: self.current_path.parent().unwrap().to_owned(), + }); + } + contents.extend(read_dir(&self.current_path).unwrap().map(|entry| { + let path = entry.unwrap().path(); + DirectoryChild { + override_name: None, + is_file: path.is_file(), + tree_open: false, + path, + } + })); + contents } } diff --git a/src/bin/malvim.rs b/src/bin/malvim.rs index f316515..f4f37f1 100644 --- a/src/bin/malvim.rs +++ b/src/bin/malvim.rs @@ -130,7 +130,7 @@ impl WindowLike for Malvim { current_file.cursor_pos = spaces; } else if key_press.key == '𐘁' { //backspace if current_length > 0 && current_file.cursor_pos > 0 { - current_file.content[current_file.line_pos] = line.remove(current_file.cursor_pos, 1); + current_file.content[current_file.line_pos] = line.remove(current_file.cursor_pos - 1, 1); current_file.cursor_pos -= 1; } else { if current_file.line_pos > 0 { @@ -168,6 +168,7 @@ impl WindowLike for Malvim { let new_length = current_file.content[current_file.line_pos].len(); current_file.cursor_pos = calc_new_cursor_pos(current_file.cursor_pos, new_length); } else if key_press.key == 'w' { + //todo: currently doesn't work on a single space? let line = ¤t_file.content[current_file.line_pos]; if line.len() > 0 { //offset until space or eol @@ -192,11 +193,21 @@ impl WindowLike for Malvim { } else if self.state == State::Find || self.state == State::BackFind { let old_pos = current_file.cursor_pos; let find_pos = if self.state == State::Find { - old_pos + current_file.content[current_file.line_pos].chars().skip(old_pos + 1).position(|c| c == key_press.key).unwrap_or(0) + 1 + if old_pos < current_file.content[current_file.line_pos].len() { + old_pos + current_file.content[current_file.line_pos].chars().skip(old_pos + 1).position(|c| c == key_press.key).unwrap_or(0) + 1 + } else { + old_pos + } } else { - old_pos - current_file.content[current_file.line_pos].chars().rev().skip(current_length - old_pos + 1).position(|c| c == key_press.key).unwrap_or(0)- 2 + //how does this work again? no idea + if old_pos != 0 { + old_pos - current_file.content[current_file.line_pos].chars().rev().skip(current_length - old_pos + 1).position(|c| c == key_press.key).unwrap_or(0) - 2 + } else { + old_pos //0 + } }; current_file.cursor_pos = find_pos; + changed = false; self.state = State::None; } else if key_press.key == 'x' { if current_length > 0 && current_file.cursor_pos < current_length { @@ -237,7 +248,7 @@ impl WindowLike for Malvim { current_file.cursor_pos = current_file.content[current_file.line_pos].len(); changed = false; } else if key_press.key == '^' { - current_file.line_pos = current_file.content[current_file.line_pos].chars().position(|c| c != ' ').unwrap_or(0); + current_file.cursor_pos = current_file.content[current_file.line_pos].chars().position(|c| c != ' ').unwrap_or(0); changed = false; } else if key_press.key == 'r' { self.state = State::Replace; @@ -452,7 +463,7 @@ impl Malvim { } else if first == "e" || first == "edit" || ((first == "t" || first == "tabe") && self.files.len() > 0) { //find the file and open it let mut failed = false; - let mut new_path = if self.files.len() > 0 { + let mut new_path = if self.files.len() > 0 && !arg.starts_with("/") { PathBuf::from(self.files[self.current_file_index].path.clone()).parent().unwrap().to_path_buf() } else { PathBuf::from("/") @@ -509,6 +520,7 @@ impl Malvim { let current_file = &self.files[self.current_file_index]; let _ = write(¤t_file.path, ¤t_file.content.join("\n")); self.files[self.current_file_index].changed = false; + self.bottom_message = Some("Written".to_string()); } else if first == "q" || first == "quit" { self.files.remove(self.current_file_index); self.current_file_index = self.current_file_index.checked_sub(1).unwrap_or(0); diff --git a/src/bin/terminal.rs b/src/bin/terminal.rs index 51b4b65..33f2a24 100644 --- a/src/bin/terminal.rs +++ b/src/bin/terminal.rs @@ -31,6 +31,7 @@ pub struct Terminal { current_input: String, current_path: String, running_process: Option, + last_command: Option, } //for some reason key presses, then moving the window leaves the old window still there, behind it. weird @@ -59,6 +60,7 @@ impl WindowLike for Terminal { } } else if key_press.key == '𐘂' { //the enter key self.lines.push("$ ".to_string() + &self.current_input); + self.last_command = Some(self.current_input.clone()); self.state = self.process_command(); self.current_input = String::new(); } else { @@ -96,6 +98,17 @@ impl WindowLike for Terminal { let _ = self.running_process.take().unwrap().kill(); self.state = State::Input; WindowMessageResponse::JustRerender + } else if self.state == State::Input && (key_press.key == 'p' || key_press.key == 'n') { + //only the last command is saved unlike other terminals. good enough for me + if key_press.key == 'p' && self.last_command.is_some() { + self.current_input = self.last_command.clone().unwrap(); + WindowMessageResponse::JustRerender + } else if key_press.key == 'n' { + self.current_input = String::new(); + WindowMessageResponse::JustRerender + } else { + WindowMessageResponse::DoNothing + } } else { WindowMessageResponse::DoNothing } diff --git a/src/bin/test.rs b/src/bin/test.rs index a34f2ac..9e651c1 100644 --- a/src/bin/test.rs +++ b/src/bin/test.rs @@ -3,13 +3,9 @@ use std::io::{ Read, Write }; use ron; +use ming_wm::messages::WindowMessage; + fn main() { - println!("{}", 'だ'); - println!("a"); - let mut a = Command::new("cargo").arg("run").arg("-q").arg("--bin").arg("start_menu").stdout(Stdio::piped()).stdin(Stdio::piped()).stderr(Stdio::null()).spawn().unwrap(); - a.stdin.unwrap().write_all("subtype\n".to_string().as_bytes()); - let mut output = String::new(); - let _ = a.stdout.as_mut().unwrap().read_to_string(&mut output); - println!("{}", output); + println!("{}", ron::to_string(&WindowMessage::Init([100,100])).unwrap()); //println!("{}", &ron::to_string(&[122, 400]).unwrap()); } diff --git a/src/framebuffer.rs b/src/framebuffer.rs index 8667f36..15d5ea2 100644 --- a/src/framebuffer.rs +++ b/src/framebuffer.rs @@ -48,9 +48,9 @@ pub struct FramebufferWriter { } impl FramebufferWriter { - pub fn init(&mut self, info: FramebufferInfo, buffer_length: usize) { + pub fn init(&mut self, info: FramebufferInfo) { self.info = info; - self.buffer = vec![0; buffer_length]; + self.buffer = vec![0; self.info.byte_len]; } pub fn get_info(&self) -> FramebufferInfo { @@ -102,7 +102,9 @@ impl FramebufferWriter { start_pos = ((top_left[1] + row + char_info.2 as usize) * self.info.stride + top_left[0]) * self.info.bytes_per_pixel; for col in &char_info.1[row] { if col > &0 { - self._draw_pixel(start_pos, color_with_alpha(color, bg_color, *col)); + if start_pos < self.info.byte_len { + self._draw_pixel(start_pos, color_with_alpha(color, bg_color, *col)); + } } start_pos += self.info.bytes_per_pixel; } diff --git a/src/ipc.rs b/src/ipc.rs index 5e7a5d8..d61c6e0 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -1,4 +1,5 @@ use std::io::{ stdin, BufRead }; +use std::panic; //use serde::{ Deserialize, Serialize }; use ron; @@ -30,6 +31,20 @@ pub trait WindowLike { const LOG: bool = false; pub fn listen(mut window_like: impl WindowLike) { + panic::set_hook(Box::new(|panic_info| { + let (filename, line) = panic_info.location().map(|l| (l.file(), l.line())).unwrap_or(("", 0)); + + let cause = if let Some(s) = panic_info.payload().downcast_ref::<&str>() { + format!("{:?}", s) + } else if let Some(s) = panic_info.payload().downcast_ref::() { + format!("{:?}", s) + } else { + "panic occurred".to_string() + }; + + log(&format!("A panic occurred at {}:{}: {}", filename, line, cause)); + })); + let stdin = stdin(); for line in stdin.lock().lines() { let line = line.unwrap().clone(); diff --git a/src/window_manager.rs b/src/window_manager.rs index 47480f2..af57e26 100644 --- a/src/window_manager.rs +++ b/src/window_manager.rs @@ -35,7 +35,7 @@ pub fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) { println!("bg: {}x{}", dimensions[0], dimensions[1] - TASKBAR_HEIGHT - INDICATOR_HEIGHT); - WRITER.lock().unwrap().init(framebuffer_info.clone(), framebuffer_info.height * framebuffer_info.stride * framebuffer_info.bytes_per_pixel); + WRITER.lock().unwrap().init(framebuffer_info.clone()); let mut wm: WindowManager = WindowManager::new(framebuffer, dimensions); @@ -51,8 +51,8 @@ pub fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) { for c in stdin.keys() { if let Some(kc) = key_to_char(c.unwrap()) { //do not allow exit when locked unless debugging - if kc == KeyChar::Alt('e') { - //if kc == KeyChar::Alt('e') && !wm.locked { + //if kc == KeyChar::Alt('e') { + if kc == KeyChar::Alt('e') && !wm.locked { write!(stdout, "{}", cursor::Show).unwrap(); stdout.suspend_raw_mode().unwrap(); exit(0); @@ -454,6 +454,7 @@ impl WindowManager { if let Some(focused_index) = self.get_focused_index() { let window_like = &self.window_infos[focused_index].window_like; if window_like.subtype() == WindowLikeType::Window && window_like.resizable() { + self.window_infos[focused_index].fullscreen = false; //full height, half width self.window_infos[focused_index].top_left = [0, INDICATOR_HEIGHT]; let new_dimensions = [self.dimensions[0] / 2, self.dimensions[1] - INDICATOR_HEIGHT - TASKBAR_HEIGHT]; @@ -635,9 +636,10 @@ impl WindowManager { framebuffer_info.width = window_width; framebuffer_info.height = window_height; framebuffer_info.stride = window_width; + framebuffer_info.byte_len = window_width * window_height * bytes_per_pixel; //make a writer just for the window let mut window_writer: FramebufferWriter = Default::default(); - window_writer.init(framebuffer_info, window_width * window_height * bytes_per_pixel); + window_writer.init(framebuffer_info); for instruction in instructions { //unsafe { SERIAL1.lock().write_text(&format!("{:?}\n", instruction)); } match instruction {