dynamically find window binaries instead of hardcode
also, terminal compile fix whoops
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,5 +2,6 @@ target/
|
|||||||
Cargo.lock
|
Cargo.lock
|
||||||
ming-wm
|
ming-wm
|
||||||
password.txt
|
password.txt
|
||||||
|
password.env
|
||||||
bmps/ignore-*.bmp
|
bmps/ignore-*.bmp
|
||||||
*.alpha
|
*.alpha
|
||||||
|
|||||||
27
Cargo.toml
27
Cargo.toml
@@ -2,7 +2,7 @@
|
|||||||
name = "ming-wm"
|
name = "ming-wm"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
default-run = "main"
|
default-run = "ming"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@@ -32,13 +32,32 @@ audio_player = [ "id3", "mp4ameta", "metaflac", "rand", "rodio" ]
|
|||||||
lto = true
|
lto = true
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "main"
|
name = "ming"
|
||||||
|
path = "src/bin/main.rs"
|
||||||
required-features = [ "main" ]
|
required-features = [ "main" ]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "audio_player"
|
name = "mingFiles_Audio_Player"
|
||||||
|
path = "src/bin/audio_player.rs"
|
||||||
required-features = [ "audio_player" ]
|
required-features = [ "audio_player" ]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "terminal"
|
name = "mingGames_Minesweeper"
|
||||||
|
path = "src/bin/minesweeper.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "mingUtils_Terminal"
|
||||||
|
path = "src/bin/terminal.rs"
|
||||||
required-features = [ "terminal" ]
|
required-features = [ "terminal" ]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "mingFiles_File_Explorer"
|
||||||
|
path = "src/bin/file_explorer.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "mingEditing_Malvim"
|
||||||
|
path = "src/bin/malvim.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "mingGames_Reversi"
|
||||||
|
path = "src/bin/reversi.rs"
|
||||||
|
|||||||
41
README.md
41
README.md
@@ -3,22 +3,37 @@ Ming-wm is a keyboard-based, retro-themed window manager for Linux. It is single
|
|||||||

|

|
||||||

|

