start menu dynamically find window binaries, break out ming-wm-lib

also, fix so audio player compiles, fix for empty draw instructions vec
This commit is contained in:
stjet
2025-03-03 07:12:29 +00:00
parent 1b9922d70f
commit 9eb9ace77f
40 changed files with 912 additions and 202 deletions

View File

@@ -13,14 +13,14 @@ use id3::TagLike;
use mp4ameta;
use metaflac;
use ming_wm::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm::messages::{ WindowMessage, WindowMessageResponse };
use ming_wm::framebuffer::Dimensions;
use ming_wm::themes::ThemeInfo;
use ming_wm::utils::{ concat_paths, format_seconds, Substring };
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse };
use ming_wm_lib::framebuffer_types::Dimensions;
use ming_wm_lib::themes::ThemeInfo;
use ming_wm_lib::utils::{ concat_paths, format_seconds, Substring };
use ming_wm_lib::dirs::home;
use ming_wm_lib::ipc::listen;
use ming_wm::fs::get_all_files;
use ming_wm::dirs::home;
use ming_wm::ipc::listen;
fn get_artist(path: &PathBuf) -> Option<String> {
let ext = path.extension().unwrap();
@@ -29,11 +29,12 @@ fn get_artist(path: &PathBuf) -> Option<String> {
tag.artist().map(|s| s.to_string())
} else if ext == "flac" {
let tag = metaflac::Tag::read_from_path(path).unwrap();
if let Some(mut artists) = tag.get_vorbis("Artist") {
let x = if let Some(mut artists) = tag.get_vorbis("Artist") {
Some(artists.next().unwrap().to_string()) //get the first one
} else {
None
}
};
x
} else if ext == "mp3" {
let tag = id3::Tag::read_from_path(path).unwrap();
tag.artist().map(|s| s.to_string())

View File

@@ -3,11 +3,11 @@ use std::vec;
use std::fs::{ read_dir, metadata, Metadata };
use std::path::PathBuf;
use ming_wm::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm::messages::{ WindowMessage, WindowMessageResponse };
use ming_wm::framebuffer::Dimensions;
use ming_wm::themes::ThemeInfo;
use ming_wm::ipc::listen;
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse };
use ming_wm_lib::framebuffer_types::Dimensions;
use ming_wm_lib::themes::ThemeInfo;
use ming_wm_lib::ipc::listen;
const HEIGHT: usize = 20;

View File

@@ -10,11 +10,26 @@ use linux_framebuffer::Framebuffer;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
use termion::{ clear, cursor };
use termion::event::Key;
use ming_wm_lib::window_manager_types::KeyChar;
use ming_wm_lib::messages::*;
use ming_wm::framebuffer::{ FramebufferWriter, FramebufferInfo };
use ming_wm::window_manager::{ WindowManager, KeyChar };
use ming_wm::utils::key_to_char;
use ming_wm::messages::*;
use ming_wm::window_manager::WindowManager;
//use Linear A for escape, backspace, enter
//Linear A used only internally in onscreen keyboard: 𐘎 is alt, 𐘧 is switch board, 𐘾 is ctrl
fn key_to_char(key: Key) -> Option<KeyChar> {
match key {
Key::Char('\n') => Some(KeyChar::Press('𐘂')),
Key::Char(c) => Some(KeyChar::Press(c)),
Key::Alt(c) => Some(KeyChar::Alt(c)),
Key::Ctrl(c) => Some(KeyChar::Ctrl(c)),
Key::Backspace => Some(KeyChar::Press('𐘁')),
Key::Esc => Some(KeyChar::Press('𐘃')),
_ => None,
}
}
pub enum ThreadMessage {
KeyChar(KeyChar),

View File

@@ -4,13 +4,13 @@ use std::fmt;
use std::path::PathBuf;
use std::fs::{ read_to_string, write };
use ming_wm::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest, ShortcutType };
use ming_wm::themes::ThemeInfo;
use ming_wm::framebuffer::Dimensions;
use ming_wm::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm::utils::{ calc_actual_lines, calc_new_cursor_pos, Substring };
use ming_wm::dirs::home;
use ming_wm::ipc::listen;
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest, ShortcutType };
use ming_wm_lib::themes::ThemeInfo;
use ming_wm_lib::framebuffer_types::Dimensions;
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm_lib::utils::{ calc_actual_lines, calc_new_cursor_pos, Substring };
use ming_wm_lib::dirs::home;
use ming_wm_lib::ipc::listen;
const MONO_WIDTH: u8 = 10;
const LINE_HEIGHT: usize = 18;

