diff --git a/Cargo.toml b/Cargo.toml index 91622b7..68d514d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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] diff --git a/README.md b/README.md index 26e32df..3045183 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/bmps/nimbus-roman/é0.bmp b/bmps/nimbus-roman/é0.bmp new file mode 100644 index 0000000..d3ea6e4 Binary files /dev/null and b/bmps/nimbus-roman/é0.bmp differ diff --git a/bmps/nimbus-romono/é0.bmp b/bmps/nimbus-romono/é0.bmp new file mode 100644 index 0000000..d3ea6e4 Binary files /dev/null and b/bmps/nimbus-romono/é0.bmp differ diff --git a/bmps/nimbus-romono/𐘁0.bmp b/bmps/nimbus-romono/𐘁0.bmp new file mode 100644 index 0000000..0d6158b Binary files /dev/null and b/bmps/nimbus-romono/𐘁0.bmp differ diff --git a/bmps/nimbus-romono/𐘂0.bmp b/bmps/nimbus-romono/𐘂0.bmp new file mode 100644 index 0000000..fc5b5b7 Binary files /dev/null and b/bmps/nimbus-romono/𐘂0.bmp differ diff --git a/bmps/nimbus-romono/𐘃0.bmp b/bmps/nimbus-romono/𐘃0.bmp new file mode 100644 index 0000000..b593476 Binary files /dev/null and b/bmps/nimbus-romono/𐘃0.bmp differ diff --git a/bmps/shippori-mincho/一5.bmp b/bmps/shippori-mincho/一5.bmp new file mode 100644 index 0000000..12600a7 Binary files /dev/null and b/bmps/shippori-mincho/一5.bmp differ diff --git a/bmps/shippori-mincho/上1.bmp b/bmps/shippori-mincho/上1.bmp new file mode 100644 index 0000000..46dc640 Binary files /dev/null and b/bmps/shippori-mincho/上1.bmp differ diff --git a/bmps/shippori-mincho/下0.bmp b/bmps/shippori-mincho/下0.bmp new file mode 100644 index 0000000..3bae7cf Binary files /dev/null and b/bmps/shippori-mincho/下0.bmp differ diff --git a/bmps/shippori-mincho/世0.bmp b/bmps/shippori-mincho/世0.bmp new file mode 100644 index 0000000..95fc593 Binary files /dev/null and b/bmps/shippori-mincho/世0.bmp differ diff --git a/bmps/shippori-mincho/人0.bmp b/bmps/shippori-mincho/人0.bmp new file mode 100644 index 0000000..2f98952 Binary files /dev/null and b/bmps/shippori-mincho/人0.bmp differ diff --git a/bmps/shippori-mincho/僕0.bmp b/bmps/shippori-mincho/僕0.bmp new file mode 100644 index 0000000..94bc472 Binary files /dev/null and b/bmps/shippori-mincho/僕0.bmp differ diff --git a/bmps/shippori-mincho/冬0.bmp b/bmps/shippori-mincho/冬0.bmp new file mode 100644 index 0000000..4b26b7f Binary files /dev/null and b/bmps/shippori-mincho/冬0.bmp differ diff --git a/bmps/shippori-mincho/前0.bmp b/bmps/shippori-mincho/前0.bmp new file mode 100644 index 0000000..53b9054 Binary files /dev/null and b/bmps/shippori-mincho/前0.bmp differ diff --git a/bmps/shippori-mincho/参0.bmp b/bmps/shippori-mincho/参0.bmp new file mode 100644 index 0000000..ddb9823 Binary files /dev/null and b/bmps/shippori-mincho/参0.bmp differ diff --git a/bmps/shippori-mincho/君0.bmp b/bmps/shippori-mincho/君0.bmp new file mode 100644 index 0000000..750a577 Binary files /dev/null and b/bmps/shippori-mincho/君0.bmp differ diff --git a/bmps/shippori-mincho/夏0.bmp b/bmps/shippori-mincho/夏0.bmp new file mode 100644 index 0000000..b971fde Binary files /dev/null and b/bmps/shippori-mincho/夏0.bmp differ diff --git a/bmps/shippori-mincho/夜0.bmp b/bmps/shippori-mincho/夜0.bmp new file mode 100644 index 0000000..39166b0 Binary files /dev/null and b/bmps/shippori-mincho/夜0.bmp differ diff --git a/bmps/shippori-mincho/夢0.bmp b/bmps/shippori-mincho/夢0.bmp new file mode 100644 index 0000000..de52970 Binary files /dev/null and b/bmps/shippori-mincho/夢0.bmp differ diff --git a/bmps/shippori-mincho/心1.bmp b/bmps/shippori-mincho/心1.bmp new file mode 100644 index 0000000..48e0f61 Binary files /dev/null and b/bmps/shippori-mincho/心1.bmp differ diff --git a/bmps/shippori-mincho/愛0.bmp b/bmps/shippori-mincho/愛0.bmp new file mode 100644 index 0000000..cb7a211 Binary files /dev/null and b/bmps/shippori-mincho/愛0.bmp differ diff --git a/bmps/shippori-mincho/日1.bmp b/bmps/shippori-mincho/日1.bmp new file mode 100644 index 0000000..77d9912 Binary files /dev/null and b/bmps/shippori-mincho/日1.bmp differ diff --git a/bmps/shippori-mincho/明0.bmp b/bmps/shippori-mincho/明0.bmp new file mode 100644 index 0000000..8f29277 Binary files /dev/null and b/bmps/shippori-mincho/明0.bmp differ diff --git a/bmps/shippori-mincho/星0.bmp b/bmps/shippori-mincho/星0.bmp new file mode 100644 index 0000000..e73fddc Binary files /dev/null and b/bmps/shippori-mincho/星0.bmp differ diff --git a/bmps/shippori-mincho/春0.bmp b/bmps/shippori-mincho/春0.bmp new file mode 100644 index 0000000..1bf722c Binary files /dev/null and b/bmps/shippori-mincho/春0.bmp differ diff --git a/bmps/shippori-mincho/時0.bmp b/bmps/shippori-mincho/時0.bmp new file mode 100644 index 0000000..7c0bd7f Binary files /dev/null and b/bmps/shippori-mincho/時0.bmp differ diff --git a/bmps/shippori-mincho/晴0.bmp b/bmps/shippori-mincho/晴0.bmp new file mode 100644 index 0000000..9038dca Binary files /dev/null and b/bmps/shippori-mincho/晴0.bmp differ diff --git a/bmps/shippori-mincho/月1.bmp b/bmps/shippori-mincho/月1.bmp new file mode 100644 index 0000000..3a46e98 Binary files /dev/null and b/bmps/shippori-mincho/月1.bmp differ diff --git a/bmps/shippori-mincho/未0.bmp b/bmps/shippori-mincho/未0.bmp new file mode 100644 index 0000000..edb987d Binary files /dev/null and b/bmps/shippori-mincho/未0.bmp differ diff --git a/bmps/shippori-mincho/来0.bmp b/bmps/shippori-mincho/来0.bmp new file mode 100644 index 0000000..211c89e Binary files /dev/null and b/bmps/shippori-mincho/来0.bmp differ diff --git a/bmps/shippori-mincho/機0.bmp b/bmps/shippori-mincho/機0.bmp new file mode 100644 index 0000000..6d74e38 Binary files /dev/null and b/bmps/shippori-mincho/機0.bmp differ diff --git a/bmps/shippori-mincho/水0.bmp b/bmps/shippori-mincho/水0.bmp new file mode 100644 index 0000000..3e5452e Binary files /dev/null and b/bmps/shippori-mincho/水0.bmp differ diff --git a/bmps/shippori-mincho/白0.bmp b/bmps/shippori-mincho/白0.bmp new file mode 100644 index 0000000..c8f2618 Binary files /dev/null and b/bmps/shippori-mincho/白0.bmp differ diff --git a/bmps/shippori-mincho/真0.bmp b/bmps/shippori-mincho/真0.bmp new file mode 100644 index 0000000..4bda9eb Binary files /dev/null and b/bmps/shippori-mincho/真0.bmp differ diff --git a/bmps/shippori-mincho/眠0.bmp b/bmps/shippori-mincho/眠0.bmp new file mode 100644 index 0000000..b667084 Binary files /dev/null and b/bmps/shippori-mincho/眠0.bmp differ diff --git a/bmps/shippori-mincho/神0.bmp b/bmps/shippori-mincho/神0.bmp new file mode 100644 index 0000000..64ea354 Binary files /dev/null and b/bmps/shippori-mincho/神0.bmp differ diff --git a/bmps/shippori-mincho/秋0.bmp b/bmps/shippori-mincho/秋0.bmp new file mode 100644 index 0000000..49da9a9 Binary files /dev/null and b/bmps/shippori-mincho/秋0.bmp differ diff --git a/bmps/shippori-mincho/線0.bmp b/bmps/shippori-mincho/線0.bmp new file mode 100644 index 0000000..9b03e64 Binary files /dev/null and b/bmps/shippori-mincho/線0.bmp differ diff --git a/bmps/shippori-mincho/色0.bmp b/bmps/shippori-mincho/色0.bmp new file mode 100644 index 0000000..90df67b Binary files /dev/null and b/bmps/shippori-mincho/色0.bmp differ diff --git a/bmps/shippori-mincho/花0.bmp b/bmps/shippori-mincho/花0.bmp new file mode 100644 index 0000000..26e99c3 Binary files /dev/null and b/bmps/shippori-mincho/花0.bmp differ diff --git a/bmps/shippori-mincho/行0.bmp b/bmps/shippori-mincho/行0.bmp new file mode 100644 index 0000000..e91cd64 Binary files /dev/null and b/bmps/shippori-mincho/行0.bmp differ diff --git a/bmps/shippori-mincho/赤0.bmp b/bmps/shippori-mincho/赤0.bmp new file mode 100644 index 0000000..8c3b0e0 Binary files /dev/null and b/bmps/shippori-mincho/赤0.bmp differ diff --git a/bmps/shippori-mincho/間0.bmp b/bmps/shippori-mincho/間0.bmp new file mode 100644 index 0000000..dd84239 Binary files /dev/null and b/bmps/shippori-mincho/間0.bmp differ diff --git a/bmps/shippori-mincho/雨0.bmp b/bmps/shippori-mincho/雨0.bmp new file mode 100644 index 0000000..4954f76 Binary files /dev/null and b/bmps/shippori-mincho/雨0.bmp differ diff --git a/bmps/shippori-mincho/電0.bmp b/bmps/shippori-mincho/電0.bmp new file mode 100644 index 0000000..7f96ab0 Binary files /dev/null and b/bmps/shippori-mincho/電0.bmp differ diff --git a/bmps/shippori-mincho/青0.bmp b/bmps/shippori-mincho/青0.bmp new file mode 100644 index 0000000..a5508cc Binary files /dev/null and b/bmps/shippori-mincho/青0.bmp differ diff --git a/bmps/shippori-mincho/音0.bmp b/bmps/shippori-mincho/音0.bmp new file mode 100644 index 0000000..1a1aef6 Binary files /dev/null and b/bmps/shippori-mincho/音0.bmp differ diff --git a/bmps/shippori-mincho/飛0.bmp b/bmps/shippori-mincho/飛0.bmp new file mode 100644 index 0000000..714d0a1 Binary files /dev/null and b/bmps/shippori-mincho/飛0.bmp differ diff --git a/bmps/shippori-mincho/黒0.bmp b/bmps/shippori-mincho/黒0.bmp new file mode 100644 index 0000000..862e3a5 Binary files /dev/null and b/bmps/shippori-mincho/黒0.bmp differ diff --git a/build.rs b/build.rs index d3bd018..ec0677c 100644 --- a/build.rs +++ b/build.rs @@ -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::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(); } diff --git a/docs/images/window_example.png b/docs/images/window_example.png new file mode 100644 index 0000000..288dd31 Binary files /dev/null and b/docs/images/window_example.png differ diff --git a/docs/images/ws1.png b/docs/images/ws1.png index 674036f..ab2868b 100644 Binary files a/docs/images/ws1.png and b/docs/images/ws1.png differ diff --git a/docs/images/ws3.png b/docs/images/ws3.png index fa41082..de4e4ed 100644 Binary files a/docs/images/ws3.png and b/docs/images/ws3.png differ diff --git a/docs/system/ipc.md b/docs/system/ipc.md deleted file mode 100644 index 8337712..0000000 --- a/docs/system/ipc.md +++ /dev/null @@ -1 +0,0 @@ -// diff --git a/docs/system/writing_windows.md b/docs/system/writing_windows.md new file mode 100644 index 0000000..2392fb4 --- /dev/null +++ b/docs/system/writing_windows.md @@ -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_`. Where `` 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 { + 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. + diff --git a/docs/window-likes/malvim.md b/docs/window-likes/malvim.md new file mode 100644 index 0000000..1ff12bc --- /dev/null +++ b/docs/window-likes/malvim.md @@ -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` +- `gg` +- `f` +- `F` +- `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). diff --git a/docs/window-likes/reversi.md b/docs/window-likes/reversi.md new file mode 100644 index 0000000..a4d6cf8 --- /dev/null +++ b/docs/window-likes/reversi.md @@ -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. diff --git a/docs/window-likes/terminal.md b/docs/window-likes/terminal.md index e0c747c..e36c655 100644 --- a/docs/window-likes/terminal.md +++ b/docs/window-likes/terminal.md @@ -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 diff --git a/install b/install index 36339d0..8833815 100755 --- a/install +++ b/install @@ -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 diff --git a/linux/Cargo.toml b/linux/Cargo.toml new file mode 100644 index 0000000..3c7f3de --- /dev/null +++ b/linux/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "linux" +version = "0.1.0" +license = "GPL-3.0-or-later" +edition = "2021" + +[dependencies] +libc = "0.2" diff --git a/linux/src/fb.rs b/linux/src/fb.rs new file mode 100644 index 0000000..c8400d5 --- /dev/null +++ b/linux/src/fb.rs @@ -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 { + 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 { + OpenOptions::new().read(true).write(true).open(path).map_err(|_| ()) + } + + fn get_vscreeninfo(raw_fd: i32) -> Result { + 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 { + 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); + } + } +} + diff --git a/linux/src/lib.rs b/linux/src/lib.rs new file mode 100644 index 0000000..049ad2d --- /dev/null +++ b/linux/src/lib.rs @@ -0,0 +1,2 @@ +pub mod fb; + diff --git a/local-install b/local-install index f44b77e..dbda156 100755 --- a/local-install +++ b/local-install @@ -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 diff --git a/src/bin/audio_player.rs b/src/bin/audio_player.rs index 076a59e..1abb733 100644 --- a/src/bin/audio_player.rs +++ b/src/bin/audio_player.rs @@ -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)) }; diff --git a/src/bin/file_explorer.rs b/src/bin/file_explorer.rs index e279c60..54b1749 100644 --- a/src/bin/file_explorer.rs +++ b/src/bin/file_explorer.rs @@ -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 // } diff --git a/src/bin/main.rs b/src/bin/main.rs index 8156470..9afc259 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -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, diff --git a/src/bin/malvim.rs b/src/bin/malvim.rs index a108a21..0b6ba5c 100644 --- a/src/bin/malvim.rs +++ b/src/bin/malvim.rs @@ -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 } diff --git a/src/bin/terminal.rs b/src/bin/terminal.rs index 1ae0f83..f7eaaa8 100644 --- a/src/bin/terminal.rs +++ b/src/bin/terminal.rs @@ -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, - pty_outerr_rx: Option>, + process_current_line: Vec, //bytes of line + pty_outerr_rx: Option>, pty_in_tx: Option>, last_command: Option, } @@ -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() diff --git a/src/essential/about.rs b/src/essential/about.rs index 336bf20..fd00b48 100644 --- a/src/essential/about.rs +++ b/src/essential/about.rs @@ -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) => { diff --git a/src/essential/help.rs b/src/essential/help.rs index fd845c1..fba8577 100644 --- a/src/essential/help.rs +++ b/src/essential/help.rs @@ -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 { - 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], diff --git a/src/essential/start_menu.rs b/src/essential/start_menu.rs index 2dc0923..339ff07 100644 --- a/src/essential/start_menu.rs +++ b/src/essential/start_menu.rs @@ -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 diff --git a/src/framebuffer.rs b/src/framebuffer.rs index 48e2c29..9a662e8 100644 --- a/src/framebuffer.rs +++ b/src/framebuffer.rs @@ -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; + } } } } diff --git a/src/fs.rs b/src/fs.rs index b5d96ac..d2706fd 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -24,12 +24,12 @@ fn get_font_char(dir: &str, c: char) -> Option<(char, Vec>, u8)> { pub fn get_font_char_from_fonts(fonts: &[String], c: char) -> (char, Vec>, 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() } diff --git a/src/window_manager.rs b/src/window_manager.rs index 3af9717..1bf6ffc 100644 --- a/src/window_manager.rs +++ b/src/window_manager.rs @@ -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 };