V1.1 #2
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ming-wm"
|
name = "ming-wm"
|
||||||
version = "1.0.3"
|
version = "1.1.0"
|
||||||
repository = "https://github.com/stjet/ming-wm"
|
repository = "https://github.com/stjet/ming-wm"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 534 B After Width: | Height: | Size: 534 B |
Binary file not shown.
|
Before Width: | Height: | Size: 534 B After Width: | Height: | Size: 534 B |
@@ -15,6 +15,8 @@ Since the windows and the window manager are separate binaries, they need some w
|
|||||||
|
|
||||||
The serialization format is in `ming-wm-lib/src/serialize.rs`. Make sure any newlines (`\n`) in strings are removed before/after serializations. When doing IPC, the window manager assumes the response to a query is one line, so if a newline is present, it will fail to parse the response.
|
The serialization format is in `ming-wm-lib/src/serialize.rs`. Make sure any newlines (`\n`) in strings are removed before/after serializations. When doing IPC, the window manager assumes the response to a query is one line, so if a newline is present, it will fail to parse the response.
|
||||||
|
|
||||||
|
> In the case of `WindowMessage::Request(WindowManagerRequest::ClipboardCopy(<copy_string>))`, 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!
|
## Hello, World!
|
||||||
|
|
||||||
A minimal example using `ming-wm-lib`.
|
A minimal example using `ming-wm-lib`.
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ Type to write commands, backspace to delete last character, and enter to run com
|
|||||||
|
|
||||||
Tab completion is supported for the `<dir>` and `<dir / playlist file>` arguments.
|
Tab completion is supported for the `<dir>` and `<dir / playlist file>` arguments.
|
||||||
|
|
||||||
|
The copy shortcut will copy the currently playing song's file name, if there is a currently playing song.
|
||||||
|
|
||||||
## Playlists
|
## Playlists
|
||||||
|
|
||||||
Example playlist file:
|
Example playlist file:
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ To get sudo to read from stdin, the `-S` option will need to be used (eg, `sudo
|
|||||||
|
|
||||||
## Copy / Paste
|
## 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
|
## Notes
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ming-wm-lib"
|
name = "ming-wm-lib"
|
||||||
version = "0.1.7"
|
version = "0.2.0"
|
||||||
repository = "https://github.com/stjet/ming-wm"
|
repository = "https://github.com/stjet/ming-wm"
|
||||||
description = "library for building windows for ming-wm in rust"
|
description = "library for building windows for ming-wm in rust"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
@@ -58,9 +58,11 @@ pub fn listen(mut window_like: impl WindowLike) {
|
|||||||
let arg = &parts.collect::<Vec<&str>>().join(" ");
|
let arg = &parts.collect::<Vec<&str>>().join(" ");
|
||||||
let output = match method {
|
let output = match method {
|
||||||
"handle_message" => {
|
"handle_message" => {
|
||||||
|
//newlines allowed for ClipboardCopy, but represented by the Linear A char
|
||||||
window_like.handle_message(WindowMessage::deserialize(arg).unwrap()).serialize().to_string()
|
window_like.handle_message(WindowMessage::deserialize(arg).unwrap()).serialize().to_string()
|
||||||
},
|
},
|
||||||
"draw" => {
|
"draw" => {
|
||||||
|
//newlines never allowed
|
||||||
window_like.draw(&ThemeInfo::deserialize(arg).unwrap()).serialize().replace("\n", "").to_string()
|
window_like.draw(&ThemeInfo::deserialize(arg).unwrap()).serialize().replace("\n", "").to_string()
|
||||||
},
|
},
|
||||||
"title" => {
|
"title" => {
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ impl Serializable for WindowMessageResponse {
|
|||||||
WindowMessageResponse::Request(req) => {
|
WindowMessageResponse::Request(req) => {
|
||||||
let req = match req {
|
let req = match req {
|
||||||
WindowManagerRequest::OpenWindow(name) => format!("OpenWindow/{}", name),
|
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::CloseStartMenu => "CloseStartMenu".to_string(),
|
||||||
WindowManagerRequest::Unlock => "Unlock".to_string(),
|
WindowManagerRequest::Unlock => "Unlock".to_string(),
|
||||||
WindowManagerRequest::Lock => "Lock".to_string(),
|
WindowManagerRequest::Lock => "Lock".to_string(),
|
||||||
@@ -646,7 +646,7 @@ impl Serializable for WindowMessage {
|
|||||||
"FullscreenWindow" => Some(ShortcutType::FullscreenWindow),
|
"FullscreenWindow" => Some(ShortcutType::FullscreenWindow),
|
||||||
"HalfWidthWindow" => Some(ShortcutType::HalfWidthWindow),
|
"HalfWidthWindow" => Some(ShortcutType::HalfWidthWindow),
|
||||||
"ClipboardCopy" => Some(ShortcutType::ClipboardCopy),
|
"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,
|
_ => None,
|
||||||
};
|
};
|
||||||
if let Some(shortcut) = shortcut {
|
if let Some(shortcut) = shortcut {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use mp4ameta;
|
|||||||
use metaflac;
|
use metaflac;
|
||||||
|
|
||||||
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType };
|
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::framebuffer_types::Dimensions;
|
||||||
use ming_wm_lib::themes::ThemeInfo;
|
use ming_wm_lib::themes::ThemeInfo;
|
||||||
use ming_wm_lib::utils::{ concat_paths, get_all_files, path_autocomplete, format_seconds, Substring };
|
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 {
|
impl WindowLike for AudioPlayer {
|
||||||
fn handle_message(&mut self, message: WindowMessage) -> WindowMessageResponse {
|
fn handle_message(&mut self, message: WindowMessage) -> WindowMessageResponse {
|
||||||
//
|
|
||||||
match message {
|
match message {
|
||||||
WindowMessage::Init(dimensions) => {
|
WindowMessage::Init(dimensions) => {
|
||||||
self.dimensions = dimensions;
|
self.dimensions = dimensions;
|
||||||
@@ -116,6 +115,27 @@ impl WindowLike for AudioPlayer {
|
|||||||
}
|
}
|
||||||
WindowMessageResponse::JustRedraw
|
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
|
WindowMessageResponse::DoNothing
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -457,11 +457,21 @@ impl WindowLike for Malvim {
|
|||||||
ShortcutType::ClipboardPaste(copy_string) => {
|
ShortcutType::ClipboardPaste(copy_string) => {
|
||||||
if self.mode == Mode::Insert {
|
if self.mode == Mode::Insert {
|
||||||
let current_file = &mut self.files[self.current_file_index];
|
let current_file = &mut self.files[self.current_file_index];
|
||||||
|
for (i, cs) in copy_string.split("\n").enumerate() {
|
||||||
|
if i == 0 {
|
||||||
|
//modify current line
|
||||||
let line = ¤t_file.content[current_file.line_pos];
|
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.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();
|
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_top_line_pos();
|
||||||
self.calc_current(); //too over zealous but whatever
|
self.calc_current();
|
||||||
self.files[self.current_file_index].changed = true;
|
self.files[self.current_file_index].changed = true;
|
||||||
WindowMessageResponse::JustRedraw
|
WindowMessageResponse::JustRedraw
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use std::fmt;
|
|||||||
use pty_process::blocking;
|
use pty_process::blocking;
|
||||||
|
|
||||||
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType };
|
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::framebuffer_types::Dimensions;
|
||||||
use ming_wm_lib::themes::ThemeInfo;
|
use ming_wm_lib::themes::ThemeInfo;
|
||||||
use ming_wm_lib::utils::{ concat_paths, path_autocomplete, Substring };
|
use ming_wm_lib::utils::{ concat_paths, path_autocomplete, Substring };
|
||||||
@@ -46,6 +46,11 @@ fn strip_ansi_escape_codes(line: String) -> String {
|
|||||||
new_line
|
new_line
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bytes_to_string(bytes: Vec<u8>) -> String {
|
||||||
|
let bytes_len = bytes.len();
|
||||||
|
String::from_utf8(bytes).unwrap_or("?".repeat(bytes_len))
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, PartialEq)]
|
#[derive(Default, PartialEq)]
|
||||||
enum Mode {
|
enum Mode {
|
||||||
#[default]
|
#[default]
|
||||||
@@ -78,6 +83,7 @@ pub struct Terminal {
|
|||||||
current_path: String,
|
current_path: String,
|
||||||
running_process: Option<Child>,
|
running_process: Option<Child>,
|
||||||
process_current_line: Vec<u8>, //bytes of line
|
process_current_line: Vec<u8>, //bytes of line
|
||||||
|
output: String, //current or previous running output of command
|
||||||
pty_outerr_rx: Option<Receiver<u8>>,
|
pty_outerr_rx: Option<Receiver<u8>>,
|
||||||
pty_in_tx: Option<Sender<String>>,
|
pty_in_tx: Option<Sender<String>>,
|
||||||
history: Vec<String>,
|
history: Vec<String>,
|
||||||
@@ -116,6 +122,7 @@ impl WindowLike for Terminal {
|
|||||||
self.history_index = None;
|
self.history_index = None;
|
||||||
self.mode = self.process_command();
|
self.mode = self.process_command();
|
||||||
self.current_input = String::new();
|
self.current_input = String::new();
|
||||||
|
self.output = String::new();
|
||||||
} else if key_press.key == '\t' { //tab
|
} else if key_press.key == '\t' { //tab
|
||||||
//autocomplete assuming it's a file system path
|
//autocomplete assuming it's a file system path
|
||||||
//...mostly working
|
//...mostly working
|
||||||
@@ -144,8 +151,10 @@ impl WindowLike for Terminal {
|
|||||||
loop {
|
loop {
|
||||||
if let Ok(ci) = self.pty_outerr_rx.as_mut().unwrap().recv_timeout(Duration::from_millis(5)) {
|
if let Ok(ci) = self.pty_outerr_rx.as_mut().unwrap().recv_timeout(Duration::from_millis(5)) {
|
||||||
if char::from(ci) == '\n' {
|
if char::from(ci) == '\n' {
|
||||||
let pcl_len = self.process_current_line.len();
|
let append_line = strip_ansi_escape_codes(bytes_to_string(self.process_current_line.clone()));
|
||||||
self.lines.push(strip_ansi_escape_codes(String::from_utf8(self.process_current_line.clone()).unwrap_or("?".repeat(pcl_len))));
|
self.output += &append_line;
|
||||||
|
self.output += "\n";
|
||||||
|
self.lines.push(append_line);
|
||||||
self.process_current_line = Vec::new();
|
self.process_current_line = Vec::new();
|
||||||
} else if char::from(ci) == '\r' {
|
} else if char::from(ci) == '\r' {
|
||||||
//for now, ignore
|
//for now, ignore
|
||||||
@@ -166,7 +175,14 @@ impl WindowLike for Terminal {
|
|||||||
//process exited
|
//process exited
|
||||||
self.pty_outerr_rx = None;
|
self.pty_outerr_rx = None;
|
||||||
self.mode = Mode::Input;
|
self.mode = Mode::Input;
|
||||||
|
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();
|
self.process_current_line = Vec::new();
|
||||||
|
}
|
||||||
changed = true;
|
changed = true;
|
||||||
} else {
|
} else {
|
||||||
if key_press.key == 'i' {
|
if key_press.key == 'i' {
|
||||||
@@ -187,8 +203,9 @@ impl WindowLike for Terminal {
|
|||||||
} else if key_press.is_enter() {
|
} else if key_press.is_enter() {
|
||||||
let _ = self.pty_in_tx.as_mut().unwrap().send(self.current_stdin_input.clone());
|
let _ = self.pty_in_tx.as_mut().unwrap().send(self.current_stdin_input.clone());
|
||||||
self.mode = Mode::Running;
|
self.mode = Mode::Running;
|
||||||
let pcl_len = self.process_current_line.len();
|
let append_line = strip_ansi_escape_codes(bytes_to_string(self.process_current_line.clone()) + &self.current_stdin_input);
|
||||||
self.lines.push(strip_ansi_escape_codes(String::from_utf8(self.process_current_line.clone()).unwrap_or("?".repeat(pcl_len))) + &self.current_stdin_input);
|
self.output += &append_line;
|
||||||
|
self.lines.push(append_line);
|
||||||
self.current_stdin_input = String::new();
|
self.current_stdin_input = String::new();
|
||||||
self.process_current_line = Vec::new();
|
self.process_current_line = Vec::new();
|
||||||
} else if key_press.is_backspace() {
|
} else if key_press.is_backspace() {
|
||||||
@@ -210,6 +227,12 @@ impl WindowLike for Terminal {
|
|||||||
//kills and running_process is now None
|
//kills and running_process is now None
|
||||||
let _ = self.running_process.take().unwrap().kill();
|
let _ = self.running_process.take().unwrap().kill();
|
||||||
self.mode = Mode::Input;
|
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
|
WindowMessageResponse::JustRedraw
|
||||||
} else if self.mode == Mode::Input && (key_press.key == 'p' || key_press.key == 'n') {
|
} 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
|
//only the last command is saved unlike other terminals. good enough for me
|
||||||
@@ -231,6 +254,7 @@ impl WindowLike for Terminal {
|
|||||||
},
|
},
|
||||||
WindowMessage::Shortcut(shortcut) => {
|
WindowMessage::Shortcut(shortcut) => {
|
||||||
match shortcut {
|
match shortcut {
|
||||||
|
ShortcutType::ClipboardCopy => WindowMessageResponse::Request(WindowManagerRequest::ClipboardCopy(self.output.clone())),
|
||||||
ShortcutType::ClipboardPaste(copy_string) => {
|
ShortcutType::ClipboardPaste(copy_string) => {
|
||||||
if self.mode == Mode::Input || self.mode == Mode::Stdin {
|
if self.mode == Mode::Input || self.mode == Mode::Stdin {
|
||||||
if self.mode == Mode::Input {
|
if self.mode == Mode::Input {
|
||||||
@@ -381,8 +405,7 @@ impl Terminal {
|
|||||||
//must_add_current_line will be false
|
//must_add_current_line will be false
|
||||||
"$ ".to_string() + &self.current_input + "█"
|
"$ ".to_string() + &self.current_input + "█"
|
||||||
} else {
|
} else {
|
||||||
let pcl_len = self.process_current_line.len();
|
strip_ansi_escape_codes(bytes_to_string(self.process_current_line.clone()) + &self.current_stdin_input.clone() + "█")
|
||||||
strip_ansi_escape_codes(String::from_utf8(self.process_current_line.clone()).unwrap_or("?".repeat(pcl_len))) + &self.current_stdin_input.clone() + "█"
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.lines[line_num].clone()
|
self.lines[line_num].clone()
|
||||||
|
|||||||
Reference in New Issue
Block a user