View File

@@ -3,12 +3,12 @@ use std::vec;
use std::collections::VecDeque;
use core::convert::TryFrom;
use ming_wm::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm::messages::{ WindowMessage, WindowMessageResponse };
use ming_wm::framebuffer::Dimensions;
use ming_wm::themes::ThemeInfo;
use ming_wm::utils::{ u8_to_hex, hex_to_u8, HEX_CHARS };
use ming_wm::ipc::listen;
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse };
use ming_wm_lib::framebuffer_types::Dimensions;
use ming_wm_lib::themes::ThemeInfo;
use ming_wm_lib::utils::{ u8_to_hex, hex_to_u8, HEX_CHARS };
use ming_wm_lib::ipc::listen;
//16x16 with 40 mines

View File

@@ -1,11 +1,11 @@
use std::vec::Vec;
use std::vec;
use ming_wm::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm::messages::{ WindowMessage, WindowMessageResponse };
use ming_wm::framebuffer::{ Dimensions, RGBColor };
use ming_wm::themes::ThemeInfo;
use ming_wm::ipc::listen;
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse };
use ming_wm_lib::framebuffer_types::{ Dimensions, RGBColor };
use ming_wm_lib::themes::ThemeInfo;
use ming_wm_lib::ipc::listen;
const REVERSI_GREEN: RGBColor = [72, 93, 63];

View File

@@ -10,13 +10,13 @@ use std::fmt;
use pty_process::blocking;
use ming_wm::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm::messages::{ WindowMessage, WindowMessageResponse, ShortcutType };
use ming_wm::framebuffer::Dimensions;
use ming_wm::themes::ThemeInfo;
use ming_wm::utils::{ concat_paths, Substring };
use ming_wm::dirs::home;
use ming_wm::ipc::listen;
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, ShortcutType };
use ming_wm_lib::framebuffer_types::Dimensions;
use ming_wm_lib::themes::ThemeInfo;
use ming_wm_lib::utils::{ concat_paths, Substring };
use ming_wm_lib::dirs::home;
use ming_wm_lib::ipc::listen;
//todo: support copy and paste

View File

