beta 1: remove framebuffer crate, replace with own code, small fixes/features added

added some kanji, docs
This commit is contained in:
stjet
2025-03-13 06:48:53 +00:00
parent 3b5c37520e
commit 8c0b85ae9f
75 changed files with 408 additions and 64 deletions

View File

@@ -1,6 +1,6 @@
[package]
name = "ming-wm"
version = "0.1.0"
version = "1.0.0-beta.0"
repository = "https://github.com/stjet/ming-wm"
license = "GPL-3.0-or-later"
edition = "2021"
@@ -9,22 +9,23 @@ default-run = "ming"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[workspace]
members = ["linux"]
[build-dependencies]
bmp-rust = "0.4.1"
bmp-rust = "0.5.0"
blake2 = { version = "0.10.6", default-features = false }
[dependencies]
ming-wm-lib = { path = "ming-wm-lib" }
blake2 = { version = "0.10.6", default-features = false }
linux_framebuffer = { package = "framebuffer", version = "0.3.1" }
linux = { path = "linux" }
termion = { version = "4.0.3", optional = true }
rodio = { version = "0.19.0", optional = true }
rand = { version = "0.9.0", default-features = false, features = [ "small_rng" ], optional = true }
id3 = { version = "1.10.0", optional = true }
mp4ameta = { version = "0.11.0", optional = true }
metaflac = { version = "0.2.5", optional = true }
bmp-rust = "0.4.1"
bmp-rust = "0.5.0"
pty-process = { version = "0.5.1", optional = true }
[features]

View File

