handle window crashes, various fixes

add reversi, fixes for audio player and terminal
This commit is contained in:
stjet
2024-10-28 05:07:35 +00:00
parent cfece80c66
commit 4be9bbc411
15 changed files with 319 additions and 93 deletions

View File

@@ -2,7 +2,7 @@ use std::vec::Vec;
use std::vec; use std::vec;
use std::io::BufReader; use std::io::BufReader;
use std::path::PathBuf; use std::path::PathBuf;
use std::fs::File; use std::fs::{ read_to_string, File };
use rodio::{ Decoder, OutputStream, Sink, Source }; use rodio::{ Decoder, OutputStream, Sink, Source };
use rand::prelude::*; use rand::prelude::*;
@@ -19,7 +19,7 @@ const MONO_WIDTH: u8 = 10;
const LINE_HEIGHT: usize = 18; const LINE_HEIGHT: usize = 18;
#[derive(Default)] #[derive(Default)]
pub struct AudioPlayer { struct AudioPlayer {
dimensions: Dimensions, dimensions: Dimensions,
base_directory: String, base_directory: String,
queue: Vec<(PathBuf, u64)>, queue: Vec<(PathBuf, u64)>,
@@ -139,8 +139,18 @@ impl AudioPlayer {
if let Some(sink) = &mut self.sink { if let Some(sink) = &mut self.sink {
sink.clear(); sink.clear();
} }
let mut queue = if new_path.ends_with(".playlist") { let mut queue = if parts[1].ends_with(".playlist") {
Vec::new() //placeholder let mut queue = Vec::new();
let contents = read_to_string(new_path).unwrap();
for line in contents.split("\n") {
//todo: handle more edge cases later
if line.ends_with("/*") {
queue.extend(get_all_files(concat_paths(&self.base_directory, &line[..line.len() - 2]).unwrap()));
} else if line.len() > 0 {
queue.push(concat_paths(&self.base_directory, &(line.to_owned() + ".mp3")).unwrap());
}
}
queue
} else { } else {
get_all_files(PathBuf::from(new_path)) get_all_files(PathBuf::from(new_path))
}; };

View File

@@ -69,7 +69,7 @@ struct Current {
} }
#[derive(Default)] #[derive(Default)]
pub struct Malvim { struct Malvim {
dimensions: Dimensions, dimensions: Dimensions,
state: State, state: State,
mode: Mode, mode: Mode,

View File

@@ -42,7 +42,7 @@ enum State {
} }
#[derive(Default)] #[derive(Default)]
pub struct Minesweeper { struct Minesweeper {
dimensions: Dimensions, dimensions: Dimensions,
state: State, state: State,
tiles: [[MineTile; 16]; 16], tiles: [[MineTile; 16]; 16],

111
src/bin/reversi.rs Normal file
View File

@@ -0,0 +1,111 @@
use std::vec::Vec;
use std::vec;
use std::fmt;
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;
#[derive(Default, PartialEq)]
enum Tile {
#[default]
Empty,
White,
Black,
}
impl Tile {
pub fn to_color(&self) -> Option<RGBColor> {
match self {
Tile::Empty => None,
Tile::White => Some([255, 255, 255]),
Tile::Black => Some([0, 0, 0]),
}
}
}
#[derive(Default)]
struct Reversi {
dimensions: Dimensions,
tiles: [[Tile; 8]; 8],
//
}
impl WindowLike for Reversi {
fn handle_message(&mut self, message: WindowMessage) -> WindowMessageResponse {
match message {
WindowMessage::Init(dimensions) => {
self.dimensions = dimensions;
self.new_tiles();
WindowMessageResponse::JustRerender
},
//
_ => WindowMessageResponse::DoNothing,
}
}
fn draw(&self, theme_info: &ThemeInfo) -> Vec<DrawInstructions> {
let mut instructions = vec![
DrawInstructions::Rect([0, 0], self.dimensions, [72, 93, 63]),
];
let square_width = (self.dimensions[0] - 10) / 8;
for l in 0..9 {
instructions.extend([
DrawInstructions::Rect([5 + square_width * l, 5], [2, self.dimensions[1] - 10], [0, 0, 0]),
DrawInstructions::Rect([5, 5 + square_width * l], [self.dimensions[0] - 10, 2], [0, 0, 0]),
]);
}
instructions.extend([
DrawInstructions::Circle([5 + square_width * 2, 5 + square_width * 2], 4, [0, 0, 0]),
DrawInstructions::Circle([5 + square_width * 6, 5 + square_width * 2], 4, [0, 0, 0]),
DrawInstructions::Circle([5 + square_width * 2, 5 + square_width * 6], 4, [0, 0, 0]),
DrawInstructions::Circle([5 + square_width * 6, 5 + square_width * 6], 4, [0, 0, 0]),
]);
for y in 0..8 {
for x in 0..8 {
let tile = &self.tiles[y][x];
if tile == &Tile::Empty {
//
} else {
instructions.push(DrawInstructions::Circle([x * square_width + square_width / 2 + 5, y * square_width + square_width / 2 + 5], square_width / 2 - 3, tile.to_color().unwrap()));
}
}
}
instructions
}
//properties
fn title(&self) -> String {
"Reversi".to_string()
}
fn subtype(&self) -> WindowLikeType {
WindowLikeType::Window
}
fn ideal_dimensions(&self, _dimensions: Dimensions) -> Dimensions {
[300, 300]
}
}
impl Reversi {
pub fn new() -> Self {
Default::default()
}
pub fn new_tiles(&mut self) {
self.tiles = Default::default();
self.tiles[3][3] = Tile::White;
self.tiles[4][3] = Tile::Black;
self.tiles[3][4] = Tile::Black;
self.tiles[4][4] = Tile::White;
}
}
pub fn main() {
listen(Reversi::new());
}

View File

@@ -1,8 +1,7 @@
use std::vec::Vec; use std::vec::Vec;
use std::vec; use std::vec;
use std::process::{ Command, Output }; use std::process::{ Command, Child, Stdio };
use std::str::from_utf8; use std::io::Read;
use std::io;
use ming_wm::window_manager::{ DrawInstructions, WindowLike, WindowLikeType }; use ming_wm::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm::messages::{ WindowMessage, WindowMessageResponse }; use ming_wm::messages::{ WindowMessage, WindowMessageResponse };
@@ -15,19 +14,23 @@ const MONO_WIDTH: u8 = 10;
const LINE_HEIGHT: usize = 15; const LINE_HEIGHT: usize = 15;
const PADDING: usize = 4; const PADDING: usize = 4;
enum CommandResponse { #[derive(Default, PartialEq)]
ActualCommand(io::Result<Output>), enum State {
Custom, #[default]
Input, //typing in to run command
Running, //running command
} }
#[derive(Default)] #[derive(Default)]
pub struct Terminal { pub struct Terminal {
dimensions: Dimensions, dimensions: Dimensions,
state: State,
lines: Vec<String>, lines: Vec<String>,
actual_lines: Vec<String>, //wrapping actual_lines: Vec<String>, //wrapping
actual_line_num: usize, //what line # is at the top, for scrolling actual_line_num: usize, //what line # is at the top, for scrolling
current_input: String, current_input: String,
current_path: String, current_path: String,
running_process: Option<Child>,
} }
//for some reason key presses, then moving the window leaves the old window still there, behind it. weird //for some reason key presses, then moving the window leaves the old window still there, behind it. weird
@@ -47,35 +50,55 @@ impl WindowLike for Terminal {
WindowMessageResponse::JustRerender WindowMessageResponse::JustRerender
}, },
WindowMessage::KeyPress(key_press) => { WindowMessage::KeyPress(key_press) => {
if key_press.key == '𐘁' { //backspace if self.state == State::Input {
if self.current_input.len() > 0 { if key_press.key == '𐘁' { //backspace
self.current_input = self.current_input[..self.current_input.len() - 1].to_string(); if self.current_input.len() > 0 {
} else { self.current_input = self.current_input[..self.current_input.len() - 1].to_string();
return WindowMessageResponse::DoNothing;
}
} else if key_press.key == '𐘂' { //the enter key
self.lines.push("$ ".to_string() + &self.current_input);
if let CommandResponse::ActualCommand(maybe_output) = self.process_command() {
if let Ok(output) = maybe_output {
let write_output = if output.status.success() {
output.stdout
} else {
output.stderr
};
for line in from_utf8(&write_output).unwrap_or("Failed to parse process output as utf-8").split("\n") {
self.lines.push(line.to_string());
}
} else { } else {
self.lines.push("Failed to execute process".to_string()); return WindowMessageResponse::DoNothing;
} }
} else if key_press.key == '𐘂' { //the enter key
self.lines.push("$ ".to_string() + &self.current_input);
self.state = self.process_command();
self.current_input = String::new();
} else {
self.current_input += &key_press.key.to_string();
} }
self.current_input = String::new(); self.calc_actual_lines();
self.actual_line_num = self.actual_lines.len().checked_sub(self.get_max_lines()).unwrap_or(0);
WindowMessageResponse::JustRerender
} else { } else {
self.current_input += &key_press.key.to_string(); //update
let running_process = self.running_process.as_mut().unwrap();
if let Some(status) = running_process.try_wait().unwrap() {
//process exited
let mut output = String::new();
if status.success() {
let _ = running_process.stdout.as_mut().unwrap().read_to_string(&mut output);
} else {
let _ = running_process.stderr.as_mut().unwrap().read_to_string(&mut output);
}
for line in output.split("\n") {
self.lines.push(line.to_string());
}
self.state = State::Input;
self.calc_actual_lines();
WindowMessageResponse::JustRerender
} else {
//still running
WindowMessageResponse::DoNothing
}
}
},
WindowMessage::CtrlKeyPress(key_press) => {
if self.state == State::Running && key_press.key == 'c' {
//kills and running_process is now None
let _ = self.running_process.take().unwrap().kill();
self.state = State::Input;
WindowMessageResponse::JustRerender
} else {
WindowMessageResponse::DoNothing
} }
self.calc_actual_lines();
self.actual_line_num = self.actual_lines.len().checked_sub(self.get_max_lines()).unwrap_or(0);
WindowMessageResponse::JustRerender
}, },
_ => WindowMessageResponse::DoNothing, _ => WindowMessageResponse::DoNothing,
} }
@@ -125,10 +148,10 @@ impl Terminal {
(self.dimensions[1] - PADDING * 2) / LINE_HEIGHT (self.dimensions[1] - PADDING * 2) / LINE_HEIGHT
} }
fn process_command(&mut self) -> CommandResponse { fn process_command(&mut self) -> State {
if self.current_input.starts_with("clear ") || self.current_input == "clear" { if self.current_input.starts_with("clear ") || self.current_input == "clear" {
self.lines = Vec::new(); self.lines = Vec::new();
CommandResponse::Custom State::Input
} else if self.current_input.starts_with("cd ") { } else if self.current_input.starts_with("cd ") {
let mut cd_split = self.current_input.split(" "); let mut cd_split = self.current_input.split(" ");
cd_split.next().unwrap(); cd_split.next().unwrap();
@@ -138,16 +161,22 @@ impl Terminal {
self.current_path = new_path.to_str().unwrap().to_string(); self.current_path = new_path.to_str().unwrap().to_string();
} }
} }
CommandResponse::Custom State::Input
} else { } else {
CommandResponse::ActualCommand(Command::new("sh").arg("-c").arg(&self.current_input).current_dir(&self.current_path).output()) self.running_process = Some(Command::new("sh").arg("-c").arg(&self.current_input).current_dir(&self.current_path).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn().unwrap());
State::Running
} }
} }
fn calc_actual_lines(&mut self) { fn calc_actual_lines(&mut self) {
self.actual_lines = Vec::new(); self.actual_lines = Vec::new();
let max_chars_per_line = (self.dimensions[0] - PADDING * 2) / MONO_WIDTH as usize; let max_chars_per_line = (self.dimensions[0] - PADDING * 2) / MONO_WIDTH as usize;
for line_num in 0..=self.lines.len() { let end = if self.state == State::Input {
self.lines.len()
} else {
self.lines.len() - 1
};
for line_num in 0..=end {
let mut working_line = if line_num == self.lines.len() { let mut working_line = if line_num == self.lines.len() {
"$ ".to_string() + &self.current_input + "" "$ ".to_string() + &self.current_input + ""
} else { } else {

View File

@@ -1,6 +1,8 @@
use std::process::{ Command, Stdio }; use std::process::{ Command, Stdio };
use std::io::{ Read, Write }; use std::io::{ Read, Write };
use ron;
fn main() { fn main() {
println!("a"); println!("a");
let mut a = Command::new("cargo").arg("run").arg("-q").arg("--bin").arg("start_menu").stdout(Stdio::piped()).stdin(Stdio::piped()).stderr(Stdio::null()).spawn().unwrap(); let mut a = Command::new("cargo").arg("run").arg("-q").arg("--bin").arg("start_menu").stdout(Stdio::piped()).stdin(Stdio::piped()).stderr(Stdio::null()).spawn().unwrap();
@@ -8,4 +10,5 @@ fn main() {
let mut output = String::new(); let mut output = String::new();
a.stdout.as_mut().unwrap().read_to_string(&mut output); a.stdout.as_mut().unwrap().read_to_string(&mut output);
println!("{}", output); println!("{}", output);
//println!("{}", &ron::to_string(&[122, 400]).unwrap());
} }

View File

@@ -2,4 +2,5 @@ pub mod desktop_background;
pub mod taskbar; pub mod taskbar;
pub mod lock_screen; pub mod lock_screen;
pub mod workspace_indicator; pub mod workspace_indicator;
pub mod start_menu;

View File

@@ -4,13 +4,13 @@ use std::vec;
use std::vec::Vec; use std::vec::Vec;
use std::boxed::Box; use std::boxed::Box;
use ming_wm::window_manager::{ DrawInstructions, WindowLike, WindowLikeType }; use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest }; use crate::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest };
use ming_wm::framebuffer::Dimensions; use crate::framebuffer::Dimensions;
use ming_wm::themes::ThemeInfo; use crate::themes::ThemeInfo;
use ming_wm::components::Component; use crate::components::Component;
use ming_wm::components::highlight_button::HighlightButton; use crate::components::highlight_button::HighlightButton;
use ming_wm::ipc::listen; use crate::ipc::listen;
//todo: move to essential //todo: move to essential
@@ -145,7 +145,7 @@ impl StartMenu {
//add window buttons //add window buttons
let mut to_add: Vec<&str> = Vec::new(); let mut to_add: Vec<&str> = Vec::new();
if name == "Games" { if name == "Games" {
to_add.push("Minesweeper"); to_add.extend(["Minesweeper", "Reversi"]);
} else if name == "Editing" { } else if name == "Editing" {
to_add.push("Malvim"); to_add.push("Malvim");
} else if name == "Utils" { } else if name == "Utils" {

View File

@@ -1,5 +1,6 @@
use std::vec; use std::vec;
use std::vec::Vec; use std::vec::Vec;
use std::time::{ SystemTime, UNIX_EPOCH };
use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType, INDICATOR_HEIGHT }; use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType, INDICATOR_HEIGHT };
use crate::messages::{ WindowMessage, WindowMessageResponse, ShortcutType }; use crate::messages::{ WindowMessage, WindowMessageResponse, ShortcutType };
@@ -7,6 +8,9 @@ use crate::framebuffer::Dimensions;
use crate::themes::ThemeInfo; use crate::themes::ThemeInfo;
const WIDTH: usize = 15; const WIDTH: usize = 15;
const ONE_MINUTE: u64 = 60;
const ONE_HOUR: u64 = 60 * ONE_MINUTE;
const ONE_DAY: u64 = 24 * ONE_HOUR;
pub struct WorkspaceIndicator { pub struct WorkspaceIndicator {
dimensions: Dimensions, dimensions: Dimensions,
@@ -49,6 +53,12 @@ impl WindowLike for WorkspaceIndicator {
instructions.push(DrawInstructions::Text([w * WIDTH + 5, 4], "times-new-roman".to_string(), (w + 1).to_string(), theme_info.text, theme_info.background, None, None)); instructions.push(DrawInstructions::Text([w * WIDTH + 5, 4], "times-new-roman".to_string(), (w + 1).to_string(), theme_info.text, theme_info.background, None, None));
} }
} }
//also add the utc time in the right edge
let today_secs = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() % ONE_DAY;
let hours = (today_secs / ONE_HOUR).to_string();
let minutes = ((today_secs % ONE_HOUR) / ONE_MINUTE).to_string();
let time_string = format!("{}:{}~ UTC", if hours.len() == 1 { "0".to_string() + &hours } else { hours }, if minutes.len() == 1 { "0".to_string() + &minutes } else { minutes });
instructions.push(DrawInstructions::Text([self.dimensions[0] - 90, 4], "times-new-roman".to_string(), time_string, theme_info.text, theme_info.background, None, None));
instructions instructions
} }

View File

@@ -17,12 +17,16 @@ fn color_with_alpha(color: RGBColor, bg_color: RGBColor, alpha: u8) -> RGBColor
(bg_color[2] as f32 * (1.0 - factor)) as u8 + (color[2] as f32 * factor) as u8, (bg_color[2] as f32 * (1.0 - factor)) as u8 + (color[2] as f32 * factor) as u8,
]*/ ]*/
//255 * 255 < max(u16) //255 * 255 < max(u16)
let alpha = alpha as u16; if alpha == 255 {
[ color
(bg_color[0] as u16 * (255 - alpha) / 255) as u8 + (color[0] as u16 * alpha / 255) as u8, } else {
(bg_color[1] as u16 * (255 - alpha) / 255) as u8 + (color[1] as u16 * alpha / 255) as u8, let alpha = alpha as u16;
(bg_color[2] as u16 * (255 - alpha) / 255) as u8 + (color[2] as u16 * alpha / 255) as u8, [
] (bg_color[0] as u16 * (255 - alpha) / 255) as u8 + (color[0] as u16 * alpha / 255) as u8,
(bg_color[1] as u16 * (255 - alpha) / 255) as u8 + (color[1] as u16 * alpha / 255) as u8,
(bg_color[2] as u16 * (255 - alpha) / 255) as u8 + (color[2] as u16 * alpha / 255) as u8,
]
}
} }
#[derive(Clone, Default, Debug)] #[derive(Clone, Default, Debug)]
@@ -126,6 +130,21 @@ impl FramebufferWriter {
} }
} }
//can optimise (?) by turning into lines and doing _draw_line instead?
pub fn draw_circle(&mut self, centre: Point, radius: usize, color: RGBColor) {
//x^2 + y^2 <= r^2
for y in 0..radius {
for x in 0..radius {
if (x.pow(2) + y.pow(2)) <= radius.pow(2) {
self.draw_pixel([centre[0] + x, centre[1] + y], color);
self.draw_pixel([centre[0] - x, centre[1] + y], color);
self.draw_pixel([centre[0] - x, centre[1] - y], color);
self.draw_pixel([centre[0] + x, centre[1] - y], color);
}
}
}
}
//direction is top to bottom //direction is top to bottom
pub fn draw_gradient(&mut self, top_left: Point, dimensions: Dimensions, start_color: RGBColor, end_color: RGBColor, steps: usize) { pub fn draw_gradient(&mut self, top_left: Point, dimensions: Dimensions, start_color: RGBColor, end_color: RGBColor, steps: usize) {
let delta_r = (end_color[0] as f32 - start_color[0] as f32) / steps as f32; let delta_r = (end_color[0] as f32 - start_color[0] as f32) / steps as f32;
@@ -159,6 +178,7 @@ impl FramebufferWriter {
//text //text
//this, draw_char, and get_font_char should be more much optimised
pub fn draw_text(&mut self, top_left: Point, font_name: &str, text: &str, color: RGBColor, bg_color: RGBColor, horiz_spacing: usize, mono_width: Option<u8>) { pub fn draw_text(&mut self, top_left: Point, font_name: &str, text: &str, color: RGBColor, bg_color: RGBColor, horiz_spacing: usize, mono_width: Option<u8>) {
let mut top_left = top_left; let mut top_left = top_left;
//todo, config space //todo, config space

View File

@@ -44,7 +44,7 @@ pub fn listen(mut window_like: impl WindowLike) {
} else if method == "subtype" { } else if method == "subtype" {
println!("{}", ron::to_string(&window_like.subtype()).unwrap()); println!("{}", ron::to_string(&window_like.subtype()).unwrap());
} else if method == "ideal_dimensions" { } else if method == "ideal_dimensions" {
println!("{:?}", window_like.ideal_dimensions(ron::from_str(arg).unwrap())); println!("{}", ron::to_string(&window_like.ideal_dimensions(ron::from_str(arg).unwrap())).unwrap());
} }
} }
} }