@@ -2,10 +2,10 @@ use std::vec;
use std::vec::Vec;
use crate::components::Component;
use crate::framebuffer::{ Dimensions, Point };
use crate::themes::ThemeInfo;
use crate::messages::WindowMessage;
use crate::window_manager::DrawInstructions;
use ming_wm_lib::framebuffer_types::{ Dimensions, Point };
use ming_wm_lib::themes::ThemeInfo;
use ming_wm_lib::messages::WindowMessage;
use ming_wm_lib::window_manager_types::DrawInstructions;
pub struct HighlightButton<T> {
name_: String,

View File

@@ -1,8 +1,8 @@
use std::vec::Vec;
use crate::themes::ThemeInfo;
use crate::messages::WindowMessage;
use crate::window_manager::DrawInstructions;
use ming_wm_lib::themes::ThemeInfo;
use ming_wm_lib::messages::WindowMessage;
use ming_wm_lib::window_manager_types::DrawInstructions;
pub mod toggle_button;
pub mod highlight_button;

View File

@@ -1,12 +1,13 @@
use std::vec;
use std::vec::Vec;
use ming_wm_lib::framebuffer_types::{ Dimensions, Point };
use ming_wm_lib::themes::ThemeInfo;
use ming_wm_lib::messages::WindowMessage;
use ming_wm_lib::window_manager_types::DrawInstructions;
use ming_wm_lib::utils::calc_actual_lines;
use crate::components::Component;
use crate::framebuffer::{ Dimensions, Point };
use crate::themes::ThemeInfo;
use crate::messages::WindowMessage;
use crate::window_manager::DrawInstructions;
use crate::utils::calc_actual_lines;
const MONO_WIDTH: u8 = 10;
const LINE_HEIGHT: usize = 18;

View File

@@ -2,10 +2,10 @@ use std::vec;
use std::vec::Vec;
use crate::components::Component;
use crate::framebuffer::{ Dimensions, Point };
use crate::themes::ThemeInfo;
use crate::messages::WindowMessage;
use crate::window_manager::{ DrawInstructions };
use ming_wm_lib::framebuffer_types::{ Dimensions, Point };
use ming_wm_lib::themes::ThemeInfo;
use ming_wm_lib::messages::WindowMessage;
use ming_wm_lib::window_manager_types::DrawInstructions;
const MONO_WIDTH: u8 = 10;

View File

@@ -2,10 +2,10 @@ use std::vec;
use std::vec::Vec;
use crate::components::Component;
use crate::framebuffer::{ Dimensions, Point };
use crate::themes::ThemeInfo;
use crate::messages::WindowMessage;
use crate::window_manager::DrawInstructions;
use ming_wm_lib::framebuffer_types::{ Dimensions, Point };
use ming_wm_lib::themes::ThemeInfo;
use ming_wm_lib::messages::WindowMessage;
use ming_wm_lib::window_manager_types::DrawInstructions;
pub struct ToggleButton<T> {
name_: String,

View File

@@ -1,49 +0,0 @@
use std::env;
use std::path::PathBuf;
pub fn home() -> Option<PathBuf> {
if let Ok(home) = env::var("HOME") {
Some(PathBuf::from(home))
} else {
None
}
}
pub fn data_dir() -> Option<PathBuf> {
//$XDG_DATA_HOME or $HOME/.local/share
if let Ok(data_home) = env::var("XDG_DATA_HOME") {
Some(PathBuf::from(data_home))
} else {
if let Some(mut data_home) = home() {
data_home.push(".local");
data_home.push("share");
Some(data_home)
} else {
None
}
}
}
pub fn config_dir() -> Option<PathBuf> {
//$XDG_CONFIG_HOME or $HOME/.config
if let Ok(config_home) = env::var("XDG_CONFIG_HOME") {
Some(PathBuf::from(config_home))
} else {
if let Some(mut config_home) = home() {
config_home.push(".config");
Some(config_home)
} else {
None
}
}
}
pub fn exe_dir(add: Option<&str>) -> PathBuf {
let mut exe_dir = env::current_exe().unwrap();
exe_dir.pop();
if let Some(add) = add {
exe_dir.push(add);
}
exe_dir
}

View File

@@ -2,10 +2,10 @@ use std::vec::Vec;
use std::boxed::Box;
use std::fs::read_to_string;
use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
use crate::messages::{ WindowMessage, WindowMessageResponse };
use crate::framebuffer::Dimensions;
use crate::themes::ThemeInfo;
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse };
use ming_wm_lib::framebuffer_types::Dimensions;
use ming_wm_lib::themes::ThemeInfo;
use crate::components::Component;
use crate::components::paragraph::Paragraph;

View File

@@ -3,12 +3,12 @@ use std::vec::Vec;
use std::fs::File;
use std::io::Read;
use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType, TASKBAR_HEIGHT, INDICATOR_HEIGHT };
use crate::messages::{ WindowMessage, WindowMessageResponse, ShortcutType };
use crate::framebuffer::Dimensions;
use crate::themes::ThemeInfo;
use crate::utils::{ hex_to_u8, is_hex };
use crate::dirs::config_dir;
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType, TASKBAR_HEIGHT, INDICATOR_HEIGHT };
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, ShortcutType };
use ming_wm_lib::framebuffer_types::Dimensions;
use ming_wm_lib::themes::ThemeInfo;
use ming_wm_lib::utils::{ hex_to_u8, is_hex };
use ming_wm_lib::dirs::config_dir;
pub struct DesktopBackground {
dimensions: Dimensions,

View File

@@ -3,10 +3,10 @@ use std::boxed::Box;
use std::fs::{ read_to_string, read_dir };
use std::path::PathBuf;
use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
use crate::messages::{ WindowMessage, WindowMessageResponse };
use crate::framebuffer::Dimensions;
use crate::themes::ThemeInfo;
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse };
use ming_wm_lib::framebuffer_types::Dimensions;
use ming_wm_lib::themes::ThemeInfo;
use crate::components::paragraph::Paragraph;
use crate::components::Component;

View File

@@ -1,10 +1,10 @@
use std::vec;
use std::vec::Vec;
use crate::framebuffer::Dimensions;
use crate::themes::ThemeInfo;
use crate::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest };
use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm_lib::framebuffer_types::Dimensions;
use ming_wm_lib::themes::ThemeInfo;
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest };
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType };
use blake2::{ Blake2b512, Digest };
include!(concat!(env!("OUT_DIR"), "/password.rs"));

