inhouse serialize/deserialize, remove ron and serde deps
malvim gets 'A'
This commit is contained in:
@@ -16,8 +16,6 @@ linux_framebuffer = { package = "framebuffer", version = "0.3.1" }
|
|||||||
termion = "4.0.3"
|
termion = "4.0.3"
|
||||||
rodio = "0.19.0"
|
rodio = "0.19.0"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
ron = "0.8"
|
|
||||||
serde = { version = "1", features = ["derive"] }
|
|
||||||
audiotags = "0.5.0"
|
audiotags = "0.5.0"
|
||||||
bmp-rust = "0.4.1"
|
bmp-rust = "0.4.1"
|
||||||
dirs = "5.0.1"
|
dirs = "5.0.1"
|
||||||
|
|||||||
18
README.md
18
README.md
@@ -15,3 +15,21 @@ cargo build --release
|
|||||||
|
|
||||||
Though just `cargo run --release` can be done.
|
Though just `cargo run --release` can be done.
|
||||||
|
|
||||||
|
### Running on Mobile Linux
|
||||||
|
|
||||||
|
Running with an onscreen keyboard. The framebuffer may not be redrawn to the screen without a (real) key press. The volume down button seems to work.
|
||||||
|
|
||||||
|
```
|
||||||
|
cargo build --release
|
||||||
|
./target/release/main touch
|
||||||
|
```
|
||||||
|
|
||||||
|
Optionally, in landscape mode (todo: osk may be broken in landscape mode):
|
||||||
|
|
||||||
|
```
|
||||||
|
cargo build --release
|
||||||
|
./target/release/main touch rotate
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|||||||
13
bmps/times-new-roman/𐚆.alpha
Normal file
13
bmps/times-new-roman/𐚆.alpha
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
0
|
||||||
|
179,1,0,0,0
|
||||||
|
119,61,0,0,0
|
||||||
|
27,150,0,0,0
|
||||||
|
0,174,7,0,0
|
||||||
|
0,100,80,0,0
|
||||||
|
0,14,162,0,0
|
||||||
|
0,0,164,16,0
|
||||||
|
0,0,81,99,0
|
||||||
|
0,0,6,171,0
|
||||||
|
0,0,0,151,29
|
||||||
|
0,0,0,62,118
|
||||||
|
0,0,0,1,176
|
||||||
BIN
bmps/times-new-roman/𐚆0.bmp
Normal file
BIN
bmps/times-new-roman/𐚆0.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 294 B |
13
bmps/times-new-romono/𐚆.alpha
Normal file
13
bmps/times-new-romono/𐚆.alpha
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
0
|
||||||
|
179,1,0,0,0
|
||||||
|
119,61,0,0,0
|
||||||
|
27,150,0,0,0
|
||||||
|
0,174,7,0,0
|
||||||
|
0,100,80,0,0
|
||||||
|
0,14,162,0,0
|
||||||
|
0,0,164,16,0
|
||||||
|
0,0,81,99,0
|
||||||
|
0,0,6,171,0
|
||||||
|
0,0,0,151,29
|
||||||
|
0,0,0,62,118
|
||||||
|
0,0,0,1,176
|
||||||
BIN
bmps/times-new-romono/𐚆0.bmp
Normal file
BIN
bmps/times-new-romono/𐚆0.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 294 B |
BIN
docs/images/mobile.png
Normal file
BIN
docs/images/mobile.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
@@ -100,7 +100,11 @@ impl WindowLike for Malvim {
|
|||||||
self.mode = Mode::Command;
|
self.mode = Mode::Command;
|
||||||
self.command = Some(String::new());
|
self.command = Some(String::new());
|
||||||
changed = false;
|
changed = false;
|
||||||
} else if key_press.key == 'i' && self.mode == Mode::Normal && self.state == State::None && self.files.len() > 0 {
|
} else if (key_press.key == 'i' || key_press.key == 'A') && self.mode == Mode::Normal && self.state == State::None && self.files.len() > 0 {
|
||||||
|
if key_press.key == 'A' {
|
||||||
|
let current_file = &mut self.files[self.current_file_index];
|
||||||
|
current_file.cursor_pos = current_file.content[current_file.line_pos].len();
|
||||||
|
}
|
||||||
self.mode = Mode::Insert;
|
self.mode = Mode::Insert;
|
||||||
changed = false;
|
changed = false;
|
||||||
} else if self.mode == Mode::Insert {
|
} else if self.mode == Mode::Insert {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::path::PathBuf;
|
|||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
fn get_font_char(dir: &str, c: char) -> Option<(char, Vec<Vec<u8>>, u8)> {
|
fn get_font_char(dir: &str, c: char) -> Option<(char, Vec<Vec<u8>>, u8)> {
|
||||||
let c = if c == '/' { '𐘋' } else if c == '.' { '𐘅' } else { c };
|
let c = if c == '/' { '𐘋' } else if c == '\\' { '𐚆' } else if c == '.' { '𐘅' } else { c };
|
||||||
if let Ok(mut file) = File::open(dir.to_string() + "/" + &c.to_string() + ".alpha") {
|
if let Ok(mut file) = File::open(dir.to_string() + "/" + &c.to_string() + ".alpha") {
|
||||||
let mut ch: Vec<Vec<u8>> = Vec::new();
|
let mut ch: Vec<Vec<u8>> = Vec::new();
|
||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
|
|||||||
16
src/ipc.rs
16
src/ipc.rs
@@ -1,10 +1,11 @@
|
|||||||
use std::io::{ stdin, BufRead };
|
use std::io::{ stdin, BufRead };
|
||||||
use std::panic;
|
use std::panic;
|
||||||
|
|
||||||
//use serde::{ Deserialize, Serialize };
|
|
||||||
use ron;
|
|
||||||
|
|
||||||
use crate::window_manager::WindowLike;
|
use crate::window_manager::WindowLike;
|
||||||
|
use crate::serialize::Serializable;
|
||||||
|
use crate::themes::ThemeInfo;
|
||||||
|
use crate::framebuffer::Dimensions;
|
||||||
|
use crate::messages::WindowMessage;
|
||||||
use crate::logging::log;
|
use crate::logging::log;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -56,10 +57,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" => {
|
||||||
format!("{}", ron::to_string(&window_like.handle_message(ron::from_str(arg).unwrap())).unwrap())
|
log(arg);
|
||||||
|
format!("{}", &window_like.handle_message(WindowMessage::deserialize(arg).unwrap()).serialize())
|
||||||
},
|
},
|
||||||
"draw" => {
|
"draw" => {
|
||||||
format!("{}", ron::to_string(&window_like.draw(&ron::from_str(arg).unwrap())).unwrap())
|
format!("{}", &window_like.draw(&ThemeInfo::deserialize(arg).unwrap()).serialize())
|
||||||
},
|
},
|
||||||
"title" => {
|
"title" => {
|
||||||
format!("{}", window_like.title())
|
format!("{}", window_like.title())
|
||||||
@@ -68,10 +70,10 @@ pub fn listen(mut window_like: impl WindowLike) {
|
|||||||
format!("{}", window_like.resizable())
|
format!("{}", window_like.resizable())
|
||||||
},
|
},
|
||||||
"subtype" => {
|
"subtype" => {
|
||||||
format!("{}", ron::to_string(&window_like.subtype()).unwrap())
|
format!("{}", &window_like.subtype().serialize())
|
||||||
},
|
},
|
||||||
"ideal_dimensions" => {
|
"ideal_dimensions" => {
|
||||||
format!("{}", ron::to_string(&window_like.ideal_dimensions(ron::from_str(arg).unwrap())).unwrap())
|
format!("{}", &window_like.ideal_dimensions(Dimensions::deserialize(arg).unwrap()).serialize())
|
||||||
},
|
},
|
||||||
_ => String::new(),
|
_ => String::new(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ pub mod fs;
|
|||||||
pub mod utils;
|
pub mod utils;
|
||||||
pub mod logging;
|
pub mod logging;
|
||||||
pub mod ipc;
|
pub mod ipc;
|
||||||
|
pub mod serialize;
|
||||||
mod proxy_window_like;
|
mod proxy_window_like;
|
||||||
mod essential;
|
mod essential;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
use std::boxed::Box;
|
use std::boxed::Box;
|
||||||
use std::fmt;
|
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
use serde::{ Deserialize, Serialize };
|
|
||||||
|
|
||||||
use crate::framebuffer::Dimensions;
|
use crate::framebuffer::Dimensions;
|
||||||
use crate::window_manager::{ WindowLike, KeyChar };
|
use crate::window_manager::{ WindowLike, KeyChar };
|
||||||
|
|
||||||
@@ -24,9 +21,10 @@ impl PartialEq for WindowBox {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum WindowManagerRequest {
|
pub enum WindowManagerRequest {
|
||||||
OpenWindow(String),
|
OpenWindow(String),
|
||||||
|
//may not work in \x1E, \x1F or \x1D are in the paste string
|
||||||
ClipboardCopy(String),
|
ClipboardCopy(String),
|
||||||
CloseStartMenu,
|
CloseStartMenu,
|
||||||
Unlock,
|
Unlock,
|
||||||
@@ -35,7 +33,7 @@ pub enum WindowManagerRequest {
|
|||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Serialize, Deserialize)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub enum WindowMessageResponse {
|
pub enum WindowMessageResponse {
|
||||||
Request(WindowManagerRequest),
|
Request(WindowManagerRequest),
|
||||||
JustRedraw,
|
JustRedraw,
|
||||||
@@ -52,12 +50,11 @@ impl WindowMessageResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct KeyPress {
|
pub struct KeyPress {
|
||||||
pub key: char,
|
pub key: char,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub enum Direction {
|
pub enum Direction {
|
||||||
Left,
|
Left,
|
||||||
Down,
|
Down,
|
||||||
@@ -66,7 +63,7 @@ pub enum Direction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//todo, rename to CommandType
|
//todo, rename to CommandType
|
||||||
#[derive(PartialEq, Serialize, Deserialize)]
|
#[derive(PartialEq)]
|
||||||
pub enum ShortcutType {
|
pub enum ShortcutType {
|
||||||
StartMenu,
|
StartMenu,
|
||||||
SwitchWorkspace(u8),
|
SwitchWorkspace(u8),
|
||||||
@@ -80,20 +77,19 @@ pub enum ShortcutType {
|
|||||||
FullscreenWindow,
|
FullscreenWindow,
|
||||||
HalfWidthWindow, //half width, full height
|
HalfWidthWindow, //half width, full height
|
||||||
ClipboardCopy,
|
ClipboardCopy,
|
||||||
|
//may not work in \x1E, \x1F or \x1D are in the paste string
|
||||||
ClipboardPaste(String),
|
ClipboardPaste(String),
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type WindowsVec = Vec<(usize, String)>;
|
pub type WindowsVec = Vec<(usize, String)>;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub enum InfoType {
|
pub enum InfoType {
|
||||||
//let taskbar know what the current windows in the workspace are
|
//let taskbar know what the current windows in the workspace are
|
||||||
WindowsInWorkspace(WindowsVec, usize), //Vec<title, name)>, focused id
|
WindowsInWorkspace(WindowsVec, usize), //Vec<(id, name)>, focused id
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub enum WindowMessage {
|
pub enum WindowMessage {
|
||||||
Init(Dimensions),
|
Init(Dimensions),
|
||||||
KeyPress(KeyPress),
|
KeyPress(KeyPress),
|
||||||
|
|||||||
@@ -5,34 +5,33 @@ use std::cell::RefCell;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
use ron;
|
|
||||||
|
|
||||||
use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
|
use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
|
||||||
use crate::messages::{ WindowMessage, WindowMessageResponse };
|
use crate::messages::{ WindowMessage, WindowMessageResponse };
|
||||||
use crate::framebuffer::Dimensions;
|
use crate::framebuffer::Dimensions;
|
||||||
use crate::themes::ThemeInfo;
|
use crate::themes::ThemeInfo;
|
||||||
|
use crate::serialize::{ Serializable, DrawInstructionsVec };
|
||||||
|
|
||||||
|
|
||||||
pub struct ProxyWindowLike {
|
pub struct ProxyWindowLike {
|
||||||
process: RefCell<Child>,
|
process: RefCell<Child>,
|
||||||
}
|
}
|
||||||
|
|
||||||
//try to handle panics of child processes so the entire wm doesn't crash
|
//try to handle panics of child processes so the entire wm doesn't crash
|
||||||
//we can "guarantee" that the ron::to_string(...).unwrap() calls will never panic
|
|
||||||
impl WindowLike for ProxyWindowLike {
|
impl WindowLike for ProxyWindowLike {
|
||||||
fn handle_message(&mut self, message: WindowMessage) -> WindowMessageResponse {
|
fn handle_message(&mut self, message: WindowMessage) -> WindowMessageResponse {
|
||||||
if let Some(stdin) = self.process.borrow_mut().stdin.as_mut() {
|
if let Some(stdin) = self.process.borrow_mut().stdin.as_mut() {
|
||||||
let _ = stdin.write_all(("handle_message ".to_string() + &ron::to_string(&message).unwrap() + "\n").as_bytes());
|
let _ = stdin.write_all(("handle_message ".to_string() + &message.serialize() + "\n").as_bytes());
|
||||||
}
|
}
|
||||||
let output = self.read_line();
|
let output = self.read_line();
|
||||||
ron::from_str(&output).unwrap_or(WindowMessageResponse::JustRedraw)
|
WindowMessageResponse::deserialize(&output).unwrap_or(WindowMessageResponse::JustRedraw)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&self, theme_info: &ThemeInfo) -> Vec<DrawInstructions> {
|
fn draw(&self, theme_info: &ThemeInfo) -> Vec<DrawInstructions> {
|
||||||
if let Some(stdin) = self.process.borrow_mut().stdin.as_mut() {
|
if let Some(stdin) = self.process.borrow_mut().stdin.as_mut() {
|
||||||
let _ = stdin.write_all(("draw ".to_string() + &ron::to_string(&theme_info).unwrap() + "\n").as_bytes());
|
let _ = stdin.write_all(("draw ".to_string() + &theme_info.serialize() + "\n").as_bytes());
|
||||||
}
|
}
|
||||||
let output = self.read_line();
|
let output = self.read_line();
|
||||||
ron::from_str(&output).unwrap_or(Vec::new())
|
DrawInstructionsVec::deserialize(&output).unwrap_or(Vec::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
//properties
|
//properties
|
||||||
@@ -44,11 +43,12 @@ impl WindowLike for ProxyWindowLike {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn resizable(&self) -> bool {
|
fn resizable(&self) -> bool {
|
||||||
|
//serialize for bool is just true -> "true", false -> "false"
|
||||||
if let Some(stdin) = self.process.borrow_mut().stdin.as_mut() {
|
if let Some(stdin) = self.process.borrow_mut().stdin.as_mut() {
|
||||||
let _ = stdin.write_all("resizable\n".to_string().as_bytes());
|
let _ = stdin.write_all("resizable\n".to_string().as_bytes());
|
||||||
}
|
}
|
||||||
let output = self.read_line();
|
let output = self.read_line();
|
||||||
ron::from_str(&output).unwrap_or(false)
|
output == "true\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subtype(&self) -> WindowLikeType {
|
fn subtype(&self) -> WindowLikeType {
|
||||||
@@ -56,15 +56,15 @@ impl WindowLike for ProxyWindowLike {
|
|||||||
let _ = stdin.write_all("subtype\n".to_string().as_bytes());
|
let _ = stdin.write_all("subtype\n".to_string().as_bytes());
|
||||||
}
|
}
|
||||||
let output = self.read_line();
|
let output = self.read_line();
|
||||||
ron::from_str(&output).unwrap_or(WindowLikeType::Window)
|
WindowLikeType::deserialize(&output).unwrap_or(WindowLikeType::Window)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ideal_dimensions(&self, dimensions: Dimensions) -> Dimensions {
|
fn ideal_dimensions(&self, dimensions: Dimensions) -> Dimensions {
|
||||||
if let Some(stdin) = self.process.borrow_mut().stdin.as_mut() {
|
if let Some(stdin) = self.process.borrow_mut().stdin.as_mut() {
|
||||||
let _ = stdin.write_all(("ideal_dimensions ".to_string() + &ron::to_string(&dimensions).unwrap() + "\n").as_bytes());
|
let _ = stdin.write_all(("ideal_dimensions ".to_string() + &dimensions.serialize() + "\n").as_bytes());
|
||||||
}
|
}
|
||||||
let output = self.read_line();
|
let output = self.read_line();
|
||||||
ron::from_str(&output).unwrap_or([420, 420])
|
Dimensions::deserialize(&output).unwrap_or([420, 420])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
753
src/serialize.rs
Normal file
753
src/serialize.rs
Normal file
@@ -0,0 +1,753 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use crate::themes::ThemeInfo;
|
||||||
|
use crate::messages::{ WindowMessageResponse, WindowManagerRequest, KeyPress, WindowMessage, Direction, ShortcutType, InfoType };
|
||||||
|
use crate::window_manager::{ KeyChar, DrawInstructions, WindowLikeType };
|
||||||
|
use crate::framebuffer::Dimensions;
|
||||||
|
|
||||||
|
//serde + ron but worse! yay
|
||||||
|
//not same as ron - simplified
|
||||||
|
//very messy
|
||||||
|
|
||||||
|
//todo: bug with extra byte when copy/pasting because of this... maybe it's the newline or something?
|
||||||
|
|
||||||
|
//I can't do `impl fmt::Display for RGBColor` which is annoying
|
||||||
|
|
||||||
|
fn array_to_string<T: Display>(array: &[T]) -> String {
|
||||||
|
let mut output = String::new();
|
||||||
|
for item in array {
|
||||||
|
output += &format!("{}{}", if output == String::new() {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
"\x1F"
|
||||||
|
}, item);
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
fn option_to_string<T: Display>(option: &Option<T>) -> String {
|
||||||
|
if let Some(value) = option {
|
||||||
|
format!("S{}", value)
|
||||||
|
} else {
|
||||||
|
"N".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_rest_of_split(split: &mut dyn Iterator<Item = &str>, sep: Option<&str>) -> String {
|
||||||
|
let mut rest = String::new();
|
||||||
|
let mut n = split.next();
|
||||||
|
loop {
|
||||||
|
rest += &n.unwrap();
|
||||||
|
n = split.next();
|
||||||
|
if n.is_some() {
|
||||||
|
rest += sep.unwrap_or("");
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rest
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_color(serialized: &str) -> Result<[u8; 3], ()> {
|
||||||
|
let rgb = serialized.split("\x1F");
|
||||||
|
let mut color = [0; 3];
|
||||||
|
let mut c_i = 0;
|
||||||
|
//won't return error if rgb is 0, 1, or 2 elements.
|
||||||
|
//I guess that's okay, since it doesn't panic either
|
||||||
|
for c in rgb {
|
||||||
|
if c_i == 3 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
if let Ok(c) = c.parse() {
|
||||||
|
color[c_i] = c;
|
||||||
|
} else {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
c_i += 1;
|
||||||
|
}
|
||||||
|
Ok(color)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_two_array(serialized: &str) -> Result<[usize; 2], ()> {
|
||||||
|
let mut arg = serialized.split("\x1F");
|
||||||
|
let mut a = [0; 2];
|
||||||
|
for i in 0..2 {
|
||||||
|
if let Some(n) = arg.next() {
|
||||||
|
if let Ok(n) = n.parse() {
|
||||||
|
a[i] = n;
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
return Ok(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Serializable {
|
||||||
|
fn serialize(&self) -> String;
|
||||||
|
fn deserialize(serialized: &str) -> Result<Self, ()> where Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
//ripe for macros when I figure them out
|
||||||
|
|
||||||
|
impl Serializable for ThemeInfo {
|
||||||
|
fn serialize(&self) -> String {
|
||||||
|
format!("{}:{}:{}:{}:{}:{}:{}:{}:{}", array_to_string(&self.top), array_to_string(&self.background), array_to_string(&self.border_left_top), array_to_string(&self.border_right_bottom), array_to_string(&self.text), array_to_string(&self.top_text), array_to_string(&self.alt_background), array_to_string(&self.alt_text), array_to_string(&self.alt_secondary))
|
||||||
|
}
|
||||||
|
fn deserialize(serialized: &str) -> Result<Self, ()> {
|
||||||
|
//strip newline at the end
|
||||||
|
let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized };
|
||||||
|
let mut theme_info: ThemeInfo = Default::default();
|
||||||
|
let arrays = serialized.split(":");
|
||||||
|
let mut a_i = 0;
|
||||||
|
//won't error or panic if less than 9... rest will just be black by default I guess
|
||||||
|
for a in arrays {
|
||||||
|
if a_i == 9 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let color = get_color(a)?;
|
||||||
|
match a_i {
|
||||||
|
0 => {
|
||||||
|
theme_info.top = color;
|
||||||
|
},
|
||||||
|
1 => {
|
||||||
|
theme_info.background = color;
|
||||||
|
},
|
||||||
|
2 => {
|
||||||
|
theme_info.border_left_top = color;
|
||||||
|
},
|
||||||
|
3 => {
|
||||||
|
theme_info.border_right_bottom = color;
|
||||||
|
},
|
||||||
|
4 => {
|
||||||
|
theme_info.text = color;
|
||||||
|
},
|
||||||
|
5 => {
|
||||||
|
theme_info.top_text = color;
|
||||||
|
},
|
||||||
|
6 => {
|
||||||
|
theme_info.alt_background = color;
|
||||||
|
},
|
||||||
|
7 => {
|
||||||
|
theme_info.alt_text = color;
|
||||||
|
},
|
||||||
|
8 => {
|
||||||
|
theme_info.alt_secondary = color;
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
};
|
||||||
|
if a_i == 8 {
|
||||||
|
return Ok(theme_info);
|
||||||
|
}
|
||||||
|
a_i += 1;
|
||||||
|
}
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn theme_info_serialize_deserialize() {
|
||||||
|
use crate::themes::{ Themes, get_theme_info };
|
||||||
|
let theme_info = get_theme_info(&Default::default()).unwrap();
|
||||||
|
let serialized = theme_info.serialize();
|
||||||
|
assert!(serialized == ThemeInfo::deserialize(&serialized).unwrap().serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for WindowMessageResponse {
|
||||||
|
fn serialize(&self) -> String {
|
||||||
|
match self {
|
||||||
|
WindowMessageResponse::JustRedraw => "JustRedraw".to_string(),
|
||||||
|
WindowMessageResponse::DoNothing => "DoNothing".to_string(),
|
||||||
|
WindowMessageResponse::Request(req) => {
|
||||||
|
let req = match req {
|
||||||
|
WindowManagerRequest::OpenWindow(name) => format!("OpenWindow/{}", name),
|
||||||
|
WindowManagerRequest::ClipboardCopy(name) => format!("ClipboardCopy/{}", name),
|
||||||
|
WindowManagerRequest::CloseStartMenu => "CloseStartMenu".to_string(),
|
||||||
|
WindowManagerRequest::Unlock => "Unlock".to_string(),
|
||||||
|
WindowManagerRequest::Lock => "Lock".to_string(),
|
||||||
|
WindowManagerRequest::DoKeyChar(kc) => format!("DoKeyChar/{}", match kc {
|
||||||
|
KeyChar::Press(c) => format!("Press/{}", c),
|
||||||
|
KeyChar::Alt(c) => format!("Alt/{}", c),
|
||||||
|
KeyChar::Ctrl(c) => format!("Ctrl/{}", c),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
format!("Request/{}", req)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn deserialize(serialized: &str) -> Result<Self, ()> {
|
||||||
|
//strip newline at the end
|
||||||
|
let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized };
|
||||||
|
let mut parts = serialized.split("/");
|
||||||
|
match parts.next().unwrap_or("Invalid") {
|
||||||
|
"JustRedraw" => Ok(WindowMessageResponse::JustRedraw),
|
||||||
|
"DoNothing" => Ok(WindowMessageResponse::DoNothing),
|
||||||
|
"Request" => {
|
||||||
|
let req = match parts.next().unwrap_or("Invalid") {
|
||||||
|
//do get_rest_of_split instead of .next() because it is possible for window name or copy to have "/"
|
||||||
|
"OpenWindow" => Some(WindowManagerRequest::OpenWindow(get_rest_of_split(&mut parts, Some("/")))),
|
||||||
|
"ClipboardCopy" => Some(WindowManagerRequest::ClipboardCopy(get_rest_of_split(&mut parts, Some("/")))),
|
||||||
|
"CloseStartMenu" => Some(WindowManagerRequest::CloseStartMenu),
|
||||||
|
"Unlock" => Some(WindowManagerRequest::Unlock),
|
||||||
|
"Lock" => Some(WindowManagerRequest::Lock),
|
||||||
|
"DoKeyChar" => Some(WindowManagerRequest::DoKeyChar(
|
||||||
|
match parts.next().unwrap_or("Invalid") {
|
||||||
|
"Press" => KeyChar::Press(parts.next().unwrap_or("?").chars().next().unwrap()),
|
||||||
|
"Alt" => KeyChar::Alt(parts.next().unwrap_or("?").chars().next().unwrap()),
|
||||||
|
"Ctrl" => KeyChar::Ctrl(parts.next().unwrap_or("?").chars().next().unwrap()),
|
||||||
|
_ => KeyChar::Press('?'), //yeah.
|
||||||
|
}
|
||||||
|
)),
|
||||||
|
_ => None, //yeah...
|
||||||
|
};
|
||||||
|
if let Some(req) = req {
|
||||||
|
Ok(WindowMessageResponse::Request(req))
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn window_message_response_serialize_deserialize() {
|
||||||
|
let resp = WindowMessageResponse::JustRedraw;
|
||||||
|
let serialized = resp.serialize();
|
||||||
|
assert!(resp == WindowMessageResponse::deserialize(&serialized).unwrap());
|
||||||
|
let resp = WindowMessageResponse::Request(WindowManagerRequest::OpenWindow("a".to_string()));
|
||||||
|
let serialized = resp.serialize();
|
||||||
|
assert!(resp == WindowMessageResponse::deserialize(&serialized).unwrap());
|
||||||
|
let resp = WindowMessageResponse::Request(WindowManagerRequest::Unlock);
|
||||||
|
let serialized = resp.serialize();
|
||||||
|
assert!(resp == WindowMessageResponse::deserialize(&serialized).unwrap());
|
||||||
|
let resp = WindowMessageResponse::Request(WindowManagerRequest::DoKeyChar(KeyChar::Alt('e')));
|
||||||
|
let serialized = resp.serialize();
|
||||||
|
assert!(resp == WindowMessageResponse::deserialize(&serialized).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for DrawInstructions {
|
||||||
|
fn serialize(&self) -> String {
|
||||||
|
match self {
|
||||||
|
//use \x1E (record separator) because it won't be in strings. it better fucking not be at least
|
||||||
|
DrawInstructions::Rect(p, d, c) => format!("Rect/{}\x1E{}\x1E{}", array_to_string(p), array_to_string(d), array_to_string(c)),
|
||||||
|
DrawInstructions::Text(p, vs, s, c1, c2, ou1, ou2) => format!("Text/{}\x1E{}\x1E{}\x1E{}\x1E{}\x1E{}\x1E{}", array_to_string(p), array_to_string(&vs), s, array_to_string(c1), array_to_string(c2), option_to_string(ou1), option_to_string(ou2)),
|
||||||
|
DrawInstructions::Gradient(p, d, c1, c2, u) => format!("Gradient/{}\x1E{}\x1E{}\x1E{}\x1E{}", array_to_string(p), array_to_string(d), array_to_string(c1), array_to_string(c2), u),
|
||||||
|
DrawInstructions::Bmp(p, s, b) => format!("Bmp/{}\x1E{}\x1E{}", array_to_string(p), s, b),
|
||||||
|
DrawInstructions::Circle(p, u, c) => format!("Circle/{}\x1E{}\x1E{}", array_to_string(p), u, array_to_string(c)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn deserialize(serialized: &str) -> Result<Self, ()> {
|
||||||
|
//no need to strip newlines cause the impl for Vec<DrawInstructions> does that for us
|
||||||
|
let mut parts = serialized.split("/");
|
||||||
|
match parts.next().unwrap_or("Invalid") {
|
||||||
|
"Rect" => {
|
||||||
|
let rest = get_rest_of_split(&mut parts, Some("/"));
|
||||||
|
let mut args = rest.split("\x1E");
|
||||||
|
let arg = args.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let p = get_two_array(arg.unwrap())?;
|
||||||
|
let arg = args.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let d = get_two_array(arg.unwrap())?;
|
||||||
|
let arg = args.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let c = get_color(arg.unwrap())?;
|
||||||
|
Ok(DrawInstructions::Rect(p, d, c))
|
||||||
|
},
|
||||||
|
"Text" => {
|
||||||
|
let rest = get_rest_of_split(&mut parts, Some("/"));
|
||||||
|
//(p, vs, s, c1, c2, ou1, ou2)
|
||||||
|
let mut args = rest.split("\x1E");
|
||||||
|
let arg = args.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let p = get_two_array(arg.unwrap())?;
|
||||||
|
let arg = args.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let mut vs = Vec::new();
|
||||||
|
for s in arg.unwrap().split("\x1F") {
|
||||||
|
vs.push(s.to_string());
|
||||||
|
}
|
||||||
|
let arg = args.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let s = arg.unwrap();
|
||||||
|
let arg = args.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let c1 = get_color(arg.unwrap())?;
|
||||||
|
let arg = args.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let c2 = get_color(arg.unwrap())?;
|
||||||
|
let arg = args.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let arg = arg.unwrap();
|
||||||
|
let o1 = match arg {
|
||||||
|
"N" => None,
|
||||||
|
_ => {
|
||||||
|
if arg.len() > 1 {
|
||||||
|
if let Ok(n) = arg[1..].parse() {
|
||||||
|
Some(n)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let arg = args.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let arg = arg.unwrap();
|
||||||
|
let o2 = match arg {
|
||||||
|
"N" => None,
|
||||||
|
_ => {
|
||||||
|
if arg.len() > 1 {
|
||||||
|
if let Ok(n) = arg[1..].parse() {
|
||||||
|
Some(n)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Ok(DrawInstructions::Text(p, vs, s.to_string(), c1, c2, o1, o2))
|
||||||
|
},
|
||||||
|
"Gradient" => {
|
||||||
|
let rest = get_rest_of_split(&mut parts, Some("/"));
|
||||||
|
//(p, d, c1, c2, u)
|
||||||
|
let mut args = rest.split("\x1E");
|
||||||
|
let arg = args.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let p = get_two_array(arg.unwrap())?;
|
||||||
|
let arg = args.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let d = get_two_array(arg.unwrap())?;
|
||||||
|
let arg = args.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let c1 = get_color(arg.unwrap())?;
|
||||||
|
let arg = args.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let c2 = get_color(arg.unwrap())?;
|
||||||
|
let arg = args.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let u = arg.unwrap().parse();
|
||||||
|
if u.is_err() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
Ok(DrawInstructions::Gradient(p, d, c1, c2, u.unwrap()))
|
||||||
|
},
|
||||||
|
"Bmp" => {
|
||||||
|
let rest = get_rest_of_split(&mut parts, Some("/"));
|
||||||
|
//(p, s, b)
|
||||||
|
let mut args = rest.split("\x1E");
|
||||||
|
let arg = args.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let p = get_two_array(arg.unwrap())?;
|
||||||
|
let arg = args.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let s = arg.unwrap();
|
||||||
|
let arg = args.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let arg = arg.unwrap();
|
||||||
|
if arg != "true" && arg != "false" {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let b = arg == "true";
|
||||||
|
Ok(DrawInstructions::Bmp(p, s.to_string(), b))
|
||||||
|
},
|
||||||
|
"Circle" => {
|
||||||
|
let rest = get_rest_of_split(&mut parts, Some("/"));
|
||||||
|
//(p, u, c)
|
||||||
|
let mut args = rest.split("\x1E");
|
||||||
|
let arg = args.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let p = get_two_array(arg.unwrap())?;
|
||||||
|
let arg = args.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let u = arg.unwrap().parse();
|
||||||
|
if u.is_err() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let arg = args.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let c = get_color(arg.unwrap())?;
|
||||||
|
Ok(DrawInstructions::Circle(p, u.unwrap(), c))
|
||||||
|
},
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type DrawInstructionsVec = Vec<DrawInstructions>;
|
||||||
|
|
||||||
|
impl Serializable for DrawInstructionsVec {
|
||||||
|
fn serialize(&self) -> String {
|
||||||
|
let collected: Vec<_> = self.into_iter().map(|ins| ins.serialize()).collect();
|
||||||
|
collected.join("\x1D")
|
||||||
|
}
|
||||||
|
fn deserialize(serialized: &str) -> Result<Self, ()> {
|
||||||
|
//strip newline at the end
|
||||||
|
let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized };
|
||||||
|
let mut instructions = Vec::new();
|
||||||
|
for ser_ins in serialized.split("\x1D") {
|
||||||
|
if let Ok(ser_ins) = DrawInstructions::deserialize(ser_ins) {
|
||||||
|
instructions.push(ser_ins);
|
||||||
|
} else {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(instructions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn draw_instructions_serialize_deserialize() {
|
||||||
|
use std::vec;
|
||||||
|
let instructions = vec![
|
||||||
|
DrawInstructions::Rect([15, 24], [100, 320], [255, 0, 128]),
|
||||||
|
DrawInstructions::Text([0, 158], vec!["times-new-roman".to_string(), "shippori-mincho".to_string()], "Test test 1234 testing\nmictest / mictest is this thing\non?".to_string(), [12, 36, 108], [128, 128, 128], Some(1), None),
|
||||||
|
DrawInstructions::Gradient([0, 500], [750, 125], [255, 255, 255], [0, 0, 0], 12),
|
||||||
|
DrawInstructions::Text([123, 999], vec!["times-new-romono".to_string()], "print!(\"{}\", variable_name);".to_string(), [12, 36, 108], [128, 128, 128], Some(44), Some(200)),
|
||||||
|
DrawInstructions::Bmp([55, 98], "mingde".to_string(), true),
|
||||||
|
DrawInstructions::Bmp([55, 98], "wooooo".to_string(), false),
|
||||||
|
DrawInstructions::Circle([0, 1], 19, [128, 128, 128]),
|
||||||
|
];
|
||||||
|
let serialized = instructions.serialize();
|
||||||
|
assert!(serialized == DrawInstructionsVec::deserialize(&serialized).unwrap().serialize());
|
||||||
|
let instructions = vec![
|
||||||
|
DrawInstructions::Rect([0, 0], [410, 410], [0, 0, 0]),
|
||||||
|
DrawInstructions::Text([4, 4], vec!["times-new-romono".to_string()], "Mingde Terminal".to_string(), [255, 255, 255], [0, 0, 0], Some(0), Some(10)),
|
||||||
|
DrawInstructions::Text([4, 34], vec!["times-new-romono".to_string()], "$ a".to_string(), [255, 255, 255], [0, 0, 0], Some(0), Some(10)),
|
||||||
|
];
|
||||||
|
let serialized = instructions.serialize() + "\n";
|
||||||
|
assert!(serialized[..serialized.len() - 1] == DrawInstructionsVec::deserialize(&serialized).unwrap().serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for WindowLikeType {
|
||||||
|
fn serialize(&self) -> String {
|
||||||
|
match self {
|
||||||
|
WindowLikeType::LockScreen => "LockScreen".to_string(),
|
||||||
|
WindowLikeType::Window => "Window".to_string(),
|
||||||
|
WindowLikeType::DesktopBackground => "DesktopBackground".to_string(),
|
||||||
|
WindowLikeType::Taskbar => "Taskbar".to_string(),
|
||||||
|
WindowLikeType::StartMenu => "StartMenu".to_string(),
|
||||||
|
WindowLikeType::WorkspaceIndicator => "WorkspaceIndicator".to_string(),
|
||||||
|
WindowLikeType::OnscreenKeyboard => "OnscreenKeyboard".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn deserialize(serialized: &str) -> Result<Self, ()> {
|
||||||
|
let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized };
|
||||||
|
match serialized {
|
||||||
|
"LockScreen" => Ok(WindowLikeType::LockScreen),
|
||||||
|
"Window" => Ok(WindowLikeType::Window),
|
||||||
|
"DesktopBackground" => Ok(WindowLikeType::DesktopBackground),
|
||||||
|
"Taskbar" => Ok(WindowLikeType::Taskbar),
|
||||||
|
"StartMenu" => Ok(WindowLikeType::StartMenu),
|
||||||
|
"WorkspaceIndicator" => Ok(WindowLikeType::WorkspaceIndicator),
|
||||||
|
"OnscreenKeyboard" => Ok(WindowLikeType::OnscreenKeyboard),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn window_like_type_serialize_deserialize() {
|
||||||
|
let wl_type = WindowLikeType::Window;
|
||||||
|
let serialized = wl_type.serialize();
|
||||||
|
assert!(serialized == WindowLikeType::deserialize(&serialized).unwrap().serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for Dimensions {
|
||||||
|
fn serialize(&self) -> String {
|
||||||
|
array_to_string(self)
|
||||||
|
}
|
||||||
|
fn deserialize(serialized: &str) -> Result<Self, ()> {
|
||||||
|
//strip newline at the end
|
||||||
|
let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized };
|
||||||
|
let d = get_two_array(serialized)?;
|
||||||
|
Ok(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for WindowMessage {
|
||||||
|
fn serialize(&self) -> String {
|
||||||
|
match self {
|
||||||
|
WindowMessage::Init(d) => format!("Init/{}", array_to_string(d)),
|
||||||
|
WindowMessage::KeyPress(kp) => format!("KeyPress/{}", kp.key),
|
||||||
|
WindowMessage::CtrlKeyPress(kp) => format!("CtrlKeyPress/{}", kp.key),
|
||||||
|
WindowMessage::Shortcut(st) => format!("Shortcut/{}", match st {
|
||||||
|
ShortcutType::StartMenu => "StartMenu".to_string(),
|
||||||
|
ShortcutType::SwitchWorkspace(u) => format!("SwitchWorkspace/{}", u),
|
||||||
|
ShortcutType::MoveWindowToWorkspace(u) => format!("MoveWindowToWorkspace/{}", u),
|
||||||
|
ShortcutType::FocusPrevWindow => "FocusPrevWindow".to_string(),
|
||||||
|
ShortcutType::FocusNextWindow => "FocusNextWindow".to_string(),
|
||||||
|
ShortcutType::QuitWindow => "QuitWindow".to_string(),
|
||||||
|
ShortcutType::MoveWindow(d) => format!("MoveWindow/{}", match d {
|
||||||
|
Direction::Left => "Left",
|
||||||
|
Direction::Down => "Down",
|
||||||
|
Direction::Up => "Up",
|
||||||
|
Direction::Right => "Right",
|
||||||
|
}),
|
||||||
|
ShortcutType::MoveWindowToEdge(d) => format!("MoveWindowToEdge/{}", match d {
|
||||||
|
Direction::Left => "Left",
|
||||||
|
Direction::Down => "Down",
|
||||||
|
Direction::Up => "Up",
|
||||||
|
Direction::Right => "Right",
|
||||||
|
}),
|
||||||
|
ShortcutType::CenterWindow => "CenterWindow".to_string(),
|
||||||
|
ShortcutType::FullscreenWindow => "FullscreenWindow".to_string(),
|
||||||
|
ShortcutType::HalfWidthWindow => "HalfWidthWindow".to_string(),
|
||||||
|
ShortcutType::ClipboardCopy => "ClipboardCopy".to_string(),
|
||||||
|
ShortcutType::ClipboardPaste(s) => format!("ClipboardPaste/{}", s),
|
||||||
|
}),
|
||||||
|
WindowMessage::Info(i) => format!("Info/{}", match i {
|
||||||
|
InfoType::WindowsInWorkspace(wv, u) => {
|
||||||
|
let mut wv_string = String::new();
|
||||||
|
for w in wv {
|
||||||
|
wv_string += &format!("{}\x1F{}\x1F", w.0, w.1);
|
||||||
|
}
|
||||||
|
wv_string = wv_string[..wv_string.len() - 1].to_string();
|
||||||
|
format!("WindowsInWorkspace/{}\x1E{}", wv_string, u)
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
WindowMessage::Focus => "Focus".to_string(),
|
||||||
|
WindowMessage::Unfocus => "Unfocus".to_string(),
|
||||||
|
WindowMessage::FocusClick => "FocusClick".to_string(),
|
||||||
|
WindowMessage::ChangeDimensions(d) => format!("ChangeDimensions/{}", array_to_string(d)),
|
||||||
|
WindowMessage::Touch(u1, u2) => format!("Touch/{}\x1E{}", u1, u2),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn deserialize(serialized: &str) -> Result<Self, ()> {
|
||||||
|
let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized };
|
||||||
|
let mut parts = serialized.split("/");
|
||||||
|
match parts.next().unwrap_or("Invalid") {
|
||||||
|
"Init" => {
|
||||||
|
let arg = parts.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let d = get_two_array(arg.unwrap())?;
|
||||||
|
Ok(WindowMessage::Init(d))
|
||||||
|
},
|
||||||
|
"KeyPress" => {
|
||||||
|
let charg = get_rest_of_split(&mut parts, Some("/")).chars().next();
|
||||||
|
if let Some(charg) = charg {
|
||||||
|
Ok(WindowMessage::KeyPress(KeyPress { key: charg }))
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"CtrlKeyPress" => {
|
||||||
|
let charg = get_rest_of_split(&mut parts, Some("/")).chars().next();
|
||||||
|
if let Some(charg) = charg {
|
||||||
|
Ok(WindowMessage::CtrlKeyPress(KeyPress { key: charg }))
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Shortcut" => {
|
||||||
|
let arg = parts.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let arg = arg.unwrap();
|
||||||
|
let shortcut = match arg {
|
||||||
|
"StartMenu" => Some(ShortcutType::StartMenu),
|
||||||
|
"SwitchWorkspace" | "MoveWindowToWorkspace" => {
|
||||||
|
let narg = parts.next();
|
||||||
|
if narg.is_none() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let narg = narg.unwrap();
|
||||||
|
if let Ok(n) = narg.parse() {
|
||||||
|
if arg == "SwitchWorkspace" {
|
||||||
|
Some(ShortcutType::SwitchWorkspace(n))
|
||||||
|
} else {
|
||||||
|
Some(ShortcutType::MoveWindowToWorkspace(n))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"FocusPrevWindow" => Some(ShortcutType::FocusPrevWindow),
|
||||||
|
"FocusNextWindow" => Some(ShortcutType::FocusNextWindow),
|
||||||
|
"QuitWindow" => Some(ShortcutType::QuitWindow),
|
||||||
|
"MoveWindow" | "MoveWindowToEdge" => {
|
||||||
|
let darg = parts.next();
|
||||||
|
if let Some(darg) = darg {
|
||||||
|
let direction = match darg {
|
||||||
|
"Left" => Some(Direction::Left),
|
||||||
|
"Up" => Some(Direction::Up),
|
||||||
|
"Down" => Some(Direction::Down),
|
||||||
|
"Right" => Some(Direction::Right),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
if let Some(direction) = direction {
|
||||||
|
if arg == "MoveWindow" {
|
||||||
|
Some(ShortcutType::MoveWindow(direction))
|
||||||
|
} else {
|
||||||
|
Some(ShortcutType::MoveWindowToEdge(direction))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"CenterWindow" => Some(ShortcutType::CenterWindow),
|
||||||
|
"FullscreenWindow" => Some(ShortcutType::FullscreenWindow),
|
||||||
|
"HalfWidthWindow" => Some(ShortcutType::HalfWidthWindow),
|
||||||
|
"ClipboardCopy" => Some(ShortcutType::ClipboardCopy),
|
||||||
|
"ClipboardPaste" => Some(ShortcutType::ClipboardPaste(get_rest_of_split(&mut parts, Some("/")))),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
if let Some(shortcut) = shortcut {
|
||||||
|
Ok(WindowMessage::Shortcut(shortcut))
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Info" => {
|
||||||
|
//skip WindowsInWorkspace cause that's the only possible InfoType atm
|
||||||
|
if parts.next().is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let arg = parts.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let mut parts2 = arg.unwrap().split("\x1E");
|
||||||
|
let arg2 = parts2.next();
|
||||||
|
if arg2.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let mut w_tuple: (usize, String) = Default::default();
|
||||||
|
let mut w_vec = Vec::new();
|
||||||
|
let mut i = 0;
|
||||||
|
for a in arg2.unwrap().split("\x1F") {
|
||||||
|
if i % 2 == 0 {
|
||||||
|
if let Ok(n) = a.parse() {
|
||||||
|
w_tuple.0 = n;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
w_tuple.1 = a.to_string();
|
||||||
|
w_vec.push(w_tuple.clone());
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
let arg2 = parts2.next();
|
||||||
|
if arg2.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
if let Ok(n) = arg2.unwrap().parse() {
|
||||||
|
return Ok(WindowMessage::Info(InfoType::WindowsInWorkspace(w_vec, n)));
|
||||||
|
} else {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Focus" => Ok(WindowMessage::Focus),
|
||||||
|
"Unfocus" => Ok(WindowMessage::Unfocus),
|
||||||
|
"FocusClick" => Ok(WindowMessage::FocusClick),
|
||||||
|
"ChangeDimensions" => {
|
||||||
|
let arg = parts.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let d = get_two_array(arg.unwrap())?;
|
||||||
|
Ok(WindowMessage::ChangeDimensions(d))
|
||||||
|
},
|
||||||
|
"Touch" => {
|
||||||
|
let arg = parts.next();
|
||||||
|
if arg.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let mut parts2 = arg.unwrap().split("\x1E");
|
||||||
|
let arg2 = parts2.next();
|
||||||
|
if arg2.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let u1 = arg2.unwrap().parse();
|
||||||
|
let arg2 = parts2.next();
|
||||||
|
if u1.is_err() || arg2.is_none() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let u2 = arg2.unwrap().parse();
|
||||||
|
if u2.is_err() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
Ok(WindowMessage::Touch(u1.unwrap(), u2.unwrap()))
|
||||||
|
},
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn window_message_serialize_deserialize() {
|
||||||
|
for wm in [
|
||||||
|
WindowMessage::Init([1000, 1001]),
|
||||||
|
WindowMessage::KeyPress(KeyPress { key: 'a' }),
|
||||||
|
WindowMessage::KeyPress(KeyPress { key: '/' }),
|
||||||
|
WindowMessage::KeyPress(KeyPress { key: '𐘂' }),
|
||||||
|
WindowMessage::CtrlKeyPress(KeyPress { key: ';' }),
|
||||||
|
WindowMessage::Shortcut(ShortcutType::StartMenu),
|
||||||
|
WindowMessage::Shortcut(ShortcutType::MoveWindowToWorkspace(7)),
|
||||||
|
WindowMessage::Shortcut(ShortcutType::ClipboardPaste("105/20 Azumanga".to_string())),
|
||||||
|
WindowMessage::Info(InfoType::WindowsInWorkspace(vec![(1, "Terminal".to_string()), (2, "Minesweeper".to_string()), (12, "Test Test".to_string())], 5)),
|
||||||
|
WindowMessage::Focus,
|
||||||
|
WindowMessage::Unfocus,
|
||||||
|
WindowMessage::FocusClick,
|
||||||
|
WindowMessage::ChangeDimensions([999, 250]),
|
||||||
|
WindowMessage::Touch(12, 247),
|
||||||
|
] {
|
||||||
|
let serialized = wm.serialize();
|
||||||
|
assert!(serialized == WindowMessage::deserialize(&serialized).unwrap().serialize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
use serde::{ Deserialize, Serialize };
|
|
||||||
|
|
||||||
use crate::framebuffer::RGBColor;
|
use crate::framebuffer::RGBColor;
|
||||||
|
|
||||||
#[derive(PartialEq, Default)]
|
#[derive(PartialEq, Default)]
|
||||||
@@ -9,7 +7,7 @@ pub enum Themes {
|
|||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Default)]
|
||||||
pub struct ThemeInfo {
|
pub struct ThemeInfo {
|
||||||
pub top: RGBColor,
|
pub top: RGBColor,
|
||||||
pub background: RGBColor,
|
pub background: RGBColor,
|
||||||
@@ -20,7 +18,6 @@ pub struct ThemeInfo {
|
|||||||
pub alt_background: RGBColor,
|
pub alt_background: RGBColor,
|
||||||
pub alt_text: RGBColor,
|
pub alt_text: RGBColor,
|
||||||
pub alt_secondary: RGBColor,
|
pub alt_secondary: RGBColor,
|
||||||
//
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const THEME_INFOS: [(Themes, ThemeInfo); 1] = [
|
const THEME_INFOS: [(Themes, ThemeInfo); 1] = [
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ use linux_framebuffer::Framebuffer;
|
|||||||
use termion::input::TermRead;
|
use termion::input::TermRead;
|
||||||
use termion::raw::IntoRawMode;
|
use termion::raw::IntoRawMode;
|
||||||
use termion::{ clear, cursor };
|
use termion::{ clear, cursor };
|
||||||
use serde::{ Deserialize, Serialize };
|
|
||||||
|
|
||||||
use crate::framebuffer::{ FramebufferWriter, FramebufferInfo, Point, Dimensions, RGBColor };
|
use crate::framebuffer::{ FramebufferWriter, FramebufferInfo, Point, Dimensions, RGBColor };
|
||||||
use crate::themes::{ ThemeInfo, Themes, get_theme_info };
|
use crate::themes::{ ThemeInfo, Themes, get_theme_info };
|
||||||
@@ -39,7 +38,7 @@ pub const TASKBAR_HEIGHT: usize = 38;
|
|||||||
pub const INDICATOR_HEIGHT: usize = 20;
|
pub const INDICATOR_HEIGHT: usize = 20;
|
||||||
const WINDOW_TOP_HEIGHT: usize = 26;
|
const WINDOW_TOP_HEIGHT: usize = 26;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum KeyChar {
|
pub enum KeyChar {
|
||||||
Press(char),
|
Press(char),
|
||||||
Alt(char),
|
Alt(char),
|
||||||
@@ -169,7 +168,7 @@ pub fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug)]
|
||||||
pub enum DrawInstructions {
|
pub enum DrawInstructions {
|
||||||
Rect(Point, Dimensions, RGBColor),
|
Rect(Point, Dimensions, RGBColor),
|
||||||
Text(Point, Vec<String>, String, RGBColor, RGBColor, Option<usize>, Option<u8>), //font and text
|
Text(Point, Vec<String>, String, RGBColor, RGBColor, Option<usize>, Option<u8>), //font and text
|
||||||
@@ -178,7 +177,7 @@ pub enum DrawInstructions {
|
|||||||
Circle(Point, usize, RGBColor),
|
Circle(Point, usize, RGBColor),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum WindowLikeType {
|
pub enum WindowLikeType {
|
||||||
LockScreen,
|
LockScreen,
|
||||||
Window,
|
Window,
|
||||||
|
|||||||
Reference in New Issue
Block a user