From 724ffbd49435ba435bd0e1e7191f44cfa7106bae Mon Sep 17 00:00:00 2001 From: stjet <49297268+stjet@users.noreply.github.com> Date: Sat, 26 Apr 2025 05:17:06 +0000 Subject: [PATCH] multi-line copy/paste, more copy/paste fix C and D in nimbus romono --- Cargo.toml | 2 +- bmps/nimbus-romono/C0.bmp | Bin 534 -> 534 bytes bmps/nimbus-romono/D0.bmp | Bin 534 -> 534 bytes docs/system/writing_windows.md | 2 ++ docs/window-likes/audio-player.md | 2 ++ docs/window-likes/terminal.md | 2 +- ming-wm-lib/Cargo.toml | 2 +- ming-wm-lib/src/ipc.rs | 2 ++ ming-wm-lib/src/serialize.rs | 4 +-- src/bin/audio_player.rs | 24 ++++++++++++++++-- src/bin/malvim.rs | 18 +++++++++++--- src/bin/terminal.rs | 39 ++++++++++++++++++++++++------ 12 files changed, 78 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index abd30d0..fea1aeb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ming-wm" -version = "1.0.3" +version = "1.1.0" repository = "https://github.com/stjet/ming-wm" license = "GPL-3.0-or-later" edition = "2021" diff --git a/bmps/nimbus-romono/C0.bmp b/bmps/nimbus-romono/C0.bmp index 50a59263a28a07a3fbeeaa3b10d8e44829aad7a7..e0fee452d8150375fe8aecf0be70bbf10f710261 100644 GIT binary patch literal 534 zcmZwDKMMh29KiACWRNUUXSu;(P%?Nd;fujoSN?n}r_}PLWP=Zy#99JB%#tda+rc3_8 z3Z7BVk`K{C5ijhJ)HCKuy@3hj;*Qyi$MjFpTrS=-b7tOnnl)R@%mea%j;*h2^be5t z%j=r$q%Yqsb1hq6YOFq7+_1&p-PGj&`_X)z_>M7B`nkkaoUt@&9{C7+_}E~90j6L4 C4%#>fM7{OQRBm+YptOMz&I@{B$%VA`-=V z&t_J}#3awmoO8bHZnoBRV=7!F_5zYf!BHN&QU@nIezqV8lwjpC#SwdKFotVn8q^P5 zQ8oHy>LrTE;RbnVC(1-8y)^n5Kr(M+qPcxC+v(ido|!e7r_T9i-@cQP@0a&(?U`9K z_jT@M_U${Fp3c2E6MyD$`ZHv4fxNr*C)cIFkUpz|JJ$FIsgCw diff --git a/bmps/nimbus-romono/D0.bmp b/bmps/nimbus-romono/D0.bmp index 707771287b98e48eb7328e15a679a3e49dc9fe5f..8ca36a0fcc144570834ae337765d4f341fc4f009 100644 GIT binary patch literal 534 zcma)(y$%6E6h>!N{%VbaLSmo51L#%aLC|^wYNAvsR*OO;N}*9GPmgMGs#9` z%_L{$e)pc)&1UP3LP|xe#_mHF^5Cg5yHZy#I=;6qjXS(y2`kvb7<{d@vXB>WiRCo8 z19HC=>(9tXo9#)j2RA55kM+yTF?&Ei$uqMJ-oe3};)M=gAn$hY_IRfxk9iSZF#6+7 kaO8Wd#+y^0r8YT39>5ctrq`wpL7p%FWeqzRLdD+w059IuDgXcg literal 534 zcmaiwu?oU45QbC5MVuW)Hz(1>sgt{#ui=xp=?DYr4t)X9N02TGYW%KT zp(v8Tcj^84(u4H+)q+V^m)e9fR3I>Qsxg-!-M-71;0+EESi%lg(00z54Ehs@x0Z}m zggHE5kn;}oBG_EkpqDu^qo((NJk4Vqxj*uFTlHd(H&rk3cvii;$IIrPJ>FISXa9Me kx72yEchoy-Hq*XDAA!8zP_qf&FOYY-!2 In the case of `WindowMessage::Request(WindowManagerRequest::ClipboardCopy())`, windows should convert any `\n` into `𐘂` when copying to clipboard and vice versa when pasting, in order to allow for multi-line clipboard contents. + ## Hello, World! A minimal example using `ming-wm-lib`. diff --git a/docs/window-likes/audio-player.md b/docs/window-likes/audio-player.md index e0553ae..ae2bd90 100644 --- a/docs/window-likes/audio-player.md +++ b/docs/window-likes/audio-player.md @@ -14,6 +14,8 @@ Type to write commands, backspace to delete last character, and enter to run com Tab completion is supported for the `` and `` arguments. +The copy shortcut will copy the currently playing song's file name, if there is a currently playing song. + ## Playlists Example playlist file: diff --git a/docs/window-likes/terminal.md b/docs/window-likes/terminal.md index 3a56fbb..d3a7563 100644 --- a/docs/window-likes/terminal.md +++ b/docs/window-likes/terminal.md @@ -25,7 +25,7 @@ To get sudo to read from stdin, the `-S` option will need to be used (eg, `sudo ## Copy / Paste -This window-like supports the paste [shortcut](../system/shortcuts.md) (`Alt+P`) if in INPUT or STDIN mode. +This window-like supports the paste [shortcut](../system/shortcuts.md) (`Alt+P`) if in INPUT or STDIN mode. The copy shortcut will copy the output of the last ran command. ## Notes diff --git a/ming-wm-lib/Cargo.toml b/ming-wm-lib/Cargo.toml index c821a1c..a420e9d 100644 --- a/ming-wm-lib/Cargo.toml +++ b/ming-wm-lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ming-wm-lib" -version = "0.1.7" +version = "0.2.0" repository = "https://github.com/stjet/ming-wm" description = "library for building windows for ming-wm in rust" readme = "README.md" diff --git a/ming-wm-lib/src/ipc.rs b/ming-wm-lib/src/ipc.rs index 7b7772e..76cbbf0 100644 --- a/ming-wm-lib/src/ipc.rs +++ b/ming-wm-lib/src/ipc.rs @@ -58,9 +58,11 @@ pub fn listen(mut window_like: impl WindowLike) { let arg = &parts.collect::>().join(" "); let output = match method { "handle_message" => { + //newlines allowed for ClipboardCopy, but represented by the Linear A char window_like.handle_message(WindowMessage::deserialize(arg).unwrap()).serialize().to_string() }, "draw" => { + //newlines never allowed window_like.draw(&ThemeInfo::deserialize(arg).unwrap()).serialize().replace("\n", "").to_string() }, "title" => { diff --git a/ming-wm-lib/src/serialize.rs b/ming-wm-lib/src/serialize.rs index b2aaf4d..12e3ed1 100644 --- a/ming-wm-lib/src/serialize.rs +++ b/ming-wm-lib/src/serialize.rs @@ -146,7 +146,7 @@ impl Serializable for WindowMessageResponse { WindowMessageResponse::Request(req) => { let req = match req { WindowManagerRequest::OpenWindow(name) => format!("OpenWindow/{}", name), - WindowManagerRequest::ClipboardCopy(name) => format!("ClipboardCopy/{}", name), + WindowManagerRequest::ClipboardCopy(copy_string) => format!("ClipboardCopy/{}", copy_string.replace("\n", "𐘂")), //serialised output must be 1 line WindowManagerRequest::CloseStartMenu => "CloseStartMenu".to_string(), WindowManagerRequest::Unlock => "Unlock".to_string(), WindowManagerRequest::Lock => "Lock".to_string(), @@ -646,7 +646,7 @@ impl Serializable for WindowMessage { "FullscreenWindow" => Some(ShortcutType::FullscreenWindow), "HalfWidthWindow" => Some(ShortcutType::HalfWidthWindow), "ClipboardCopy" => Some(ShortcutType::ClipboardCopy), - "ClipboardPaste" => Some(ShortcutType::ClipboardPaste(get_rest_of_split(&mut parts, Some("/")))), + "ClipboardPaste" => Some(ShortcutType::ClipboardPaste(get_rest_of_split(&mut parts, Some("/")).replace("𐘂", "\n"))), _ => None, }; if let Some(shortcut) = shortcut { diff --git a/src/bin/audio_player.rs b/src/bin/audio_player.rs index 5c3101a..ff62d36 100644 --- a/src/bin/audio_player.rs +++ b/src/bin/audio_player.rs @@ -14,7 +14,7 @@ use mp4ameta; use metaflac; use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType }; -use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse }; +use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest, ShortcutType }; use ming_wm_lib::framebuffer_types::Dimensions; use ming_wm_lib::themes::ThemeInfo; use ming_wm_lib::utils::{ concat_paths, get_all_files, path_autocomplete, format_seconds, Substring }; @@ -79,7 +79,6 @@ pub struct AudioPlayer { impl WindowLike for AudioPlayer { fn handle_message(&mut self, message: WindowMessage) -> WindowMessageResponse { - // match message { WindowMessage::Init(dimensions) => { self.dimensions = dimensions; @@ -116,6 +115,27 @@ impl WindowLike for AudioPlayer { } WindowMessageResponse::JustRedraw }, + WindowMessage::Shortcut(shortcut) => { + match shortcut { + ShortcutType::ClipboardPaste(paste_string) => { + self.command += &paste_string.replace("\n", ""); + WindowMessageResponse::JustRedraw + }, + ShortcutType::ClipboardCopy => { + let internal_locked = self.internal.lock().unwrap(); + let sink_len = internal_locked.sink.len(); + if sink_len > 0 { + let queue = &internal_locked.queue; + let current = &queue[queue.len() - sink_len]; + let current_name = current.0.file_name().unwrap().to_string_lossy().into_owned(); + WindowMessageResponse::Request(WindowManagerRequest::ClipboardCopy(current_name)) + } else { + WindowMessageResponse::DoNothing + } + }, + _ => WindowMessageResponse::DoNothing, + } + }, _ => { WindowMessageResponse::DoNothing }, diff --git a/src/bin/malvim.rs b/src/bin/malvim.rs index 96bbca6..3c4879d 100644 --- a/src/bin/malvim.rs +++ b/src/bin/malvim.rs @@ -457,11 +457,21 @@ impl WindowLike for Malvim { ShortcutType::ClipboardPaste(copy_string) => { if self.mode == Mode::Insert { let current_file = &mut self.files[self.current_file_index]; - let line = ¤t_file.content[current_file.line_pos]; - current_file.content[current_file.line_pos] = line.substring(0, current_file.cursor_pos).to_string() + ©_string + line.substring(current_file.cursor_pos, line.chars().count()); - current_file.cursor_pos += copy_string.len(); + for (i, cs) in copy_string.split("\n").enumerate() { + if i == 0 { + //modify current line + let line = ¤t_file.content[current_file.line_pos]; + current_file.content[current_file.line_pos] = line.substring(0, current_file.cursor_pos).to_string() + &cs + line.substring(current_file.cursor_pos, line.chars().count()); + current_file.cursor_pos += copy_string.len(); + } else { + //insert a new line + current_file.content.insert(current_file.line_pos + 1, cs.to_string()); + current_file.line_pos += 1; + current_file.cursor_pos = cs.chars().count(); + } + } self.calc_top_line_pos(); - self.calc_current(); //too over zealous but whatever + self.calc_current(); self.files[self.current_file_index].changed = true; WindowMessageResponse::JustRedraw } else { diff --git a/src/bin/terminal.rs b/src/bin/terminal.rs index 912e8bc..4a79753 100644 --- a/src/bin/terminal.rs +++ b/src/bin/terminal.rs @@ -11,7 +11,7 @@ use std::fmt; use pty_process::blocking; use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType }; -use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, ShortcutType }; +use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest, ShortcutType }; use ming_wm_lib::framebuffer_types::Dimensions; use ming_wm_lib::themes::ThemeInfo; use ming_wm_lib::utils::{ concat_paths, path_autocomplete, Substring }; @@ -46,6 +46,11 @@ fn strip_ansi_escape_codes(line: String) -> String { new_line } +fn bytes_to_string(bytes: Vec) -> String { + let bytes_len = bytes.len(); + String::from_utf8(bytes).unwrap_or("?".repeat(bytes_len)) +} + #[derive(Default, PartialEq)] enum Mode { #[default] @@ -78,6 +83,7 @@ pub struct Terminal { current_path: String, running_process: Option, process_current_line: Vec, //bytes of line + output: String, //current or previous running output of command pty_outerr_rx: Option>, pty_in_tx: Option>, history: Vec, @@ -116,6 +122,7 @@ impl WindowLike for Terminal { self.history_index = None; self.mode = self.process_command(); self.current_input = String::new(); + self.output = String::new(); } else if key_press.key == '\t' { //tab //autocomplete assuming it's a file system path //...mostly working @@ -144,8 +151,10 @@ impl WindowLike for Terminal { loop { if let Ok(ci) = self.pty_outerr_rx.as_mut().unwrap().recv_timeout(Duration::from_millis(5)) { if char::from(ci) == '\n' { - let pcl_len = self.process_current_line.len(); - self.lines.push(strip_ansi_escape_codes(String::from_utf8(self.process_current_line.clone()).unwrap_or("?".repeat(pcl_len)))); + let append_line = strip_ansi_escape_codes(bytes_to_string(self.process_current_line.clone())); + self.output += &append_line; + self.output += "\n"; + self.lines.push(append_line); self.process_current_line = Vec::new(); } else if char::from(ci) == '\r' { //for now, ignore @@ -166,7 +175,14 @@ impl WindowLike for Terminal { //process exited self.pty_outerr_rx = None; self.mode = Mode::Input; - self.process_current_line = Vec::new(); + if self.process_current_line.len() > 0 { + //add to lines + let append_line = strip_ansi_escape_codes(bytes_to_string(self.process_current_line.clone())); + self.output += &append_line; + self.lines.push(append_line); + //only need to reset if not empty + self.process_current_line = Vec::new(); + } changed = true; } else { if key_press.key == 'i' { @@ -187,8 +203,9 @@ impl WindowLike for Terminal { } else if key_press.is_enter() { let _ = self.pty_in_tx.as_mut().unwrap().send(self.current_stdin_input.clone()); self.mode = Mode::Running; - let pcl_len = self.process_current_line.len(); - self.lines.push(strip_ansi_escape_codes(String::from_utf8(self.process_current_line.clone()).unwrap_or("?".repeat(pcl_len))) + &self.current_stdin_input); + let append_line = strip_ansi_escape_codes(bytes_to_string(self.process_current_line.clone()) + &self.current_stdin_input); + self.output += &append_line; + self.lines.push(append_line); self.current_stdin_input = String::new(); self.process_current_line = Vec::new(); } else if key_press.is_backspace() { @@ -210,6 +227,12 @@ impl WindowLike for Terminal { //kills and running_process is now None let _ = self.running_process.take().unwrap().kill(); self.mode = Mode::Input; + if self.process_current_line.len() > 0 { + let append_line = strip_ansi_escape_codes(bytes_to_string(self.process_current_line.clone())); + self.output += &append_line; + self.lines.push(append_line); + self.process_current_line = Vec::new(); + } WindowMessageResponse::JustRedraw } else if self.mode == Mode::Input && (key_press.key == 'p' || key_press.key == 'n') { //only the last command is saved unlike other terminals. good enough for me @@ -231,6 +254,7 @@ impl WindowLike for Terminal { }, WindowMessage::Shortcut(shortcut) => { match shortcut { + ShortcutType::ClipboardCopy => WindowMessageResponse::Request(WindowManagerRequest::ClipboardCopy(self.output.clone())), ShortcutType::ClipboardPaste(copy_string) => { if self.mode == Mode::Input || self.mode == Mode::Stdin { if self.mode == Mode::Input { @@ -381,8 +405,7 @@ impl Terminal { //must_add_current_line will be false "$ ".to_string() + &self.current_input + "█" } else { - let pcl_len = self.process_current_line.len(); - strip_ansi_escape_codes(String::from_utf8(self.process_current_line.clone()).unwrap_or("?".repeat(pcl_len))) + &self.current_stdin_input.clone() + "█" + strip_ansi_escape_codes(bytes_to_string(self.process_current_line.clone()) + &self.current_stdin_input.clone() + "█") } } else { self.lines[line_num].clone()