View File

@@ -2,13 +2,13 @@ use std::vec;
use std::vec::Vec;
use std::collections::HashMap;
use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType, KeyChar };
use crate::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest };
use crate::framebuffer::Dimensions;
use crate::themes::ThemeInfo;
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType, KeyChar };
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest };
use ming_wm_lib::framebuffer_types::Dimensions;
use ming_wm_lib::themes::ThemeInfo;
use ming_wm_lib::utils::point_inside;
use crate::components::Component;
use crate::components::press_button::PressButton;
use crate::utils::point_inside;
//seems like framebuffer only updates if (real) key is pressed...
//on mobile, volume down button seems to work but is annoying

View File

@@ -2,14 +2,14 @@ use std::vec;
use std::vec::Vec;
use std::boxed::Box;
use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
use crate::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest };
use crate::framebuffer::Dimensions;
use crate::themes::ThemeInfo;
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest };
use ming_wm_lib::framebuffer_types::Dimensions;
use ming_wm_lib::themes::ThemeInfo;
use ming_wm_lib::dirs::exe_dir;
use crate::fs::{ ExeWindowInfos, get_all_executable_windows };
use crate::components::Component;
use crate::components::highlight_button::HighlightButton;
use crate::dirs::exe_dir;
static CATEGORIES: [&'static str; 9] = ["About", "Utils", "Games", "Editing", "Files", "Internet", "Misc", "Help", "Logout"];

View File

@@ -2,10 +2,10 @@ use std::vec;
use std::vec::Vec;
use std::boxed::Box;
use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType, TASKBAR_HEIGHT };
use crate::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest, ShortcutType, InfoType, WindowsVec };
use crate::framebuffer::Dimensions;
use crate::themes::ThemeInfo;
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType, TASKBAR_HEIGHT };
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest, ShortcutType, InfoType, WindowsVec };
use ming_wm_lib::framebuffer_types::Dimensions;
use ming_wm_lib::themes::ThemeInfo;
use crate::components::Component;
use crate::components::toggle_button::ToggleButton;

View File

@@ -2,10 +2,10 @@ use std::vec;
use std::vec::Vec;
use std::time::{ SystemTime, UNIX_EPOCH };
use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType, INDICATOR_HEIGHT };
use crate::messages::{ WindowMessage, WindowMessageResponse, ShortcutType };
use crate::framebuffer::Dimensions;
use crate::themes::ThemeInfo;
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType, INDICATOR_HEIGHT };
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, ShortcutType };
use ming_wm_lib::framebuffer_types::Dimensions;
use ming_wm_lib::themes::ThemeInfo;
const WIDTH: usize = 15;
const ONE_MINUTE: u64 = 60;

View File