View File

@@ -4,6 +4,7 @@ use termion::event::Key;
pub enum KeyChar { pub enum KeyChar {
Press(char), Press(char),
Alt(char), Alt(char),
Ctrl(char),
} }
//use Linear A for escape, backspace, enter //use Linear A for escape, backspace, enter
@@ -12,6 +13,7 @@ pub fn key_to_char(key: Key) -> Option<KeyChar> {
Key::Char('\n') => Some(KeyChar::Press('𐘂')), Key::Char('\n') => Some(KeyChar::Press('𐘂')),
Key::Char(c) => Some(KeyChar::Press(c)), Key::Char(c) => Some(KeyChar::Press(c)),
Key::Alt(c) => Some(KeyChar::Alt(c)), Key::Alt(c) => Some(KeyChar::Alt(c)),
Key::Ctrl(c) => Some(KeyChar::Ctrl(c)),
Key::Backspace => Some(KeyChar::Press('𐘁')), Key::Backspace => Some(KeyChar::Press('𐘁')),
Key::Esc => Some(KeyChar::Press('𐘃')), Key::Esc => Some(KeyChar::Press('𐘃')),
_ => None, _ => None,

View File

@@ -49,7 +49,6 @@ pub enum WindowMessageResponse {
#[derive(Serialize, Deserialize)] #[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, Serialize, Deserialize)]
@@ -90,6 +89,7 @@ pub enum InfoType {
pub enum WindowMessage { pub enum WindowMessage {
Init(Dimensions), Init(Dimensions),
KeyPress(KeyPress), KeyPress(KeyPress),
CtrlKeyPress(KeyPress),
Shortcut(ShortcutType), Shortcut(ShortcutType),
Info(InfoType), Info(InfoType),
Focus, Focus,

View File

@@ -1,6 +1,6 @@
use std::vec::Vec; use std::vec::Vec;
use std::process::{ Command, Child, Stdio }; use std::process::{ Command, Child, Stdio };
use std::io::{ BufReader, BufRead, Read, Write }; use std::io::{ BufReader, BufRead, Write };
use std::cell::RefCell; use std::cell::RefCell;
use ron; use ron;
@@ -14,66 +14,91 @@ pub struct ProxyWindowLike {
process: RefCell<Child>, process: RefCell<Child>,
} }
//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 {
self.process.borrow_mut().stdin.as_mut().unwrap().write_all(("handle_message ".to_string() + &ron::to_string(&message).unwrap() + "\n").as_bytes()); 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 output = self.read_line(); let output = self.read_line();
ron::from_str(&output).unwrap() ron::from_str(&output).unwrap_or(WindowMessageResponse::JustRerender)
} }
fn draw(&self, theme_info: &ThemeInfo) -> Vec<DrawInstructions> { fn draw(&self, theme_info: &ThemeInfo) -> Vec<DrawInstructions> {
self.process.borrow_mut().stdin.as_mut().unwrap().write_all(("draw ".to_string() + &ron::to_string(&theme_info).unwrap() + "\n").as_bytes()); 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 output = self.read_line(); let output = self.read_line();
ron::from_str(&output).unwrap() ron::from_str(&output).unwrap_or(Vec::new())
} }
//properties //properties
fn title(&self) -> String { fn title(&self) -> String {
self.process.borrow_mut().stdin.as_mut().unwrap().write_all("title\n".as_bytes()); if let Some(stdin) = self.process.borrow_mut().stdin.as_mut() {
self.read_line() let _ = stdin.write_all("title\n".as_bytes());
}
self.read_line().chars().filter(|c| *c != '\n').collect()
} }
fn resizable(&self) -> bool { fn resizable(&self) -> bool {
self.process.borrow_mut().stdin.as_mut().unwrap().write_all("resizable\n".to_string().as_bytes()); if let Some(stdin) = self.process.borrow_mut().stdin.as_mut() {
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() ron::from_str(&output).unwrap_or(false)
} }
fn subtype(&self) -> WindowLikeType { fn subtype(&self) -> WindowLikeType {
self.process.borrow_mut().stdin.as_mut().unwrap().write_all("subtype\n".to_string().as_bytes()); if let Some(stdin) = self.process.borrow_mut().stdin.as_mut() {
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() ron::from_str(&output).unwrap_or(WindowLikeType::Window)
} }
fn ideal_dimensions(&self, dimensions: Dimensions) -> Dimensions { fn ideal_dimensions(&self, dimensions: Dimensions) -> Dimensions {
self.process.borrow_mut().stdin.as_mut().unwrap().write_all(("ideal_dimensions".to_string() + &ron::to_string(&dimensions).unwrap() + "\n").as_bytes()); 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 output = self.read_line(); let output = self.read_line();
ron::from_str(&output).unwrap() ron::from_str(&output).unwrap_or([420, 420])
} }
} }
//kill process when this window like dropped //kill process when this window like dropped
impl Drop for ProxyWindowLike { impl Drop for ProxyWindowLike {
fn drop(&mut self) { fn drop(&mut self) {
self.process.borrow_mut().kill(); let _ = self.process.borrow_mut().kill();
} }
} }
impl ProxyWindowLike { impl ProxyWindowLike {
pub fn new(file: &str) -> Self { pub fn new(command: &mut Command) -> Self {
ProxyWindowLike { ProxyWindowLike {
//--quiet process: RefCell::new(command.stdout(Stdio::piped()).stdin(Stdio::piped()).stderr(Stdio::null()).spawn().unwrap()),
process: RefCell::new(Command::new("cargo").arg("run").arg("--quiet").arg("--release").arg("--bin").arg(file).stdout(Stdio::piped()).stdin(Stdio::piped()).stderr(Stdio::null()).spawn().unwrap()),
} }
} }
pub fn new_rust(file: &str) -> Self {
ProxyWindowLike::new(Command::new("cargo").arg("run").arg("--quiet").arg("--release").arg("--bin").arg(file).stdout(Stdio::piped()).stdin(Stdio::piped()).stderr(Stdio::null()))
}
//return empty string if error, do not propogate Err becuase that's messy
//or maybe return "panicked"?
fn read_line(&self) -> String { fn read_line(&self) -> String {
let mut output = String::new();
let mut buffer = self.process.borrow_mut(); let mut buffer = self.process.borrow_mut();
let buffer = buffer.stdout.as_mut().unwrap(); if let Some(buffer) = buffer.stdout.as_mut() {
let mut reader = BufReader::new(buffer); let mut output = String::new();
reader.read_line(&mut output).unwrap(); let mut reader = BufReader::new(buffer);
output if let Ok(_) = reader.read_line(&mut output) {
output
} else {
String::new()
}
} else {
String::new()
}
} }
} }

View File

@@ -22,8 +22,7 @@ use crate::essential::desktop_background::DesktopBackground;
use crate::essential::taskbar::Taskbar; use crate::essential::taskbar::Taskbar;
use crate::essential::lock_screen::LockScreen; use crate::essential::lock_screen::LockScreen;
use crate::essential::workspace_indicator::WorkspaceIndicator; use crate::essential::workspace_indicator::WorkspaceIndicator;
use crate::essential::start_menu::StartMenu;
//todo, better error handling for windows
pub const TASKBAR_HEIGHT: usize = 38; pub const TASKBAR_HEIGHT: usize = 38;
pub const INDICATOR_HEIGHT: usize = 20; pub const INDICATOR_HEIGHT: usize = 20;
@@ -46,6 +45,7 @@ pub fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) {
write!(stdout, "{}", cursor::Hide).unwrap(); write!(stdout, "{}", cursor::Hide).unwrap();
stdout.flush().unwrap(); stdout.flush().unwrap();
for c in stdin.keys() { for c in stdin.keys() {
if let Some(kc) = key_to_char(c.unwrap()) { if let Some(kc) = key_to_char(c.unwrap()) {
//do not allow exit when locked unless debugging //do not allow exit when locked unless debugging
@@ -73,6 +73,7 @@ pub enum DrawInstructions {
Text(Point, String, String, RGBColor, RGBColor, Option<usize>, Option<u8>), //font and text Text(Point, String, String, RGBColor, RGBColor, Option<usize>, Option<u8>), //font and text
Gradient(Point, Dimensions, RGBColor, RGBColor, usize), Gradient(Point, Dimensions, RGBColor, RGBColor, usize),
Mingde(Point), Mingde(Point),
Circle(Point, usize, RGBColor),
} }
#[derive(Debug, PartialEq, Serialize, Deserialize)] #[derive(Debug, PartialEq, Serialize, Deserialize)]
@@ -462,13 +463,19 @@ impl WindowManager {
} }
press_response press_response
}, },
KeyChar::Press(c) => { KeyChar::Press(c) | KeyChar::Ctrl(c) => {
let mut press_response = WindowMessageResponse::DoNothing; let mut press_response = WindowMessageResponse::DoNothing;
//send to focused window //send to focused window
if let Some(focused_index) = self.get_focused_index() { if let Some(focused_index) = self.get_focused_index() {
press_response = self.window_infos[focused_index].window_like.handle_message(WindowMessage::KeyPress(KeyPress { press_response = self.window_infos[focused_index].window_like.handle_message(if key_char == KeyChar::Press(c) {
key: c, WindowMessage::KeyPress(KeyPress {
})); key: c,
})
} else {
WindowMessage::CtrlKeyPress(KeyPress {
key: c,
})
});
//at most, only the focused window needs to be rerendered //at most, only the focused window needs to be rerendered
redraw_ids = Some(vec![self.window_infos[focused_index].id]); redraw_ids = Some(vec![self.window_infos[focused_index].id]);
//requests can result in window openings and closings, etc //requests can result in window openings and closings, etc
@@ -499,14 +506,19 @@ impl WindowManager {
if subtype != WindowLikeType::Taskbar && subtype != WindowLikeType::StartMenu { if subtype != WindowLikeType::Taskbar && subtype != WindowLikeType::StartMenu {
return; return;
} }
let w: WindowBox = match w.as_str() { let w: Option<WindowBox> = match w.as_str() {
"Minesweeper" => Box::new(ProxyWindowLike::new("minesweeper")), "Minesweeper" => Some(Box::new(ProxyWindowLike::new_rust("minesweeper"))),
"Malvim" => Box::new(ProxyWindowLike::new("malvim")), "Reversi" => Some(Box::new(ProxyWindowLike::new_rust("reversi"))),
"Terminal" => Box::new(ProxyWindowLike::new("terminal")), "Malvim" => Some(Box::new(ProxyWindowLike::new_rust("malvim"))),
"Audio Player" => Box::new(ProxyWindowLike::new("audio_player")), "Terminal" => Some(Box::new(ProxyWindowLike::new_rust("terminal"))),
"StartMenu" => Box::new(ProxyWindowLike::new("start_menu")), "Audio Player" => Some(Box::new(ProxyWindowLike::new_rust("audio_player"))),
_ => panic!("window not found"), //todo: do not panic "StartMenu" => Some(Box::new(StartMenu::new())),
_ => None,
}; };
if w.is_none() {
return;
}
let w = w.unwrap();
//close start menu if open //close start menu if open
self.toggle_start_menu(true); self.toggle_start_menu(true);
let ideal_dimensions = w.ideal_dimensions(self.dimensions); let ideal_dimensions = w.ideal_dimensions(self.dimensions);
@@ -587,6 +599,7 @@ impl WindowManager {
instructions = instructions.iter().map(|instruction| { instructions = instructions.iter().map(|instruction| {
match instruction { match instruction {
DrawInstructions::Rect(top_left, dimensions, color) => DrawInstructions::Rect(WindowManager::get_true_top_left(top_left, is_window), *dimensions, *color), DrawInstructions::Rect(top_left, dimensions, color) => DrawInstructions::Rect(WindowManager::get_true_top_left(top_left, is_window), *dimensions, *color),
DrawInstructions::Circle(centre, radius, color) => DrawInstructions::Circle(WindowManager::get_true_top_left(centre, is_window), *radius, *color),
DrawInstructions::Text(top_left, font_name, text, color, bg_color, horiz_spacing, mono_width) => DrawInstructions::Text(WindowManager::get_true_top_left(top_left, is_window), font_name.clone(), text.clone(), *color, *bg_color, *horiz_spacing, *mono_width), DrawInstructions::Text(top_left, font_name, text, color, bg_color, horiz_spacing, mono_width) => DrawInstructions::Text(WindowManager::get_true_top_left(top_left, is_window), font_name.clone(), text.clone(), *color, *bg_color, *horiz_spacing, *mono_width),
DrawInstructions::Mingde(top_left) => DrawInstructions::Mingde(WindowManager::get_true_top_left(top_left, is_window)), DrawInstructions::Mingde(top_left) => DrawInstructions::Mingde(WindowManager::get_true_top_left(top_left, is_window)),
DrawInstructions::Gradient(top_left, dimensions, start_color, end_color, steps) => DrawInstructions::Gradient(WindowManager::get_true_top_left(top_left, is_window), *dimensions, *start_color, *end_color, *steps), DrawInstructions::Gradient(top_left, dimensions, start_color, end_color, steps) => DrawInstructions::Gradient(WindowManager::get_true_top_left(top_left, is_window), *dimensions, *start_color, *end_color, *steps),
@@ -630,6 +643,9 @@ impl WindowManager {
]; ];
window_writer.draw_rect(top_left, true_dimensions, color); window_writer.draw_rect(top_left, true_dimensions, color);
}, },
DrawInstructions::Circle(centre, radius, color) => {
window_writer.draw_circle(centre, radius, color);
},
DrawInstructions::Text(top_left, font_name, text, color, bg_color, horiz_spacing, mono_width) => { DrawInstructions::Text(top_left, font_name, text, color, bg_color, horiz_spacing, mono_width) => {
window_writer.draw_text(top_left, &font_name, &text, color, bg_color, horiz_spacing.unwrap_or(1), mono_width); window_writer.draw_text(top_left, &font_name, &text, color, bg_color, horiz_spacing.unwrap_or(1), mono_width);
}, },
@@ -643,7 +659,6 @@ impl WindowManager {
} }
WRITER.lock().unwrap().draw_buffer(window_info.top_left, window_dimensions[1], window_dimensions[0] * bytes_per_pixel, &window_writer.get_buffer()); WRITER.lock().unwrap().draw_buffer(window_info.top_left, window_dimensions[1], window_dimensions[0] * bytes_per_pixel, &window_writer.get_buffer());
w_index += 1; w_index += 1;
//core::mem::drop(temp_vec);
} }
self.framebuffer.write_frame(WRITER.lock().unwrap().get_buffer()); self.framebuffer.write_frame(WRITER.lock().unwrap().get_buffer());
} }