diff --git a/Cargo.toml b/Cargo.toml
index 5a2f5ae..1d51c57 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "ming-wm"
-version = "1.0.1"
+version = "1.0.2"
repository = "https://github.com/stjet/ming-wm"
license = "GPL-3.0-or-later"
edition = "2021"
diff --git a/bmps/ming-wm.bmp b/bmps/ming-wm.bmp
new file mode 100644
index 0000000..2ec2773
Binary files /dev/null and b/bmps/ming-wm.bmp differ
diff --git a/bmps/ming-wm.svg b/bmps/ming-wm.svg
new file mode 100644
index 0000000..f0dc962
--- /dev/null
+++ b/bmps/ming-wm.svg
@@ -0,0 +1,73 @@
+
+
+
+
diff --git a/bmps/mingde.bmp b/bmps/mingde.bmp
deleted file mode 100644
index c2986cd..0000000
Binary files a/bmps/mingde.bmp and /dev/null differ
diff --git a/bmps/shippori-mincho/、9.bmp b/bmps/shippori-mincho/、9.bmp
new file mode 100644
index 0000000..0146d1b
Binary files /dev/null and b/bmps/shippori-mincho/、9.bmp differ
diff --git a/bmps/shippori-mincho/ァ5.bmp b/bmps/shippori-mincho/ァ5.bmp
new file mode 100644
index 0000000..19c2d95
Binary files /dev/null and b/bmps/shippori-mincho/ァ5.bmp differ
diff --git a/bmps/shippori-mincho/ガ3.bmp b/bmps/shippori-mincho/ガ3.bmp
new file mode 100644
index 0000000..5817564
Binary files /dev/null and b/bmps/shippori-mincho/ガ3.bmp differ
diff --git a/bmps/shippori-mincho/ギ3.bmp b/bmps/shippori-mincho/ギ3.bmp
new file mode 100644
index 0000000..069ae5f
Binary files /dev/null and b/bmps/shippori-mincho/ギ3.bmp differ
diff --git a/bmps/shippori-mincho/グ3.bmp b/bmps/shippori-mincho/グ3.bmp
new file mode 100644
index 0000000..33fde4d
Binary files /dev/null and b/bmps/shippori-mincho/グ3.bmp differ
diff --git a/bmps/shippori-mincho/ゲ3.bmp b/bmps/shippori-mincho/ゲ3.bmp
new file mode 100644
index 0000000..18e78fb
Binary files /dev/null and b/bmps/shippori-mincho/ゲ3.bmp differ
diff --git a/bmps/shippori-mincho/ゴ1.bmp b/bmps/shippori-mincho/ゴ1.bmp
new file mode 100644
index 0000000..0ee9b9c
Binary files /dev/null and b/bmps/shippori-mincho/ゴ1.bmp differ
diff --git a/bmps/shippori-mincho/ザ2.bmp b/bmps/shippori-mincho/ザ2.bmp
new file mode 100644
index 0000000..4695081
Binary files /dev/null and b/bmps/shippori-mincho/ザ2.bmp differ
diff --git a/bmps/shippori-mincho/ジ3.bmp b/bmps/shippori-mincho/ジ3.bmp
new file mode 100644
index 0000000..f0ee373
Binary files /dev/null and b/bmps/shippori-mincho/ジ3.bmp differ
diff --git a/bmps/shippori-mincho/ズ3.bmp b/bmps/shippori-mincho/ズ3.bmp
new file mode 100644
index 0000000..2b4199b
Binary files /dev/null and b/bmps/shippori-mincho/ズ3.bmp differ
diff --git a/bmps/shippori-mincho/ゼ3.bmp b/bmps/shippori-mincho/ゼ3.bmp
new file mode 100644
index 0000000..6ea3ab2
Binary files /dev/null and b/bmps/shippori-mincho/ゼ3.bmp differ
diff --git a/bmps/shippori-mincho/ゾ3.bmp b/bmps/shippori-mincho/ゾ3.bmp
new file mode 100644
index 0000000..d62f590
Binary files /dev/null and b/bmps/shippori-mincho/ゾ3.bmp differ
diff --git a/bmps/shippori-mincho/ダ3.bmp b/bmps/shippori-mincho/ダ3.bmp
new file mode 100644
index 0000000..f506e5e
Binary files /dev/null and b/bmps/shippori-mincho/ダ3.bmp differ
diff --git a/bmps/shippori-mincho/ヂ3.bmp b/bmps/shippori-mincho/ヂ3.bmp
new file mode 100644
index 0000000..5182391
Binary files /dev/null and b/bmps/shippori-mincho/ヂ3.bmp differ
diff --git a/bmps/shippori-mincho/ッ6.bmp b/bmps/shippori-mincho/ッ6.bmp
new file mode 100644
index 0000000..1742b88
Binary files /dev/null and b/bmps/shippori-mincho/ッ6.bmp differ
diff --git a/bmps/shippori-mincho/ヅ3.bmp b/bmps/shippori-mincho/ヅ3.bmp
new file mode 100644
index 0000000..8626432
Binary files /dev/null and b/bmps/shippori-mincho/ヅ3.bmp differ
diff --git a/bmps/shippori-mincho/デ3.bmp b/bmps/shippori-mincho/デ3.bmp
new file mode 100644
index 0000000..a34ddb8
Binary files /dev/null and b/bmps/shippori-mincho/デ3.bmp differ
diff --git a/bmps/shippori-mincho/ド3.bmp b/bmps/shippori-mincho/ド3.bmp
new file mode 100644
index 0000000..8b4b43d
Binary files /dev/null and b/bmps/shippori-mincho/ド3.bmp differ
diff --git a/bmps/shippori-mincho/バ6.bmp b/bmps/shippori-mincho/バ6.bmp
new file mode 100644
index 0000000..4b8b64e
Binary files /dev/null and b/bmps/shippori-mincho/バ6.bmp differ
diff --git a/bmps/shippori-mincho/パ6.bmp b/bmps/shippori-mincho/パ6.bmp
new file mode 100644
index 0000000..944566d
Binary files /dev/null and b/bmps/shippori-mincho/パ6.bmp differ
diff --git a/bmps/shippori-mincho/ビ5.bmp b/bmps/shippori-mincho/ビ5.bmp
new file mode 100644
index 0000000..61266d7
Binary files /dev/null and b/bmps/shippori-mincho/ビ5.bmp differ
diff --git a/bmps/shippori-mincho/ピ4.bmp b/bmps/shippori-mincho/ピ4.bmp
new file mode 100644
index 0000000..fadb3f0
Binary files /dev/null and b/bmps/shippori-mincho/ピ4.bmp differ
diff --git a/bmps/shippori-mincho/ブ3.bmp b/bmps/shippori-mincho/ブ3.bmp
new file mode 100644
index 0000000..fbb5d7c
Binary files /dev/null and b/bmps/shippori-mincho/ブ3.bmp differ
diff --git a/bmps/shippori-mincho/プ0.bmp b/bmps/shippori-mincho/プ0.bmp
new file mode 100644
index 0000000..ed5ff18
Binary files /dev/null and b/bmps/shippori-mincho/プ0.bmp differ
diff --git a/bmps/shippori-mincho/ボ3.bmp b/bmps/shippori-mincho/ボ3.bmp
new file mode 100644
index 0000000..b0326c0
Binary files /dev/null and b/bmps/shippori-mincho/ボ3.bmp differ
diff --git a/bmps/shippori-mincho/ポ4.bmp b/bmps/shippori-mincho/ポ4.bmp
new file mode 100644
index 0000000..99610bf
Binary files /dev/null and b/bmps/shippori-mincho/ポ4.bmp differ
diff --git a/bmps/shippori-mincho/ャ5.bmp b/bmps/shippori-mincho/ャ5.bmp
new file mode 100644
index 0000000..1583ea8
Binary files /dev/null and b/bmps/shippori-mincho/ャ5.bmp differ
diff --git a/bmps/shippori-mincho/ュ8.bmp b/bmps/shippori-mincho/ュ8.bmp
new file mode 100644
index 0000000..96c6443
Binary files /dev/null and b/bmps/shippori-mincho/ュ8.bmp differ
diff --git a/bmps/shippori-mincho/ョ7.bmp b/bmps/shippori-mincho/ョ7.bmp
new file mode 100644
index 0000000..1f5a480
Binary files /dev/null and b/bmps/shippori-mincho/ョ7.bmp differ
diff --git a/bmps/shippori-mincho/全0.bmp b/bmps/shippori-mincho/全0.bmp
new file mode 100644
index 0000000..a20442c
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..6332eff
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..5695f7e
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..bbb957f
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..1a96f02
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..04ce37a
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..5898a59
Binary files /dev/null and b/bmps/shippori-mincho/度0.bmp differ
diff --git a/bmps/shippori-mincho/y0.bmp b/bmps/shippori-mincho/強0.bmp
similarity index 100%
rename from bmps/shippori-mincho/y0.bmp
rename to bmps/shippori-mincho/強0.bmp
diff --git a/bmps/shippori-mincho/思1.bmp b/bmps/shippori-mincho/思1.bmp
new file mode 100644
index 0000000..2014295
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..a0a70fb
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..ac3373b
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..3b7c653
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..e6ae6e0
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..f00b619
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..86d7adc
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..60c66a6
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..9e37af6
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..a52ef75
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..65a7efa
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..264d383
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..86643fc
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 100755
index 0000000..810e03b
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..7fb592f
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..9993be3
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..d0c0e39
Binary files /dev/null and b/bmps/shippori-mincho/騒0.bmp differ
diff --git a/docs/images/ws3.png b/docs/images/ws3.png
index de4e4ed..5fcc0d1 100644
Binary files a/docs/images/ws3.png and b/docs/images/ws3.png differ
diff --git a/docs/system/keys.md b/docs/system/keys.md
new file mode 100644
index 0000000..29c52ab
--- /dev/null
+++ b/docs/system/keys.md
@@ -0,0 +1,26 @@
+Relevant section taken from `src/bin/main.rs`:
+
+```rust
+fn key_to_char(key: Key) -> Option {
+ match key {
+ Key::Char('\n') => Some(KeyChar::Press('𐘂')),
+ Key::Char(c) => Some(KeyChar::Press(c)),
+ Key::Alt(c) => Some(KeyChar::Alt(c)),
+ Key::Ctrl(c) => Some(KeyChar::Ctrl(c)),
+ Key::Backspace => Some(KeyChar::Press('𐘁')),
+ Key::Esc => Some(KeyChar::Press('𐘃')),
+ Key::Up => Some(KeyChar::Press('𐙘')),
+ Key::Down => Some(KeyChar::Press('𐘞')),
+ Key::Left => Some(KeyChar::Press('𐙣')),
+ Key::Right => Some(KeyChar::Press('𐙥')),
+ _ => None,
+ }
+}
+```
+
+The special keys backspace, enter, escape, and the arrows, are represented by a single Linear A char. For ease, there are [methods](https://docs.rs/ming-wm-lib/latest/ming_wm_lib/messages/struct.KeyPress.html) to check whether a key press is a backspace, enter, etc, without pasting the Linear A into the code.
+
+Although the arrow keys are supported, please try and support the Vim `hjkl` if possible!
+
+The `Press` events are sent as `WindowMessage::KeyPress(KeyPress)`, and the `Ctrl` events are sent as `WindowMessage::CtrlKeyPress(KeyPress)`. Any keys pressed along with the Alt key are not passed to the windows.
+
diff --git a/docs/system/writing_windows.md b/docs/system/writing_windows.md
index 2392fb4..7b874b2 100644
--- a/docs/system/writing_windows.md
+++ b/docs/system/writing_windows.md
@@ -101,8 +101,17 @@ mv target/release/mingMisc_Example /usr/bin/mingMisc_Example #or whatever direct

+## Now what?
+
+Handle various inputs in `handle_message`, and have it mutate the state. Draw the relevant state in `draw`. Not too hard, eh?
+
+Besides looking at the examples (Koxinga, `src/bin`), read the [ming-wm-lib docs](https://docs.rs/ming-wm-lib)!
+
+Also, `docs/system/keys.md` may be useful.
+
## 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.
+- Window panics will be logged to `~/.local/share/ming-wm/logs.txt`.
diff --git a/docs/window-likes/file-explorer.md b/docs/window-likes/file-explorer.md
index 594092f..7c87b36 100644
--- a/docs/window-likes/file-explorer.md
+++ b/docs/window-likes/file-explorer.md
@@ -2,8 +2,8 @@ Barebones file explorer.
## Navigation
-- `j`: Move down
-- `k`: Move up
+- `j` (or down arrow): Move down
+- `k` (or up arrow): Move up
- `i`: See info about current file / directory (press any key to escape info screen)
- `Enter`: Go into directory
diff --git a/docs/window-likes/malvim.md b/docs/window-likes/malvim.md
index 97b5a2e..e52cee0 100644
--- a/docs/window-likes/malvim.md
+++ b/docs/window-likes/malvim.md
@@ -29,8 +29,8 @@ Tab completion is supported for the `` argument.
- `f`
- `F`
- `x`
-- `h`, `j`, `k`, `l`
-- `h`, `j`, `k`, `l`
+- `h` (or left arrow), `j` (or down arrow), `k` (or up arrow), `l` (or right arrow)
+- `h`, `j` (or down arrow), `k` (or up arrow), `l`
- `0`, `^`, `$`
### Malvim Specific
diff --git a/docs/window-likes/start-menu.md b/docs/window-likes/start-menu.md
index 9780264..4ac23a9 100644
--- a/docs/window-likes/start-menu.md
+++ b/docs/window-likes/start-menu.md
@@ -2,9 +2,8 @@ The start menu, which can be opened and closed with the [Alt+s shortcut](../syst
## Navigation
-Use the 'j' and 'k' keys to move down and up respectively. Going down past the last option returns the selection to the first option, and vice versa (it loops).
+Use the 'j' and 'k' keys to move down and up respectively. The up and down arrow keys can also be used. Going down past the last option returns the selection to the first option, and vice versa (it loops).
Hitting the 'enter' key will either open a subfolder containing window-likes, if a category is selected, or the corresponding window-like, if a window-like is selected. It may also exit a subfolder and return to the category selection if "Back" is selected and 'enter' is hit. There is another special case: hitting 'enter' while the "Logout" option is selected will, as the name implies, logout and return to the lock screen (this does not log out of the Linux user!).
For faster navigation, hitting any lower-case letter (besides 'j' and 'k') will move the selection to the next option that starts with that letter (non-looping). For example, when first opening the start menu, hitting 'e' will move the selection to "Editing". Hitting any upper-case letter will move the selection to any previous option that starts with that letter (also non-looping). For example, when initially opening the start menu, the selected option is "About". After hitting 'j' (moving down), hitting 'A' will return the selection to "About".
-
diff --git a/docs/window-likes/terminal.md b/docs/window-likes/terminal.md
index 709a3d3..3a56fbb 100644
--- a/docs/window-likes/terminal.md
+++ b/docs/window-likes/terminal.md
@@ -8,8 +8,8 @@ The terminal starts off in INPUT mode, which allows entering commands to run. If
In INPUT mode, commands can be freely typed. There are a few special control sequences:
-- `ctrl+p`: Equivalent to the up arrow in most terminals. Brings up the previous command in the command history, and so on.
-- `ctrl+n`: Equivalent to the down arrow in most terminals. Either clears the current input if not in a previous command, else brings up the next command in the command history.
+- `ctrl+p` (or up arrow): Equivalent to the up arrow in most terminals. Brings up the previous command in the command history, and so on.
+- `ctrl+n` (or down arrow): Equivalent to the down arrow in most terminals. Either clears the current input if not in a previous command, else brings up the next command in the command history.
Tab completion is also supported, though only for file/directory paths.
diff --git a/install b/install
index 4bb0f57..63b5905 100755
--- a/install
+++ b/install
@@ -1,10 +1,11 @@
#!/bin/sh
rm -rf /usr/local/bin/ming_bmps
-cp -r ./bmps /usr/local/bin/ming_bmps
-rm -rf /usr/local/bin/ming_bmps/nimbus-roman/*.bmp
-rm -rf /usr/local/bin/ming_bmps/nimbus-romano/*.bmp
-rm -rf /usr/local/bin/ming_bmps/shippori-mincho/*.bmp
-rm /usr/local/bin/ming_bmps/*1440x842.bmp
+cp -f ./bmps /usr/local/bin/ming_bmps
+rm -f /usr/local/bin/ming_bmps/nimbus-roman/*.bmp
+rm -f /usr/local/bin/ming_bmps/nimbus-romano/*.bmp
+rm -f /usr/local/bin/ming_bmps/shippori-mincho/*.bmp
+rm -f /usr/local/bin/ming_bmps/*1440x842.bmp
+rm -f /usr/local/bin/ming_bmps/*.svg
rm -rf /usr/local/bin/ming_docs
cp -r ./docs /usr/local/bin/ming_docs
rm -rf /usr/local/bin/ming_docs/images
diff --git a/local-install b/local-install
index f121c34..77de66a 100755
--- a/local-install
+++ b/local-install
@@ -1,10 +1,11 @@
#!/bin/sh
rm -rf ~/.local/bin/ming_bmps
cp -r ./bmps ~/.local/bin/ming_bmps
-rm -rf ~/.local/bin/ming_bmps/nimbus-roman/*.bmp
-rm -rf ~/.local/bin/ming_bmps/nimbus-romano/*.bmp
-rm -rf ~/.local/bin/ming_bmps/shippori-mincho/*.bmp
-rm ~/.local/bin/ming_bmps/*1440x842.bmp
+rm -f ~/.local/bin/ming_bmps/nimbus-roman/*.bmp
+rm -f ~/.local/bin/ming_bmps/nimbus-romano/*.bmp
+rm -f ~/.local/bin/ming_bmps/shippori-mincho/*.bmp
+rm -f ~/.local/bin/ming_bmps/*1440x842.bmp
+rm -f ~/.local/bin/ming_bmps/*.svg
rm -rf ~/.local/bin/ming_docs
cp -r ./docs ~/.local/bin/ming_docs
rm -rf ~/.local/bin/ming_docs/images
diff --git a/ming-wm-lib/Cargo.toml b/ming-wm-lib/Cargo.toml
index 155a3c7..ba016f7 100644
--- a/ming-wm-lib/Cargo.toml
+++ b/ming-wm-lib/Cargo.toml
@@ -1,8 +1,9 @@
[package]
name = "ming-wm-lib"
-version = "0.1.4"
+version = "0.1.5"
repository = "https://github.com/stjet/ming-wm"
description = "library for building windows for ming-wm in rust"
+readme = "README.md"
license = "GPL-3.0-or-later"
edition = "2021"
diff --git a/ming-wm-lib/README.md b/ming-wm-lib/README.md
index 7503e28..5ab185a 100644
--- a/ming-wm-lib/README.md
+++ b/ming-wm-lib/README.md
@@ -1 +1,6 @@
Library for building [ming-wm](https://github.com/stjet/ming-wm) windows in Rust.
+
+Documentation [here](https://docs.rs/ming-wm-lib).
+
+The most likely usage of this crate is to implement the [WindowLike trait](https://docs.rs/ming-wm-lib/latest/ming_wm_lib/window_manager_types/trait.WindowLike.html), and then set up [IPC](https://docs.rs/ming-wm-lib/latest/ming_wm_lib/ipc/fn.listen.html) (which automatically handles serialisation).
+
diff --git a/ming-wm-lib/src/ipc.rs b/ming-wm-lib/src/ipc.rs
index 2a09348..d4b1484 100644
--- a/ming-wm-lib/src/ipc.rs
+++ b/ming-wm-lib/src/ipc.rs
@@ -31,6 +31,7 @@ pub trait WindowLike {
const LOG: bool = false;
+/// Listen and process what the window manager writes to our stdin
pub fn listen(mut window_like: impl WindowLike) {
panic::set_hook(Box::new(|panic_info| {
let (filename, line) = panic_info.location().map(|l| (l.file(), l.line())).unwrap_or(("", 0));
diff --git a/ming-wm-lib/src/logging.rs b/ming-wm-lib/src/logging.rs
index 5a1e98a..8a1a46c 100644
--- a/ming-wm-lib/src/logging.rs
+++ b/ming-wm-lib/src/logging.rs
@@ -3,6 +3,7 @@ use std::io::Write;
use crate::dirs::data_dir;
+/// Writes to `/ming-wm/logs.txt`. Use only for debugging!
pub fn log(message: &str) {
let data = data_dir().unwrap().into_os_string().into_string().unwrap();
let _ = create_dir(format!("{}/ming-wm", data));
diff --git a/ming-wm-lib/src/messages.rs b/ming-wm-lib/src/messages.rs
index d2664a5..b89a0d7 100644
--- a/ming-wm-lib/src/messages.rs
+++ b/ming-wm-lib/src/messages.rs
@@ -4,12 +4,14 @@ use std::vec::Vec;
use crate::framebuffer_types::Dimensions;
use crate::window_manager_types::{ WindowLike, KeyChar };
+/// Window manager internal usage
pub enum WindowManagerMessage {
KeyChar(KeyChar),
Touch(usize, usize),
//
}
+/// Window manager internal usage
pub type WindowBox = Box;
/*
@@ -50,10 +52,50 @@ impl WindowMessageResponse {
}
}
+//struct because may add more fields later (so struct is better for code backward compatibility)
pub struct KeyPress {
pub key: char,
}
+impl KeyPress {
+ pub fn is_enter(&self) -> bool {
+ self.key == '𐘂'
+ }
+
+ pub fn is_backspace(&self) -> bool {
+ self.key == '𐘁'
+ }
+
+ pub fn is_escape(&self) -> bool {
+ self.key == '𐘃'
+ }
+
+ pub fn is_up_arrow(&self) -> bool {
+ self.key == '𐙘'
+ }
+
+ pub fn is_down_arrow(&self) -> bool {
+ self.key == '𐘞'
+ }
+
+ pub fn is_left_arrow(&self) -> bool {
+ self.key == '𐙣'
+ }
+
+ pub fn is_right_arrow(&self) -> bool {
+ self.key == '𐙥'
+ }
+
+ pub fn is_arrow(&self) -> bool {
+ self.is_up_arrow() || self.is_down_arrow() || self.is_left_arrow() || self.is_right_arrow()
+ }
+
+ /// Is not enter, backspace, arrow keys (the Linear A stuff)
+ pub fn is_regular(&self) -> bool {
+ !self.is_enter() && !self.is_backspace() && !self.is_escape() && !self.is_arrow()
+ }
+}
+
#[derive(Clone, Copy, PartialEq)]
pub enum Direction {
Left,
@@ -87,7 +129,7 @@ pub type WindowsVec = Vec<(usize, String)>;
#[non_exhaustive]
pub enum InfoType {
- //let taskbar know what the current windows in the workspace are
+ /// Let taskbar know what the current windows in the workspace are
WindowsInWorkspace(WindowsVec, usize), //Vec<(id, name)>, focused id
//
}
@@ -102,7 +144,8 @@ pub enum WindowMessage {
Unfocus,
FocusClick,
ChangeDimensions(Dimensions),
- Touch(usize, usize), //for onscreen keyboard only
+ /// For onscreen keyboard only
+ Touch(usize, usize),
//
}
diff --git a/ming-wm-lib/src/themes.rs b/ming-wm-lib/src/themes.rs
index ba07e25..28b9976 100644
--- a/ming-wm-lib/src/themes.rs
+++ b/ming-wm-lib/src/themes.rs
@@ -97,6 +97,7 @@ const THEME_INFOS: [(Themes, ThemeInfo); 5] = [
//
];
+/// Window manager internal usage
pub fn get_theme_info(theme: &Themes) -> Option {
for pair in THEME_INFOS {
if &pair.0 == theme {
diff --git a/src/bin/file_explorer.rs b/src/bin/file_explorer.rs
index 83fbdb5..a059cb9 100644
--- a/src/bin/file_explorer.rs
+++ b/src/bin/file_explorer.rs
@@ -62,7 +62,7 @@ impl WindowLike for FileExplorer {
},
WindowMessage::KeyPress(key_press) => {
self.state = State::List;
- if key_press.key == '𐘂' { //the enter key
+ if key_press.is_enter() {
if self.current_dir_contents.len() > 0 {
let selected_entry = &self.current_dir_contents[self.position];
if !selected_entry.is_file {
@@ -74,8 +74,8 @@ impl WindowLike for FileExplorer {
}
}
WindowMessageResponse::DoNothing
- } else if key_press.key == 'j' || key_press.key == 'k' {
- if key_press.key == 'j' {
+ } else if key_press.key == 'j' || key_press.is_down_arrow() || key_press.key == 'k' || key_press.is_up_arrow() {
+ if key_press.key == 'j' || key_press.is_down_arrow() {
//down
if self.position == self.current_dir_contents.len() - 1 {
self.position = 0;
@@ -94,7 +94,7 @@ impl WindowLike for FileExplorer {
let max_height = self.dimensions[1] - HEIGHT;
if self.position > self.top_position {
let current_height = (self.position - self.top_position + 1) * HEIGHT;
- if current_height > self.dimensions[1] {
+ if current_height > max_height {
//somehow this is slightly off sometimes
self.top_position += (current_height - max_height).div_ceil(HEIGHT);
}
@@ -138,7 +138,7 @@ impl WindowLike for FileExplorer {
} else {
name.unwrap()
};
- instructions.push(DrawInstructions::Text([5, start_y], vec!["nimbus-roman".to_string(), "shippori-mincho".to_string()], name, if is_selected { theme_info.top_text } else { theme_info.text }, if is_selected { theme_info.top } else { theme_info.background }, None, None));
+ instructions.push(DrawInstructions::Text([5, start_y + 4], vec!["nimbus-roman".to_string(), "shippori-mincho".to_string()], name, if is_selected { theme_info.top_text } else { theme_info.text }, if is_selected { theme_info.top } else { theme_info.background }, None, None));
start_y += HEIGHT;
i += 1;
}
diff --git a/src/bin/main.rs b/src/bin/main.rs
index 9afc259..74ff82f 100644
--- a/src/bin/main.rs
+++ b/src/bin/main.rs
@@ -9,7 +9,6 @@ use std::env;
use linux::fb::Framebuffer;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
-use termion::{ clear, cursor };
use termion::event::Key;
use ming_wm_lib::window_manager_types::KeyChar;
@@ -17,7 +16,11 @@ use ming_wm_lib::messages::*;
use ming_wm::framebuffer::{ FramebufferWriter, FramebufferInfo };
use ming_wm::window_manager::WindowManager;
-//use Linear A for escape, backspace, enter
+const CLEAR_ALL: &'static str = "\x1b[2J";
+const HIDE_CURSOR: &'static str = "\x1b[?25l";
+const SHOW_CURSOR: &'static str = "\x1b[?25h";
+
+//use Linear A for escape, backspace, enter, arrow keys
//Linear A used only internally in onscreen keyboard: 𐘎 is alt, 𐘧 is switch board, 𐘾 is ctrl
fn key_to_char(key: Key) -> Option {
match key {
@@ -27,6 +30,10 @@ fn key_to_char(key: Key) -> Option {
Key::Ctrl(c) => Some(KeyChar::Ctrl(c)),
Key::Backspace => Some(KeyChar::Press('𐘁')),
Key::Esc => Some(KeyChar::Press('𐘃')),
+ Key::Up => Some(KeyChar::Press('𐙘')),
+ Key::Down => Some(KeyChar::Press('𐘞')),
+ Key::Left => Some(KeyChar::Press('𐙣')),
+ Key::Right => Some(KeyChar::Press('𐙥')),
_ => None,
}
}
@@ -68,9 +75,9 @@ fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) {
let mut stdout = stdout().into_raw_mode().unwrap();
- write!(stdout, "{}", clear::All).unwrap();
+ write!(stdout, "{}", CLEAR_ALL).unwrap();
- write!(stdout, "{}", cursor::Hide).unwrap();
+ write!(stdout, "{}", HIDE_CURSOR).unwrap();
stdout.flush().unwrap();
@@ -147,12 +154,12 @@ fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) {
ThreadMessage::KeyChar(kc) => wm.handle_message(WindowManagerMessage::KeyChar(kc.clone())),
ThreadMessage::Touch(x, y) => wm.handle_message(WindowManagerMessage::Touch(x, y)),
ThreadMessage::Clear => {
- write!(stdout, "{}", clear::All).unwrap();
+ write!(stdout, "{}", CLEAR_ALL).unwrap();
stdout.flush().unwrap();
},
ThreadMessage::Exit => {
if !wm.locked {
- write!(stdout, "{}", cursor::Show).unwrap();
+ write!(stdout, "{}", SHOW_CURSOR).unwrap();
stdout.suspend_raw_mode().unwrap();
exit(0);
}
diff --git a/src/bin/malvim.rs b/src/bin/malvim.rs
index 247df8d..fff1d77 100644
--- a/src/bin/malvim.rs
+++ b/src/bin/malvim.rs
@@ -94,7 +94,7 @@ impl WindowLike for Malvim {
WindowMessage::KeyPress(key_press) => {
let mut changed = true;
let mut new = false;
- if key_press.key == '𐘃' { //escape key
+ if key_press.is_escape() {
self.mode = Mode::Normal;
self.state = State::None;
changed = false;
@@ -113,7 +113,7 @@ impl WindowLike for Malvim {
let current_file = &mut self.files[self.current_file_index];
let current_length = current_file.content[current_file.line_pos].chars().count();
let line = ¤t_file.content[current_file.line_pos];
- if key_press.key == '𐘂' { //the enter key
+ if key_press.is_enter() {
let mut line: Vec = line.chars().collect();
let (left, right) = line.split_at_mut(current_file.cursor_pos);
let left = left.into_iter().map(|c| c.to_string()).collect::>().join("");
@@ -136,7 +136,7 @@ impl WindowLike for Malvim {
current_file.content.insert(current_file.line_pos + 1, " ".repeat(spaces) + &right);
current_file.line_pos += 1;
current_file.cursor_pos = spaces;
- } else if key_press.key == '𐘁' { //backspace
+ } else if key_press.is_backspace() {
if current_length > 0 && current_file.cursor_pos > 0 {
current_file.content[current_file.line_pos] = line.remove(current_file.cursor_pos - 1, 1);
current_file.cursor_pos -= 1;
@@ -150,7 +150,7 @@ impl WindowLike for Malvim {
current_file.cursor_pos = old_previous_line_length;
}
}
- } else {
+ } else if !key_press.is_arrow() { //arrow keys in insert mode is something i cannot support in good conscience
current_file.content[current_file.line_pos] = line.substring(0, current_file.cursor_pos).to_string() + &key_press.key.to_string() + line.substring(current_file.cursor_pos, line.chars().count());
current_file.cursor_pos += 1;
}
@@ -241,11 +241,11 @@ impl WindowLike for Malvim {
current_file.cursor_pos = 0;
}
}
- } else if key_press.key == 'h' {
+ } else if key_press.key == 'h' || key_press.is_left_arrow() {
current_file.cursor_pos = current_file.cursor_pos.checked_sub(self.maybe_num.unwrap_or(1)).unwrap_or(0);
changed = false;
- } else if key_press.key == 'j' || key_press.key == 'k' {
- if key_press.key == 'j' {
+ } else if key_press.key == 'j' || key_press.is_down_arrow() || key_press.key == 'k' || key_press.is_up_arrow() {
+ if key_press.key == 'j' || key_press.is_down_arrow() {
current_file.line_pos += self.maybe_num.unwrap_or(1);
if current_file.line_pos >= current_file.content.len() {
current_file.line_pos = current_file.content.len() - 1;
@@ -256,7 +256,7 @@ impl WindowLike for Malvim {
let new_length = current_file.content[current_file.line_pos].chars().count();
current_file.cursor_pos = calc_new_cursor_pos(current_file.cursor_pos, new_length);
changed = false;
- } else if key_press.key == 'l' {
+ } else if key_press.key == 'l' || key_press.is_right_arrow() {
if current_length > 0 {
current_file.cursor_pos += self.maybe_num.unwrap_or(1);
let line_len = current_file.content[current_file.line_pos].chars().count();
@@ -309,7 +309,7 @@ impl WindowLike for Malvim {
} else if self.mode == Mode::Command {
self.bottom_message = None;
let command = self.command.clone().unwrap_or("".to_string());
- if key_press.key == '𐘂' { //the enter key
+ if key_press.is_enter() {
new = self.process_command();
self.command = None;
self.mode = Mode::Normal;
@@ -330,7 +330,7 @@ impl WindowLike for Malvim {
}
}
}
- } else if key_press.key == '𐘁' { //backspace
+ } else if key_press.is_backspace() {
if command.len() > 0 {
self.command = Some(command[..command.len() - 1].to_string());
}
diff --git a/src/bin/minesweeper.rs b/src/bin/minesweeper.rs
index afc7ab8..541869f 100644
--- a/src/bin/minesweeper.rs
+++ b/src/bin/minesweeper.rs
@@ -54,6 +54,7 @@ impl WindowLike for Minesweeper {
self.random_chars = String::new();
self.state = State::BeforePlaying;
} else {
+ //since must be u8, the Linear A (enter, backspace, etc) stuff won't do anything
if u8::try_from(key_press.key).is_ok() {
self.random_chars.push(key_press.key);
}
diff --git a/src/bin/terminal.rs b/src/bin/terminal.rs
index f279654..97b43cc 100644
--- a/src/bin/terminal.rs
+++ b/src/bin/terminal.rs
@@ -104,13 +104,13 @@ impl WindowLike for Terminal {
WindowMessage::KeyPress(key_press) => {
match self.mode {
Mode::Input => {
- if key_press.key == '𐘁' { //backspace
+ if key_press.is_backspace() {
if self.current_input.len() > 0 {
self.current_input = self.current_input.remove_last();
} else {
return WindowMessageResponse::DoNothing;
}
- } else if key_press.key == '𐘂' { //the enter key
+ } else if key_press.is_enter() {
self.lines.push("$ ".to_string() + &self.current_input);
self.history.push(self.current_input.clone());
self.history_index = None;
@@ -127,6 +127,10 @@ impl WindowLike for Terminal {
return WindowMessageResponse::DoNothing;
}
}
+ } else if key_press.is_up_arrow() {
+ self.prev();
+ } else if key_press.is_down_arrow() {
+ self.next();
} else {
self.current_input += &key_press.key.to_string();
}
@@ -175,19 +179,16 @@ impl WindowLike for Terminal {
}
},
Mode::Stdin => {
- if key_press.key == '𐘃' {
- //esc
+ if key_press.is_escape() {
self.mode = Mode::Running;
- } else if key_press.key == '𐘂' {
- //enter
+ } else if key_press.is_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
+ } else if key_press.is_backspace() {
if self.current_stdin_input.len() > 0 {
self.current_stdin_input = self.current_stdin_input.remove_last();
} else {
@@ -210,24 +211,12 @@ impl WindowLike for Terminal {
} else if self.mode == Mode::Input && (key_press.key == 'p' || key_press.key == 'n') {
//only the last command is saved unlike other terminals. good enough for me
if key_press.key == 'p' && self.history.len() > 0 {
- if let Some(history_index) = self.history_index {
- if history_index > 0 {
- self.history_index = Some(history_index - 1);
- }
- } else {
- self.history_index = Some(self.history.len() - 1);
- }
- self.current_input = self.history[self.history_index.unwrap()].clone();
+
+ self.prev();
self.calc_actual_lines();
WindowMessageResponse::JustRedraw
} else if key_press.key == 'n' {
- if self.history_index.is_none() || self.history_index.unwrap() == self.history.len() - 1 {
- self.history_index = None;
- self.current_input = String::new();
- } else {
- self.history_index = Some(self.history_index.unwrap() + 1);
- self.current_input = self.history[self.history_index.unwrap()].clone();
- }
+ self.next();
self.calc_actual_lines();
WindowMessageResponse::JustRedraw
} else {
@@ -300,6 +289,27 @@ impl Terminal {
Default::default()
}
+ fn prev(&mut self) {
+ if let Some(history_index) = self.history_index {
+ if history_index > 0 {
+ self.history_index = Some(history_index - 1);
+ }
+ } else {
+ self.history_index = Some(self.history.len() - 1);
+ }
+ self.current_input = self.history[self.history_index.unwrap()].clone();
+ }
+
+ fn next(&mut self) {
+ if self.history_index.is_none() || self.history_index.unwrap() == self.history.len() - 1 {
+ self.history_index = None;
+ self.current_input = String::new();
+ } else {
+ self.history_index = Some(self.history_index.unwrap() + 1);
+ self.current_input = self.history[self.history_index.unwrap()].clone();
+ }
+ }
+
fn get_max_lines(&self) -> usize {
(self.dimensions[1] - PADDING * 2 - LINE_HEIGHT) / LINE_HEIGHT
}
diff --git a/src/essential/help.rs b/src/essential/help.rs
index abfb3c3..9e5786c 100644
--- a/src/essential/help.rs
+++ b/src/essential/help.rs
@@ -28,12 +28,12 @@ impl WindowLike for Help {
} else {
String::new()
};
- self.paragraph = Some(Box::new(Paragraph::new("help".to_string(), [2, 22], [self.dimensions[0] - 4, self.dimensions[1] - 24], "Press the 'h' and 'l' keys to read the different help pages".to_string() + &first_content, ())));
+ self.paragraph = Some(Box::new(Paragraph::new("help".to_string(), [2, 22], [self.dimensions[0] - 4, self.dimensions[1] - 24], "Press the 'h' and 'l' keys (or the left and right arrow keys) to read the different help pages".to_string() + &first_content, ())));
WindowMessageResponse::JustRedraw
},
WindowMessage::KeyPress(key_press) => {
- if key_press.key == 'h' || key_press.key == 'l' {
- if key_press.key == 'h' {
+ if key_press.key == 'h' || key_press.is_left_arrow() || key_press.key == 'l' || key_press.is_right_arrow() {
+ if key_press.key == 'h' || key_press.is_left_arrow() {
if self.file_index == 0 {
self.file_index = self.files.len() - 1;
} else {
diff --git a/src/essential/lock_screen.rs b/src/essential/lock_screen.rs
index e63f042..dc42165 100644
--- a/src/essential/lock_screen.rs
+++ b/src/essential/lock_screen.rs
@@ -24,7 +24,7 @@ impl WindowLike for LockScreen {
WindowMessageResponse::JustRedraw
},
WindowMessage::KeyPress(key_press) => {
- if key_press.key == '𐘂' { //the enter key
+ if key_press.is_enter() {
//check password
let mut hasher = Blake2b512::new();
hasher.update((self.input_password.clone() + "salt?sorrycryptographers").as_bytes());
@@ -34,15 +34,17 @@ impl WindowLike for LockScreen {
self.input_password = String::new();
WindowMessageResponse::JustRedraw
}
- } else if key_press.key == '𐘁' { //backspace
+ } else if key_press.is_backspace() {
let p_len = self.input_password.len();
if p_len != 0 {
self.input_password = self.input_password[..p_len - 1].to_string();
}
WindowMessageResponse::JustRedraw
- } else {
+ } else if key_press.is_regular() {
self.input_password += &key_press.key.to_string();
WindowMessageResponse::JustRedraw
+ } else {
+ WindowMessageResponse::DoNothing
}
},
_ => WindowMessageResponse::DoNothing,
diff --git a/src/essential/start_menu.rs b/src/essential/start_menu.rs
index 339ff07..092c0ef 100644
--- a/src/essential/start_menu.rs
+++ b/src/essential/start_menu.rs
@@ -42,15 +42,15 @@ impl WindowLike for StartMenu {
},
WindowMessage::KeyPress(key_press) => {
//up and down
- if key_press.key == 'k' || key_press.key == 'j' {
+ if key_press.key == 'j' || key_press.is_down_arrow() || key_press.key == 'k' || key_press.is_up_arrow() {
let old_focus_index = self.get_focus_index().unwrap();
self.components[old_focus_index].handle_message(WindowMessage::Unfocus);
- let current_focus_index = if key_press.key == 'j' {
- if old_focus_index + 1 == self.components.len() {
- 0
- } else {
- old_focus_index + 1
- }
+ let current_focus_index = if key_press.key == 'j' || key_press.is_down_arrow() {
+ if old_focus_index + 1 == self.components.len() {
+ 0
+ } else {
+ old_focus_index + 1
+ }
} else {
if old_focus_index == 0 {
self.components.len() - 1
@@ -61,7 +61,7 @@ impl WindowLike for StartMenu {
self.current_focus = self.components[current_focus_index].name().to_string();
self.components[current_focus_index].handle_message(WindowMessage::Focus);
WindowMessageResponse::JustRedraw
- } else if key_press.key == '𐘂' { //the enter key
+ } else if key_press.is_enter() {
let focus_index = self.get_focus_index();
if let Some(focus_index) = focus_index {
let r = self.components[focus_index].handle_message(WindowMessage::FocusClick);
@@ -108,13 +108,20 @@ impl WindowLike for StartMenu {
DrawInstructions::Rect([self.dimensions[0] - 1, 0], [1, self.dimensions[1]], theme_info.border_right_bottom),
//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("ming_bmps/mingde.bmp")).to_string_lossy().to_string(), false),
+ //ming-wm logo
+ DrawInstructions::Bmp([2, 2], exe_dir(Some("ming_bmps/ming-wm.bmp")).to_string_lossy().to_string(), true),
//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, 100], 15), //[225, 219, 77]
];
+ let max_per_page = CATEGORIES.len();
+ let current_focus = self.get_focus_index().unwrap();
+ let mut index = 0;
for component in &self.components {
- instructions.extend(component.draw(theme_info));
+ //supports multiple pages of window options per category
+ if (index >= max_per_page && current_focus >= max_per_page) || (index < max_per_page && current_focus < max_per_page) {
+ instructions.extend(component.draw(theme_info));
+ }
+ index += 1;
}
instructions
}
@@ -152,9 +159,13 @@ impl StartMenu {
];
//add window buttons
if let Some(to_add) = self.executable_windows.get(&("ming".to_string() + name)) {
- for a in 0..to_add.len() {
- self.components.push(Box::new(HighlightButton::new(
- 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
+ let max_per_page = CATEGORIES.len();
+ //starts at 1 because of back button
+ for a in 1..=to_add.len() {
+ let ta = &to_add[a - 1];
+ //the modulo is for multiple pages for windows per category
+ self.components.push(Box::new(HighlightButton::new(
+ ta.1.to_string(), [42, (a % max_per_page) * self.y_each], [self.dimensions[0] - 42 - 1, self.y_each], ta.0.to_string(), StartMenuMessage::WindowClick(ta.1.clone()), StartMenuMessage::ChangeAcknowledge, false
)));
}
}