handle window crashes, various fixes
add reversi, fixes for audio player and terminal
This commit is contained in:
@@ -2,7 +2,7 @@ use std::vec::Vec;
|
||||
use std::vec;
|
||||
use std::io::BufReader;
|
||||
use std::path::PathBuf;
|
||||
use std::fs::File;
|
||||
use std::fs::{ read_to_string, File };
|
||||
|
||||
use rodio::{ Decoder, OutputStream, Sink, Source };
|
||||
use rand::prelude::*;
|
||||
@@ -19,7 +19,7 @@ const MONO_WIDTH: u8 = 10;
|
||||
const LINE_HEIGHT: usize = 18;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct AudioPlayer {
|
||||
struct AudioPlayer {
|
||||
dimensions: Dimensions,
|
||||
base_directory: String,
|
||||
queue: Vec<(PathBuf, u64)>,
|
||||
@@ -139,8 +139,18 @@ impl AudioPlayer {
|
||||
if let Some(sink) = &mut self.sink {
|
||||
sink.clear();
|
||||
}
|
||||
let mut queue = if new_path.ends_with(".playlist") {
|
||||
Vec::new() //placeholder
|
||||
let mut queue = if parts[1].ends_with(".playlist") {
|
||||
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 {
|
||||
get_all_files(PathBuf::from(new_path))
|
||||
};
|
||||
|
||||
@@ -69,7 +69,7 @@ struct Current {
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Malvim {
|
||||
struct Malvim {
|
||||
dimensions: Dimensions,
|
||||
state: State,
|
||||
mode: Mode,
|
||||
|
||||
@@ -42,7 +42,7 @@ enum State {
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Minesweeper {
|
||||
struct Minesweeper {
|
||||
dimensions: Dimensions,
|
||||
state: State,
|
||||
tiles: [[MineTile; 16]; 16],
|
||||
|
||||
111
src/bin/reversi.rs
Normal file
111
src/bin/reversi.rs
Normal 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());
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use std::vec::Vec;
|
||||
use std::vec;
|
||||
use std::process::{ Command, Output };
|
||||
use std::str::from_utf8;
|
||||
use std::io;
|
||||
use std::process::{ Command, Child, Stdio };
|
||||
use std::io::Read;
|
||||
|
||||
use ming_wm::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
|
||||
use ming_wm::messages::{ WindowMessage, WindowMessageResponse };
|
||||
@@ -15,19 +14,23 @@ const MONO_WIDTH: u8 = 10;
|
||||
const LINE_HEIGHT: usize = 15;
|
||||
const PADDING: usize = 4;
|
||||
|
||||
enum CommandResponse {
|
||||
ActualCommand(io::Result<Output>),
|
||||
Custom,
|
||||
#[derive(Default, PartialEq)]
|
||||
enum State {
|
||||
#[default]
|
||||
Input, //typing in to run command
|
||||
Running, //running command
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Terminal {
|
||||
dimensions: Dimensions,
|
||||
state: State,
|
||||
lines: Vec<String>,
|
||||
actual_lines: Vec<String>, //wrapping
|
||||
actual_line_num: usize, //what line # is at the top, for scrolling
|
||||
current_input: 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
|
||||
@@ -47,6 +50,7 @@ impl WindowLike for Terminal {
|
||||
WindowMessageResponse::JustRerender
|
||||
},
|
||||
WindowMessage::KeyPress(key_press) => {
|
||||
if self.state == State::Input {
|
||||
if key_press.key == '𐘁' { //backspace
|
||||
if self.current_input.len() > 0 {
|
||||
self.current_input = self.current_input[..self.current_input.len() - 1].to_string();
|
||||
@@ -55,20 +59,7 @@ impl WindowLike for Terminal {
|
||||
}
|
||||
} 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 {
|
||||
self.lines.push("Failed to execute process".to_string());
|
||||
}
|
||||
}
|
||||
self.state = self.process_command();
|
||||
self.current_input = String::new();
|
||||
} else {
|
||||
self.current_input += &key_press.key.to_string();
|
||||
@@ -76,6 +67,38 @@ impl WindowLike for Terminal {
|
||||
self.calc_actual_lines();
|
||||
self.actual_line_num = self.actual_lines.len().checked_sub(self.get_max_lines()).unwrap_or(0);
|
||||
WindowMessageResponse::JustRerender
|
||||
} else {
|
||||
//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
|
||||
}
|
||||
},
|
||||
_ => WindowMessageResponse::DoNothing,
|
||||
}
|
||||
@@ -125,10 +148,10 @@ impl Terminal {
|
||||
(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" {
|
||||
self.lines = Vec::new();
|
||||
CommandResponse::Custom
|
||||
State::Input
|
||||
} else if self.current_input.starts_with("cd ") {
|
||||
let mut cd_split = self.current_input.split(" ");
|
||||
cd_split.next().unwrap();
|
||||
@@ -138,16 +161,22 @@ impl Terminal {
|
||||
self.current_path = new_path.to_str().unwrap().to_string();
|
||||
}
|
||||
}
|
||||
CommandResponse::Custom
|
||||
State::Input
|
||||
} 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) {
|
||||
self.actual_lines = Vec::new();
|
||||
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() {
|
||||
"$ ".to_string() + &self.current_input + "█"
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use std::process::{ Command, Stdio };
|
||||
use std::io::{ Read, Write };
|
||||
|
||||
use ron;
|
||||
|
||||
fn main() {
|
||||
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();
|
||||
@@ -8,4 +10,5 @@ fn main() {
|
||||
let mut output = String::new();
|
||||
a.stdout.as_mut().unwrap().read_to_string(&mut output);
|
||||
println!("{}", output);
|
||||
//println!("{}", &ron::to_string(&[122, 400]).unwrap());
|
||||
}
|
||||
|
||||
@@ -2,4 +2,5 @@ pub mod desktop_background;
|
||||
pub mod taskbar;
|
||||
pub mod lock_screen;
|
||||
pub mod workspace_indicator;
|
||||
pub mod start_menu;
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@ use std::vec;
|
||||
use std::vec::Vec;
|
||||
use std::boxed::Box;
|
||||
|
||||
use ming_wm::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
|
||||
use ming_wm::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest };
|
||||
use ming_wm::framebuffer::Dimensions;
|
||||
use ming_wm::themes::ThemeInfo;
|
||||
use ming_wm::components::Component;
|
||||
use ming_wm::components::highlight_button::HighlightButton;
|
||||
use ming_wm::ipc::listen;
|
||||
use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
|
||||
use crate::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest };
|
||||
use crate::framebuffer::Dimensions;
|
||||
use crate::themes::ThemeInfo;
|
||||
use crate::components::Component;
|
||||
use crate::components::highlight_button::HighlightButton;
|
||||
use crate::ipc::listen;
|
||||
|
||||
//todo: move to essential
|
||||
|
||||
@@ -145,7 +145,7 @@ impl StartMenu {
|
||||
//add window buttons
|
||||
let mut to_add: Vec<&str> = Vec::new();
|
||||
if name == "Games" {
|
||||
to_add.push("Minesweeper");
|
||||
to_add.extend(["Minesweeper", "Reversi"]);
|
||||
} else if name == "Editing" {
|
||||
to_add.push("Malvim");
|
||||
} else if name == "Utils" {
|
||||
@@ -1,5 +1,6 @@
|
||||
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 };
|
||||
@@ -7,6 +8,9 @@ use crate::framebuffer::Dimensions;
|
||||
use crate::themes::ThemeInfo;
|
||||
|
||||
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 {
|
||||
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));
|
||||
}
|
||||
}
|
||||
//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
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
]*/
|
||||
//255 * 255 < max(u16)
|
||||
if alpha == 255 {
|
||||
color
|
||||
} else {
|
||||
let alpha = alpha as u16;
|
||||
[
|
||||
(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)]
|
||||
@@ -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
|
||||
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;
|
||||
@@ -159,6 +178,7 @@ impl FramebufferWriter {
|
||||
|
||||
//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>) {
|
||||
let mut top_left = top_left;
|
||||
//todo, config space
|
||||
|
||||
@@ -44,7 +44,7 @@ pub fn listen(mut window_like: impl WindowLike) {
|
||||
} else if method == "subtype" {
|
||||
println!("{}", ron::to_string(&window_like.subtype()).unwrap());
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ use termion::event::Key;
|
||||
pub enum KeyChar {
|
||||
Press(char),
|
||||
Alt(char),
|
||||
Ctrl(char),
|
||||
}
|
||||
|
||||
//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(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,
|
||||
|
||||
@@ -49,7 +49,6 @@ pub enum WindowMessageResponse {
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct KeyPress {
|
||||
pub key: char,
|
||||
//
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
@@ -90,6 +89,7 @@ pub enum InfoType {
|
||||
pub enum WindowMessage {
|
||||
Init(Dimensions),
|
||||
KeyPress(KeyPress),
|
||||
CtrlKeyPress(KeyPress),
|
||||
Shortcut(ShortcutType),
|
||||
Info(InfoType),
|
||||
Focus,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::vec::Vec;
|
||||
use std::process::{ Command, Child, Stdio };
|
||||
use std::io::{ BufReader, BufRead, Read, Write };
|
||||
use std::io::{ BufReader, BufRead, Write };
|
||||
use std::cell::RefCell;
|
||||
|
||||
use ron;
|
||||
@@ -14,66 +14,91 @@ pub struct ProxyWindowLike {
|
||||
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 {
|
||||
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();
|
||||
ron::from_str(&output).unwrap()
|
||||
ron::from_str(&output).unwrap_or(WindowMessageResponse::JustRerender)
|
||||
}
|
||||
|
||||
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();
|
||||
ron::from_str(&output).unwrap()
|
||||
ron::from_str(&output).unwrap_or(Vec::new())
|
||||
}
|
||||
|
||||
//properties
|
||||
fn title(&self) -> String {
|
||||
self.process.borrow_mut().stdin.as_mut().unwrap().write_all("title\n".as_bytes());
|
||||
self.read_line()
|
||||
if let Some(stdin) = self.process.borrow_mut().stdin.as_mut() {
|
||||
let _ = stdin.write_all("title\n".as_bytes());
|
||||
}
|
||||
self.read_line().chars().filter(|c| *c != '\n').collect()
|
||||
}
|
||||
|
||||
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();
|
||||
ron::from_str(&output).unwrap()
|
||||
ron::from_str(&output).unwrap_or(false)
|
||||
}
|
||||
|
||||
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();
|
||||
ron::from_str(&output).unwrap()
|
||||
ron::from_str(&output).unwrap_or(WindowLikeType::Window)
|
||||
}
|
||||
|
||||
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();
|
||||
ron::from_str(&output).unwrap()
|
||||
ron::from_str(&output).unwrap_or([420, 420])
|
||||
}
|
||||
}
|
||||
|
||||
//kill process when this window like dropped
|
||||
impl Drop for ProxyWindowLike {
|
||||
fn drop(&mut self) {
|
||||
self.process.borrow_mut().kill();
|
||||
let _ = self.process.borrow_mut().kill();
|
||||
}
|
||||
}
|
||||
|
||||
impl ProxyWindowLike {
|
||||
pub fn new(file: &str) -> Self {
|
||||
pub fn new(command: &mut Command) -> Self {
|
||||
ProxyWindowLike {
|
||||
//--quiet
|
||||
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()),
|
||||
process: RefCell::new(command.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 {
|
||||
let mut output = String::new();
|
||||
let mut buffer = self.process.borrow_mut();
|
||||
let buffer = buffer.stdout.as_mut().unwrap();
|
||||
if let Some(buffer) = buffer.stdout.as_mut() {
|
||||
let mut output = String::new();
|
||||
let mut reader = BufReader::new(buffer);
|
||||
reader.read_line(&mut output).unwrap();
|
||||
if let Ok(_) = reader.read_line(&mut output) {
|
||||
output
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,7 @@ use crate::essential::desktop_background::DesktopBackground;
|
||||
use crate::essential::taskbar::Taskbar;
|
||||
use crate::essential::lock_screen::LockScreen;
|
||||
use crate::essential::workspace_indicator::WorkspaceIndicator;
|
||||
|
||||
//todo, better error handling for windows
|
||||
use crate::essential::start_menu::StartMenu;
|
||||
|
||||
pub const TASKBAR_HEIGHT: usize = 38;
|
||||
pub const INDICATOR_HEIGHT: usize = 20;
|
||||
@@ -46,6 +45,7 @@ pub fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) {
|
||||
write!(stdout, "{}", cursor::Hide).unwrap();
|
||||
stdout.flush().unwrap();
|
||||
|
||||
|
||||
for c in stdin.keys() {
|
||||
if let Some(kc) = key_to_char(c.unwrap()) {
|
||||
//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
|
||||
Gradient(Point, Dimensions, RGBColor, RGBColor, usize),
|
||||
Mingde(Point),
|
||||
Circle(Point, usize, RGBColor),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
@@ -462,13 +463,19 @@ impl WindowManager {
|
||||
}
|
||||
press_response
|
||||
},
|
||||
KeyChar::Press(c) => {
|
||||
KeyChar::Press(c) | KeyChar::Ctrl(c) => {
|
||||
let mut press_response = WindowMessageResponse::DoNothing;
|
||||
//send to focused window
|
||||
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) {
|
||||
WindowMessage::KeyPress(KeyPress {
|
||||
key: c,
|
||||
}));
|
||||
})
|
||||
} else {
|
||||
WindowMessage::CtrlKeyPress(KeyPress {
|
||||
key: c,
|
||||
})
|
||||
});
|
||||
//at most, only the focused window needs to be rerendered
|
||||
redraw_ids = Some(vec![self.window_infos[focused_index].id]);
|
||||
//requests can result in window openings and closings, etc
|
||||
@@ -499,14 +506,19 @@ impl WindowManager {
|
||||
if subtype != WindowLikeType::Taskbar && subtype != WindowLikeType::StartMenu {
|
||||
return;
|
||||
}
|
||||
let w: WindowBox = match w.as_str() {
|
||||
"Minesweeper" => Box::new(ProxyWindowLike::new("minesweeper")),
|
||||
"Malvim" => Box::new(ProxyWindowLike::new("malvim")),
|
||||
"Terminal" => Box::new(ProxyWindowLike::new("terminal")),
|
||||
"Audio Player" => Box::new(ProxyWindowLike::new("audio_player")),
|
||||
"StartMenu" => Box::new(ProxyWindowLike::new("start_menu")),
|
||||
_ => panic!("window not found"), //todo: do not panic
|
||||
let w: Option<WindowBox> = match w.as_str() {
|
||||
"Minesweeper" => Some(Box::new(ProxyWindowLike::new_rust("minesweeper"))),
|
||||
"Reversi" => Some(Box::new(ProxyWindowLike::new_rust("reversi"))),
|
||||
"Malvim" => Some(Box::new(ProxyWindowLike::new_rust("malvim"))),
|
||||
"Terminal" => Some(Box::new(ProxyWindowLike::new_rust("terminal"))),
|
||||
"Audio Player" => Some(Box::new(ProxyWindowLike::new_rust("audio_player"))),
|
||||
"StartMenu" => Some(Box::new(StartMenu::new())),
|
||||
_ => None,
|
||||
};
|
||||
if w.is_none() {
|
||||
return;
|
||||
}
|
||||
let w = w.unwrap();
|
||||
//close start menu if open
|
||||
self.toggle_start_menu(true);
|
||||
let ideal_dimensions = w.ideal_dimensions(self.dimensions);
|
||||
@@ -587,6 +599,7 @@ impl WindowManager {
|
||||
instructions = instructions.iter().map(|instruction| {
|
||||
match instruction {
|
||||
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::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),
|
||||
@@ -630,6 +643,9 @@ impl WindowManager {
|
||||
];
|
||||
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) => {
|
||||
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());
|
||||
w_index += 1;
|
||||
//core::mem::drop(temp_vec);
|
||||
}
|
||||
self.framebuffer.write_frame(WRITER.lock().unwrap().get_buffer());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user