diff --git a/Cargo.toml b/Cargo.toml index fcb18be..51518bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ming-wm" -version = "1.0.3" +version = "1.1.0" repository = "https://github.com/stjet/ming-wm" license = "GPL-3.0-or-later" edition = "2021" @@ -9,29 +9,25 @@ default-run = "ming" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [workspace] -members = ["linux"] +members = [ "wm", "linux" ] [build-dependencies] bmp-rust = "0.5.0" -blake2 = { version = "0.10.6", default-features = false } +bitcoin_hashes = { version = "0.16.0", default-features = false } [dependencies] ming-wm-lib = { path = "ming-wm-lib" } -blake2 = { version = "0.10.6", default-features = false } -linux = { path = "linux" } -termion = { version = "4.0.3", optional = true } +wm = { path = "wm", optional = true } +linux = { path = "linux", optional = true } rodio = { version = "0.19.0", default-features = false, features = [ "flac", "mp3", "symphonia-vorbis", "wav" ], 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.5.0" -pty-process = { version = "0.5.1", optional = true } [features] -default = [ "main", "terminal" ] -main = [ "termion" ] -terminal = [ "pty-process" ] +default = [ "wm", "terminal" ] +terminal = [ "linux" ] audio_player = [ "id3", "mp4ameta", "metaflac", "rand", "rodio" ] [profile.release] @@ -39,8 +35,8 @@ lto = true [[bin]] name = "ming" -path = "src/bin/main.rs" -required-features = [ "main" ] +path = "src/bin/wm.rs" +required-features = [ "wm" ] [[bin]] name = "mingFiles_Audio_Player" diff --git a/README.md b/README.md index 85a1e04..999fa84 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Ming-wm is a keyboard-based, retro-themed window manager for Linux. It is neither for Wayland or the X Window System - it writes directly to the framebuffer. Inspirations include i3, Haiku, SerenityOS, and Windows98, and it is a conceptual successor to the previous [mingde](https://github.com/stjet/mingde) and [ming-os](https://github.com/stjet/ming-os). +Ming-wm is a keyboard-based, retro-themed window manager for Linux. It is neither for Wayland or the X Window System - it writes directly to the framebuffer. Inspirations include i3, Haiku, SerenityOS, and Windows98, and it is a conceptual successor to my previous projects [ming-de](https://github.com/stjet/mingde) and [ming-os](https://github.com/stjet/ming-os). ![example 1](/docs/images/ws1.png) ![example 2](/docs/images/ws3.png) @@ -49,9 +49,9 @@ Usage for most of the included windows and window-likes are included in `docs/wi ## 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. If someone knows why this is the case, and/or how to fix this, please let me know. +More or the less the same, but includes with an onscreen keyboard for touchscreens. -`evtest` needs to be installed. Currently, the input device is assumed to be at `/dev/first-touchscreen`. +Currently, the touchscreen input device is assumed to be at `/dev/input/by-path/first-touchscreen`, but this is easily editable (see `src/bin/wm.rs`). For touchscreen support, the user running `ming` needs to have read permissions for that `/dev/input/` file. ``` ming touch @@ -73,12 +73,18 @@ See [/docs/philosophy.md](/docs/philosophy.md) for some hopefully interesting ra 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. -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), and (incomplete) documentation on the workings of ming-wm. +The `docs` directory includes a [brief introduction to writing windows](docs/system/writing_windows.md), and (incomplete) documentation on the workings of ming-wm. + +See [koxinga](https://github.com/stjet/koxinga) or `src/bin` for examples. + +A (very poorly written, and WIP) window is being written in Lisp Scheme: [ming-flashcards](https://github.com/stjet/ming-flashcards). ## Security Make sure the permissions of `password.env` are so other users cannot read or write to it. If there is no plan to recompile, just delete it. +Understand the implications of adding the user to the `video` group. And if the permissions of a `/dev/input/` file was changed for touchscreen support, understand those implications too. + Obviously, don't run the executable with `sudo` or `doas`, or as the root user! ## License diff --git a/bmps/ming1440x842.bmp b/bmps/ming1440x842.bmp deleted file mode 100644 index 1b4d34f..0000000 Binary files a/bmps/ming1440x842.bmp and /dev/null differ diff --git a/bmps/nimbus-romono/-8.bmp b/bmps/nimbus-romono/-6.bmp similarity index 100% rename from bmps/nimbus-romono/-8.bmp rename to bmps/nimbus-romono/-6.bmp diff --git a/bmps/nimbus-romono/<2.bmp b/bmps/nimbus-romono/<3.bmp similarity index 100% rename from bmps/nimbus-romono/<2.bmp rename to bmps/nimbus-romono/<3.bmp diff --git a/bmps/nimbus-romono/>2.bmp b/bmps/nimbus-romono/>3.bmp similarity index 100% rename from bmps/nimbus-romono/>2.bmp rename to bmps/nimbus-romono/>3.bmp diff --git a/bmps/nimbus-romono/C0.bmp b/bmps/nimbus-romono/C0.bmp index ada45ba..e0fee45 100644 Binary files a/bmps/nimbus-romono/C0.bmp and b/bmps/nimbus-romono/C0.bmp differ diff --git a/bmps/nimbus-romono/D0.bmp b/bmps/nimbus-romono/D0.bmp index c4f25c6..8ca36a0 100644 Binary files a/bmps/nimbus-romono/D0.bmp and b/bmps/nimbus-romono/D0.bmp differ diff --git a/build.rs b/build.rs index 9f598a4..825ba13 100644 --- a/build.rs +++ b/build.rs @@ -4,7 +4,7 @@ use std::env; use std::path::Path; use std::process::Command; -use blake2::{ Blake2b512, Digest }; +use bitcoin_hashes::Sha512; use bmp_rust::bmp::BMP; fn font_chars_to_alphas(dir: &str) { @@ -47,12 +47,11 @@ fn font_chars_to_alphas(dir: &str) { fn main() { //hash + "salt" password and add to build - let password = read_to_string("password.env").unwrap_or("password".to_string()).replace("\n", "") + "salt?sorrycryptographers"; - let mut hasher = Blake2b512::new(); - hasher.update(password.as_bytes()); + let password = read_to_string("password.env").unwrap_or("password".to_string()).replace("\n", "") + "salt?sorrycryptographers!1!"; + let hash = Sha512::hash(password.as_bytes()).to_byte_array(); let out_dir = env::var_os("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("password.rs"); - write(&dest_path, format!("pub const PASSWORD_HASH: [u8; 64] = {:?};", hasher.finalize())).unwrap(); + write(&dest_path, format!("pub const PASSWORD_HASH: [u8; 64] = {:?};", hash)).unwrap(); //process bmps for entry in read_dir("./bmps").unwrap() { let path = entry.unwrap().path(); diff --git a/docs/philosophy.md b/docs/philosophy.md index f7d9d5e..2cdeb74 100644 --- a/docs/philosophy.md +++ b/docs/philosophy.md @@ -69,5 +69,10 @@ More often than not, not relying on dependencies removes unnecessary bloat and c Expect to see more dependencies in Cargo.toml eliminated soon. PS: -1. `rodio` is unlikely to ever be eliminated (simply because audio is *complex*), and it's optional (if the audio player is not wanted) -2.`bmp-rust` is written by me and so isn't technically an external dependency +1. `rodio` is unlikely to ever be eliminated (simply because audio is *complex*), and it's optional (if the audio player is not wanted). The dependency which provides hashing will also not be removed as I am not a cryptographer and not (quite) stupid enough to pretend being one +2. `bmp-rust` is written by me and so isn't technically an external dependency + +> ### Update +> Ignoring the audio player dependencies and `bmp-rust`, the only dependencies not written by me are now `libc` for libc Rust bindings and `bitcoin-hashes` for SHA-512 password hashing (yes, I know SHA-512 is not the greatest password hash function, but it is lightweight, relatively). +> +> Prior dependencies like `termion`, `linux_framebuffer`, `pty-process`, `evtest` have been removed and replaced by a me-written version in the `linux` crate. As of v1.1, the dependency removing goal has been achieved. Huzzah! diff --git a/docs/system/integration.md b/docs/system/integration.md new file mode 100644 index 0000000..e9a2119 --- /dev/null +++ b/docs/system/integration.md @@ -0,0 +1,3 @@ +## Launch on Login + +To launch on user login, simply add `ming` as the last line of `~/.profile`. This should work for most shells. diff --git a/docs/system/writing_windows.md b/docs/system/writing_windows.md index 7b874b2..0735ce5 100644 --- a/docs/system/writing_windows.md +++ b/docs/system/writing_windows.md @@ -15,6 +15,8 @@ Since the windows and the window manager are separate binaries, they need some w 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. +> In the case of `WindowMessage::Request(WindowManagerRequest::ClipboardCopy())`, windows should convert any `\n` into `𐘂` when copying to clipboard and vice versa when pasting, in order to allow for multi-line clipboard contents. + ## Hello, World! A minimal example using `ming-wm-lib`. diff --git a/docs/window-likes/audio-player.md b/docs/window-likes/audio-player.md index e0553ae..ae2bd90 100644 --- a/docs/window-likes/audio-player.md +++ b/docs/window-likes/audio-player.md @@ -14,6 +14,8 @@ Type to write commands, backspace to delete last character, and enter to run com Tab completion is supported for the `` and `` arguments. +The copy shortcut will copy the currently playing song's file name, if there is a currently playing song. + ## Playlists Example playlist file: diff --git a/docs/window-likes/terminal.md b/docs/window-likes/terminal.md index 3a56fbb..d3a7563 100644 --- a/docs/window-likes/terminal.md +++ b/docs/window-likes/terminal.md @@ -25,7 +25,7 @@ To get sudo to read from stdin, the `-S` option will need to be used (eg, `sudo ## Copy / Paste -This window-like supports the paste [shortcut](../system/shortcuts.md) (`Alt+P`) if in INPUT or STDIN mode. +This window-like supports the paste [shortcut](../system/shortcuts.md) (`Alt+P`) if in INPUT or STDIN mode. The copy shortcut will copy the output of the last ran command. ## Notes diff --git a/linux/src/input.rs b/linux/src/input.rs new file mode 100644 index 0000000..1e17910 --- /dev/null +++ b/linux/src/input.rs @@ -0,0 +1,108 @@ +use std::os::fd::RawFd; +use std::mem::size_of; +use std::ffi::CString; + +use libc::{ open, close, read, poll, pollfd, input_event, timeval, __u16, __s32, c_void }; + +//https://stackoverflow.com/questions/15949163/read-from-dev-input#15949311 +//https://www.man7.org/linux/man-pages/man2/poll.2.html + +#[allow(non_camel_case_types)] +#[repr(u16)] +#[derive(Clone, Copy, PartialEq)] +pub enum EventType { + EV_SYN = 0, //event sep + EV_KEY, + EV_REL, + EV_ABS, + //nothing below will probably ever be relevant to ming-wm + EV_MSC, //misc + EV_SW, //switch/toggle + EV_LED, + EV_SND, + EV_REP, + EV_FF, + EV_PWR, + EV_FF_STATUS, + Unknown(__u16), +} + +impl TryFrom<__u16> for EventType { + type Error = (); + + fn try_from(value: __u16) -> Result { + //if the list is any longer should probably somehow make this a macro + let values = [Self::EV_SYN, Self::EV_KEY, Self::EV_REL, Self::EV_ABS, Self::EV_MSC, Self::EV_SW, Self::EV_LED, Self::EV_SND, Self::EV_REP, Self::EV_FF, Self::EV_PWR, Self::EV_FF_STATUS]; + let value = value as usize; + if value >= values.len() { + Err(()) + } else { + Ok(values[value]) + } + } +} + +//we do not care about time. no one cares about time (probably) +pub struct InputEvent { + pub type_: EventType, + pub code: __u16, //depends on EventType + pub value: __s32, +} + +pub struct Input(RawFd); + +impl Input { + pub fn new(input_name: &str) -> Result { + let input_name = CString::new(input_name).unwrap(); + let fd = unsafe { open(input_name.as_ptr(), libc::O_RDONLY | libc::O_NONBLOCK) }; + if fd == -1 { + Err(()) + } else { + Ok(Self(fd)) + } + } +} + +impl Iterator for Input { + type Item = InputEvent; + + fn next(&mut self) -> Option { + //wait until there is something available + let mut fds = vec![pollfd { + fd: self.0, + events: libc::POLLIN, //return when "there is data to read" + revents: 0, + }]; + let poll_result = unsafe { poll(fds.as_mut_ptr(), 1, -1) }; //neg num means no timeout + if poll_result == -1 { + return None; + } + //now read the event + let ie_size = size_of::(); + let mut ie = input_event { + time: timeval { + tv_sec: 0, + tv_usec: 0, + }, + type_: 0, + code: 0, + value: 0, + }; + let read_result = unsafe { read(self.0, &mut ie as *mut _ as *mut c_void, ie_size) }; + if read_result == -1 { + return None; + } + let type_ = ie.type_.try_into().unwrap_or(EventType::Unknown(ie.type_)); + Some(Self::Item { + type_, + code: ie.code, + value: ie.value, + }) + } +} + +impl Drop for Input { + fn drop(&mut self) { + unsafe { close(self.0) }; + } +} diff --git a/linux/src/keys.rs b/linux/src/keys.rs new file mode 100644 index 0000000..5e8fc59 --- /dev/null +++ b/linux/src/keys.rs @@ -0,0 +1,89 @@ +use std::io::{ Read, Stdin }; +use std::sync::mpsc::{ channel, Receiver }; +use std::thread; + +//includes a section on reading keys +//https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html + +const ALPHABET: [char; 26] = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']; + +pub enum Key { + Char(char), + Alt(char), + Ctrl(char), + Backspace, + Esc, + ArrowUp, + ArrowDown, + ArrowLeft, + ArrowRight, + Other(u8), //we don't get about anything else, lmao +} + +pub struct RawStdin { + //bytes: Peekable>>, + receiver: Receiver, +} + +impl RawStdin { + pub fn new(stdin: Stdin) -> Self { + let (sender, receiver) = channel(); + thread::spawn(move || { + let bytes = stdin.lock().bytes(); + for b in bytes { + sender.send(b.unwrap()).unwrap(); + } + }); + RawStdin { + //bytes: stdin.lock().bytes().peekable(), + receiver, + } + } +} + +impl Iterator for RawStdin { + type Item = Key; + + fn next(&mut self) -> Option { + let first = self.receiver.recv().unwrap(); + Some(match first { + 1..=26 => { + //ctrl + if first == 9 { + Key::Char('\t') + } else if first == 13 { + //ctrl+m and enter give the same thing + Key::Char('\n') + } else { + Key::Ctrl(ALPHABET[first as usize - 1]) + } + }, + 27 => { + //escape sequence + //not handling escape sequences that are 3+ bytes is probably going to come back to bite us + let n = self.receiver.try_recv(); + if let Ok(b'[') = n { + let n = self.receiver.recv().unwrap(); + match n { + b'A' => Key::ArrowUp, + b'B' => Key::ArrowDown, + b'C' => Key::ArrowRight, + b'D' => Key::ArrowLeft, + _ => Key::Other(n), + } + } else if n.is_ok() { + //Alt+ sends Esc+ + Key::Alt(char::from(n.unwrap())) + } else { + Key::Esc + } + }, + 127 => { + Key::Backspace + }, + _ => { + Key::Char(char::from(first)) + }, + }) + } +} diff --git a/linux/src/lib.rs b/linux/src/lib.rs index 049ad2d..2f4b4b7 100644 --- a/linux/src/lib.rs +++ b/linux/src/lib.rs @@ -1,2 +1,5 @@ pub mod fb; - +pub mod raw; +pub mod keys; +pub mod pty; +pub mod input; diff --git a/linux/src/pty.rs b/linux/src/pty.rs new file mode 100644 index 0000000..922f8a4 --- /dev/null +++ b/linux/src/pty.rs @@ -0,0 +1,55 @@ +use std::ptr; +use std::process::{ Command, Child }; +use std::fs::File; +use std::os::fd::{ OwnedFd, FromRawFd }; + +use libc::openpty; + +//basically the master and slave are linked? slave behaves just like normal terminal +//I don't totally get it, I guess just attach the command's stdout and stderr to ptyslave for reading? + +pub struct PtyMaster { + pub file: File, +} + +impl PtyMaster { + pub fn new(fd: OwnedFd) -> Self { + Self { + file: File::from(fd), + } + } +} + +pub struct PtySlave { + pub file: File, + fd: OwnedFd, +} + +impl PtySlave { + pub fn new(fd: OwnedFd) -> Self { + Self { + file: File::from(fd.try_clone().unwrap()), + fd, + } + } + + //assume stdin is piped + pub fn attach_and_spawn(&self, command: &mut Command) -> std::io::Result { + command.stdout(self.fd.try_clone().unwrap()); + command.stderr(self.fd.try_clone().unwrap()); + command.spawn() + } +} + +pub fn open_pty() -> Result<(PtyMaster, PtySlave), ()> { + let mut master_fd = 0; + let mut slave_fd = 0; + let result = unsafe { openpty(&mut master_fd, &mut slave_fd, ptr::null_mut(), ptr::null_mut(), ptr::null_mut()) }; + if result == -1 { + Err(()) + } else { + let master_fd = unsafe { OwnedFd::from_raw_fd(master_fd) }; + let slave_fd = unsafe { OwnedFd::from_raw_fd(slave_fd) }; + Ok((PtyMaster::new(master_fd), PtySlave::new(slave_fd))) + } +} diff --git a/linux/src/raw.rs b/linux/src/raw.rs new file mode 100644 index 0000000..e75e737 --- /dev/null +++ b/linux/src/raw.rs @@ -0,0 +1,65 @@ +use std::io::Stdout; +use std::mem::zeroed; +use std::os::fd::AsRawFd; + +use libc::{ cfmakeraw, c_int, tcgetattr, tcsetattr, termios, TCSAFLUSH }; + +//https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html +//on TCSAFLUSH: "The TCSAFLUSH argument specifies when to apply the change: in this case, it waits for all pending output to be written to the terminal, and also discards any input that hasn't been read." + +//https://www.man7.org/linux/man-pages/man3/termios.3.html +//(use cfmakeraw instead doing all those bitwise stuff manually) + +//enter and exit tty raw mode + +pub struct RawStdout { + pub stdout: Stdout, + old_termios: termios, +} + +impl RawStdout { + pub fn new(stdout: Stdout) -> Self { + RawStdout { + stdout, + old_termios: unsafe { zeroed() }, + } + } + + pub fn get_termios(raw_fd: c_int) -> Result { + let mut termios_struct: termios = unsafe { zeroed() }; + let result = unsafe { + tcgetattr(raw_fd, &mut termios_struct) + }; + if result != -1 { + Ok(termios_struct) + } else { + Err(()) + } + } + + pub fn enter_raw_mode(&mut self) -> Result<(), ()> { + let raw_fd = self.stdout.as_raw_fd(); + let mut termios_struct = Self::get_termios(raw_fd)?; + self.old_termios = termios_struct; + let result = unsafe { + cfmakeraw(&mut termios_struct); + tcsetattr(raw_fd, TCSAFLUSH, &mut termios_struct) + }; + if result != -1 { + Ok(()) + } else { + Err(()) + } + } + + pub fn exit_raw_mode(&mut self) -> Result<(), ()> { + let result = unsafe { + tcsetattr(self.stdout.as_raw_fd(), TCSAFLUSH, &mut self.old_termios) + }; + if result != -1 { + Ok(()) + } else { + Err(()) + } + } +} diff --git a/ming-wm-lib/Cargo.toml b/ming-wm-lib/Cargo.toml index 99c25f9..a420e9d 100644 --- a/ming-wm-lib/Cargo.toml +++ b/ming-wm-lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ming-wm-lib" -version = "0.1.6" +version = "0.2.0" repository = "https://github.com/stjet/ming-wm" description = "library for building windows for ming-wm in rust" readme = "README.md" diff --git a/src/components/highlight_button.rs b/ming-wm-lib/src/components/highlight_button.rs similarity index 91% rename from src/components/highlight_button.rs rename to ming-wm-lib/src/components/highlight_button.rs index e00368d..4f9cfa6 100644 --- a/src/components/highlight_button.rs +++ b/ming-wm-lib/src/components/highlight_button.rs @@ -2,10 +2,10 @@ use std::vec; use std::vec::Vec; use crate::components::Component; -use ming_wm_lib::framebuffer_types::{ Dimensions, Point }; -use ming_wm_lib::themes::ThemeInfo; -use ming_wm_lib::messages::WindowMessage; -use ming_wm_lib::window_manager_types::DrawInstructions; +use crate::framebuffer_types::{ Dimensions, Point }; +use crate::themes::ThemeInfo; +use crate::messages::WindowMessage; +use crate::window_manager_types::DrawInstructions; pub struct HighlightButton { name_: String, diff --git a/src/components/mod.rs b/ming-wm-lib/src/components/mod.rs similarity index 81% rename from src/components/mod.rs rename to ming-wm-lib/src/components/mod.rs index 4a64068..a9e1e43 100644 --- a/src/components/mod.rs +++ b/ming-wm-lib/src/components/mod.rs @@ -1,8 +1,8 @@ use std::vec::Vec; -use ming_wm_lib::themes::ThemeInfo; -use ming_wm_lib::messages::WindowMessage; -use ming_wm_lib::window_manager_types::DrawInstructions; +use crate::themes::ThemeInfo; +use crate::messages::WindowMessage; +use crate::window_manager_types::DrawInstructions; pub mod toggle_button; pub mod highlight_button; diff --git a/src/components/paragraph.rs b/ming-wm-lib/src/components/paragraph.rs similarity index 90% rename from src/components/paragraph.rs rename to ming-wm-lib/src/components/paragraph.rs index 0102fea..19b3366 100644 --- a/src/components/paragraph.rs +++ b/ming-wm-lib/src/components/paragraph.rs @@ -1,12 +1,11 @@ use std::vec; use std::vec::Vec; - -use ming_wm_lib::framebuffer_types::{ Dimensions, Point }; -use ming_wm_lib::themes::ThemeInfo; -use ming_wm_lib::messages::WindowMessage; -use ming_wm_lib::window_manager_types::DrawInstructions; -use ming_wm_lib::utils::calc_actual_lines; +use crate::framebuffer_types::{ Dimensions, Point }; +use crate::themes::ThemeInfo; +use crate::messages::WindowMessage; +use crate::window_manager_types::DrawInstructions; +use crate::utils::calc_actual_lines; use crate::components::Component; const MONO_WIDTH: u8 = 10; diff --git a/src/components/press_button.rs b/ming-wm-lib/src/components/press_button.rs similarity index 90% rename from src/components/press_button.rs rename to ming-wm-lib/src/components/press_button.rs index 8151db9..d907779 100644 --- a/src/components/press_button.rs +++ b/ming-wm-lib/src/components/press_button.rs @@ -2,10 +2,10 @@ use std::vec; use std::vec::Vec; use crate::components::Component; -use ming_wm_lib::framebuffer_types::{ Dimensions, Point }; -use ming_wm_lib::themes::ThemeInfo; -use ming_wm_lib::messages::WindowMessage; -use ming_wm_lib::window_manager_types::DrawInstructions; +use crate::framebuffer_types::{ Dimensions, Point }; +use crate::themes::ThemeInfo; +use crate::messages::WindowMessage; +use crate::window_manager_types::DrawInstructions; const MONO_WIDTH: u8 = 10; diff --git a/src/components/toggle_button.rs b/ming-wm-lib/src/components/toggle_button.rs similarity index 92% rename from src/components/toggle_button.rs rename to ming-wm-lib/src/components/toggle_button.rs index 105258c..dce7653 100644 --- a/src/components/toggle_button.rs +++ b/ming-wm-lib/src/components/toggle_button.rs @@ -2,10 +2,10 @@ use std::vec; use std::vec::Vec; use crate::components::Component; -use ming_wm_lib::framebuffer_types::{ Dimensions, Point }; -use ming_wm_lib::themes::ThemeInfo; -use ming_wm_lib::messages::WindowMessage; -use ming_wm_lib::window_manager_types::DrawInstructions; +use crate::framebuffer_types::{ Dimensions, Point }; +use crate::themes::ThemeInfo; +use crate::messages::WindowMessage; +use crate::window_manager_types::DrawInstructions; pub struct ToggleButton { name_: String, diff --git a/ming-wm-lib/src/ipc.rs b/ming-wm-lib/src/ipc.rs index 7b7772e..76cbbf0 100644 --- a/ming-wm-lib/src/ipc.rs +++ b/ming-wm-lib/src/ipc.rs @@ -58,9 +58,11 @@ pub fn listen(mut window_like: impl WindowLike) { let arg = &parts.collect::>().join(" "); let output = match method { "handle_message" => { + //newlines allowed for ClipboardCopy, but represented by the Linear A char window_like.handle_message(WindowMessage::deserialize(arg).unwrap()).serialize().to_string() }, "draw" => { + //newlines never allowed window_like.draw(&ThemeInfo::deserialize(arg).unwrap()).serialize().replace("\n", "").to_string() }, "title" => { diff --git a/ming-wm-lib/src/lib.rs b/ming-wm-lib/src/lib.rs index 5ff514f..96cb963 100644 --- a/ming-wm-lib/src/lib.rs +++ b/ming-wm-lib/src/lib.rs @@ -4,6 +4,7 @@ pub mod themes; pub mod serialize; pub mod messages; pub mod ipc; +pub mod components; pub mod dirs; pub mod utils; pub mod logging; diff --git a/ming-wm-lib/src/serialize.rs b/ming-wm-lib/src/serialize.rs index b2aaf4d..12e3ed1 100644 --- a/ming-wm-lib/src/serialize.rs +++ b/ming-wm-lib/src/serialize.rs @@ -146,7 +146,7 @@ impl Serializable for WindowMessageResponse { WindowMessageResponse::Request(req) => { let req = match req { WindowManagerRequest::OpenWindow(name) => format!("OpenWindow/{}", name), - WindowManagerRequest::ClipboardCopy(name) => format!("ClipboardCopy/{}", name), + WindowManagerRequest::ClipboardCopy(copy_string) => format!("ClipboardCopy/{}", copy_string.replace("\n", "𐘂")), //serialised output must be 1 line WindowManagerRequest::CloseStartMenu => "CloseStartMenu".to_string(), WindowManagerRequest::Unlock => "Unlock".to_string(), WindowManagerRequest::Lock => "Lock".to_string(), @@ -646,7 +646,7 @@ impl Serializable for WindowMessage { "FullscreenWindow" => Some(ShortcutType::FullscreenWindow), "HalfWidthWindow" => Some(ShortcutType::HalfWidthWindow), "ClipboardCopy" => Some(ShortcutType::ClipboardCopy), - "ClipboardPaste" => Some(ShortcutType::ClipboardPaste(get_rest_of_split(&mut parts, Some("/")))), + "ClipboardPaste" => Some(ShortcutType::ClipboardPaste(get_rest_of_split(&mut parts, Some("/")).replace("𐘂", "\n"))), _ => None, }; if let Some(shortcut) = shortcut { diff --git a/ming-wm-lib/src/utils.rs b/ming-wm-lib/src/utils.rs index e32a4d8..68f4dd3 100644 --- a/ming-wm-lib/src/utils.rs +++ b/ming-wm-lib/src/utils.rs @@ -214,3 +214,16 @@ pub fn path_autocomplete(current_path: &str, partial_path: &str) -> Option Vec { + let mut files = Vec::new(); + for entry in read_dir(dir).unwrap() { + let path = entry.unwrap().path(); + if path.is_dir() { + files.extend(get_all_files(path)); + } else { + files.push(path); + } + } + files +} diff --git a/src/bin/audio_player.rs b/src/bin/audio_player.rs index e940b98..ff62d36 100644 --- a/src/bin/audio_player.rs +++ b/src/bin/audio_player.rs @@ -14,13 +14,12 @@ use mp4ameta; use metaflac; use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType }; -use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse }; +use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest, ShortcutType }; use ming_wm_lib::framebuffer_types::Dimensions; use ming_wm_lib::themes::ThemeInfo; -use ming_wm_lib::utils::{ concat_paths, path_autocomplete, format_seconds, Substring }; +use ming_wm_lib::utils::{ concat_paths, get_all_files, path_autocomplete, format_seconds, Substring }; use ming_wm_lib::dirs::home; use ming_wm_lib::ipc::listen; -use ming_wm::fs::get_all_files; fn get_artist(path: &PathBuf) -> Option { let ext = path.extension().unwrap(); @@ -80,7 +79,6 @@ pub struct AudioPlayer { impl WindowLike for AudioPlayer { fn handle_message(&mut self, message: WindowMessage) -> WindowMessageResponse { - // match message { WindowMessage::Init(dimensions) => { self.dimensions = dimensions; @@ -110,11 +108,34 @@ impl WindowLike for AudioPlayer { } else { return WindowMessageResponse::DoNothing; } - } else { + } else if key_press.is_regular() { self.command += &key_press.key.to_string(); + } else { + return WindowMessageResponse::DoNothing } WindowMessageResponse::JustRedraw }, + WindowMessage::Shortcut(shortcut) => { + match shortcut { + ShortcutType::ClipboardPaste(paste_string) => { + self.command += &paste_string.replace("\n", ""); + WindowMessageResponse::JustRedraw + }, + ShortcutType::ClipboardCopy => { + let internal_locked = self.internal.lock().unwrap(); + let sink_len = internal_locked.sink.len(); + if sink_len > 0 { + let queue = &internal_locked.queue; + let current = &queue[queue.len() - sink_len]; + let current_name = current.0.file_name().unwrap().to_string_lossy().into_owned(); + WindowMessageResponse::Request(WindowManagerRequest::ClipboardCopy(current_name)) + } else { + WindowMessageResponse::DoNothing + } + }, + _ => WindowMessageResponse::DoNothing, + } + }, _ => { WindowMessageResponse::DoNothing }, diff --git a/src/bin/malvim.rs b/src/bin/malvim.rs index 96bbca6..3c4879d 100644 --- a/src/bin/malvim.rs +++ b/src/bin/malvim.rs @@ -457,11 +457,21 @@ impl WindowLike for Malvim { ShortcutType::ClipboardPaste(copy_string) => { if self.mode == Mode::Insert { let current_file = &mut self.files[self.current_file_index]; - let line = ¤t_file.content[current_file.line_pos]; - current_file.content[current_file.line_pos] = line.substring(0, current_file.cursor_pos).to_string() + ©_string + line.substring(current_file.cursor_pos, line.chars().count()); - current_file.cursor_pos += copy_string.len(); + for (i, cs) in copy_string.split("\n").enumerate() { + if i == 0 { + //modify current line + let line = ¤t_file.content[current_file.line_pos]; + current_file.content[current_file.line_pos] = line.substring(0, current_file.cursor_pos).to_string() + &cs + line.substring(current_file.cursor_pos, line.chars().count()); + current_file.cursor_pos += copy_string.len(); + } else { + //insert a new line + current_file.content.insert(current_file.line_pos + 1, cs.to_string()); + current_file.line_pos += 1; + current_file.cursor_pos = cs.chars().count(); + } + } self.calc_top_line_pos(); - self.calc_current(); //too over zealous but whatever + self.calc_current(); self.files[self.current_file_index].changed = true; WindowMessageResponse::JustRedraw } else { diff --git a/src/bin/terminal.rs b/src/bin/terminal.rs index c87f249..2e27db1 100644 --- a/src/bin/terminal.rs +++ b/src/bin/terminal.rs @@ -3,15 +3,16 @@ use std::vec; use std::sync::mpsc::{ channel, Receiver, Sender }; use std::thread; use std::process::{ Child, Stdio }; +use std::process::Command; use std::io::{ Read, Write }; use std::time::Duration; use std::path::PathBuf; use std::fmt; -use pty_process::blocking; +use linux::pty::open_pty; use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType }; -use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, ShortcutType }; +use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest, ShortcutType }; use ming_wm_lib::framebuffer_types::Dimensions; use ming_wm_lib::themes::ThemeInfo; use ming_wm_lib::utils::{ concat_paths, path_autocomplete, Substring }; @@ -46,6 +47,11 @@ fn strip_ansi_escape_codes(line: String) -> String { new_line } +fn bytes_to_string(bytes: Vec) -> String { + let bytes_len = bytes.len(); + String::from_utf8(bytes).unwrap_or("?".repeat(bytes_len)) +} + #[derive(Default, PartialEq)] enum Mode { #[default] @@ -78,6 +84,7 @@ pub struct Terminal { current_path: String, running_process: Option, process_current_line: Vec, //bytes of line + output: String, //current or previous running output of command pty_outerr_rx: Option>, pty_in_tx: Option>, history: Vec, @@ -116,6 +123,7 @@ impl WindowLike for Terminal { self.history_index = None; self.mode = self.process_command(); self.current_input = String::new(); + self.output = String::new(); } else if key_press.key == '\t' { //tab //autocomplete assuming it's a file system path //...mostly working @@ -144,12 +152,17 @@ impl WindowLike for Terminal { loop { 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)))); + let append_line = strip_ansi_escape_codes(bytes_to_string(self.process_current_line.clone())); + self.output += &append_line; + self.output += "\n"; + self.lines.push(append_line); self.process_current_line = Vec::new(); } else if char::from(ci) == '\r' { //for now, ignore // + } else if char::from(ci) == '\t' { + //for now, interpret as space + self.process_current_line.push(b' '); } else { self.process_current_line.push(ci); } @@ -163,7 +176,14 @@ impl WindowLike for Terminal { //process exited self.pty_outerr_rx = None; self.mode = Mode::Input; - self.process_current_line = Vec::new(); + if self.process_current_line.len() > 0 { + //add to lines + let append_line = strip_ansi_escape_codes(bytes_to_string(self.process_current_line.clone())); + self.output += &append_line; + self.lines.push(append_line); + //only need to reset if not empty + self.process_current_line = Vec::new(); + } changed = true; } else { if key_press.key == 'i' { @@ -184,8 +204,9 @@ impl WindowLike for Terminal { } 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); + let append_line = strip_ansi_escape_codes(bytes_to_string(self.process_current_line.clone()) + &self.current_stdin_input); + self.output += &append_line; + self.lines.push(append_line); self.current_stdin_input = String::new(); self.process_current_line = Vec::new(); } else if key_press.is_backspace() { @@ -207,6 +228,12 @@ impl WindowLike for Terminal { //kills and running_process is now None let _ = self.running_process.take().unwrap().kill(); self.mode = Mode::Input; + if self.process_current_line.len() > 0 { + let append_line = strip_ansi_escape_codes(bytes_to_string(self.process_current_line.clone())); + self.output += &append_line; + self.lines.push(append_line); + self.process_current_line = Vec::new(); + } WindowMessageResponse::JustRedraw } 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 @@ -228,6 +255,7 @@ impl WindowLike for Terminal { }, WindowMessage::Shortcut(shortcut) => { match shortcut { + ShortcutType::ClipboardCopy => WindowMessageResponse::Request(WindowManagerRequest::ClipboardCopy(self.output.clone())), ShortcutType::ClipboardPaste(copy_string) => { if self.mode == Mode::Input || self.mode == Mode::Stdin { if self.mode == Mode::Input { @@ -331,11 +359,13 @@ impl Terminal { } Mode::Input } else { - let (pty, pts) = blocking::open().unwrap(); - 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 (pty, pts) = open_pty().unwrap(); + let mut cmd = Command::new("sh"); + let cmd = cmd.arg("-c").arg(&self.current_input).current_dir(&self.current_path).stdin(Stdio::piped()); + self.running_process = Some(pts.attach_and_spawn(cmd).unwrap()); let (tx1, rx1) = channel(); thread::spawn(move || { - for ci in pty.bytes() { + for ci in pty.file.bytes() { if let Ok(ci) = ci { tx1.send(ci).unwrap(); } else { @@ -378,8 +408,7 @@ impl Terminal { //must_add_current_line will be false "$ ".to_string() + &self.current_input + "█" } else { - 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() + "█" + strip_ansi_escape_codes(bytes_to_string(self.process_current_line.clone()) + &self.current_stdin_input.clone() + "█") } } else { self.lines[line_num].clone() diff --git a/src/bin/main.rs b/src/bin/wm.rs similarity index 61% rename from src/bin/main.rs rename to src/bin/wm.rs index 4c09208..8b57f92 100644 --- a/src/bin/main.rs +++ b/src/bin/wm.rs @@ -1,20 +1,21 @@ -use std::process::{ Command, Stdio }; use std::sync::mpsc; use std::thread; use std::time::Duration; -use std::io::{ stdin, stdout, BufReader, BufRead, Write }; +use std::io::{ stdin, stdout, Write }; use std::process::exit; use std::env; use linux::fb::Framebuffer; -use termion::input::TermRead; -use termion::raw::IntoRawMode; -use termion::event::Key; +use linux::raw::RawStdout; +use linux::keys::{ RawStdin, Key }; +use linux::input::{ Input, EventType }; +use wm::framebuffer::{ FramebufferWriter, FramebufferInfo }; +use wm::window_manager::WindowManager; use ming_wm_lib::window_manager_types::KeyChar; use ming_wm_lib::messages::*; -use ming_wm::framebuffer::{ FramebufferWriter, FramebufferInfo }; -use ming_wm::window_manager::WindowManager; + +include!(concat!(env!("OUT_DIR"), "/password.rs")); const CLEAR_ALL: &'static str = "\x1b[2J"; const HIDE_CURSOR: &'static str = "\x1b[?25l"; @@ -30,10 +31,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('𐙥')), + Key::ArrowUp => Some(KeyChar::Press('𐙘')), + Key::ArrowDown => Some(KeyChar::Press('𐘞')), + Key::ArrowLeft => Some(KeyChar::Press('𐙣')), + Key::ArrowRight => Some(KeyChar::Press('𐙥')), _ => None, } } @@ -71,15 +72,16 @@ fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) { writer.init(framebuffer_info.clone()); - let mut wm: WindowManager = WindowManager::new(writer, framebuffer, dimensions, rotate, grayscale); + let mut wm: WindowManager = WindowManager::new(writer, framebuffer, dimensions, rotate, grayscale, PASSWORD_HASH); - let mut stdout = stdout().into_raw_mode().unwrap(); + let mut stdout = RawStdout::new(stdout()); + stdout.enter_raw_mode().unwrap(); - write!(stdout, "{}", CLEAR_ALL).unwrap(); + write!(stdout.stdout, "{}", CLEAR_ALL).unwrap(); - write!(stdout, "{}", HIDE_CURSOR).unwrap(); + write!(stdout.stdout, "{}", HIDE_CURSOR).unwrap(); - stdout.flush().unwrap(); + stdout.stdout.flush().unwrap(); wm.draw(None, false); @@ -89,9 +91,9 @@ fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) { //read key presses thread::spawn(move || { - let stdin = stdin().lock(); - for c in stdin.keys() { - if let Some(kc) = key_to_char(c.unwrap()) { + let stdin = RawStdin::new(stdin()); + for c in stdin { + if let Some(kc) = key_to_char(c) { //do not allow exit when locked unless debugging //if kc == KeyChar::Alt('E') { if kc == KeyChar::Alt('E') { @@ -110,34 +112,35 @@ fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) { thread::spawn(move || { //spawn evtest, parse it for touch coords if touch { - let mut evtest = Command::new("evtest").arg("/dev/input/by-path/first-touchscreen").stdout(Stdio::piped()).spawn().unwrap(); - let reader = BufReader::new(evtest.stdout.as_mut().unwrap()); + let mut events = Input::new("/dev/input/by-path/first-touchscreen").unwrap(); //panics in threads don't matter in this case let mut x: Option = None; let mut y: Option = None; - for line in reader.lines() { - let line = line.unwrap(); - if line.contains("ABS_X), value ") || line.contains("ABS_Y), value ") { - let value: Vec<_> = line.split("), value ").collect(); - let value = value[value.len() - 1].parse::().unwrap(); - if line.contains("ABS_X") { - x = Some(value); - } else { - y = Some(value); - } - if x.is_some() && y.is_some() { - let (x2, y2) = if rotate { - (dimensions[0] - y.unwrap(), x.unwrap()) + loop { + let event = events.next(); + if let Some(event) = event { + //ABS_X = 0, ABS_Y = 1 + if event.type_ == EventType::EV_ABS && (event.code == 0 || event.code == 1) { + if event.code == 0 { + x = Some(event.value as usize); //event.value is u16 so this should be fine. unless usize is u8, lmao } else { - (x.unwrap(), y.unwrap()) - }; - //top right, clear - //useful sometimes, I think. - if x2 > dimensions[0] - 100 && y2 < 100 { - tx1.send(ThreadMessage::Clear).unwrap(); + y = Some(event.value as usize); + } + if x.is_some() && y.is_some() { + let (x2, y2) = if rotate { + (dimensions[0] - y.unwrap(), x.unwrap()) + } else { + (x.unwrap(), y.unwrap()) + }; + //top right, clear + //useful sometimes, I think. + if x2 > dimensions[0] - 100 && y2 < 100 { + tx1.send(ThreadMessage::Clear).unwrap(); + } + println!(" "); //without any stdout, on my phone, for some reason the framebuffer doesn't get redrawn to the screen + tx1.send(ThreadMessage::Touch(x2, y2)).unwrap(); + x = None; + y = None; } - tx1.send(ThreadMessage::Touch(x2, y2)).unwrap(); - x = None; - y = None; } } thread::sleep(Duration::from_millis(1)); @@ -154,13 +157,13 @@ 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(); - stdout.flush().unwrap(); + write!(stdout.stdout, "{}", CLEAR_ALL).unwrap(); + stdout.stdout.flush().unwrap(); }, ThreadMessage::Exit => { if !wm.locked { - write!(stdout, "{}", SHOW_CURSOR).unwrap(); - stdout.suspend_raw_mode().unwrap(); + write!(stdout.stdout, "{}", SHOW_CURSOR).unwrap(); + stdout.exit_raw_mode().unwrap(); exit(0); } }, diff --git a/wm/Cargo.toml b/wm/Cargo.toml new file mode 100644 index 0000000..c5a1f46 --- /dev/null +++ b/wm/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "wm" +version = "1.0.3" +repository = "https://github.com/stjet/ming-wm" +license = "GPL-3.0-or-later" +edition = "2021" + +[dependencies] +ming-wm-lib = { path = "../ming-wm-lib" } +linux = { path = "../linux" } +bitcoin_hashes = { version = "0.16.0", default-features = false } +bmp-rust = "0.5.0" diff --git a/src/essential/about.rs b/wm/src/essential/about.rs similarity index 95% rename from src/essential/about.rs rename to wm/src/essential/about.rs index fd00b48..b2bf7f1 100644 --- a/src/essential/about.rs +++ b/wm/src/essential/about.rs @@ -7,8 +7,8 @@ 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; +use ming_wm_lib::components::Component; +use ming_wm_lib::components::paragraph::Paragraph; pub struct About { dimensions: Dimensions, diff --git a/src/essential/desktop_background.rs b/wm/src/essential/desktop_background.rs similarity index 100% rename from src/essential/desktop_background.rs rename to wm/src/essential/desktop_background.rs diff --git a/src/essential/help.rs b/wm/src/essential/help.rs similarity index 97% rename from src/essential/help.rs rename to wm/src/essential/help.rs index 9e5786c..c4ddcbf 100644 --- a/src/essential/help.rs +++ b/wm/src/essential/help.rs @@ -8,8 +8,8 @@ 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; -use crate::components::Component; +use ming_wm_lib::components::paragraph::Paragraph; +use ming_wm_lib::components::Component; pub struct Help { dimensions: Dimensions, diff --git a/src/essential/lock_screen.rs b/wm/src/essential/lock_screen.rs similarity index 91% rename from src/essential/lock_screen.rs rename to wm/src/essential/lock_screen.rs index dc42165..97b8b66 100644 --- a/src/essential/lock_screen.rs +++ b/wm/src/essential/lock_screen.rs @@ -5,15 +5,14 @@ use ming_wm_lib::framebuffer_types::Dimensions; use ming_wm_lib::themes::ThemeInfo; use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest }; use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType }; -use blake2::{ Blake2b512, Digest }; - -include!(concat!(env!("OUT_DIR"), "/password.rs")); +use bitcoin_hashes::Sha512; //const PASSWORD_HASH: [u8; 64] = [220, 88, 183, 188, 240, 27, 107, 181, 58, 191, 198, 170, 114, 38, 7, 148, 6, 179, 75, 128, 231, 171, 172, 220, 85, 38, 36, 113, 116, 146, 70, 197, 163, 179, 158, 192, 130, 53, 247, 48, 47, 209, 95, 96, 179, 211, 4, 122, 254, 127, 21, 165, 139, 199, 151, 226, 216, 176, 123, 41, 194, 221, 58, 69]; pub struct LockScreen { dimensions: Dimensions, input_password: String, + password_hash: [u8; 64], } impl WindowLike for LockScreen { @@ -26,9 +25,7 @@ impl WindowLike for LockScreen { WindowMessage::KeyPress(key_press) => { if key_press.is_enter() { //check password - let mut hasher = Blake2b512::new(); - hasher.update((self.input_password.clone() + "salt?sorrycryptographers").as_bytes()); - if hasher.finalize() == PASSWORD_HASH.into() { + if Sha512::hash((self.input_password.clone() + "salt?sorrycryptographers!1!").as_bytes()).to_byte_array() == self.password_hash { WindowMessageResponse::Request(WindowManagerRequest::Unlock) } else { self.input_password = String::new(); @@ -73,10 +70,11 @@ impl WindowLike for LockScreen { } impl LockScreen { - pub fn new() -> Self { + pub fn new(password_hash: [u8; 64]) -> Self { Self { dimensions: [0, 0], input_password: String::new(), + password_hash, } } } diff --git a/src/essential/mod.rs b/wm/src/essential/mod.rs similarity index 100% rename from src/essential/mod.rs rename to wm/src/essential/mod.rs diff --git a/src/essential/onscreen_keyboard.rs b/wm/src/essential/onscreen_keyboard.rs similarity index 98% rename from src/essential/onscreen_keyboard.rs rename to wm/src/essential/onscreen_keyboard.rs index 3284677..68ac6a7 100644 --- a/src/essential/onscreen_keyboard.rs +++ b/wm/src/essential/onscreen_keyboard.rs @@ -7,8 +7,8 @@ use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, WindowManager use ming_wm_lib::framebuffer_types::Dimensions; use ming_wm_lib::themes::ThemeInfo; use ming_wm_lib::utils::point_inside; -use crate::components::Component; -use crate::components::press_button::PressButton; +use ming_wm_lib::components::Component; +use ming_wm_lib::components::press_button::PressButton; //seems like framebuffer only updates if (real) key is pressed... //on mobile, volume down button seems to work but is annoying diff --git a/src/essential/start_menu.rs b/wm/src/essential/start_menu.rs similarity index 98% rename from src/essential/start_menu.rs rename to wm/src/essential/start_menu.rs index 30e517b..972e928 100644 --- a/src/essential/start_menu.rs +++ b/wm/src/essential/start_menu.rs @@ -7,9 +7,9 @@ use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, WindowManager use ming_wm_lib::framebuffer_types::Dimensions; use ming_wm_lib::themes::ThemeInfo; use ming_wm_lib::dirs::exe_dir; +use ming_wm_lib::components::Component; +use ming_wm_lib::components::highlight_button::HighlightButton; 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", "Lock"]; diff --git a/src/essential/taskbar.rs b/wm/src/essential/taskbar.rs similarity index 97% rename from src/essential/taskbar.rs rename to wm/src/essential/taskbar.rs index 478eace..f806590 100644 --- a/src/essential/taskbar.rs +++ b/wm/src/essential/taskbar.rs @@ -6,8 +6,8 @@ use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLik use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest, ShortcutType, InfoType, WindowsVec }; use ming_wm_lib::framebuffer_types::Dimensions; use ming_wm_lib::themes::ThemeInfo; -use crate::components::Component; -use crate::components::toggle_button::ToggleButton; +use ming_wm_lib::components::Component; +use ming_wm_lib::components::toggle_button::ToggleButton; const PADDING: usize = 4; const META_WIDTH: usize = 175; //of the window button diff --git a/src/essential/workspace_indicator.rs b/wm/src/essential/workspace_indicator.rs similarity index 100% rename from src/essential/workspace_indicator.rs rename to wm/src/essential/workspace_indicator.rs diff --git a/src/framebuffer.rs b/wm/src/framebuffer.rs similarity index 100% rename from src/framebuffer.rs rename to wm/src/framebuffer.rs diff --git a/src/fs.rs b/wm/src/fs.rs similarity index 88% rename from src/fs.rs rename to wm/src/fs.rs index 2b9e0c5..30a3185 100644 --- a/src/fs.rs +++ b/wm/src/fs.rs @@ -1,5 +1,4 @@ use std::fs::{ read_dir, File }; -use std::path::PathBuf; use std::io::Read; use std::collections::HashMap; @@ -34,19 +33,6 @@ pub fn get_font_char_from_fonts(fonts: &[String], c: char) -> (char, Vec get_font_char(&p, '?').unwrap_or(('?', vec![vec![0]], 0)) } -pub fn get_all_files(dir: PathBuf) -> Vec { - let mut files = Vec::new(); - for entry in read_dir(dir).unwrap() { - let path = entry.unwrap().path(); - if path.is_dir() { - files.extend(get_all_files(path)); - } else { - files.push(path); - } - } - files -} - //Category, Vec pub type ExeWindowInfos = HashMap>; diff --git a/src/lib.rs b/wm/src/lib.rs similarity index 82% rename from src/lib.rs rename to wm/src/lib.rs index 6564cee..e927891 100644 --- a/src/lib.rs +++ b/wm/src/lib.rs @@ -1,6 +1,7 @@ +pub use linux; + pub mod framebuffer; pub mod window_manager; -pub mod components; pub mod fs; mod proxy_window_like; mod essential; diff --git a/src/proxy_window_like.rs b/wm/src/proxy_window_like.rs similarity index 100% rename from src/proxy_window_like.rs rename to wm/src/proxy_window_like.rs diff --git a/src/window_manager.rs b/wm/src/window_manager.rs similarity index 97% rename from src/window_manager.rs rename to wm/src/window_manager.rs index 0b5d09c..948e259 100644 --- a/src/window_manager.rs +++ b/wm/src/window_manager.rs @@ -24,7 +24,6 @@ use crate::essential::start_menu::StartMenu; use crate::essential::about::About; use crate::essential::help::Help; use crate::essential::onscreen_keyboard::OnscreenKeyboard; -//use crate::logging::log; //todo: a lot of the usize should be changed to u16 @@ -40,6 +39,7 @@ struct WindowLikeInfo { id: usize, window_like: WindowBox, top_left: Point, + old_top_left: Point, dimensions: Dimensions, workspace: Workspace, fullscreen: bool, @@ -65,12 +65,13 @@ pub struct WindowManager { current_workspace: u8, framebuffer: Framebuffer, clipboard: Option, + password_hash: [u8; 64], } //1 is up, 2 is down impl WindowManager { - pub fn new(writer: FramebufferWriter, framebuffer: Framebuffer, dimensions: Dimensions, rotate: bool, grayscale: bool) -> Self { + pub fn new(writer: FramebufferWriter, framebuffer: Framebuffer, dimensions: Dimensions, rotate: bool, grayscale: bool, password_hash: [u8; 64]) -> Self { //println!("bg: {}x{}", dimensions[0], dimensions[1] - TASKBAR_HEIGHT - INDICATOR_HEIGHT); let mut wm = WindowManager { writer: RefCell::new(writer), @@ -86,6 +87,7 @@ impl WindowManager { current_workspace: 0, framebuffer, clipboard: None, + password_hash, }; wm.lock(); wm.change_theme(); @@ -103,6 +105,7 @@ impl WindowManager { id, window_like, top_left, + old_top_left: top_left, dimensions, workspace: if subtype == WindowLikeType::Window { Workspace::Workspace(self.current_workspace) @@ -136,7 +139,7 @@ impl WindowManager { fn lock(&mut self) { self.locked = true; self.window_infos = Vec::new(); - self.add_window_like(Box::new(LockScreen::new()), [0, 0], None); + self.add_window_like(Box::new(LockScreen::new(self.password_hash)), [0, 0], None); } fn unlock(&mut self) { @@ -456,17 +459,20 @@ impl WindowManager { let window_like = &self.window_infos[focused_index].window_like; if window_like.subtype() == WindowLikeType::Window && window_like.resizable() { //toggle fullscreen - self.window_infos[focused_index].fullscreen ^= true; + let window_info = &mut self.window_infos[focused_index]; + window_info.fullscreen ^= true; //todo: send message to window about resize let new_dimensions; - if self.window_infos[focused_index].fullscreen { + if window_info.fullscreen { new_dimensions = [self.dimensions[0], self.dimensions[1] - TASKBAR_HEIGHT - INDICATOR_HEIGHT]; - self.window_infos[focused_index].top_left = [0, INDICATOR_HEIGHT]; - redraw_ids = Some(vec![self.window_infos[focused_index].id]); + window_info.old_top_left = window_info.top_left; + window_info.top_left = [0, INDICATOR_HEIGHT]; + redraw_ids = Some(vec![window_info.id]); } else { - new_dimensions = self.window_infos[focused_index].dimensions; + window_info.top_left = window_info.old_top_left; + new_dimensions = window_info.dimensions; } - self.window_infos[focused_index].window_like.handle_message(WindowMessage::ChangeDimensions([new_dimensions[0], new_dimensions[1] - WINDOW_TOP_HEIGHT])); + window_info.window_like.handle_message(WindowMessage::ChangeDimensions([new_dimensions[0], new_dimensions[1] - WINDOW_TOP_HEIGHT])); press_response = WindowMessageResponse::JustRedraw; } }