|
||||||
|
|
||||||
## Running
|
## Building
|
||||||
|
|
||||||
Create a `password.txt` file in the same directory as `build.rs`, otherwise the default password will be "incorrect mule lightbulb niche".
|
Create a `password.env` file in the same directory as `build.rs`, otherwise the default password will be "incorrect mule lightbulb niche".
|
||||||
|
|
||||||
For best performance:
|
For best performance:
|
||||||
|
|
||||||
```
|
```
|
||||||
cargo build --release --all-features
|
cargo build --release --all-features
|
||||||
# Either,
|
|
||||||
./target/release/main
|
|
||||||
# or
|
|
||||||
cargo run --release
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The user may need to be added to the `video` group.
|
||||||
|
|
||||||
Exclude `--all-features` if the audio player window is not needed. To compile and use the audio player window, ALSA dev packages need to be installed (`alsa-lib-dev` on Alpine, `libasound2-dev` on Debian, `alsa-lib-devl` on Fedora, already included with `alsa-lib` on Arch).
|
Exclude `--all-features` if the audio player window is not needed. To compile and use the audio player window, ALSA dev packages need to be installed (`alsa-lib-dev` on Alpine, `libasound2-dev` on Debian, `alsa-lib-devl` on Fedora, already included with `alsa-lib` on Arch).
|
||||||
|
|
||||||
### Running on Mobile Linux
|
## Installing
|
||||||
|
|
||||||
|
After building, to properly install ming-wm, run the following to put the necessary binaries, font data, and bmp files into `/usr/local/bin`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x ./install
|
||||||
|
sudo ./install
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, to move the binaries to `~/.local/bin` (which probably needs to be added to `PATH`, run the following:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x local-install
|
||||||
|
sudo ./local-install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running on Mobile Linux
|
||||||
|
|
||||||
Running with an onscreen keyboard. The framebuffer may not be redrawn to the screen without a (real) key press. The volume down button seems to work.
|
Running with an onscreen keyboard. The framebuffer may not be redrawn to the screen without a (real) key press. The volume down button seems to work.
|
||||||
|
|
||||||
@@ -26,22 +41,30 @@ Running with an onscreen keyboard. The framebuffer may not be redrawn to the scr
|
|||||||
|
|
||||||
```
|
```
|
||||||
cargo build --release
|
cargo build --release
|
||||||
./target/release/main touch
|
./target/release/ming touch
|
||||||
```
|
```
|
||||||
|
|
||||||
Optionally, in landscape mode (todo: osk may be broken in landscape mode):
|
Optionally, in landscape mode (todo: osk may be broken in landscape mode):
|
||||||
|
|
||||||
```
|
```
|
||||||
cargo build --release
|
cargo build --release
|
||||||
./target/release/main touch rotate
|
./target/release/ming touch rotate
|
||||||
```
|
```
|
||||||
|
|
||||||
|
After testing, the install scripts in the previous section can be used.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Philosophy
|
## Philosophy
|
||||||
|
|
||||||
See [/docs/philosophy.md](/docs/philosophy.md) for some hopefully interesting ramblings.
|
See [/docs/philosophy.md](/docs/philosophy.md) for some hopefully interesting ramblings.
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
Make sure the permissions of `password.env` are so other users cannot read or write to it. If there is no plan to recompile, just delete it.
|
||||||
|
|
||||||
|
Obviously, don't run the executable with `sudo` or `doas`, or as the root user!
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Licensed under the GPLv3. The font data in the `bmps/shippori-mincho` folder are covered by the OFL. The font was created by FONTDASU. The font data in `bmps/nimbus-roman` are licensed under the AGPL. This is a very slightly modified version of the font was created by URW Studio. The font data in `bmps/nimbus-romono` is also licensed under the AGPL. This is a slightly modified version of the Nimbus Roman font by URW Studio.
|
Licensed under the GPLv3. The font data in the `bmps/shippori-mincho` folder are covered by the OFL. The font was created by FONTDASU. The font data in `bmps/nimbus-roman` are licensed under the AGPL. This is a very slightly modified version of the font was created by URW Studio. The font data in `bmps/nimbus-romono` is also licensed under the AGPL. This is a slightly modified version of the Nimbus Roman font by URW Studio.
|
||||||
|
|||||||
6
build.rs
6
build.rs
@@ -2,6 +2,7 @@ use std::fs::{ read_dir, read_to_string, write, File };
|
|||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
use blake2::{ Blake2b512, Digest };
|
use blake2::{ Blake2b512, Digest };
|
||||||
use bmp_rust::bmp::BMP;
|
use bmp_rust::bmp::BMP;
|
||||||
@@ -48,7 +49,7 @@ fn font_chars_to_alphas(dir: &str) {
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
//hash + "salt" password and add to build
|
//hash + "salt" password and add to build
|
||||||
let password = read_to_string("password.txt").unwrap_or("incorrect mule lightbulb niche".to_string()).replace("\n", "") + "salt?sorrycryptographers";
|
let password = read_to_string("password.env").unwrap_or("incorrect mule lightbulb niche".to_string()).replace("\n", "") + "salt?sorrycryptographers";
|
||||||
let mut hasher = Blake2b512::new();
|
let mut hasher = Blake2b512::new();
|
||||||
hasher.update(password.as_bytes());
|
hasher.update(password.as_bytes());
|
||||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
@@ -61,4 +62,7 @@ fn main() {
|
|||||||
font_chars_to_alphas(path.to_str().unwrap());
|
font_chars_to_alphas(path.to_str().unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//copy bmp folders to target
|
||||||
|
let profile = env::var_os("PROFILE").unwrap().to_string_lossy().to_string();
|
||||||
|
Command::new("cp").arg("-r").arg("./bmps").arg(format!("./target/{}/bmps", profile)).output().unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,16 +8,16 @@ In `$XDG_CONFIG_DIR/ming-wm/desktop-background`, you can configure what the desk
|
|||||||
|
|
||||||
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 "#", 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.
|
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. The path should be absolute.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```
|
```
|
||||||
#008080
|
#008080
|
||||||
#003153
|
#003153
|
||||||
r./bmps/castle1440x842.bmp
|
r/home/username/Pictures/castle1440x842.bmp
|
||||||
r./bmps/ming1440x842.bmp
|
r/home/username/Pictures/ming1440x842.bmp
|
||||||
r./bmps/blur1440x842.bmp
|
r/home/username/Pictures/blur1440x842.bmp
|
||||||
```
|
```
|
||||||
|
|
||||||
## Unrelated: Themes Config
|
## Unrelated: Themes Config
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ Once a command is entered, hit 'enter' to execute it. The terminal will change i
|
|||||||
|
|
||||||
In STDIN mode, any keys typed followed by the 'enter' key will send those keys to the command's STDIN, if it is still running. To escape STDIN mode, use the `esc` key.
|
In STDIN mode, any keys typed followed by the 'enter' key will send those keys to the command's STDIN, if it is still running. To escape STDIN mode, use the `esc` key.
|
||||||
|
|
||||||
|
### Sudo
|
||||||
|
|
||||||
|
To get sudo to read from stdin, the `-S` option will need to be used (eg, `sudo -S ls`). Also, the password prompt will not show since the terminal is line-buffered. Just switch to STDIN mode, type in the password and hit enter.
|
||||||
|
|
||||||
## Copy / Paste
|
## Copy / Paste
|
||||||
|
|
||||||
This window-like supports the paste [shortcut](../system/shortcuts.md) (`Alt+P`) if in INPUT or STDIN mode.
|
This window-like supports the paste [shortcut](../system/shortcuts.md) (`Alt+P`) if in INPUT or STDIN mode.
|
||||||
|
|||||||
9
install
Executable file
9
install
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
cp -r ./target/release/bmps /usr/local/bin/bmps
|
||||||
|
cp ./target/release/ming /usr/local/bin/ming
|
||||||
|
cp ./target/release/mingUtils_Terminal /usr/local/bin/mingUtils_Terminal
|
||||||
|
cp ./target/release/mingGames_Reversi /usr/local/bin/mingGames_Reversi
|
||||||
|
cp ./target/release/mingGames_Minesweeper /usr/local/bin/mingGames_Minesweeper
|
||||||
|
cp ./target/release/mingFiles_File_Explorer /usr/local/bin/mingFiles_File_Explorer
|
||||||
|
cp ./target/release/mingFiles_Audio_Player /usr/local/bin/mingFiles_Audio_Player
|
||||||
|
cp ./target/release/mingEditing_Malvim /usr/local/bin/mingEditing_Malvim
|
||||||
9
local-install
Executable file
9
local-install
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
cp -r ./target/release/bmps ~/.local/bin/bmps
|
||||||
|
cp ./target/release/ming ~/.local/bin/ming
|
||||||
|
cp ./target/release/mingUtils_Terminal ~/.local/bin/mingUtils_Terminal
|
||||||
|
cp ./target/release/mingGames_Reversi ~/.local/bin/mingGames_Reversi
|
||||||
|
cp ./target/release/mingGames_Minesweeper ~/.local/bin/mingGames_Minesweeper
|
||||||
|
cp ./target/release/mingFiles_File_Explorer ~/.local/bin/mingFiles_File_Explorer
|
||||||
|
cp ./target/release/mingFiles_Audio_Player ~/.local/bin/mingFiles_Audio_Player
|
||||||
|
cp ./target/release/mingEditing_Malvim ~/.local/bin/mingEditing_Malvim
|
||||||
@@ -242,6 +242,8 @@ impl AudioPlayer {
|
|||||||
if new_path.exists() {
|
if new_path.exists() {
|
||||||
self.base_directory = new_path.to_str().unwrap().to_string();
|
self.base_directory = new_path.to_str().unwrap().to_string();
|
||||||
return "Set new base directory".to_string();
|
return "Set new base directory".to_string();
|
||||||
|
} else {
|
||||||
|
return "Failed to set new base directory".to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use std::fmt;
|
|||||||
use pty_process::blocking;
|
use pty_process::blocking;
|
||||||
|
|
||||||
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, ShortcutType };
|
||||||
use ming_wm::framebuffer::Dimensions;
|
use ming_wm::framebuffer::Dimensions;
|
||||||
use ming_wm::themes::ThemeInfo;
|
use ming_wm::themes::ThemeInfo;
|
||||||
use ming_wm::utils::{ concat_paths, Substring };
|
use ming_wm::utils::{ concat_paths, Substring };
|
||||||
@@ -202,9 +202,9 @@ impl WindowLike for Terminal {
|
|||||||
ShortcutType::ClipboardPaste(copy_string) => {
|
ShortcutType::ClipboardPaste(copy_string) => {
|
||||||
if self.mode == Mode::Input || self.mode == Mode::Stdin {
|
if self.mode == Mode::Input || self.mode == Mode::Stdin {
|
||||||
if self.mode == Mode::Input {
|
if self.mode == Mode::Input {
|
||||||
self.current_input += copy_string;
|
self.current_input += ©_string;
|
||||||
} else {
|
} else {
|
||||||
self.current_stdin_input += copy_string;
|
self.current_stdin_input += ©_string;
|
||||||
}
|
}
|
||||||
self.calc_actual_lines();
|
self.calc_actual_lines();
|
||||||
WindowMessageResponse::JustRedraw
|
WindowMessageResponse::JustRedraw
|
||||||
@@ -224,7 +224,7 @@ impl WindowLike for Terminal {
|
|||||||
DrawInstructions::Rect([0, 0], self.dimensions, theme_info.alt_background),
|
DrawInstructions::Rect([0, 0], self.dimensions, theme_info.alt_background),
|
||||||
];
|
];
|
||||||
//add the visible lines of text
|
//add the visible lines of text
|
||||||
let end_line = self.actual_line_num + self.get_max_lines() - 1;
|
let end_line = self.actual_line_num + self.get_max_lines();
|
||||||
let mut text_y = PADDING;
|
let mut text_y = PADDING;
|
||||||
for line_num in self.actual_line_num..end_line {
|
for line_num in self.actual_line_num..end_line {
|
||||||
if line_num == self.actual_lines.len() {
|
if line_num == self.actual_lines.len() {
|
||||||
@@ -261,7 +261,7 @@ impl Terminal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_max_lines(&self) -> usize {
|
fn get_max_lines(&self) -> usize {
|
||||||
(self.dimensions[1] - PADDING * 2) / LINE_HEIGHT
|
(self.dimensions[1] - PADDING * 2 - LINE_HEIGHT) / LINE_HEIGHT
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_command(&mut self) -> Mode {
|
fn process_command(&mut self) -> Mode {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ pub struct HighlightButton<T> {
|
|||||||
name_: String,
|
name_: String,
|
||||||
top_left: Point,
|
top_left: Point,
|
||||||
size: Dimensions,
|
size: Dimensions,
|
||||||
text: String,
|
pub text: String,
|
||||||
pub highlighted: bool,
|
pub highlighted: bool,
|
||||||
click_return: T,
|
click_return: T,
|
||||||
toggle_highlight_return: T, //also unhighlight return
|
toggle_highlight_return: T, //also unhighlight return
|
||||||
|
|||||||
@@ -38,3 +38,12 @@ pub fn config_dir() -> Option<PathBuf> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn exe_dir(add: Option<&str>) -> PathBuf {
|
||||||
|
let mut exe_dir = env::current_exe().unwrap();
|
||||||
|
exe_dir.pop();
|
||||||
|
if let Some(add) = add {
|
||||||
|
exe_dir.push(add);
|
||||||
|
}
|
||||||
|
exe_dir
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,15 +6,17 @@ use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
|
|||||||
use crate::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest };
|
use crate::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest };
|
||||||
use crate::framebuffer::Dimensions;
|
use crate::framebuffer::Dimensions;
|
||||||
use crate::themes::ThemeInfo;
|
use crate::themes::ThemeInfo;
|
||||||
|
use crate::fs::{ ExeWindowInfos, get_all_executable_windows };
|
||||||
use crate::components::Component;
|
use crate::components::Component;
|
||||||
use crate::components::highlight_button::HighlightButton;
|
use crate::components::highlight_button::HighlightButton;
|
||||||
|
use crate::dirs::exe_dir;
|
||||||
|
|
||||||
static CATEGORIES: [&'static str; 9] = ["About", "Utils", "Games", "Editing", "Files", "Internet", "Misc", "Help", "Logout"];
|
static CATEGORIES: [&'static str; 9] = ["About", "Utils", "Games", "Editing", "Files", "Internet", "Misc", "Help", "Logout"];
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
enum StartMenuMessage {
|
enum StartMenuMessage {
|
||||||
CategoryClick(&'static str),
|
CategoryClick(&'static str),
|
||||||
WindowClick(&'static str),
|
WindowClick(String),
|
||||||
Back,
|
Back,
|
||||||
ChangeAcknowledge,
|
ChangeAcknowledge,
|
||||||
}
|
}
|
||||||
@@ -22,7 +24,8 @@ enum StartMenuMessage {
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct StartMenu {
|
pub struct StartMenu {
|
||||||
dimensions: Dimensions,
|
dimensions: Dimensions,
|
||||||
components: Vec<Box<dyn Component<StartMenuMessage> + Send>>,
|
executable_windows: ExeWindowInfos,
|
||||||
|
components: Vec<Box<HighlightButton<StartMenuMessage>>>,
|
||||||
current_focus: String,
|
current_focus: String,
|
||||||
y_each: usize,
|
y_each: usize,
|
||||||
}
|
}
|
||||||
@@ -34,6 +37,7 @@ impl WindowLike for StartMenu {
|
|||||||
self.dimensions = dimensions;
|
self.dimensions = dimensions;
|
||||||
self.y_each = (self.dimensions[1] - 1) / CATEGORIES.len();
|
self.y_each = (self.dimensions[1] - 1) / CATEGORIES.len();
|
||||||
self.add_category_components();
|
self.add_category_components();
|
||||||
|
self.executable_windows = get_all_executable_windows();
|
||||||
WindowMessageResponse::JustRedraw
|
WindowMessageResponse::JustRedraw
|
||||||
},
|
},
|
||||||
WindowMessage::KeyPress(key_press) => {
|
WindowMessage::KeyPress(key_press) => {
|
||||||
@@ -69,7 +73,7 @@ impl WindowLike for StartMenu {
|
|||||||
let current_focus_index = self.get_focus_index().unwrap();
|
let current_focus_index = self.get_focus_index().unwrap();
|
||||||
if key_press.key.is_lowercase() {
|
if key_press.key.is_lowercase() {
|
||||||
//look forwards to see category/window that starts with that char
|
//look forwards to see category/window that starts with that char
|
||||||
if let Some(n_index) = self.components[current_focus_index..].iter().position(|c| c.name().chars().next().unwrap_or('𐘂').to_lowercase().next().unwrap() == key_press.key) {
|
if let Some(n_index) = self.components[current_focus_index..].iter().position(|c| c.text.chars().next().unwrap_or('𐘂').to_lowercase().next().unwrap() == key_press.key) {
|
||||||
//now old focus, not current focus
|
//now old focus, not current focus
|
||||||
self.components[current_focus_index].handle_message(WindowMessage::Unfocus);
|
self.components[current_focus_index].handle_message(WindowMessage::Unfocus);
|
||||||
self.current_focus = self.components[current_focus_index + n_index].name().to_string();
|
self.current_focus = self.components[current_focus_index + n_index].name().to_string();
|
||||||
@@ -80,7 +84,7 @@ impl WindowLike for StartMenu {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//look backwards to see category/window that starts with that char
|
//look backwards to see category/window that starts with that char
|
||||||
if let Some(n_index) = self.components[..current_focus_index].iter().rev().position(|c| c.name().chars().next().unwrap_or('𐘂').to_uppercase().next().unwrap() == key_press.key) {
|
if let Some(n_index) = self.components[..current_focus_index].iter().rev().position(|c| c.text.chars().next().unwrap_or('𐘂').to_uppercase().next().unwrap() == key_press.key) {
|
||||||
//now old focus, not current focus
|
//now old focus, not current focus
|
||||||
self.components[current_focus_index].handle_message(WindowMessage::Unfocus);
|
self.components[current_focus_index].handle_message(WindowMessage::Unfocus);
|
||||||
self.current_focus = self.components[current_focus_index - n_index - 1].name().to_string();
|
self.current_focus = self.components[current_focus_index - n_index - 1].name().to_string();
|
||||||
@@ -105,7 +109,7 @@ impl WindowLike for StartMenu {
|
|||||||
//background
|
//background
|
||||||
DrawInstructions::Rect([0, 1], [self.dimensions[0] - 1, self.dimensions[1] - 1], theme_info.background),
|
DrawInstructions::Rect([0, 1], [self.dimensions[0] - 1, self.dimensions[1] - 1], theme_info.background),
|
||||||
//mingde logo
|
//mingde logo
|
||||||
DrawInstructions::Bmp([2, 2], "./bmps/mingde.bmp".to_string(), false),
|
DrawInstructions::Bmp([2, 2], exe_dir(Some("bmps/mingde.bmp")).to_string_lossy().to_string(), false),
|
||||||
//I truly don't know why, it should be - 44 but - 30 seems to work better :shrug:
|
//I truly don't know why, it should be - 44 but - 30 seems to work better :shrug:
|
||||||
DrawInstructions::Gradient([2, 42], [40, self.dimensions[1] - 30], [255, 201, 14], [225, 219, 77], 15),
|
DrawInstructions::Gradient([2, 42], [40, self.dimensions[1] - 30], [255, 201, 14], [225, 219, 77], 15),
|
||||||
];
|
];
|
||||||
@@ -147,22 +151,12 @@ impl StartMenu {
|
|||||||
))
|
))
|
||||||
];
|
];
|
||||||
//add window buttons
|
//add window buttons
|
||||||
let mut to_add: Vec<&str> = Vec::new();
|
if let Some(to_add) = self.executable_windows.get(&("ming".to_string() + name)) {
|
||||||
if name == "Games" {
|
for a in 0..to_add.len() {
|
||||||
to_add.extend(["Minesweeper", "Reversi"]);
|
self.components.push(Box::new(HighlightButton::new(
|
||||||
} else if name == "Editing" {
|
to_add[a].1.to_string(), [42, (a + 1) * self.y_each], [self.dimensions[0] - 42 - 1, self.y_each], to_add[a].0.to_string(), StartMenuMessage::WindowClick(to_add[a].1.clone()), StartMenuMessage::ChangeAcknowledge, false
|
||||||
to_add.push("Malvim");
|
)));
|
||||||
} else if name == "Utils" {
|
}
|
||||||
to_add.push("Terminal");
|
|
||||||
} else if name == "Files" {
|
|
||||||
to_add.extend(["File Explorer", "Audio Player"]);
|
|
||||||
}
|
|
||||||
//
|
|
||||||
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.to_string(), StartMenuMessage::WindowClick(w_name), StartMenuMessage::ChangeAcknowledge, false
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
WindowMessageResponse::JustRedraw
|
WindowMessageResponse::JustRedraw
|
||||||
}
|
}
|
||||||
|
|||||||
33
src/fs.rs
33
src/fs.rs
@@ -1,6 +1,10 @@
|
|||||||
use std::fs::{ read_dir, File };
|
use std::fs::{ read_dir, File };
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::dirs;
|
||||||
|
use crate::utils::get_rest_of_split;
|
||||||
|
|
||||||
fn get_font_char(dir: &str, c: char) -> Option<(char, Vec<Vec<u8>>, u8)> {
|
fn get_font_char(dir: &str, c: char) -> Option<(char, Vec<Vec<u8>>, u8)> {
|
||||||
let c = if c == '/' { '𐘋' } else if c == '\\' { '𐚆' } else if c == '.' { '𐘅' } else { c };
|
let c = if c == '/' { '𐘋' } else if c == '\\' { '𐚆' } else if c == '.' { '𐘅' } else { c };
|
||||||
@@ -20,12 +24,14 @@ fn get_font_char(dir: &str, c: char) -> Option<(char, Vec<Vec<u8>>, u8)> {
|
|||||||
|
|
||||||
pub fn get_font_char_from_fonts(fonts: &[String], c: char) -> (char, Vec<Vec<u8>>, u8) {
|
pub fn get_font_char_from_fonts(fonts: &[String], c: char) -> (char, Vec<Vec<u8>>, u8) {
|
||||||
for font in fonts {
|
for font in fonts {
|
||||||
if let Some(font_char) = get_font_char(&("./bmps/".to_string() + font), c) {
|
let p = dirs::exe_dir(Some(&("bmps/".to_string() + &font))).to_string_lossy().to_string();
|
||||||
|
if let Some(font_char) = get_font_char(&p, c) {
|
||||||
return font_char;
|
return font_char;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let p = dirs::exe_dir(Some(&("bmps/".to_string() + &fonts[0]))).to_string_lossy().to_string();
|
||||||
//so a ? char must be in every font
|
//so a ? char must be in every font
|
||||||
get_font_char(&("./bmps/".to_string() + &fonts[0]), '?').unwrap()
|
get_font_char(&p, '?').unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all_files(dir: PathBuf) -> Vec<PathBuf> {
|
pub fn get_all_files(dir: PathBuf) -> Vec<PathBuf> {
|
||||||
@@ -41,3 +47,26 @@ pub fn get_all_files(dir: PathBuf) -> Vec<PathBuf> {
|
|||||||
files
|
files
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Category, Vec<Display name, file name>
|
||||||
|
pub type ExeWindowInfos = HashMap<String, Vec<(String, String)>>;
|
||||||
|
|
||||||
|
//well, doesn't actually look to see if its executable. Just if it contains a _ and has no file extension, and is a file
|
||||||
|
pub fn get_all_executable_windows() -> ExeWindowInfos {
|
||||||
|
let mut exes = HashMap::new();
|
||||||
|
for entry in read_dir(dirs::exe_dir(None)).unwrap() {
|
||||||
|
let pb = entry.unwrap().path();
|
||||||
|
if pb.is_file() && pb.extension().is_none() {
|
||||||
|
let parts = pb.file_stem().unwrap().to_string_lossy().to_string();
|
||||||
|
let mut parts = parts.split('_');
|
||||||
|
let category = parts.next().unwrap();
|
||||||
|
let display = get_rest_of_split(&mut parts, Some(" "));
|
||||||
|
let file_name = pb.file_name().unwrap().to_string_lossy().to_string();
|
||||||
|
if display != String::new() && category.starts_with("ming") {
|
||||||
|
let pair = (display, file_name);
|
||||||
|
exes.entry(category.to_string()).and_modify(|v: &mut Vec<(String, String)>| (*v).push(pair.clone())).or_insert(vec![pair]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exes
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ use std::vec::Vec;
|
|||||||
use std::process::{ Command, Child, Stdio };
|
use std::process::{ Command, Child, Stdio };
|
||||||
use std::io::{ BufReader, BufRead, Write };
|
use std::io::{ BufReader, BufRead, Write };
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
|
use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType };
|
||||||
use crate::messages::{ WindowMessage, WindowMessageResponse };
|
use crate::messages::{ WindowMessage, WindowMessageResponse };
|
||||||
use crate::framebuffer::Dimensions;
|
use crate::framebuffer::Dimensions;
|
||||||
use crate::themes::ThemeInfo;
|
use crate::themes::ThemeInfo;
|
||||||
|
use crate::dirs;
|
||||||
use crate::serialize::{ Serializable, DrawInstructionsVec };
|
use crate::serialize::{ Serializable, DrawInstructionsVec };
|
||||||
|
|
||||||
pub struct ProxyWindowLike {
|
pub struct ProxyWindowLike {
|
||||||
@@ -74,18 +74,10 @@ impl Drop for ProxyWindowLike {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ProxyWindowLike {
|
impl ProxyWindowLike {
|
||||||
pub fn new(command: &mut Command) -> Self {
|
pub fn new(name: &str) -> Self {
|
||||||
|
let loc = dirs::exe_dir(Some(name)).to_string_lossy().to_string();
|
||||||
ProxyWindowLike {
|
ProxyWindowLike {
|
||||||
process: RefCell::new(command.stdout(Stdio::piped()).stdin(Stdio::piped()).stderr(Stdio::null()).spawn().unwrap()),
|
process: RefCell::new(Command::new(loc).stdout(Stdio::piped()).stdin(Stdio::piped()).stderr(Stdio::null()).spawn().unwrap()),
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_rust(file: &str) -> Self {
|
|
||||||
let loc = format!("./target/release/{}", file);
|
|
||||||
if Path::new(&loc).exists() {
|
|
||||||
ProxyWindowLike::new(Command::new(loc).stdout(Stdio::piped()).stdin(Stdio::piped()).stderr(Stdio::null()))
|
|
||||||
} else {
|
|
||||||
ProxyWindowLike::new(Command::new("cargo").arg("run").arg("--quiet").arg("--release").arg("--bin").arg(file).stdout(Stdio::piped()).stdin(Stdio::piped()).stderr(Stdio::null()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use crate::themes::ThemeInfo;
|
|||||||
use crate::messages::{ WindowMessageResponse, WindowManagerRequest, KeyPress, WindowMessage, Direction, ShortcutType, InfoType };
|
use crate::messages::{ WindowMessageResponse, WindowManagerRequest, KeyPress, WindowMessage, Direction, ShortcutType, InfoType };
|
||||||
use crate::window_manager::{ KeyChar, DrawInstructions, WindowLikeType };
|
use crate::window_manager::{ KeyChar, DrawInstructions, WindowLikeType };
|
||||||
use crate::framebuffer::Dimensions;
|
use crate::framebuffer::Dimensions;
|
||||||
|
use crate::utils::get_rest_of_split;
|
||||||
|
|
||||||
//serde + ron but worse! yay
|
//serde + ron but worse! yay
|
||||||
//not same as ron - simplified
|
//not same as ron - simplified
|
||||||
@@ -33,21 +34,6 @@ fn option_to_string<T: Display>(option: &Option<T>) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_rest_of_split(split: &mut dyn Iterator<Item = &str>, sep: Option<&str>) -> String {
|
|
||||||
let mut rest = String::new();
|
|
||||||
let mut n = split.next();
|
|
||||||
loop {
|
|
||||||
rest += &n.unwrap();
|
|
||||||
n = split.next();
|
|
||||||
if n.is_some() {
|
|
||||||
rest += sep.unwrap_or("");
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rest
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_color(serialized: &str) -> Result<[u8; 3], ()> {
|
fn get_color(serialized: &str) -> Result<[u8; 3], ()> {
|
||||||
let rgb = serialized.split("\x1F");
|
let rgb = serialized.split("\x1F");
|
||||||
let mut color = [0; 3];
|
let mut color = [0; 3];
|
||||||
|
|||||||
16
src/utils.rs
16
src/utils.rs
@@ -157,3 +157,19 @@ pub fn point_inside(point: Point, top_left: Point, size: Dimensions) -> bool {
|
|||||||
x >= x2 && y >= y2 && x <= x3 && y <= y3
|
x >= x2 && y >= y2 && x <= x3 && y <= y3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_rest_of_split(split: &mut dyn Iterator<Item = &str>, sep: Option<&str>) -> String {
|
||||||
|
let mut rest = String::new();
|
||||||
|
let mut n = split.next();
|
||||||
|
loop {
|
||||||
|
if n.is_none() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rest += &n.unwrap();
|
||||||
|
n = split.next();
|
||||||
|
if n.is_some() && sep.is_some() {
|
||||||
|
rest += sep.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rest
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -633,16 +633,10 @@ impl WindowManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let w: Option<WindowBox> = match w.as_str() {
|
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"))),
|
|
||||||
"File Explorer" => Some(Box::new(ProxyWindowLike::new_rust("file_explorer"))),
|
|
||||||
"StartMenu" => Some(Box::new(StartMenu::new())),
|
"StartMenu" => Some(Box::new(StartMenu::new())),
|
||||||
"About" => Some(Box::new(About::new())),
|
"About" => Some(Box::new(About::new())),
|
||||||
"Help" => Some(Box::new(Help::new())),
|
"Help" => Some(Box::new(Help::new())),
|
||||||
_ => None,
|
_ => Some(Box::new(ProxyWindowLike::new(&w))),
|
||||||
};
|
};
|
||||||
if w.is_none() {
|
if w.is_none() {
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user