@@ -45,6 +45,8 @@ ming
Type in the password to unlock. Open the start menu by doing `Alt+s`, and use the `j` and `k` keys to move up and down (like Vim), and press the `Enter` key to select a category / open a window.
Usage for most of the included windows and window-likes are included in `docs/window-likes`, which can also be accessed from the "Help" entry in the start menu.
## 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.
@@ -67,17 +69,11 @@ ming touch rotate
See [/docs/philosophy.md](/docs/philosophy.md) for some hopefully interesting ramblings.
## Documentation
### Developing Windows
[section incomplete]
## Developing Windows
Windows (may be called apps in other window managers) can be developed in any language, though it is easiest to do so in Rust because the `ming-wm-lib` crate can be used.
### Window Usage
Usage for most of the included windows and window-likes are included in `docs/window-likes`, which can also be accessed from the "Help" entry in the start menu.
See [koxinga](https://github.com/stjet/koxinga) or `src/bin` for examples. The `docs` directory includes a [brief introduction to writing windows](docs/system/writing_windows.md). [incomplete] documentation on the architecture of ming-wm.
## Security

BIN
bmps/nimbus-roman/é0.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 B

BIN
bmps/nimbus-romono/é0.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 494 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

View File

@@ -18,7 +18,7 @@ fn font_chars_to_alphas(dir: &str) {
if file_name[1] == "bmp" {
if !path.is_dir() {
let mut ch: Vec<Vec<String>> = Vec::new();
let b = BMP::new_from_file(&path.clone().into_os_string().into_string().unwrap());
let b = BMP::new_from_file(&path.clone().into_os_string().into_string().unwrap()).unwrap();
let dib_header = b.get_dib_header().unwrap();
let width = dib_header.width as usize;
let height = dib_header.height as usize;
@@ -64,5 +64,7 @@ fn main() {
}
//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();
Command::new("cp").arg("-r").arg("./bmps").arg(format!("./target/{}/ming_bmps", profile)).output().unwrap();
//also copy the docs folder
Command::new("cp").arg("-r").arg("./docs").arg(format!("./target/{}/ming_docs", profile)).output().unwrap();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 KiB

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 914 KiB

After

Width:  |  Height:  |  Size: 948 KiB

View File

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

View File

@@ -0,0 +1,108 @@
Though some windows (windows = apps, in ming-wm terminology) are built-in to the ming binary (eg, the help and about windows), most windows are separate binaries. This makes it possible for windows to be written in any language (though it will be far easier to write on in Rust because of the `ming-wm-lib` crate), and ensures that errors / crashes in the windows don't result in the window manager also crashing.
Some good examples are in `src/bin`. Another good example is the [Koxinga web browser](https://github.com/stjet/koxinga). All of these are written in Rust, though examples of windows written in other languages will be created later.
## Window Binary Discovery
The window manager automatically searches for window binaries and adds them to the start menu if:
- The binary is in the same directory as the ming binary.
- The binary file name is in the form `ming<Category>_<App_Name>`. Where `<Category>` is a category name in the start menu. For example, the Terminal binary is named `mingUtils_Terminal`.
## Inter-Process Communication (IPC)
Since the windows and the window manager are separate binaries, they need some way to communicate. This is achieved by the window manager spawning the window binary with piped stdout and piped stdin. See `src/proxy_window_like.rs` and `ming-wm-lib/src/ipc.rs` for more information.
The serialization format is in `ming-wm-lib/src/serialize.rs`. Make sure any newlines (`\n`) in strings are removed before/after serializations. When doing IPC, the window manager assumes the response to a query is one line, so if a newline is present, it will fail to parse the response.
## Hello, World!
A minimal example using `ming-wm-lib`.
`Cargo.toml`:
```toml
[package]
name = "example"
version = "0.1.0"
edition = "2024"
[[bin]]
name = "mingMisc_Example"
path = "src/main.rs"
[dependencies]
ming-wm-lib = "0.1.3"
```
`src/main.rs`:
```rust
use std::vec::Vec;
use std::vec;
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse };
use ming_wm_lib::framebuffer_types::Dimensions;
use ming_wm_lib::themes::ThemeInfo;
use ming_wm_lib::ipc::listen;
struct Example {
//
}
impl WindowLike for Example {
fn handle_message(&mut self, message: WindowMessage) -> WindowMessageResponse {
match message {
//placeholder
_ => WindowMessageResponse::DoNothing,
}
}
fn draw(&self, theme_info: &ThemeInfo) -> Vec<DrawInstructions> {
vec![DrawInstructions::Text([2, 2], vec!["nimbus-roman".to_string()], "Hello, World!".to_string(), theme_info.text, theme_info.background, None, None)]
}
fn title(&self) -> String {
"Example".to_string()
}
fn subtype(&self) -> WindowLikeType {
WindowLikeType::Window
}
fn ideal_dimensions(&self, _dimensions: Dimensions) -> Dimensions {
[410, 410]
}
fn resizable(&self) -> bool {
true
}
}
impl Example {
fn new() -> Self {
//doesn't do anything atm
Self {}
}
}
fn main() {
listen(Example::new());
}
```
To install:
```bash
cargo build --release
mv target/release/mingMisc_Example /usr/bin/mingMisc_Example #or whatever directory the ming binary is in
```
![The example Hello World window!](/docs/images/window_example.png)
## Tips
- For windows that are separate binaries, the Elm Architecture obviously cannot be enforced (unless the window is written in Rust and uses the `ming-wm-lib`. However, the design of the IPC and the nature of the window manager being keyboard-driven makes it so using the Elm Architecture is highly recommended.
- Since the window manager currently queries and reads the responses to/from window binaries in the main thread, while the response is being waited for, the window manager is "frozen". Therefore, time-consuming tasks (>1 second) should not be done in the main thread, but rather a separate thread. For example, the ming-wm audio player (`src/bin/audio_player.rs`) does the time-consuming process of reading audio files in a separate thread to not hold up the window manager, and provide quick responses.

View File

@@ -0,0 +1,33 @@
A text editor. Specifically, a subset of a vim.
## Usage
It is probably best to read a Vim tutorial for the basics. All supportd keystrokes should *mostly* behave the same as in Vim.
### Supported in Command-line Mode
- `e[dit]`
- `t[abe]`, `[tab]n`, `[tab]p`
- `q[uit]`
- `w[rite]`
### Supported in Normal Mode
- `:`
- `i`
- `A`
- `r`
- `dd`
- `dw`
- `G`
- `gg`
- `<number>gg`
- `f<char>`
- `F<char>`
- `x`
- `h`, `j`, `k`, `l`
- `0`, `^`, `$`
### Malvim Specific
In Command-line Mode, `autoindent` can be done to toggle auto-indenting (when making new line in Insert Mode [ie, by hitting Enter/Return], space indentation of the new line will be the same as the space indentation of the current line).

View File

@@ -0,0 +1,9 @@
See [Wikipedia](https://en.wikipedia.org/wiki/Reversi) for the rules of the game.
## Usage
Each square has a two-digit number on it. Type that number to place a piece on that square. If only one digit is typed, the backspace key can be hit to clear that digit. Black goes first.
Once the game is over, the winner (if it is not a tie) will be announced, and any key can be hit to play again.
There is currently no computer opponent, so this is a pass-and-play game.

View File

@@ -1,4 +1,4 @@
A basic, line-buffered, modal terminal.
A basic, modal terminal.
## Usage
@@ -17,7 +17,7 @@ In STDIN mode, any keys typed followed by the 'enter' key will send those keys t
### 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.
To get sudo to read from stdin, the `-S` option will need to be used (eg, `sudo -S ls`). Then switch to STDIN mode, type in the password and hit enter.
## Copy / Paste

View File

@@ -1,5 +1,8 @@
#!/bin/sh
cp -r ./target/release/bmps /usr/local/bin/bmps
rm -rf /usr/local/bin/ming_bmps
cp -r ./bmps /usr/local/bin/ming_bmps
rm -rf /usr/local/bin/ming_docs
cp -r ./docs /usr/local/bin/ming_docs
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

8
linux/Cargo.toml Normal file
View File

@@ -0,0 +1,8 @@
[package]
name = "linux"
version = "0.1.0"
license = "GPL-3.0-or-later"
edition = "2021"
[dependencies]
libc = "0.2"

146
linux/src/fb.rs Normal file
View File

@@ -0,0 +1,146 @@
use std::fs::{ File, OpenOptions };
use std::os::fd::AsRawFd;
use std::ptr;
use libc::{ ioctl, mmap };
//https://stackoverflow.com/a/75402838
//https://github.com/torvalds/linux/blob/master/include/uapi/linux/fb.h
pub const FBIOGET_VSCREENINFO: u64 = 0x4600;
pub const FBIOGET_FSCREENINFO: u64 = 0x4602;
//https://www.kernel.org/doc/html/latest/fb/api.html
#[derive(Default)]
#[repr(C)]
pub struct FB_BITFIELD {
offset: u32,
length: u32,
msb_right: u32,
}
#[derive(Default)]
#[repr(C)]
pub struct FB_VAR_SCREENINFO {
pub xres: u32,
pub yres: u32,
pub xres_virtual: u32,
pub yres_virtual: u32,
pub xoffset: u32,
pub yoffset: u32,
pub bits_per_pixel: u32,
pub grayscale: u32,
pub red: FB_BITFIELD,
pub green: FB_BITFIELD,
pub blue: FB_BITFIELD,
pub transp: FB_BITFIELD,
pub nonstd: u32,
pub activate: u32,
pub height: u32,
pub width: u32,
pub accel_flags: u32,
pub pixclock: u32,
pub left_margin: u32,
pub right_margin: u32,
pub upper_margin: u32,
pub lower_margin: u32,
pub hsync_len: u32,
pub wsync_len: u32,
pub sync: u32,
pub vmode: u32,
pub rotate: u32,
pub colorspace: u32,
pub reserved: [u32; 4],
}
#[derive(Default)]
#[repr(C)]
pub struct FB_FIX_SCREENINFO {
pub id: [u8; 16],
pub smem_start: usize,
pub smem_len: u32,
pub r#type: u32,
pub type_aux: u32,
pub visual: u32,
pub xpanstep: u16,
pub ypanstep: u16,
pub ywrapstep: u16,
pub line_length: u32,
pub mmio_len: u32,
pub accel: u32,
pub capabilities: u16,
pub reserved: [u16; 2],
}
pub struct Framebuffer {
pointer: *mut libc::c_void,
pub var_screen_info: FB_VAR_SCREENINFO,
pub fix_screen_info: FB_FIX_SCREENINFO,
size: usize,
}
impl Framebuffer {
pub fn open(path: &str) -> Result<Self, ()> {
let file = Framebuffer::open_file(path)?;
let vi = Framebuffer::get_vscreeninfo(file.as_raw_fd())?;
let fi = Framebuffer::get_fscreeninfo(file.as_raw_fd())?;
//then mmap or something
let size = vi.yres_virtual * fi.line_length * (vi.bits_per_pixel / 8);
let pointer = unsafe {
mmap(ptr::null_mut(), size.try_into().unwrap(), libc::PROT_READ | libc::PROT_WRITE, libc::MAP_SHARED, file.as_raw_fd(), 0)
};
if pointer == libc::MAP_FAILED {
return Err(());
}
Ok(Self {
pointer,
var_screen_info: vi,
fix_screen_info: fi,
size: size as usize,
})
}
fn open_file(path: &str) -> Result<File, ()> {
OpenOptions::new().read(true).write(true).open(path).map_err(|_| ())
}
fn get_vscreeninfo(raw_fd: i32) -> Result<FB_VAR_SCREENINFO, ()> {
let mut vi: FB_VAR_SCREENINFO = Default::default();
let result = unsafe {
ioctl(raw_fd, FBIOGET_VSCREENINFO, &mut vi)
};
if result != -1 {
Ok(vi)
} else {
Err(())
}
}
fn get_fscreeninfo(raw_fd: i32) -> Result<FB_FIX_SCREENINFO, ()> {
let mut fi: FB_FIX_SCREENINFO = Default::default();
let result = unsafe {
ioctl(raw_fd, FBIOGET_FSCREENINFO, &mut fi)
};
if result != -1 {
Ok(fi)
} else {
Err(())
}
}
pub fn write_frame(&mut self, frame: &[u8]) {
unsafe {
ptr::copy_nonoverlapping(frame.as_ptr(), self.pointer as *mut u8, frame.len());
};
}
}
impl Drop for Framebuffer {
fn drop(&mut self) {
unsafe {
libc::munmap(self.pointer, self.size);
}
}
}

2
linux/src/lib.rs Normal file
View File

@@ -0,0 +1,2 @@
pub mod fb;

View File

@@ -1,5 +1,8 @@
#!/bin/sh
cp -r ./target/release/bmps ~/.local/bin/bmps
rm -rf ~/.local/bin/ming_bmps
cp -r ./bmps ~/.local/bin/ming_bmps
rm -rf ~/.local/bin/ming_docs
cp -r ./docs ~/.local/bin/ming_docs
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

View File

@@ -215,6 +215,8 @@ impl AudioPlayer {
}
}
queue
} else if parts[1].ends_with(".mp3") {
vec![concat_paths(&self.base_directory, parts[1]).unwrap()]
} else {
get_all_files(PathBuf::from(new_path))
};

View File

@@ -138,6 +138,10 @@ impl WindowLike for FileExplorer {
let bytes_len = metadata.len();
instructions.push(DrawInstructions::Text([5, start_y], vec!["nimbus-roman".to_string()], format!("Size: {} mb ({} b)", bytes_len / (1024_u64).pow(2), bytes_len), theme_info.text, theme_info.background, None, None));
start_y += HEIGHT;
if let Ok(elapsed) = metadata.modified().unwrap().elapsed() {
//properly format months, days, hours, minutes, secs instead
instructions.push(DrawInstructions::Text([5, start_y], vec!["nimbus-roman".to_string()], format!("Last Modified: {} minutes ago", elapsed.as_secs() / 60), theme_info.text, theme_info.background, None, None));
}
//todo: other stuff
//
}

View File

@@ -6,7 +6,7 @@ use std::io::{ stdin, stdout, BufReader, BufRead, Write };
use std::process::exit;
use std::env;
use linux_framebuffer::Framebuffer;
use linux::fb::Framebuffer;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
use termion::{ clear, cursor };
@@ -162,7 +162,7 @@ fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) {
}
fn main() {
let fb = Framebuffer::new("/dev/fb0").unwrap();
let fb = Framebuffer::open("/dev/fb0").unwrap();
let bytes_per_pixel = (fb.var_screen_info.bits_per_pixel as usize) / 8;
let fb_info = FramebufferInfo {
byte_len: (fb.var_screen_info.yres_virtual * fb.fix_screen_info.line_length) as usize,

View File

@@ -213,9 +213,9 @@ impl WindowLike for Malvim {
} else {
//how does this work again? no idea
if old_pos != 0 {
let found_index = current_file.content[current_file.line_pos].chars().rev().skip(current_length - old_pos + 1).position(|c| c == key_press.key);
let found_index = current_file.content[current_file.line_pos].chars().rev().skip(current_length - old_pos).position(|c| c == key_press.key);
if let Some(found_index) = found_index {
old_pos - found_index - 2
old_pos - found_index - 1
} else {
old_pos
}

View File

@@ -3,7 +3,7 @@ use std::vec;
use std::sync::mpsc::{ channel, Receiver, Sender };
use std::thread;
use std::process::{ Child, Stdio };
use std::io::{ BufReader, BufRead, Write };
use std::io::{ Read, Write };
use std::time::Duration;
use std::path::PathBuf;
use std::fmt;
@@ -79,7 +79,8 @@ pub struct Terminal {
current_stdin_input: String,
current_path: String,
running_process: Option<Child>,
pty_outerr_rx: Option<Receiver<String>>,
process_current_line: Vec<u8>, //bytes of line
pty_outerr_rx: Option<Receiver<u8>>,
pty_in_tx: Option<Sender<String>>,
last_command: Option<String>,
}
@@ -126,8 +127,17 @@ impl WindowLike for Terminal {
//update
let mut changed = false;
loop {
if let Ok(line) = self.pty_outerr_rx.as_mut().unwrap().recv_timeout(Duration::from_millis(5)) {
self.lines.push(strip_ansi_escape_codes(line));
if let Ok(ci) = self.pty_outerr_rx.as_mut().unwrap().recv_timeout(Duration::from_millis(5)) {
if char::from(ci) == '\n' {
let pcl_len = self.process_current_line.len();
self.lines.push(strip_ansi_escape_codes(String::from_utf8(self.process_current_line.clone()).unwrap_or("?".repeat(pcl_len))));
self.process_current_line = Vec::new();
} else if char::from(ci) == '\r' {
//for now, ignore
//
} else {
self.process_current_line.push(ci);
}
changed = true;
} else {
break;
@@ -138,6 +148,7 @@ impl WindowLike for Terminal {
//process exited
self.pty_outerr_rx = None;
self.mode = Mode::Input;
self.process_current_line = Vec::new();
changed = true;
} else {
if key_press.key == 'i' {
@@ -160,7 +171,10 @@ impl WindowLike for Terminal {
//enter
let _ = self.pty_in_tx.as_mut().unwrap().send(self.current_stdin_input.clone());
self.mode = Mode::Running;
let pcl_len = self.process_current_line.len();
self.lines.push(strip_ansi_escape_codes(String::from_utf8(self.process_current_line.clone()).unwrap_or("?".repeat(pcl_len))) + &self.current_stdin_input);
self.current_stdin_input = String::new();
self.process_current_line = Vec::new();
} else if key_press.key == '𐘁' {
//backspace
if self.current_stdin_input.len() > 0 {
@@ -285,11 +299,9 @@ impl Terminal {
self.running_process = Some(blocking::Command::new("sh").arg("-c").arg(&self.current_input).current_dir(&self.current_path).stdin(Stdio::piped()).spawn(pts).unwrap());
let (tx1, rx1) = channel();
thread::spawn(move || {
let reader = BufReader::new(pty);
for line in reader.lines() {
//panics too much
if let Ok(line) = line {
tx1.send(line.to_string()).unwrap();
for ci in pty.bytes() {
if let Ok(ci) = ci {
tx1.send(ci).unwrap();
} else {
//the process has exited. dead process = dead pty = os input/output error
break;
@@ -310,6 +322,7 @@ impl Terminal {
});
self.pty_outerr_rx = Some(rx1);
self.pty_in_tx = Some(tx2);
self.process_current_line = Vec::new();
Mode::Running
}
}
@@ -317,18 +330,20 @@ impl Terminal {
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;
let end = if self.mode != Mode::Running {
self.lines.len()
let lines_len = self.lines.len();
let end = if self.mode != Mode::Running || self.process_current_line.len() > 0 {
lines_len
} else {
self.lines.len() - 1
lines_len - 1
};
for line_num in 0..=end {
let mut working_line = if line_num == self.lines.len() {
let mut working_line = if line_num >= lines_len {
if self.mode == Mode::Input {
//must_add_current_line will be false
"$ ".to_string() + &self.current_input + ""
} else {
//Mode::Stdin
self.current_stdin_input.clone() + ""
let pcl_len = self.process_current_line.len();
strip_ansi_escape_codes(String::from_utf8(self.process_current_line.clone()).unwrap_or("?".repeat(pcl_len))) + &self.current_stdin_input.clone() + ""
}
} else {
self.lines[line_num].clone()

View File

@@ -6,6 +6,7 @@ use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLik
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse };
use ming_wm_lib::framebuffer_types::Dimensions;
use ming_wm_lib::themes::ThemeInfo;
use ming_wm_lib::dirs::exe_dir;
use crate::components::Component;
use crate::components::paragraph::Paragraph;
@@ -19,7 +20,7 @@ impl WindowLike for About {
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()), ())));
self.components.push(Box::new(Paragraph::new("help".to_string(), [2, 2], [self.dimensions[0] - 4, self.dimensions[1] - 4], read_to_string(exe_dir(Some("ming_docs/system/README.md"))).unwrap_or("ming_docs/system/README.md not found".to_string()), ())));
WindowMessageResponse::JustRedraw
},
WindowMessage::KeyPress(key_press) => {

View File

@@ -5,6 +5,7 @@ use std::path::PathBuf;
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType };
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse };
use ming_wm_lib::dirs::exe_dir;
use ming_wm_lib::framebuffer_types::Dimensions;
use ming_wm_lib::themes::ThemeInfo;
use crate::components::paragraph::Paragraph;
@@ -40,8 +41,12 @@ impl WindowLike for Help {
self.file_index += 1;
}
}
self.paragraph.as_mut().unwrap().new_text(read_to_string(self.files[self.file_index].clone()).unwrap());
WindowMessageResponse::JustRedraw
if self.files.len() > 0 {
self.paragraph.as_mut().unwrap().new_text(read_to_string(self.files[self.file_index].clone()).unwrap());
WindowMessageResponse::JustRedraw
} else {
WindowMessageResponse::DoNothing
}
} else if self.paragraph.as_mut().unwrap().handle_message(WindowMessage::KeyPress(key_press)).is_some() {
WindowMessageResponse::JustRedraw
} else {
@@ -53,14 +58,17 @@ impl WindowLike for Help {
}
fn draw(&self, theme_info: &ThemeInfo) -> Vec<DrawInstructions> {
let mut instructions = vec![DrawInstructions::Text([2, 2], vec!["nimbus-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)];
let mut instructions = Vec::new();
if self.files.len() > 0 {
instructions.push(DrawInstructions::Text([2, 2], vec!["nimbus-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()
"Help".to_string()
}
fn subtype(&self) -> WindowLikeType {
@@ -74,9 +82,12 @@ impl WindowLike for Help {
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());
let mut files = Vec::new();
if let Ok(contents) = read_dir(exe_dir(Some("ming_docs/window-likes"))) {
files.push(exe_dir(Some("ming_docs/system/shortcuts.md")));
for entry in contents {
files.push(entry.unwrap().path());
}
}
Self {
dimensions: [0, 0],

View File

@@ -11,7 +11,7 @@ use crate::fs::{ ExeWindowInfos, get_all_executable_windows };
use crate::components::Component;
use crate::components::highlight_button::HighlightButton;
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", "Lock"];
#[derive(Clone)]
enum StartMenuMessage {
@@ -109,7 +109,7 @@ impl WindowLike for StartMenu {
//background
DrawInstructions::Rect([0, 1], [self.dimensions[0] - 1, self.dimensions[1] - 1], theme_info.background),
//mingde logo
DrawInstructions::Bmp([2, 2], exe_dir(Some("bmps/mingde.bmp")).to_string_lossy().to_string(), false),
DrawInstructions::Bmp([2, 2], exe_dir(Some("ming_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:
DrawInstructions::Gradient([2, 42], [40, self.dimensions[1] - 30], [255, 201, 14], [225, 219, 77], 15),
];
@@ -138,7 +138,7 @@ impl StartMenu {
if let Some(message) = message {
match message {
StartMenuMessage::CategoryClick(name) => {
if name == "Logout" {
if name == "Lock" {
WindowMessageResponse::Request(WindowManagerRequest::Lock)
} else if name == "About" || name == "Help" {
//todo above: also do the same for Help

View File

@@ -249,17 +249,19 @@ impl FramebufferWriter {
//reverse is workaround for when my bmp lib returns rgba instead of bgra
pub fn draw_bmp(&mut self, top_left: Point, path: String, reverse: bool) {
let b = BMP::new_from_file(&path);
let dib_header = b.get_dib_header().unwrap();
let pixel_data = b.get_pixel_data().unwrap();
let height = dib_header.height as usize;
let width = dib_header.width as usize;
let mut start_pos;
for row in 0..height {
start_pos = ((top_left[1] + row) * self.info.stride + top_left[0]) * self.info.bytes_per_pixel;
for column in 0..width {
let color = b.get_color_of_pixel_efficient(column, row, &dib_header, &pixel_data).unwrap();
self._draw_pixel(start_pos, if reverse { [color[2], color[1], color[0]] } else { [color[0], color[1], color[2]] });
start_pos += self.info.bytes_per_pixel;
if let Ok(b) = b {
let dib_header = b.get_dib_header().unwrap();
let pixel_data = b.get_pixel_data().unwrap();
let height = dib_header.height as usize;
let width = dib_header.width as usize;
let mut start_pos;
for row in 0..height {
start_pos = ((top_left[1] + row) * self.info.stride + top_left[0]) * self.info.bytes_per_pixel;
for column in 0..width {
let color = b.get_color_of_pixel_efficient(column, row, &dib_header, &pixel_data).unwrap();
self._draw_pixel(start_pos, if reverse { [color[2], color[1], color[0]] } else { [color[0], color[1], color[2]] });
start_pos += self.info.bytes_per_pixel;
}
}
}
}

View File

@@ -24,12 +24,12 @@ 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) {
for font in fonts {
let p = dirs::exe_dir(Some(&("bmps/".to_string() + &font))).to_string_lossy().to_string();
let p = dirs::exe_dir(Some(&("ming_bmps/".to_string() + &font))).to_string_lossy().to_string();
if let Some(font_char) = get_font_char(&p, c) {
return font_char;
}
}
let p = dirs::exe_dir(Some(&("bmps/".to_string() + &fonts[0]))).to_string_lossy().to_string();
let p = dirs::exe_dir(Some(&("ming_bmps/".to_string() + &fonts[0]))).to_string_lossy().to_string();
//so a ? char must be in every font
get_font_char(&p, '?').unwrap()
}

View File

@@ -7,8 +7,7 @@ use std::cell::RefCell;
use std::fs::File;
use std::io::Read;
use linux_framebuffer::Framebuffer;
use linux::fb::Framebuffer;
use ming_wm_lib::framebuffer_types::{ Point, Dimensions };
use ming_wm_lib::themes::{ Themes, get_theme_info };
use ming_wm_lib::utils::{ min, point_inside };