@@ -3,12 +3,9 @@ use std::vec::Vec;
use bmp_rust::bmp::BMP;
use ming_wm_lib::framebuffer_types::*;
use crate::fs::get_font_char_from_fonts;
pub type Point = [usize; 2]; //x, y
pub type Dimensions = [usize; 2]; //width, height
pub type RGBColor = [u8; 3]; //rgb
type FontChar = (char, Vec<Vec<u8>>, u8);
fn color_with_alpha(color: RGBColor, bg_color: RGBColor, alpha: u8) -> RGBColor {

View File

@@ -3,8 +3,8 @@ use std::path::PathBuf;
use std::io::Read;
use std::collections::HashMap;
use crate::dirs;
use crate::utils::get_rest_of_split;
use ming_wm_lib::dirs;
use ming_wm_lib::utils::get_rest_of_split;
fn get_font_char(dir: &str, c: char) -> Option<(char, Vec<Vec<u8>>, u8)> {
let c = if c == '/' { '𐘋' } else if c == '\\' { '𐚆' } else if c == '.' { '𐘅' } else { c };

View File

@@ -1,87 +0,0 @@
use std::io::{ stdin, BufRead };
use std::panic;
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;
/*
pub trait WindowLike {
fn handle_message(&mut self, message: WindowMessage) -> WindowMessageResponse;
fn draw(&self, theme_info: &ThemeInfo) -> Vec<DrawInstructions>;
//properties
fn title(&self) -> &'static str {
""
}
fn resizable(&self) -> bool {
false
}
fn subtype(&self) -> WindowLikeType;
fn ideal_dimensions(&self, dimensions: Dimensions) -> Dimensions; //needs &self or its not object safe or some bullcrap
}
*/
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(("<unknown>", 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::<String>() {
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();
if LOG {
log(&line);
}
let mut parts = line.split(" ");
let method = parts.next().unwrap();
let arg = &parts.collect::<Vec<&str>>().join(" ");
let output = match method {
"handle_message" => {
format!("{}", &window_like.handle_message(WindowMessage::deserialize(arg).unwrap()).serialize())
},
"draw" => {
format!("{}", &window_like.draw(&ThemeInfo::deserialize(arg).unwrap()).serialize())
},
"title" => {
format!("{}", window_like.title())
},
"resizable" => {
format!("{}", window_like.resizable())
},
"subtype" => {
format!("{}", &window_like.subtype().serialize())
},
"ideal_dimensions" => {
format!("{}", &window_like.ideal_dimensions(Dimensions::deserialize(arg).unwrap()).serialize())
},
_ => String::new(),
};
if output != String::new() {
if LOG {
log(&output);
}
println!("{}", output);
}
}
}

View File

@@ -1,14 +1,7 @@
pub mod framebuffer;
pub mod window_manager;
pub mod components;
pub mod themes;
pub mod messages;
pub mod fs;
pub mod utils;
pub mod logging;
pub mod ipc;
pub mod serialize;
pub mod dirs;
mod proxy_window_like;
mod essential;

View File

@@ -1,11 +0,0 @@
use std::fs::{ OpenOptions, create_dir };
use std::io::Write;
use crate::dirs::data_dir;
pub fn log(message: &str) {
let data = data_dir().unwrap().into_os_string().into_string().unwrap();
let _ = create_dir(format!("{}/ming-wm", data));
let _ = writeln!(OpenOptions::new().append(true).create(true).open(format!("{}/ming-wm/logs.txt", data)).unwrap(), "{}", message);
}

View File

@@ -1,107 +0,0 @@
use std::boxed::Box;
use std::vec::Vec;
use crate::framebuffer::Dimensions;
use crate::window_manager::{ WindowLike, KeyChar };
pub enum WindowManagerMessage {
KeyChar(KeyChar),
Touch(usize, usize),
//
}
pub type WindowBox = Box<dyn WindowLike>;
/*
impl PartialEq for WindowBox {
fn eq(&self, _other: &Self) -> bool {
//lol
true
}
}
*/
#[derive(Debug, PartialEq)]
pub enum WindowManagerRequest {
OpenWindow(String),
//may not work in \x1E, \x1F or \x1D are in the paste string
ClipboardCopy(String),
CloseStartMenu,
Unlock,
Lock,
DoKeyChar(KeyChar),
//
}
#[derive(PartialEq, Debug)]
pub enum WindowMessageResponse {
Request(WindowManagerRequest),
JustRedraw,
DoNothing,
}
impl WindowMessageResponse {
pub fn is_key_char_request(&self) -> bool {
if let WindowMessageResponse::Request(WindowManagerRequest::DoKeyChar(_)) = self {
true
} else {
false
}
}
}
pub struct KeyPress {
pub key: char,
}
#[derive(Clone, Copy, PartialEq)]
pub enum Direction {
Left,
Down,
Up,
Right,
}
//todo, rename to CommandType
#[derive(PartialEq)]
pub enum ShortcutType {
StartMenu,
SwitchWorkspace(u8),
MoveWindowToWorkspace(u8),
FocusPrevWindow,
FocusNextWindow,
QuitWindow,
MoveWindow(Direction),
MoveWindowToEdge(Direction),
ChangeWindowSize(Direction),
CenterWindow,
FullscreenWindow,
HalfWidthWindow, //half width, full height
ClipboardCopy,
//may not work in \x1E, \x1F or \x1D are in the paste string
ClipboardPaste(String),
//
}
pub type WindowsVec = Vec<(usize, String)>;
pub enum InfoType {
//let taskbar know what the current windows in the workspace are
WindowsInWorkspace(WindowsVec, usize), //Vec<(id, name)>, focused id
//
}
pub enum WindowMessage {
Init(Dimensions),
KeyPress(KeyPress),
CtrlKeyPress(KeyPress),
Shortcut(ShortcutType),
Info(InfoType),
Focus,
Unfocus,
FocusClick,
ChangeDimensions(Dimensions),
Touch(usize, usize), //for onscreen keyboard only
//
}

View File

@@ -3,12 +3,12 @@ use std::process::{ Command, Child, Stdio };
use std::io::{ BufReader, BufRead, Write };
use std::cell::RefCell;
use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
use crate::messages::{ WindowMessage, WindowMessageResponse };
use crate::framebuffer::Dimensions;
use crate::themes::ThemeInfo;
use crate::dirs;
use crate::serialize::{ Serializable, DrawInstructionsVec };
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse };
use ming_wm_lib::framebuffer_types::Dimensions;
use ming_wm_lib::themes::ThemeInfo;
use ming_wm_lib::dirs;
use ming_wm_lib::serialize::{ Serializable, DrawInstructionsVec };
pub struct ProxyWindowLike {
process: RefCell<Child>,

View File

@@ -1,747 +0,0 @@
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;
use crate::utils::get_rest_of_split;
//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_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!["nimbus-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!["nimbus-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!["nimbus-romono".to_string()], "Mingde Terminal".to_string(), [255, 255, 255], [0, 0, 0], Some(0), Some(10)),
DrawInstructions::Text([4, 34], vec!["nimbus-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::ChangeWindowSize(d) => format!("ChangeWindowSize/{}", 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" | "ChangeWindowSize" => {
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 if arg == "MoveWindowToEdge" {
Some(ShortcutType::MoveWindowToEdge(direction))
} else {
Some(ShortcutType::ChangeWindowSize(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());
}
}

View File

@@ -1,108 +0,0 @@
use crate::framebuffer::RGBColor;
#[derive(PartialEq, Default)]
pub enum Themes {
#[default]
Standard,
Night,
Industrial,
Forest,
Royal,
//Parchment,
}
impl Themes {
pub fn from_str(name: &str) -> Option<Self> {
match name {
"Standard" => Some(Themes::Standard),
"Night" => Some(Themes::Night),
"Industrial" => Some(Themes::Industrial),
"Forest" => Some(Themes::Forest),
"Royal" => Some(Themes::Royal),
_ => None,
}
}
}
#[derive(Default)]
pub struct ThemeInfo {
pub top: RGBColor,
pub background: RGBColor,
pub border_left_top: RGBColor,
pub border_right_bottom: RGBColor,
pub text: RGBColor,
pub top_text: RGBColor,
pub alt_background: RGBColor,
pub alt_text: RGBColor,
pub alt_secondary: RGBColor,
}
//besides standard, these themes aren't great, I know
const THEME_INFOS: [(Themes, ThemeInfo); 5] = [
(Themes::Standard, ThemeInfo {
top: [0, 0, 128],
background: [192, 192, 192],
border_left_top: [255, 255, 255],
border_right_bottom: [0, 0, 0],
text: [0, 0, 0],
top_text: [255, 255, 255],
alt_background: [0, 0, 0],
alt_text: [255, 255, 255],
alt_secondary: [128, 128, 128],
}),
(Themes::Night, ThemeInfo {
top: [0, 0, 0],
background: [34, 34, 34],
border_left_top: [239, 239, 239],
border_right_bottom: [0, 0, 0],
text: [239, 239, 239],
top_text: [239, 239, 239],
alt_background: [0, 0, 0],
alt_text: [239, 239, 239],
alt_secondary: [128, 128, 128],
}),
(Themes::Industrial, ThemeInfo {
top: [40, 40, 40],
background: [160, 160, 160],
border_left_top: [255, 255, 255],
border_right_bottom: [0, 0, 0],
text: [0, 0, 0],
top_text: [255, 255, 255],
alt_background: [0, 0, 0],
alt_text: [255, 255, 255],
alt_secondary: [128, 128, 128],
}),
(Themes::Forest, ThemeInfo {
top: [0, 128, 0],
background: [192, 192, 192],
border_left_top: [255, 255, 255],
border_right_bottom: [0, 0, 0],
text: [0, 0, 0],
top_text: [255, 255, 255],
alt_background: [0, 0, 0],
alt_text: [255, 255, 255],
alt_secondary: [128, 128, 128],
}),
(Themes::Royal, ThemeInfo {
top: [128, 0, 128],
background: [192, 192, 192],
border_left_top: [255, 255, 255],
border_right_bottom: [0, 0, 0],
text: [0, 0, 0],
top_text: [255, 255, 255],
alt_background: [0, 0, 0],
alt_text: [255, 255, 255],
alt_secondary: [128, 128, 128],
}),
//
];
pub fn get_theme_info(theme: &Themes) -> Option<ThemeInfo> {
for pair in THEME_INFOS {
if &pair.0 == theme {
return Some(pair.1);
}
}
return None;
}

View File

@@ -1,175 +0,0 @@
use std::path::PathBuf;
use termion::event::Key;
use crate::window_manager::KeyChar;
use crate::framebuffer::{ Dimensions, Point };
//use Linear A for escape, backspace, enter
//Linear A used only internally in onscreen keyboard: 𐘎 is alt, 𐘧 is switch board, 𐘾 is ctrl
pub fn key_to_char(key: Key) -> Option<KeyChar> {
match key {
Key::Char('\n') => Some(KeyChar::Press('𐘂')),
Key::Char(c) => Some(KeyChar::Press(c)),
Key::Alt(c) => Some(KeyChar::Alt(c)),
Key::Ctrl(c) => Some(KeyChar::Ctrl(c)),
Key::Backspace => Some(KeyChar::Press('𐘁')),
Key::Esc => Some(KeyChar::Press('𐘃')),
_ => None,
}
}
pub fn min(one: usize, two: usize) -> usize {
if one > two { two } else { one }
}
pub trait Substring {
fn substring(&self, start: usize, end: usize) -> &str;
fn remove(&self, index: usize, len: usize) -> String;
fn remove_last(&self) -> String;
}
impl Substring for String {
fn substring(&self, start: usize, end: usize) -> &str {
let mut byte_start = 0;
let mut byte_end = 0;
let mut chars = self.chars();
for i in 0..end {
let char_length = chars.next().unwrap().len_utf8();
if i < start {
byte_start += char_length;
}
if i == end {
break;
}
byte_end += char_length;
}
&self[byte_start..byte_end]
}
fn remove(&self, index: usize, len: usize) -> String {
self.substring(0, index).to_string() + self.substring(index + len, self.chars().count())
}
fn remove_last(&self) -> String {
self.substring(0, self.chars().count() - 1).to_string()
}
}
//the tuple is first, line #, actual line
pub fn calc_actual_lines<'a>(lines: impl Iterator<Item = &'a String>, max_chars_per_line: usize, one_extra: bool) -> Vec<(bool, usize, String)> {
let mut actual_lines = Vec::new();
let mut line_num = 0;
for real_line in lines {
let mut line = real_line.to_string() + if one_extra { " " } else { "" };
let mut first = true;
loop {
if line.chars().count() <= max_chars_per_line {
actual_lines.push((first, line_num, line));
break;
} else {
let mut line_chars = line.chars();
let mut push_string = String::new();
for _i in 0..max_chars_per_line {
push_string += &line_chars.next().unwrap().to_string();
}
actual_lines.push((first, line_num, push_string));
line = line_chars.collect();
}
first = false;
}
line_num += 1;
}
actual_lines
}
pub fn calc_new_cursor_pos(cursor_pos: usize, new_length: usize) -> usize {
if cursor_pos >= new_length {
if new_length == 0 {
0
} else {
new_length - 1
}
} else {
cursor_pos
}
}
pub fn concat_paths(current_path: &str, add_path: &str) -> Result<PathBuf, ()> {
let mut new_path = PathBuf::from(current_path);
if add_path.starts_with("/") {
//absolute path
new_path = PathBuf::from(add_path);
} else {
//relative path
for part in add_path.split("/") {
if part == ".." {
if let Some(parent) = new_path.parent() {
new_path = parent.to_path_buf();
} else {
return Err(());
}
} else {
new_path.push(part);
}
}
}
Ok(new_path)
}
//go from seconds to minutes:seconds
pub fn format_seconds(seconds: u64) -> String {
let mut m = (seconds / 60).to_string(); //automatically rounds down
if m.len() == 1 {
m = "0".to_string() + &m;
}
let mut s = (seconds % 60).to_string();
if s.len() == 1 {
s = "0".to_string() + &s;
}
m + ":" + &s
}
pub const HEX_CHARS: [char; 16] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
pub fn u8_to_hex(u: u8) -> String {
let mut h = String::new();
h.push(HEX_CHARS[(u / 16) as usize]);
h.push(HEX_CHARS[(u % 16) as usize]);
h
}
pub fn hex_to_u8(c1: char, c2: char) -> u8 {
(HEX_CHARS.iter().position(|c| c == &c1).unwrap() * 16 + HEX_CHARS.iter().position(|c| c == &c2).unwrap()) as u8
}
pub fn is_hex(c: char) -> bool {
HEX_CHARS.iter().position(|hc| hc == &c).is_some()
}
pub fn point_inside(point: Point, top_left: Point, size: Dimensions) -> bool {
let x = point[0];
let y = point[1];
let x2 = top_left[0];
let y2 = top_left[1];
let x3 = x2 + size[0];
let y3 = y2 + size[1];
x >= x2 && y >= y2 && x <= x3 && y <= y3
}
pub 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 {
if n.is_none() {
break;
}
rest += &n.unwrap();
n = split.next();
if n.is_some() && sep.is_some() {
rest += sep.unwrap();
}
}
rest
}

View File

@@ -9,11 +9,13 @@ use std::io::Read;
use linux_framebuffer::Framebuffer;
use crate::framebuffer::{ FramebufferWriter, Point, Dimensions, RGBColor };
use crate::themes::{ ThemeInfo, Themes, get_theme_info };
use crate::utils::{ min, point_inside };
use crate::messages::*;
use crate::dirs::config_dir;
use ming_wm_lib::framebuffer_types::{ Point, Dimensions };
use ming_wm_lib::themes::{ Themes, get_theme_info };
use ming_wm_lib::utils::{ min, point_inside };
use ming_wm_lib::messages::*;
use ming_wm_lib::dirs::config_dir;
use ming_wm_lib::window_manager_types::*;
use crate::framebuffer::FramebufferWriter;
use crate::proxy_window_like::ProxyWindowLike;
use crate::essential::desktop_background::DesktopBackground;
use crate::essential::taskbar::Taskbar;
@@ -27,63 +29,15 @@ use crate::essential::onscreen_keyboard::OnscreenKeyboard;
//todo: a lot of the usize should be changed to u16
pub const TASKBAR_HEIGHT: usize = 38;
pub const INDICATOR_HEIGHT: usize = 20;
const WINDOW_TOP_HEIGHT: usize = 26;
#[derive(Clone, Debug, PartialEq)]
pub enum KeyChar {
Press(char),
Alt(char),
Ctrl(char),
}
#[derive(Debug)]
pub enum DrawInstructions {
Rect(Point, Dimensions, RGBColor),
Text(Point, Vec<String>, String, RGBColor, RGBColor, Option<usize>, Option<u8>), //font and text
Gradient(Point, Dimensions, RGBColor, RGBColor, usize),
Bmp(Point, String, bool),
Circle(Point, usize, RGBColor),
}
#[derive(Debug, PartialEq)]
pub enum WindowLikeType {
LockScreen,
Window,
DesktopBackground,
Taskbar,
StartMenu,
WorkspaceIndicator,
OnscreenKeyboard,
}
pub trait WindowLike {
fn handle_message(&mut self, message: WindowMessage) -> WindowMessageResponse;
fn draw(&self, theme_info: &ThemeInfo) -> Vec<DrawInstructions>;
//properties
fn title(&self) -> String {
String::new()
}
fn resizable(&self) -> bool {
false
}
fn subtype(&self) -> WindowLikeType;
fn ideal_dimensions(&self, dimensions: Dimensions) -> Dimensions; //needs &self or its not object safe or some bullcrap
}
#[derive(PartialEq)]
pub enum Workspace {
enum Workspace {
All,
Workspace(u8), //goes from 0-8
}
pub struct WindowLikeInfo {
struct WindowLikeInfo {
id: usize,
window_like: WindowBox,
top_left: Point,
@@ -145,7 +99,7 @@ impl WindowManager {
self.id_count = self.id_count + 1;
let id = self.id_count;
window_like.handle_message(WindowMessage::Init(dimensions));
let dimensions = if window_like.subtype() == WindowLikeType::Window { [dimensions[0], dimensions[1] + WINDOW_TOP_HEIGHT] } else { dimensions };
let dimensions = if subtype == WindowLikeType::Window { [dimensions[0], dimensions[1] + WINDOW_TOP_HEIGHT] } else { dimensions };
let window_info = WindowLikeInfo {
id,
window_like,