docs, password config, help, about

render -> draw, file explorer, writer is RefCell
This commit is contained in:
stjet
2025-01-01 09:38:11 +00:00
parent acdb59d668
commit 03f1d649e0
34 changed files with 542 additions and 152 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
target/
Cargo.lock
ming-wm
password.txt

View File

@@ -8,6 +8,7 @@ default-run = "main"
[build-dependencies]
bmp-rust = "0.4.1"
blake2 = { version = "0.10.6", default-features = false }
[dependencies]
blake2 = { version = "0.10.6", default-features = false }

View File

@@ -1,6 +1,12 @@
Ming-wm is a keyboard-based, retro-themed window manager for Linux. It is single-threaded, and is neither for Wayland or the X Window System - it writes directly to the framebuffer. Inspirations include i3, Haiku, SerenityOS, and Windows98, and it is a conceptual successor to the previous [mingde](https://github.com/stjet/mingde) and [ming-os](https://github.com/stjet/ming-os).
![example 1](/docs/images/ws1.png)
![example 2](/docs/images/ws2.png)
## Running
Create a `password.txt` file in the same directory as `build.rs`, otherwise the default password will be "incorrect mule lightbulb niche".
For best performance:
```
cargo build --release
@@ -9,27 +15,3 @@ cargo build --release
Though just `cargo run --release` can be done.
## Config
Config files should be protected so they can only be written to with root privileges.
### Desktop Backgrounds
In `$XDG_CONFIG_DIR/ming-wm/desktop-background`, you can configure what the desktop background should be for each workspace. The first line decides the background for the first workspace, and so on. If lines are missing, or empty, or the config file is missing, the default green background is used.
If a line starts with "#", and is followed by 6 lowercase hex characters, then it will interpreted as a RGB colour.
If a line starts with "r", then what follows with be interpreted as a path to a BMP image file in BGRA order, and if it starts with any other character, what follows will be interpreted as a path to a BMP image file in RGBA order.
Example:
```
#008080
#003153
r./bmps/castle1440x842.bmp
r./bmps/ming1440x842.bmp
r./bmps/blur1440x842.bmp
```
//

View File

@@ -1,6 +1,9 @@
use std::fs::{ read_dir, File };
use std::fs::{ read_dir, read_to_string, write, File };
use std::io::Write;
use std::env;
use std::path::Path;
use blake2::{ Blake2b512, Digest };
use bmp_rust::bmp::BMP;
fn font_chars_to_alphas(dir: &str) {
@@ -37,6 +40,14 @@ fn font_chars_to_alphas(dir: &str) {
}
fn main() {
//hash + "salt" password and add to build
let password = read_to_string("password.txt").unwrap_or("password".to_string()).replace("\n", "") + "salt?sorrycryptographers";
let mut hasher = Blake2b512::new();
hasher.update(password.as_bytes());
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("password.rs");
write(&dest_path, format!("pub const PASSWORD_HASH: [u8; 64] = {:?};", hasher.finalize())).unwrap();
//process bmps
for entry in read_dir("./bmps").unwrap() {
let path = entry.unwrap().path();
if path.is_dir() {

BIN
docs/images/ws1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

BIN
docs/images/ws3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 914 KiB

112
docs/system/README.md Normal file
View File

@@ -0,0 +1,112 @@
> This is not extensive technical documentation of the project, but a technical overview.
> If reading this in the "About" app inside ming-wm, use the 'j' and 'k' keys to scroll.
> Recommended reading music: Hegira by such
Ming-wm, as the name implies, is great at liberating the nation of Mongol hordes, and great at collapsing when invaded by Jurchens.
Also, it is a window manager. The window manager manages "window-likes":
```rust
pub enum WindowLikeType {
LockScreen,
Window,
DesktopBackground,
Taskbar,
StartMenu,
WorkspaceIndicator,
}
```
All of these are called "window-likes" because to the window manager they are all essentially all the same; they all implement the same trait.
```rust
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
}
```
The only thing special about `Window` window-likes is that the window manager draws window decorations for it automatically. Well, I don't want to lie. There are a few other special things but that's the main one.
## The Event Loop
The event loop goes like this:
1. Keyboard event received, sent to the window manager
2. The window manager interprets it. It could be a shortcut to say, open the start meny. Or, if a window-like is currently focused, the window manager will probably forward the keyboard event to that window-like by calling the window-like's `handle_message` method
3. (Only if sent to a window-like) The window-like receives and processes it. It returns a `WindowMessageResponse`:
>>> ```rust
pub enum WindowMessageResponse {
Request(WindowManagerRequest),
JustRerender,
DoNothing,
}
```
4. If the window manager decides the keyboard event means some or all of the screen needs to be redrawn (eg, it was a valid shortcut, or the window-like it sent the event to returned something that wasn't a `DoNothing`), it will go and get the drawing instructions from all the window-likes that need to be redrawn by looping through them and calling their `draw` method
Nothing except key presses trigger redraws. That means no mouse and no animations. This is a positive. This is a positive. I truly believe that. This is a positive. Having a window manager and windows that don't require taking hands off the keyboard (or rather, entirely designed to be keyboard operated) makes using it very fast and efficient, with no pain of not having a mice and needing to use a shitty mousepad. Videos are nice, but animations and the like are annoying and have no place in a good window manager.
## Drawing / Rendering
For each window-like it (re)draws, it creates a new framebuffer, then draws to the framebuffer, the draw instructions it received from the window-like. Then, it draws that new framebuffer onto the actual linux framebuffer (ie, draws the window-like to the screen). So, there is no issue with window-likes overlapping.
One may wonder why exactly the window manager receives drawing instructions from the window-likes and does the actual drawing of the window-likes. Why not just receive the raw pixels of the window-like from the window-like, and just handle compositing the window-likes? Receiving draw instructions does have the advantage of having a significantly better best-case scenario when transferring data between the window-like and window manager, as letting the window-like do the drawing and handing the result to the window manager will mean always transferring `height*width*bytes per pixel` bytes. Additionally, draw instructions are significantly more readably and easier to debug. Finally, asking window-likes to do their own drawing results in window-likes being required to contain lots of drawing code (especially text drawing code!!), rather than just telling the window manager to draw rectangles, lines, and text. While all the window-likes and windows in this repo all rely on the same `framebuffer.rs`, if someone were to write a window in say, Lisp Scheme, they would need to write all that logic again. But the real answer is because that is how it was written in ming-os, from which much of the core code comes from, and it works well, though the lack of a instruction length upper-bound is concerning and potentially inefficient. I don't feel like rewriting it and I don't believe it will become a problem.
## Non-window Window-likes
Recall that the `WindowLikeType`s were LockScreen, DesktopBackground, Taskbar, StartMenu and WorkspaceIndicator (ignoring Window). What they should do is fairly self-explanatory, but as a brief overview:
- Lock screen: Initial state of the window manager. Is the only window-like until the correct password is entered
- Desktop background: Displays the desktop background, behind the windows. Can be a solid colour, or a .bmp image
- Taskbar: Shows currently open windows in the current workspace, and manages the start menu
- Start menu: Shows the window (app) categories, and opens the window (apps) requested
- Workspace indicator: Shows which workspace the window manager is currently in. There are 9 workspaces, each of which can contain their own set of windows. The workspaces can be switched to or out of easily, and windows can be moved easily between them
Each of these receives special, privileged messages from the window manager, in order to be useful and function. For example, the taskbar is notified whenever a window is opened or closed, and the workspace indicator receives a message whenever the workspace is changed.
In some cases, these non-window window-likes also get special rights. For example, only the start menu and taskbar can open window(-likes). The start menu for obvious reasons (opening the windows it was asked to open), and the taskbar needs it to open the start menu. In the future, the taskbar and window manager may be rewritten so the window manager, instead of the taskbar, opens the start menu. Other examples are only the lock screen being able to unlock (if the password is correct), and only the start menu being able to lock (if the lock option is executed).
All of these non-window window-likes are compiled into the window manager binary. They are not separate binaries/processes as they aren't really expected to be modified or swapped out, and are "essential" to the function of the window manager.
## Window Window-likes
Also known as apps. "Apps" and "windows" will be used mostly interchangeably, with the important nuance that there can only be multiple windows opened of a single app.
Windows can be moved and resized over the desktop background (they cannot overlap with the workspace indicator or taskbar, or go off the screen). They also come with a window decoration on the top displaying the title of the window.
As windows are mostly just like other window-likes, they can be compiled in as part of the window manager binary. However, most apps in this repo are separate binaries (see `src/bin`). They use `proxy_window_like.rs` which implements the `WindowLike` trait and proxies talking to the window binary. The window is a child process. Messages are sent to it through piped in stdin, and responses are received through stdout. These apps aren't terribly fancy but the performance impact seems to be unnoticable.
As apps are just any old binary that support the IPC that `proxy_window_like.rs` does, they can be written in any language, and be completely separate from this project. Of course, if they are not written in Rust, extra code is needed to do that IPC.
//but what apps can be opened are currently hardcoded in start menu and the window manager
Some of the apps included are Malvim, the subset of vim (a text editor) I use, Minesweeper, and an Audio Player.
## More on Window-likes
Further documentation on specific window-likes can be found in `docs/window-likes`.
## Themeing
The window manager passes information about it's theme to all window-likes as a parameter to `draw`, so windows can have appropriate background colours, highlight colours, text colours, etc.
//can't change themes yet. in fact, no other themes yet
## Fonts / Text
//
//Japanese / Chinese characters can only be used for display, not input, as there is no CJK input system. yet. And these text inputs don't yet handle multi-byte input very gracefully

1
docs/system/ipc.md Normal file
View File

@@ -0,0 +1 @@
//

20
docs/system/shortcuts.md Normal file
View File

@@ -0,0 +1,20 @@
- Alt+E: Exit ming-wm
- Alt+s: Open start menu
- Alt+[: Focus previous window
- Alt+]: Focus next window
- Alt+q: Quit window
- Alt+c: Centre window
- Alt+f: Fullscreen window
- Alt+w: Half width window
- Alt+C: Clipboard copy
- Alt+P: Clipboard paste
- Alt+h: Move window left
- Alt+j: Move window down
- Alt+k: Move window up
- Alt+l: Move window right
- Alt+H: Move window to left edge
- Alt+J: Move window to bottom edge
- Alt+K: Move window to top edge
- Alt+L: Move window to right edge
- Alt+1, Alt+2, ..., Alt+[n], ..., Alt+9: Switch to workspace [n]
- Alt+shift+1, Alt+shift+2, ..., Alt+shift+[n], ..., Alt+shift+9: Move window to workspace [n]

View File

@@ -0,0 +1,21 @@
Displays desktop backgrounds, which can be a BMP image file or a solid colour.
## Config
Config files should have the appropriated protections so they can only be written to root privileges.
In `$XDG_CONFIG_DIR/ming-wm/desktop-background`, you can configure what the desktop background should be for each workspace. The first line decides the background for the first workspace, and so on. If lines are missing, or empty, or the config file is missing, the default green background is used.
If a line starts with "#", and is followed by 6 lowercase hex characters, then it will interpreted as a RGB colour.
If a line starts with "r", then what follows with be interpreted as a path to a BMP image file in BGRA order, and if it starts with any other character, what follows will be interpreted as a path to a BMP image file in RGBA order.
Example:
```
#008080
#003153
r./bmps/castle1440x842.bmp
r./bmps/ming1440x842.bmp
r./bmps/blur1440x842.bmp
```

View File

@@ -0,0 +1,5 @@
Shown when screen is locked. Unlocks the screen when correct password is entered.
## Config
Before building, create a `password.txt` in the same directory as the `build.rs` file with the password.

View File

@@ -0,0 +1,13 @@
The goal of the game is to reveal all tiles that are not mines. Revealing a tile that is a mine means game over.
The game consists of revealing tiles. If a revealed tile has no mines directly adjacent to it (above, below, left, right, top left, top right, etc). All tiles adjacent to it are revealed. If any of the newly revealed tiles have no mines directly adjacent to it, then... well you get the point.
Tiles with mines adjacent to it are labeled with a number stating how many mines are adjacent to it.
To start the game, first enter in a seed for the game, by typing in four random characters, then hitting enter. Then, choose the first tile to reveal. That first tile is guaranteed to be a non-mine tile with no mines adjacent to it.
Games with the same seed and first tile revealed will have the same mine layout, making the game suitable for competition.
Choosing to reveal a tile is simple. Just type in the two character name of that tile. For example, 'c4' or '15'. If you make a mistake with the first character, just use the backspace/delete key to clear.
Upon winning or losing, press a key to play again.

View File

@@ -35,11 +35,11 @@ impl WindowLike for AudioPlayer {
match message {
WindowMessage::Init(dimensions) => {
self.dimensions = dimensions;
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
WindowMessage::ChangeDimensions(dimensions) => {
self.dimensions = dimensions;
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
WindowMessage::KeyPress(key_press) => {
if key_press.key == '𐘂' { //the enter key
@@ -52,7 +52,7 @@ impl WindowLike for AudioPlayer {
} else {
self.command += &key_press.key.to_string();
}
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
_ => {
WindowMessageResponse::DoNothing

View File

@@ -16,9 +16,6 @@ struct DirectoryChild {
override_name: Option<String>,
path: PathBuf,
is_file: bool,
//can only be true if dir
//if true, means the contents of this dir should be visible too, even though it isn't the current path. like a tree
tree_open: bool,
}
#[derive(Default)]
@@ -38,11 +35,11 @@ impl WindowLike for FileExplorer {
self.current_path = PathBuf::from("/");
self.dimensions = dimensions;
self.current_dir_contents = self.get_current_dir_contents();
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
WindowMessage::ChangeDimensions(dimensions) => {
self.dimensions = dimensions;
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
WindowMessage::KeyPress(key_press) => {
if key_press.key == '𐘂' { //the enter key
@@ -53,7 +50,7 @@ impl WindowLike for FileExplorer {
self.current_dir_contents = self.get_current_dir_contents();
self.position = 0;
self.top_position = 0;
return WindowMessageResponse::JustRerender;
return WindowMessageResponse::JustRedraw;
}
}
WindowMessageResponse::DoNothing
@@ -74,15 +71,16 @@ impl WindowLike for FileExplorer {
}
}
//calculate position
let max_height = self.dimensions[1] - HEIGHT;
if self.position > self.top_position {
let current_height = (self.position - self.top_position) * HEIGHT;
if current_height > self.dimensions[1] {
self.top_position += (current_height - self.dimensions[1]) / HEIGHT + 1;
self.top_position += (current_height - max_height) / HEIGHT + 1;
}
} else {
self.top_position = self.position;
};
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
} else {
WindowMessageResponse::DoNothing
}
@@ -93,10 +91,10 @@ impl WindowLike for FileExplorer {
fn draw(&self, theme_info: &ThemeInfo) -> Vec<DrawInstructions> {
let mut instructions = Vec::new();
//top bar with path name and editing
//
//top bar with path name
instructions.push(DrawInstructions::Text([5, 0], vec!["times-new-roman".to_string(), "shippori-mincho".to_string()], "Current: ".to_string() + &self.current_path.to_string_lossy().to_string(), theme_info.text, theme_info.background, None, None));
//the actual files and directories
let mut start_y = 0;
let mut start_y = HEIGHT;
let mut i = self.top_position;
for entry in self.current_dir_contents.iter().skip(self.top_position) {
if start_y > self.dimensions[1] {
@@ -149,7 +147,6 @@ impl FileExplorer {
contents.push(DirectoryChild {
override_name: Some("..".to_string()),
is_file: false,
tree_open: false,
path: self.current_path.parent().unwrap().to_owned(),
});
}
@@ -158,7 +155,6 @@ impl FileExplorer {
DirectoryChild {
override_name: None,
is_file: path.is_file(),
tree_open: false,
path,
}
}));

View File

@@ -87,7 +87,7 @@ impl WindowLike for Malvim {
match message {
WindowMessage::Init(dimensions) => {
self.dimensions = dimensions;
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
WindowMessage::KeyPress(key_press) => {
let mut changed = true;
@@ -304,11 +304,11 @@ impl WindowLike for Malvim {
}
}
self.calc_top_line_pos();
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
WindowMessage::ChangeDimensions(dimensions) => {
self.dimensions = dimensions;
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
WindowMessage::Shortcut(shortcut) => {
match shortcut {
@@ -329,7 +329,7 @@ impl WindowLike for Malvim {
self.calc_top_line_pos();
self.calc_current(); //too over zealous but whatever
self.files[self.current_file_index].changed = true;
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
} else {
WindowMessageResponse::DoNothing
}

View File

@@ -44,7 +44,7 @@ impl WindowLike for Minesweeper {
match message {
WindowMessage::Init(dimensions) => {
self.dimensions = dimensions;
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
WindowMessage::KeyPress(key_press) => {
if self.state == State::Seed {
@@ -58,7 +58,7 @@ impl WindowLike for Minesweeper {
self.random_chars.push(key_press.key);
}
}
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
} else if self.state == State::BeforePlaying || self.state == State::Playing {
if key_press.key == '𐘁' { //backspace
self.first_char = '\0';
@@ -125,7 +125,7 @@ impl WindowLike for Minesweeper {
self.state = State::Won;
}
}
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
} else {
WindowMessageResponse::DoNothing
}

View File

@@ -59,7 +59,7 @@ impl WindowLike for Reversi {
self.dimensions = dimensions;
self.new_tiles();
self.valid_moves = self.get_valid_moves();
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
WindowMessage::KeyPress(key_press) => {
if let Ok(n) = key_press.key.to_string().parse::<u8>() {
@@ -82,7 +82,7 @@ impl WindowLike for Reversi {
} else if key_press.key == '𐘁' { //backspace
self.current_number = None;
}
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
_ => WindowMessageResponse::DoNothing,
}

View File

@@ -44,11 +44,11 @@ impl WindowLike for Terminal {
self.current_path = "/".to_string();
self.lines = vec!["Mingde Terminal".to_string(), "".to_string()];
self.calc_actual_lines();
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
WindowMessage::ChangeDimensions(dimensions) => {
self.dimensions = dimensions;
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
WindowMessage::KeyPress(key_press) => {
if self.state == State::Input {
@@ -68,7 +68,7 @@ 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
WindowMessageResponse::JustRedraw
} else if key_press.key.len_utf8() == 1 {
//update
let running_process = self.running_process.as_mut().unwrap();
@@ -85,7 +85,7 @@ impl WindowLike for Terminal {
}
self.state = State::Input;
self.calc_actual_lines();
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
} else {
//still running
WindowMessageResponse::DoNothing
@@ -101,15 +101,15 @@ impl WindowLike for Terminal {
//kills and running_process is now None
let _ = self.running_process.take().unwrap().kill();
self.state = State::Input;
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
} else if self.state == State::Input && (key_press.key == 'p' || key_press.key == 'n') {
//only the last command is saved unlike other terminals. good enough for me
if key_press.key == 'p' && self.last_command.is_some() {
self.current_input = self.last_command.clone().unwrap();
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
} else if key_press.key == 'n' {
self.current_input = String::new();
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
} else {
WindowMessageResponse::DoNothing
}

View File

@@ -1,11 +0,0 @@
use std::process::{ Command, Stdio };
use std::io::{ Read, Write };
use ron;
use ming_wm::messages::WindowMessage;
fn main() {
println!("{}", ron::to_string(&WindowMessage::Init([100,100])).unwrap());
//println!("{}", &ron::to_string(&[122, 400]).unwrap());
}

View File

@@ -11,7 +11,7 @@ pub struct HighlightButton<T> {
name_: String,
top_left: Point,
size: Dimensions,
text: &'static str,
text: String,
pub highlighted: bool,
click_return: T,
toggle_highlight_return: T, //also unhighlight return
@@ -38,12 +38,12 @@ impl<T: Clone> Component<T> for HighlightButton<T> {
vec![
//highlight background
DrawInstructions::Rect(self.top_left, self.size, theme_info.top),
DrawInstructions::Text([self.top_left[0] + 4, self.top_left[1] + (self.size[1] - font_height) / 2], vec!["times-new-roman".to_string()], self.text.to_string(), theme_info.top_text, theme_info.top, None, None),
DrawInstructions::Text([self.top_left[0] + 4, self.top_left[1] + (self.size[1] - font_height) / 2], vec!["times-new-roman".to_string()], self.text.clone(), theme_info.top_text, theme_info.top, None, None),
]
} else {
vec![
DrawInstructions::Rect(self.top_left, self.size, theme_info.background),
DrawInstructions::Text([self.top_left[0] + 4, self.top_left[1] + (self.size[1] - font_height) / 2], vec!["times-new-roman".to_string()], self.text.to_string(), theme_info.text, theme_info.background, None, None),
DrawInstructions::Text([self.top_left[0] + 4, self.top_left[1] + (self.size[1] - font_height) / 2], vec!["times-new-roman".to_string()], self.text.clone(), theme_info.text, theme_info.background, None, None),
]
}
}
@@ -63,7 +63,7 @@ impl<T: Clone> Component<T> for HighlightButton<T> {
}
impl<T> HighlightButton<T> {
pub fn new(name_: String, top_left: Point, size: Dimensions, text: &'static str, click_return: T, toggle_highlight_return: T, highlighted: bool) -> Self {
pub fn new(name_: String, top_left: Point, size: Dimensions, text: String, click_return: T, toggle_highlight_return: T, highlighted: bool) -> Self {
Self {
name_,
top_left,

View File

@@ -6,6 +6,7 @@ use crate::window_manager::DrawInstructions;
pub mod toggle_button;
pub mod highlight_button;
pub mod paragraph;
pub trait Component<T> {
fn handle_message(&mut self, message: WindowMessage) -> Option<T>;

View File

@@ -0,0 +1,91 @@
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 crate::utils::calc_actual_lines;
const MONO_WIDTH: u8 = 10;
const LINE_HEIGHT: usize = 18;
pub struct Paragraph<T> {
name_: String,
actual_lines: Vec<(bool, usize, String)>, //first, line #, actual line
top_left: Point,
size: Dimensions,
line_pos: usize,
key_return: T,
}
impl<T: Copy> Component<T> for Paragraph<T> {
fn handle_message(&mut self, message: WindowMessage) -> Option<T> {
if let WindowMessage::KeyPress(key_press) = message {
if key_press.key == 'j' {
//down
if self.line_pos != self.actual_lines.len() - 1 {
self.line_pos += 1;
}
Some(self.key_return)
} else if key_press.key == 'k' {
//up
if self.line_pos != 0 {
self.line_pos -= 1;
}
Some(self.key_return)
} else {
None
}
} else {
None
}
}
fn draw(&self, theme_info: &ThemeInfo) -> Vec<DrawInstructions> {
let mut instructions = Vec::new();
let max_lines = self.size[1] / LINE_HEIGHT;
let mut start_y = self.top_left[1];
for line in self.actual_lines.iter().skip(self.line_pos).take(max_lines) {
instructions.push(DrawInstructions::Text([self.top_left[0], start_y], vec!["times-new-romono".to_string()], line.2.clone(), theme_info.text, theme_info.background, Some(0), Some(MONO_WIDTH)));
start_y += LINE_HEIGHT;
}
instructions
}
//properties
fn focusable(&self) -> bool {
true
}
fn clickable(&self) -> bool {
false
}
fn name(&self) -> &String {
&self.name_
}
}
impl<T> Paragraph<T> {
pub fn new(name_: String, top_left: Point, size: Dimensions, text: String, key_return: T) -> Self {
let mut s = Self {
name_,
actual_lines: Vec::new(),
top_left,
size,
line_pos: 0,
key_return,
};
s.new_text(text);
s
}
pub fn new_text(&mut self, text: String) {
let max_chars_per_line = self.size[0] / MONO_WIDTH as usize;
let lines: Vec<String> = text.split("\n").map(|s| s.to_string()).collect();
self.actual_lines = calc_actual_lines(lines.iter(), max_chars_per_line, true);
}
}

View File

@@ -7,12 +7,6 @@ use crate::themes::ThemeInfo;
use crate::messages::WindowMessage;
use crate::window_manager::DrawInstructions;
//we need a text width and height measure function first
pub enum ToggleButtonAlignment {
Centre,
Left,
}
pub struct ToggleButton<T> {
name_: String,
top_left: Point,
@@ -20,7 +14,6 @@ pub struct ToggleButton<T> {
text: String,
draw_bg: bool,
pub inverted: bool, //whether is it clicked or not
alignment: ToggleButtonAlignment,
click_return: T,
unclick_return: T,
}
@@ -73,7 +66,7 @@ impl<T: Clone> Component<T> for ToggleButton<T> {
}
impl<T> ToggleButton<T> {
pub fn new(name_: String, top_left: Point, size: Dimensions, text: String, click_return: T, unclick_return: T, draw_bg: bool, alignment: Option<ToggleButtonAlignment>) -> Self {
pub fn new(name_: String, top_left: Point, size: Dimensions, text: String, click_return: T, unclick_return: T, draw_bg: bool) -> Self {
Self {
name_,
top_left,
@@ -83,7 +76,6 @@ impl<T> ToggleButton<T> {
unclick_return,
draw_bg,
inverted: false,
alignment: alignment.unwrap_or(ToggleButtonAlignment::Centre),
}
}
}

62
src/essential/about.rs Normal file
View File

@@ -0,0 +1,62 @@
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 crate::components::Component;
use crate::components::paragraph::Paragraph;
pub struct About {
dimensions: Dimensions,
components: Vec<Box<dyn Component<()> + Send>>,
}
impl WindowLike for About {
fn handle_message(&mut self, message: WindowMessage) -> WindowMessageResponse {
match message {
WindowMessage::Init(dimensions) => {
self.dimensions = dimensions;
self.components.push(Box::new(Paragraph::new("help".to_string(), [2, 2], [self.dimensions[0] - 4, self.dimensions[1] - 4], read_to_string("docs/system/README.md").unwrap_or("docs/system/README.md not found".to_string()), ())));
WindowMessageResponse::JustRedraw
},
WindowMessage::KeyPress(key_press) => {
if self.components[0].handle_message(WindowMessage::KeyPress(key_press)).is_some() {
WindowMessageResponse::JustRedraw
} else {
WindowMessageResponse::DoNothing
}
},
_ => WindowMessageResponse::DoNothing
}
}
fn draw(&self, theme_info: &ThemeInfo) -> Vec<DrawInstructions> {
self.components[0].draw(theme_info)
}
//properties
fn title(&self) -> String {
"About".to_string()
}
fn subtype(&self) -> WindowLikeType {
WindowLikeType::Window
}
fn ideal_dimensions(&self, _dimensions: Dimensions) -> Dimensions {
[500, 600]
}
}
impl About {
pub fn new() -> Self {
Self {
dimensions: [0, 0],
components: Vec::new(),
}
}
}

View File

@@ -21,13 +21,13 @@ impl WindowLike for DesktopBackground {
match message {
WindowMessage::Init(dimensions) => {
self.dimensions = dimensions;
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
WindowMessage::Shortcut(shortcut) => {
match shortcut {
ShortcutType::SwitchWorkspace(workspace) => {
self.current_workspace = workspace;
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
_ => WindowMessageResponse::DoNothing,
}

89
src/essential/help.rs Normal file
View File

@@ -0,0 +1,89 @@
use std::vec::Vec;
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 crate::components::paragraph::Paragraph;
use crate::components::Component;
pub struct Help {
dimensions: Dimensions,
file_index: usize,
files: Vec<PathBuf>,
paragraph: Option<Box<Paragraph<()>>>,
}
impl WindowLike for Help {
fn handle_message(&mut self, message: WindowMessage) -> WindowMessageResponse {
match message {
WindowMessage::Init(dimensions) => {
self.dimensions = dimensions;
self.paragraph = Some(Box::new(Paragraph::new("help".to_string(), [2, 22], [self.dimensions[0] - 4, self.dimensions[1] - 24], "Press the 'h' and 'l' keys to read the different help pages".to_string(), ())));
WindowMessageResponse::JustRedraw
},
WindowMessage::KeyPress(key_press) => {
if key_press.key == 'h' || key_press.key == 'l' {
if key_press.key == 'h' {
if self.file_index == 0 {
self.file_index = self.files.len() - 1;
} else {
self.file_index -= 1;
}
} else {
if self.file_index == self.files.len() - 1 {
self.file_index = 0;
} else {
self.file_index += 1;
}
}
self.paragraph.as_mut().unwrap().new_text(read_to_string(self.files[self.file_index].clone()).unwrap());
WindowMessageResponse::JustRedraw
} else if self.paragraph.as_mut().unwrap().handle_message(WindowMessage::KeyPress(key_press)).is_some() {
WindowMessageResponse::JustRedraw
} else {
WindowMessageResponse::DoNothing
}
},
_ => WindowMessageResponse::DoNothing
}
}
fn draw(&self, theme_info: &ThemeInfo) -> Vec<DrawInstructions> {
let mut instructions = vec![DrawInstructions::Text([2, 2], vec!["times-new-romono".to_string()], self.files[self.file_index].clone().file_name().unwrap().to_string_lossy().to_string(), theme_info.text, theme_info.background, Some(0), None)];
instructions.extend(self.paragraph.as_ref().unwrap().draw(theme_info));
instructions
}
//properties
fn title(&self) -> String {
"About".to_string()
}
fn subtype(&self) -> WindowLikeType {
WindowLikeType::Window
}
fn ideal_dimensions(&self, _dimensions: Dimensions) -> Dimensions {
[500, 600]
}
}
impl Help {
pub fn new() -> Self {
let mut files = vec![PathBuf::from("docs/system/shortcuts.md")];
for entry in read_dir("docs/window-likes").unwrap() {
files.push(entry.unwrap().path());
}
Self {
dimensions: [0, 0],
file_index: 0,
files,
paragraph: None,
}
}
}

View File

@@ -7,7 +7,9 @@ use crate::messages::{ WindowMessage, WindowMessageResponse, WindowManagerReques
use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
use blake2::{ Blake2b512, Digest };
const PASSWORD_HASH: [u8; 64] = [220, 88, 183, 188, 240, 27, 107, 181, 58, 191, 198, 170, 114, 38, 7, 148, 6, 179, 75, 128, 231, 171, 172, 220, 85, 38, 36, 113, 116, 146, 70, 197, 163, 179, 158, 192, 130, 53, 247, 48, 47, 209, 95, 96, 179, 211, 4, 122, 254, 127, 21, 165, 139, 199, 151, 226, 216, 176, 123, 41, 194, 221, 58, 69];
include!(concat!(env!("OUT_DIR"), "/password.rs"));
//const PASSWORD_HASH: [u8; 64] = [220, 88, 183, 188, 240, 27, 107, 181, 58, 191, 198, 170, 114, 38, 7, 148, 6, 179, 75, 128, 231, 171, 172, 220, 85, 38, 36, 113, 116, 146, 70, 197, 163, 179, 158, 192, 130, 53, 247, 48, 47, 209, 95, 96, 179, 211, 4, 122, 254, 127, 21, 165, 139, 199, 151, 226, 216, 176, 123, 41, 194, 221, 58, 69];
pub struct LockScreen {
dimensions: Dimensions,
@@ -19,28 +21,28 @@ impl WindowLike for LockScreen {
match message {
WindowMessage::Init(dimensions) => {
self.dimensions = dimensions;
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
WindowMessage::KeyPress(key_press) => {
if key_press.key == '𐘂' { //the enter key
//check password
let mut hasher = Blake2b512::new();
hasher.update(self.input_password.as_bytes());
hasher.update((self.input_password.clone() + "salt?sorrycryptographers").as_bytes());
if hasher.finalize() == PASSWORD_HASH.into() {
WindowMessageResponse::Request(WindowManagerRequest::Unlock)
} else {
self.input_password = String::new();
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
}
} else if key_press.key == '𐘁' { //backspace
let p_len = self.input_password.len();
if p_len != 0 {
self.input_password = self.input_password[..p_len - 1].to_string();
}
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
} else {
self.input_password += &key_press.key.to_string();
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
}
},
_ => WindowMessageResponse::DoNothing,

View File

@@ -4,3 +4,6 @@ pub mod lock_screen;
pub mod workspace_indicator;
pub mod start_menu;
pub mod about;
pub mod help;

View File

@@ -1,5 +1,3 @@
#![allow(warnings)]
use std::vec;
use std::vec::Vec;
use std::boxed::Box;
@@ -10,9 +8,6 @@ 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
static CATEGORIES: [&'static str; 9] = ["About", "Utils", "Games", "Editing", "Files", "System", "Misc", "Help", "Logout"];
@@ -39,7 +34,7 @@ impl WindowLike for StartMenu {
self.dimensions = dimensions;
self.y_each = (self.dimensions[1] - 1) / CATEGORIES.len();
self.add_category_components();
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
WindowMessage::KeyPress(key_press) => {
//up and down
@@ -62,7 +57,7 @@ impl WindowLike for StartMenu {
self.old_focus = self.current_focus.to_string();
self.current_focus = self.components[current_focus_index].name().to_string();
self.components[current_focus_index].handle_message(WindowMessage::Focus);
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
} else if key_press.key == '𐘂' { //the enter key
let focus_index = self.get_focus_index();
if let Some(focus_index) = focus_index {
@@ -79,7 +74,7 @@ impl WindowLike for StartMenu {
self.old_focus = self.current_focus.clone();
self.current_focus = self.components[current_focus_index + n_index].name().to_string();
self.components[current_focus_index + n_index].handle_message(WindowMessage::Focus);
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
} else {
WindowMessageResponse::DoNothing
}
@@ -135,11 +130,14 @@ impl StartMenu {
StartMenuMessage::CategoryClick(name) => {
if name == "Logout" {
WindowMessageResponse::Request(WindowManagerRequest::Lock)
} else if name == "About" || name == "Help" {
//todo above: also do the same for Help
WindowMessageResponse::Request(WindowManagerRequest::OpenWindow(name.to_string()))
} else {
self.current_focus = "Back".to_string();
self.components = vec![
Box::new(HighlightButton::new(
"Back".to_string(), [42, 1], [self.dimensions[0] - 42 - 1, self.y_each], "Back", StartMenuMessage::Back, StartMenuMessage::ChangeAcknowledge, true
"Back".to_string(), [42, 1], [self.dimensions[0] - 42 - 1, self.y_each], "Back".to_string(), StartMenuMessage::Back, StartMenuMessage::ChangeAcknowledge, true
))
];
//add window buttons
@@ -157,10 +155,10 @@ impl StartMenu {
for a in 0..to_add.len() {
let w_name = to_add[a];
self.components.push(Box::new(HighlightButton::new(
w_name.to_string(), [42, (a + 1) * self.y_each], [self.dimensions[0] - 42 - 1, self.y_each], w_name, StartMenuMessage::WindowClick(w_name), StartMenuMessage::ChangeAcknowledge, false
w_name.to_string(), [42, (a + 1) * self.y_each], [self.dimensions[0] - 42 - 1, self.y_each], w_name.to_string(), StartMenuMessage::WindowClick(w_name), StartMenuMessage::ChangeAcknowledge, false
)));
}
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
}
},
StartMenuMessage::WindowClick(name) => {
@@ -169,15 +167,15 @@ impl StartMenu {
},
StartMenuMessage::Back => {
self.add_category_components();
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
StartMenuMessage::ChangeAcknowledge => {
//
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
}
} else {
//maybe should be JustRerender?
//maybe should be JustRedraw?
WindowMessageResponse::DoNothing
}
}
@@ -188,7 +186,7 @@ impl StartMenu {
for c in 0..CATEGORIES.len() {
let name = CATEGORIES[c];
self.components.push(Box::new(HighlightButton::new(
name.to_string(), [42, self.y_each * c + 1], [self.dimensions[0] - 42 - 1, self.y_each], name, StartMenuMessage::CategoryClick(name), StartMenuMessage::ChangeAcknowledge, c == 0
name.to_string(), [42, self.y_each * c + 1], [self.dimensions[0] - 42 - 1, self.y_each], name.to_string(), StartMenuMessage::CategoryClick(name), StartMenuMessage::ChangeAcknowledge, c == 0
)));
}
}
@@ -198,7 +196,3 @@ impl StartMenu {
}
}
pub fn main() {
listen(StartMenu::new());
}

View File

@@ -7,7 +7,7 @@ use crate::messages::{ WindowMessage, WindowMessageResponse, WindowManagerReques
use crate::framebuffer::Dimensions;
use crate::themes::ThemeInfo;
use crate::components::Component;
use crate::components::toggle_button::{ ToggleButton, ToggleButtonAlignment };
use crate::components::toggle_button::ToggleButton;
const PADDING: usize = 4;
const META_WIDTH: usize = 175; //of the window button
@@ -33,9 +33,9 @@ impl WindowLike for Taskbar {
WindowMessage::Init(dimensions) => {
self.dimensions = dimensions;
self.components = vec![
Box::new(ToggleButton::new("start-button".to_string(), [PADDING, PADDING], [44, self.dimensions[1] - (PADDING * 2)], "Start".to_string(), TaskbarMessage::ShowStartMenu, TaskbarMessage::HideStartMenu, false, Some(ToggleButtonAlignment::Left))),
Box::new(ToggleButton::new("start-button".to_string(), [PADDING, PADDING], [44, self.dimensions[1] - (PADDING * 2)], "Start".to_string(), TaskbarMessage::ShowStartMenu, TaskbarMessage::HideStartMenu, false)),
];
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
WindowMessage::Shortcut(shortcut) => {
match shortcut {
@@ -52,7 +52,7 @@ impl WindowLike for Taskbar {
InfoType::WindowsInWorkspace(windows, focused_id) => {
self.windows_in_workspace = windows;
self.focused_id = focused_id;
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
}
_ => WindowMessageResponse::DoNothing,
}
@@ -80,7 +80,7 @@ impl WindowLike for Taskbar {
}
let info = &self.windows_in_workspace[wi];
let name = &info.1;
let mut b = ToggleButton::new(name.to_string() + "-window", [PADDING * 2 + 44 + (META_WIDTH + PADDING) * wi, PADDING], [META_WIDTH, self.dimensions[1] - (PADDING * 2)], name.to_string(), TaskbarMessage::Nothing, TaskbarMessage::Nothing, false, Some(ToggleButtonAlignment::Left));
let mut b = ToggleButton::new(name.to_string() + "-window", [PADDING * 2 + 44 + (META_WIDTH + PADDING) * wi, PADDING], [META_WIDTH, self.dimensions[1] - (PADDING * 2)], name.to_string(), TaskbarMessage::Nothing, TaskbarMessage::Nothing, false);
b.inverted = info.0 == self.focused_id;
instructions.extend(b.draw(theme_info));
}
@@ -119,7 +119,7 @@ impl Taskbar {
_ => WindowMessageResponse::DoNothing,
}
} else {
//maybe should be JustRerender?
//maybe should be JustRedraw?
WindowMessageResponse::DoNothing
}
}

View File

@@ -22,13 +22,13 @@ impl WindowLike for WorkspaceIndicator {
match message {
WindowMessage::Init(dimensions) => {
self.dimensions = dimensions;
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
WindowMessage::Shortcut(shortcut) => {
match shortcut {
ShortcutType::SwitchWorkspace(workspace) => {
self.current_workspace = workspace;
WindowMessageResponse::JustRerender
WindowMessageResponse::JustRedraw
},
_ => WindowMessageResponse::DoNothing,
}

View File

@@ -43,7 +43,7 @@ impl fmt::Debug for WindowManagerRequest{
#[derive(PartialEq, Debug, Serialize, Deserialize)]
pub enum WindowMessageResponse {
Request(WindowManagerRequest),
JustRerender,
JustRedraw,
DoNothing,
}

View File

@@ -24,7 +24,7 @@ impl WindowLike for ProxyWindowLike {
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_or(WindowMessageResponse::JustRerender)
ron::from_str(&output).unwrap_or(WindowMessageResponse::JustRedraw)
}
fn draw(&self, theme_info: &ThemeInfo) -> Vec<DrawInstructions> {

View File

@@ -3,9 +3,9 @@ use std::vec;
use std::collections::{ HashMap, VecDeque };
use std::fmt;
use std::boxed::Box;
use std::sync::{ LazyLock, Mutex };
use std::io::{ stdin, stdout, Write };
use std::process::exit;
use std::cell::RefCell;
use linux_framebuffer::Framebuffer;
use termion::input::TermRead;
@@ -23,24 +23,26 @@ use crate::essential::taskbar::Taskbar;
use crate::essential::lock_screen::LockScreen;
use crate::essential::workspace_indicator::WorkspaceIndicator;
use crate::essential::start_menu::StartMenu;
use crate::logging::log;
use crate::essential::about::About;
use crate::essential::help::Help;
//use crate::logging::log;
pub const TASKBAR_HEIGHT: usize = 38;
pub const INDICATOR_HEIGHT: usize = 20;
const WINDOW_TOP_HEIGHT: usize = 26;
static WRITER: LazyLock<Mutex<FramebufferWriter>> = LazyLock::new(|| Mutex::new(Default::default()));
pub fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) {
let dimensions = [framebuffer_info.width, framebuffer_info.height];
println!("bg: {}x{}", dimensions[0], dimensions[1] - TASKBAR_HEIGHT - INDICATOR_HEIGHT);
WRITER.lock().unwrap().init(framebuffer_info.clone());
let mut writer: FramebufferWriter = Default::default();
let mut wm: WindowManager = WindowManager::new(framebuffer, dimensions);
writer.init(framebuffer_info.clone());
wm.render(None, false);
let mut wm: WindowManager = WindowManager::new(writer, framebuffer, dimensions);
wm.draw(None, false);
let stdin = stdin().lock();
let mut stdout = stdout().into_raw_mode().unwrap();
@@ -52,8 +54,8 @@ pub fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) {
for c in stdin.keys() {
if let Some(kc) = key_to_char(c.unwrap()) {
//do not allow exit when locked unless debugging
//if kc == KeyChar::Alt('e') {
if kc == KeyChar::Alt('e') && !wm.locked {
//if kc == KeyChar::Alt('E') {
if kc == KeyChar::Alt('E') && !wm.locked {
write!(stdout, "{}", cursor::Show).unwrap();
stdout.suspend_raw_mode().unwrap();
exit(0);
@@ -130,6 +132,7 @@ impl fmt::Debug for WindowLikeInfo {
}
pub struct WindowManager {
writer: RefCell<FramebufferWriter>,
id_count: usize,
window_infos: Vec<WindowLikeInfo>,
dimensions: Dimensions,
@@ -144,8 +147,9 @@ pub struct WindowManager {
//1 is up, 2 is down
impl WindowManager {
pub fn new(framebuffer: Framebuffer, dimensions: Dimensions) -> Self {
pub fn new(writer: FramebufferWriter, framebuffer: Framebuffer, dimensions: Dimensions) -> Self {
let mut wm = WindowManager {
writer: RefCell::new(writer),
id_count: 0,
window_infos: Vec::new(),
dimensions,
@@ -186,7 +190,7 @@ impl WindowManager {
self.window_infos.iter().position(|w| w.id == self.focused_id)
}
//should return an iterator but fuck it!
//used to return Vec<&WindowLikeInfo>, doesn't anymore for good reason
fn get_windows_in_workspace(&self, include_non_window: bool) -> Vec<&WindowLikeInfo> {
self.window_infos.iter().filter(|w| {
match w.workspace {
@@ -303,7 +307,7 @@ impl WindowManager {
//send to taskbar
press_response = self.toggle_start_menu(false);
if press_response != WindowMessageResponse::Request(WindowManagerRequest::CloseStartMenu) {
//only thing that needs to be rerendered is the start menu and taskbar
//only thing that needs to be redrawed is the start menu and taskbar
let start_menu_id = self.id_count + 1;
let taskbar_id = self.window_infos.iter().find(|w| w.window_like.subtype() == WindowLikeType::Taskbar).unwrap().id;
redraw_ids = Some(vec![start_menu_id, taskbar_id]);
@@ -354,7 +358,7 @@ impl WindowManager {
}
}
if changed {
press_response = WindowMessageResponse::JustRerender;
press_response = WindowMessageResponse::JustRedraw;
//avoid drawing everything under the moving window, much more efficient
use_saved_buffer = true;
redraw_ids = Some(vec![self.focused_id]);
@@ -375,7 +379,7 @@ impl WindowManager {
self.focused_id = self.window_infos[indicator_index].id;
self.window_infos[indicator_index].window_like.handle_message(WindowMessage::Shortcut(ShortcutType::SwitchWorkspace(self.current_workspace)));
self.taskbar_update_windows();
press_response = WindowMessageResponse::JustRerender;
press_response = WindowMessageResponse::JustRedraw;
}
},
&ShortcutType::MoveWindowToWorkspace(workspace) => {
@@ -384,7 +388,7 @@ impl WindowManager {
if self.window_infos[focused_index].window_like.subtype() == WindowLikeType::Window {
self.window_infos[focused_index].workspace = Workspace::Workspace(workspace);
self.taskbar_update_windows();
press_response = WindowMessageResponse::JustRerender;
press_response = WindowMessageResponse::JustRedraw;
}
}
}
@@ -411,7 +415,7 @@ impl WindowManager {
//elevate it to the top
self.move_index_to_top(new_focus_index);
self.taskbar_update_windows();
press_response = WindowMessageResponse::JustRerender;
press_response = WindowMessageResponse::JustRedraw;
break;
} else if new_focus_index == current_index {
break; //did a full loop, found no windows
@@ -423,7 +427,7 @@ impl WindowManager {
if self.window_infos[focused_index].window_like.subtype() == WindowLikeType::Window {
self.window_infos.remove(focused_index);
self.taskbar_update_windows();
press_response = WindowMessageResponse::JustRerender;
press_response = WindowMessageResponse::JustRedraw;
}
}
},
@@ -432,7 +436,7 @@ impl WindowManager {
let window_dimensions = &self.window_infos[focused_index].dimensions;
self.window_infos[focused_index].top_left = [self.dimensions[0] / 2 - window_dimensions[0] / 2, self.dimensions[1] / 2 - window_dimensions[1] / 2];
use_saved_buffer = true;
press_response = WindowMessageResponse::JustRerender;
press_response = WindowMessageResponse::JustRedraw;
}
},
&ShortcutType::FullscreenWindow => {
@@ -451,7 +455,7 @@ impl WindowManager {
new_dimensions = self.window_infos[focused_index].dimensions;
}
self.window_infos[focused_index].window_like.handle_message(WindowMessage::ChangeDimensions([new_dimensions[0], new_dimensions[1] - WINDOW_TOP_HEIGHT]));
press_response = WindowMessageResponse::JustRerender;
press_response = WindowMessageResponse::JustRedraw;
}
}
},
@@ -465,7 +469,7 @@ impl WindowManager {
let new_dimensions = [self.dimensions[0] / 2, self.dimensions[1] - INDICATOR_HEIGHT - TASKBAR_HEIGHT];
self.window_infos[focused_index].dimensions = new_dimensions;
self.window_infos[focused_index].window_like.handle_message(WindowMessage::ChangeDimensions([new_dimensions[0], new_dimensions[1] - WINDOW_TOP_HEIGHT]));
press_response = WindowMessageResponse::JustRerender;
press_response = WindowMessageResponse::JustRedraw;
}
}
},
@@ -503,10 +507,10 @@ impl WindowManager {
key: c,
})
});
//at most, only the focused window needs to be rerendered
//at most, only the focused window needs to be redrawed
redraw_ids = Some(vec![self.window_infos[focused_index].id]);
//requests can result in window openings and closings, etc
if press_response != WindowMessageResponse::JustRerender {
if press_response != WindowMessageResponse::JustRedraw {
redraw_ids = None;
}
}
@@ -521,7 +525,7 @@ impl WindowManager {
WindowMessageResponse::Request(request) => self.handle_request(request),
_ => {},
};
self.render(redraw_ids, use_saved_buffer);
self.draw(redraw_ids, use_saved_buffer);
}
}
@@ -541,6 +545,8 @@ impl WindowManager {
"Audio Player" => Some(Box::new(ProxyWindowLike::new_rust("audio_player"))),
"File Explorer" => Some(Box::new(ProxyWindowLike::new_rust("file_explorer"))),
"StartMenu" => Some(Box::new(StartMenu::new())),
"About" => Some(Box::new(About::new())),
"Help" => Some(Box::new(Help::new())),
_ => None,
};
if w.is_none() {
@@ -581,7 +587,6 @@ impl WindowManager {
},
WindowManagerRequest::ClipboardCopy(content) => {
self.clipboard = Some(content);
log(&format!("{:?}", self.clipboard));
},
};
}
@@ -591,18 +596,18 @@ impl WindowManager {
}
//another issue with a huge vector of draw instructions; it takes up heap memory
pub fn render(&mut self, maybe_redraw_ids: Option<Vec<usize>>, use_saved_buffer: bool) {
pub fn draw(&mut self, maybe_redraw_ids: Option<Vec<usize>>, use_saved_buffer: bool) {
let theme_info = get_theme_info(&self.theme).unwrap();
//use in conjunction with redraw ids, so a window moving can work without redrawing everything,
//can just redraw the saved state + window
if use_saved_buffer {
WRITER.lock().unwrap().write_saved_buffer_to_raw();
self.writer.borrow_mut().write_saved_buffer_to_raw();
}
//get windows to redraw
let redraw_ids = maybe_redraw_ids.unwrap_or(Vec::new());
let redraw_windows = self.get_windows_in_workspace(true);
let maybe_length = redraw_windows.len();
let redraw_windows = redraw_windows.iter().filter(|w| {
let all_in_workspace = self.get_windows_in_workspace(true);
let maybe_length = all_in_workspace.len();
let redraw_windows = all_in_workspace.iter().filter(|w| {
//basically, maybe_redraw_ids was None
if redraw_ids.len() > 0 {
redraw_ids.contains(&w.id)
@@ -614,7 +619,6 @@ impl WindowManager {
let max_index = if redraw_ids.len() > 0 { redraw_ids.len() } else { maybe_length } - 1;
let mut w_index = 0;
for window_info in redraw_windows {
//unsafe { SERIAL1.lock().write_text(&format!("{:?}\n", &window_info.window_like.subtype())); }
let window_dimensions = if window_info.fullscreen {
[self.dimensions[0], self.dimensions[1] - TASKBAR_HEIGHT - INDICATOR_HEIGHT]
} else {
@@ -625,7 +629,7 @@ impl WindowManager {
if is_window {
//if this is the top most window to draw, snapshot
if w_index == max_index && !use_saved_buffer && redraw_ids.len() == 0 {
WRITER.lock().unwrap().save_buffer();
self.writer.borrow_mut().save_buffer();
}
//offset top left by the window top height for windows (because windows can't draw in that region)
instructions = instructions.iter().map(|instruction| {
@@ -654,7 +658,7 @@ impl WindowManager {
DrawInstructions::Rect([1, window_dimensions[1] - 1], [window_dimensions[0] - 1, 1], theme_info.border_right_bottom),
]);
}
let mut framebuffer_info = WRITER.lock().unwrap().get_info();
let mut framebuffer_info = self.writer.borrow().get_info();
let bytes_per_pixel = framebuffer_info.bytes_per_pixel;
let window_width = window_dimensions[0];
let window_height = window_dimensions[1];
@@ -690,9 +694,9 @@ impl WindowManager {
},
}
}
WRITER.lock().unwrap().draw_buffer(window_info.top_left, window_dimensions[1], window_dimensions[0] * bytes_per_pixel, &window_writer.get_buffer());
self.writer.borrow_mut().draw_buffer(window_info.top_left, window_dimensions[1], window_dimensions[0] * bytes_per_pixel, &window_writer.get_buffer());
w_index += 1;
}
self.framebuffer.write_frame(WRITER.lock().unwrap().get_buffer());
self.framebuffer.write_frame(self.writer.borrow().get_buffer());
}
}