V1.1 #2
22
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ming-wm"
|
name = "ming-wm"
|
||||||
version = "1.0.3"
|
version = "1.1.0"
|
||||||
repository = "https://github.com/stjet/ming-wm"
|
repository = "https://github.com/stjet/ming-wm"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
edition = "2021"
|
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
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["linux"]
|
members = [ "wm", "linux" ]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
bmp-rust = "0.5.0"
|
bmp-rust = "0.5.0"
|
||||||
blake2 = { version = "0.10.6", default-features = false }
|
bitcoin_hashes = { version = "0.16.0", default-features = false }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ming-wm-lib = { path = "ming-wm-lib" }
|
ming-wm-lib = { path = "ming-wm-lib" }
|
||||||
blake2 = { version = "0.10.6", default-features = false }
|
wm = { path = "wm", optional = true }
|
||||||
linux = { path = "linux" }
|
linux = { path = "linux", optional = true }
|
||||||
termion = { version = "4.0.3", optional = true }
|
|
||||||
rodio = { version = "0.19.0", default-features = false, features = [ "flac", "mp3", "symphonia-vorbis", "wav" ], 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 }
|
rand = { version = "0.9.0", default-features = false, features = [ "small_rng" ], optional = true }
|
||||||
id3 = { version = "1.10.0", optional = true }
|
id3 = { version = "1.10.0", optional = true }
|
||||||
mp4ameta = { version = "0.11.0", optional = true }
|
mp4ameta = { version = "0.11.0", optional = true }
|
||||||
metaflac = { version = "0.2.5", optional = true }
|
metaflac = { version = "0.2.5", optional = true }
|
||||||
bmp-rust = "0.5.0"
|
|
||||||
pty-process = { version = "0.5.1", optional = true }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = [ "main", "terminal" ]
|
default = [ "wm", "terminal" ]
|
||||||
main = [ "termion" ]
|
terminal = [ "linux" ]
|
||||||
terminal = [ "pty-process" ]
|
|
||||||
audio_player = [ "id3", "mp4ameta", "metaflac", "rand", "rodio" ]
|
audio_player = [ "id3", "mp4ameta", "metaflac", "rand", "rodio" ]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
@@ -39,8 +35,8 @@ lto = true
|
|||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "ming"
|
name = "ming"
|
||||||
path = "src/bin/main.rs"
|
path = "src/bin/wm.rs"
|
||||||
required-features = [ "main" ]
|
required-features = [ "wm" ]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "mingFiles_Audio_Player"
|
name = "mingFiles_Audio_Player"
|
||||||
|
|||||||
14
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).
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
@@ -49,9 +49,9 @@ Usage for most of the included windows and window-likes are included in `docs/wi
|
|||||||
|
|
||||||
## Running on Mobile Linux
|
## 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
|
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.
|
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
|
## 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.
|
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!
|
Obviously, don't run the executable with `sudo` or `doas`, or as the root user!
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 4.6 MiB |
|
Before Width: | Height: | Size: 70 B After Width: | Height: | Size: 70 B |
|
Before Width: | Height: | Size: 250 B After Width: | Height: | Size: 250 B |
|
Before Width: | Height: | Size: 250 B After Width: | Height: | Size: 250 B |
|
Before Width: | Height: | Size: 582 B After Width: | Height: | Size: 534 B |
|
Before Width: | Height: | Size: 630 B After Width: | Height: | Size: 534 B |
9
build.rs
@@ -4,7 +4,7 @@ use std::env;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
use blake2::{ Blake2b512, Digest };
|
use bitcoin_hashes::Sha512;
|
||||||
use bmp_rust::bmp::BMP;
|
use bmp_rust::bmp::BMP;
|
||||||
|
|
||||||
fn font_chars_to_alphas(dir: &str) {
|
fn font_chars_to_alphas(dir: &str) {
|
||||||
@@ -47,12 +47,11 @@ fn font_chars_to_alphas(dir: &str) {
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
//hash + "salt" password and add to build
|
//hash + "salt" password and add to build
|
||||||
let password = read_to_string("password.env").unwrap_or("password".to_string()).replace("\n", "") + "salt?sorrycryptographers";
|
let password = read_to_string("password.env").unwrap_or("password".to_string()).replace("\n", "") + "salt?sorrycryptographers!1!";
|
||||||
let mut hasher = Blake2b512::new();
|
let hash = Sha512::hash(password.as_bytes()).to_byte_array();
|
||||||
hasher.update(password.as_bytes());
|
|
||||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
let dest_path = Path::new(&out_dir).join("password.rs");
|
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
|
//process bmps
|
||||||
for entry in read_dir("./bmps").unwrap() {
|
for entry in read_dir("./bmps").unwrap() {
|
||||||
let path = entry.unwrap().path();
|
let path = entry.unwrap().path();
|
||||||
|
|||||||
@@ -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.
|
Expect to see more dependencies in Cargo.toml eliminated soon.
|
||||||
|
|
||||||
PS:
|
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)
|
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
|
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!
|
||||||
|
|||||||
3
docs/system/integration.md
Normal file
@@ -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.
|
||||||
@@ -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.
|
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(<copy_string>))`, 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!
|
## Hello, World!
|
||||||
|
|
||||||
A minimal example using `ming-wm-lib`.
|
A minimal example using `ming-wm-lib`.
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ Type to write commands, backspace to delete last character, and enter to run com
|
|||||||
|
|
||||||
Tab completion is supported for the `<dir>` and `<dir / playlist file>` arguments.
|
Tab completion is supported for the `<dir>` and `<dir / playlist file>` arguments.
|
||||||
|
|
||||||
|
The copy shortcut will copy the currently playing song's file name, if there is a currently playing song.
|
||||||
|
|
||||||
## Playlists
|
## Playlists
|
||||||
|
|
||||||
Example playlist file:
|
Example playlist file:
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ To get sudo to read from stdin, the `-S` option will need to be used (eg, `sudo
|
|||||||
|
|
||||||
## Copy / Paste
|
## Copy / Paste
|
||||||
|
|
||||||
This window-like supports the paste [shortcut](../system/shortcuts.md) (`Alt+P`) if in INPUT or STDIN mode.
|
This window-like supports the paste [shortcut](../system/shortcuts.md) (`Alt+P`) if in INPUT or STDIN mode. The copy shortcut will copy the output of the last ran command.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
|||||||
108
linux/src/input.rs
Normal file
@@ -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<Self, Self::Error> {
|
||||||
|
//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<Self, ()> {
|
||||||
|
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<Self::Item> {
|
||||||
|
//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::<input_event>();
|
||||||
|
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) };
|
||||||
|
}
|
||||||
|
}
|
||||||
89
linux/src/keys.rs
Normal file
@@ -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<Bytes<StdinLock<'a>>>,
|
||||||
|
receiver: Receiver<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Self::Item> {
|
||||||
|
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+<char> sends Esc+<char>
|
||||||
|
Key::Alt(char::from(n.unwrap()))
|
||||||
|
} else {
|
||||||
|
Key::Esc
|
||||||
|
}
|
||||||
|
},
|
||||||
|
127 => {
|
||||||
|
Key::Backspace
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
Key::Char(char::from(first))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +1,5 @@
|
|||||||
pub mod fb;
|
pub mod fb;
|
||||||
|
pub mod raw;
|
||||||
|
pub mod keys;
|
||||||
|
pub mod pty;
|
||||||
|
pub mod input;
|
||||||
|
|||||||
55
linux/src/pty.rs
Normal file
@@ -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<Child> {
|
||||||
|
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)))
|
||||||
|
}
|
||||||
|
}
|
||||||
65
linux/src/raw.rs
Normal file
@@ -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<termios, ()> {
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ming-wm-lib"
|
name = "ming-wm-lib"
|
||||||
version = "0.1.6"
|
version = "0.2.0"
|
||||||
repository = "https://github.com/stjet/ming-wm"
|
repository = "https://github.com/stjet/ming-wm"
|
||||||
description = "library for building windows for ming-wm in rust"
|
description = "library for building windows for ming-wm in rust"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ use std::vec;
|
|||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
use crate::components::Component;
|
use crate::components::Component;
|
||||||
use ming_wm_lib::framebuffer_types::{ Dimensions, Point };
|
use crate::framebuffer_types::{ Dimensions, Point };
|
||||||
use ming_wm_lib::themes::ThemeInfo;
|
use crate::themes::ThemeInfo;
|
||||||
use ming_wm_lib::messages::WindowMessage;
|
use crate::messages::WindowMessage;
|
||||||
use ming_wm_lib::window_manager_types::DrawInstructions;
|
use crate::window_manager_types::DrawInstructions;
|
||||||
|
|
||||||
pub struct HighlightButton<T> {
|
pub struct HighlightButton<T> {
|
||||||
name_: String,
|
name_: String,
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
use ming_wm_lib::themes::ThemeInfo;
|
use crate::themes::ThemeInfo;
|
||||||
use ming_wm_lib::messages::WindowMessage;
|
use crate::messages::WindowMessage;
|
||||||
use ming_wm_lib::window_manager_types::DrawInstructions;
|
use crate::window_manager_types::DrawInstructions;
|
||||||
|
|
||||||
pub mod toggle_button;
|
pub mod toggle_button;
|
||||||
pub mod highlight_button;
|
pub mod highlight_button;
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
use std::vec;
|
use std::vec;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
|
use crate::framebuffer_types::{ Dimensions, Point };
|
||||||
use ming_wm_lib::framebuffer_types::{ Dimensions, Point };
|
use crate::themes::ThemeInfo;
|
||||||
use ming_wm_lib::themes::ThemeInfo;
|
use crate::messages::WindowMessage;
|
||||||
use ming_wm_lib::messages::WindowMessage;
|
use crate::window_manager_types::DrawInstructions;
|
||||||
use ming_wm_lib::window_manager_types::DrawInstructions;
|
use crate::utils::calc_actual_lines;
|
||||||
use ming_wm_lib::utils::calc_actual_lines;
|
|
||||||
use crate::components::Component;
|
use crate::components::Component;
|
||||||
|
|
||||||
const MONO_WIDTH: u8 = 10;
|
const MONO_WIDTH: u8 = 10;
|
||||||
@@ -2,10 +2,10 @@ use std::vec;
|
|||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
use crate::components::Component;
|
use crate::components::Component;
|
||||||
use ming_wm_lib::framebuffer_types::{ Dimensions, Point };
|
use crate::framebuffer_types::{ Dimensions, Point };
|
||||||
use ming_wm_lib::themes::ThemeInfo;
|
use crate::themes::ThemeInfo;
|
||||||
use ming_wm_lib::messages::WindowMessage;
|
use crate::messages::WindowMessage;
|
||||||
use ming_wm_lib::window_manager_types::DrawInstructions;
|
use crate::window_manager_types::DrawInstructions;
|
||||||
|
|
||||||
const MONO_WIDTH: u8 = 10;
|
const MONO_WIDTH: u8 = 10;
|
||||||
|
|
||||||
@@ -2,10 +2,10 @@ use std::vec;
|
|||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
use crate::components::Component;
|
use crate::components::Component;
|
||||||
use ming_wm_lib::framebuffer_types::{ Dimensions, Point };
|
use crate::framebuffer_types::{ Dimensions, Point };
|
||||||
use ming_wm_lib::themes::ThemeInfo;
|
use crate::themes::ThemeInfo;
|
||||||
use ming_wm_lib::messages::WindowMessage;
|
use crate::messages::WindowMessage;
|
||||||
use ming_wm_lib::window_manager_types::DrawInstructions;
|
use crate::window_manager_types::DrawInstructions;
|
||||||
|
|
||||||
pub struct ToggleButton<T> {
|
pub struct ToggleButton<T> {
|
||||||
name_: String,
|
name_: String,
|
||||||
@@ -58,9 +58,11 @@ pub fn listen(mut window_like: impl WindowLike) {
|
|||||||
let arg = &parts.collect::<Vec<&str>>().join(" ");
|
let arg = &parts.collect::<Vec<&str>>().join(" ");
|
||||||
let output = match method {
|
let output = match method {
|
||||||
"handle_message" => {
|
"handle_message" => {
|
||||||
|
//newlines allowed for ClipboardCopy, but represented by the Linear A char
|
||||||
window_like.handle_message(WindowMessage::deserialize(arg).unwrap()).serialize().to_string()
|
window_like.handle_message(WindowMessage::deserialize(arg).unwrap()).serialize().to_string()
|
||||||
},
|
},
|
||||||
"draw" => {
|
"draw" => {
|
||||||
|
//newlines never allowed
|
||||||
window_like.draw(&ThemeInfo::deserialize(arg).unwrap()).serialize().replace("\n", "").to_string()
|
window_like.draw(&ThemeInfo::deserialize(arg).unwrap()).serialize().replace("\n", "").to_string()
|
||||||
},
|
},
|
||||||
"title" => {
|
"title" => {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ pub mod themes;
|
|||||||
pub mod serialize;
|
pub mod serialize;
|
||||||
pub mod messages;
|
pub mod messages;
|
||||||
pub mod ipc;
|
pub mod ipc;
|
||||||
|
pub mod components;
|
||||||
pub mod dirs;
|
pub mod dirs;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
pub mod logging;
|
pub mod logging;
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ impl Serializable for WindowMessageResponse {
|
|||||||
WindowMessageResponse::Request(req) => {
|
WindowMessageResponse::Request(req) => {
|
||||||
let req = match req {
|
let req = match req {
|
||||||
WindowManagerRequest::OpenWindow(name) => format!("OpenWindow/{}", name),
|
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::CloseStartMenu => "CloseStartMenu".to_string(),
|
||||||
WindowManagerRequest::Unlock => "Unlock".to_string(),
|
WindowManagerRequest::Unlock => "Unlock".to_string(),
|
||||||
WindowManagerRequest::Lock => "Lock".to_string(),
|
WindowManagerRequest::Lock => "Lock".to_string(),
|
||||||
@@ -646,7 +646,7 @@ impl Serializable for WindowMessage {
|
|||||||
"FullscreenWindow" => Some(ShortcutType::FullscreenWindow),
|
"FullscreenWindow" => Some(ShortcutType::FullscreenWindow),
|
||||||
"HalfWidthWindow" => Some(ShortcutType::HalfWidthWindow),
|
"HalfWidthWindow" => Some(ShortcutType::HalfWidthWindow),
|
||||||
"ClipboardCopy" => Some(ShortcutType::ClipboardCopy),
|
"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,
|
_ => None,
|
||||||
};
|
};
|
||||||
if let Some(shortcut) = shortcut {
|
if let Some(shortcut) = shortcut {
|
||||||
|
|||||||
@@ -214,3 +214,16 @@ pub fn path_autocomplete(current_path: &str, partial_path: &str) -> Option<Strin
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_all_files(dir: PathBuf) -> Vec<PathBuf> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,13 +14,12 @@ use mp4ameta;
|
|||||||
use metaflac;
|
use metaflac;
|
||||||
|
|
||||||
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType };
|
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::framebuffer_types::Dimensions;
|
||||||
use ming_wm_lib::themes::ThemeInfo;
|
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::dirs::home;
|
||||||
use ming_wm_lib::ipc::listen;
|
use ming_wm_lib::ipc::listen;
|
||||||
use ming_wm::fs::get_all_files;
|
|
||||||
|
|
||||||
fn get_artist(path: &PathBuf) -> Option<String> {
|
fn get_artist(path: &PathBuf) -> Option<String> {
|
||||||
let ext = path.extension().unwrap();
|
let ext = path.extension().unwrap();
|
||||||
@@ -80,7 +79,6 @@ pub struct AudioPlayer {
|
|||||||
|
|
||||||
impl WindowLike for AudioPlayer {
|
impl WindowLike for AudioPlayer {
|
||||||
fn handle_message(&mut self, message: WindowMessage) -> WindowMessageResponse {
|
fn handle_message(&mut self, message: WindowMessage) -> WindowMessageResponse {
|
||||||
//
|
|
||||||
match message {
|
match message {
|
||||||
WindowMessage::Init(dimensions) => {
|
WindowMessage::Init(dimensions) => {
|
||||||
self.dimensions = dimensions;
|
self.dimensions = dimensions;
|
||||||
@@ -110,11 +108,34 @@ impl WindowLike for AudioPlayer {
|
|||||||
} else {
|
} else {
|
||||||
return WindowMessageResponse::DoNothing;
|
return WindowMessageResponse::DoNothing;
|
||||||
}
|
}
|
||||||
} else {
|
} else if key_press.is_regular() {
|
||||||
self.command += &key_press.key.to_string();
|
self.command += &key_press.key.to_string();
|
||||||
|
} else {
|
||||||
|
return WindowMessageResponse::DoNothing
|
||||||
}
|
}
|
||||||
WindowMessageResponse::JustRedraw
|
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
|
WindowMessageResponse::DoNothing
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -457,11 +457,21 @@ impl WindowLike for Malvim {
|
|||||||
ShortcutType::ClipboardPaste(copy_string) => {
|
ShortcutType::ClipboardPaste(copy_string) => {
|
||||||
if self.mode == Mode::Insert {
|
if self.mode == Mode::Insert {
|
||||||
let current_file = &mut self.files[self.current_file_index];
|
let current_file = &mut self.files[self.current_file_index];
|
||||||
let line = ¤t_file.content[current_file.line_pos];
|
for (i, cs) in copy_string.split("\n").enumerate() {
|
||||||
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());
|
if i == 0 {
|
||||||
current_file.cursor_pos += copy_string.len();
|
//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_top_line_pos();
|
||||||
self.calc_current(); //too over zealous but whatever
|
self.calc_current();
|
||||||
self.files[self.current_file_index].changed = true;
|
self.files[self.current_file_index].changed = true;
|
||||||
WindowMessageResponse::JustRedraw
|
WindowMessageResponse::JustRedraw
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -3,15 +3,16 @@ use std::vec;
|
|||||||
use std::sync::mpsc::{ channel, Receiver, Sender };
|
use std::sync::mpsc::{ channel, Receiver, Sender };
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::process::{ Child, Stdio };
|
use std::process::{ Child, Stdio };
|
||||||
|
use std::process::Command;
|
||||||
use std::io::{ Read, Write };
|
use std::io::{ Read, Write };
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::fmt;
|
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::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::framebuffer_types::Dimensions;
|
||||||
use ming_wm_lib::themes::ThemeInfo;
|
use ming_wm_lib::themes::ThemeInfo;
|
||||||
use ming_wm_lib::utils::{ concat_paths, path_autocomplete, Substring };
|
use ming_wm_lib::utils::{ concat_paths, path_autocomplete, Substring };
|
||||||
@@ -46,6 +47,11 @@ fn strip_ansi_escape_codes(line: String) -> String {
|
|||||||
new_line
|
new_line
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bytes_to_string(bytes: Vec<u8>) -> String {
|
||||||
|
let bytes_len = bytes.len();
|
||||||
|
String::from_utf8(bytes).unwrap_or("?".repeat(bytes_len))
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, PartialEq)]
|
#[derive(Default, PartialEq)]
|
||||||
enum Mode {
|
enum Mode {
|
||||||
#[default]
|
#[default]
|
||||||
@@ -78,6 +84,7 @@ pub struct Terminal {
|
|||||||
current_path: String,
|
current_path: String,
|
||||||
running_process: Option<Child>,
|
running_process: Option<Child>,
|
||||||
process_current_line: Vec<u8>, //bytes of line
|
process_current_line: Vec<u8>, //bytes of line
|
||||||
|
output: String, //current or previous running output of command
|
||||||
pty_outerr_rx: Option<Receiver<u8>>,
|
pty_outerr_rx: Option<Receiver<u8>>,
|
||||||
pty_in_tx: Option<Sender<String>>,
|
pty_in_tx: Option<Sender<String>>,
|
||||||
history: Vec<String>,
|
history: Vec<String>,
|
||||||
@@ -116,6 +123,7 @@ impl WindowLike for Terminal {
|
|||||||
self.history_index = None;
|
self.history_index = None;
|
||||||
self.mode = self.process_command();
|
self.mode = self.process_command();
|
||||||
self.current_input = String::new();
|
self.current_input = String::new();
|
||||||
|
self.output = String::new();
|
||||||
} else if key_press.key == '\t' { //tab
|
} else if key_press.key == '\t' { //tab
|
||||||
//autocomplete assuming it's a file system path
|
//autocomplete assuming it's a file system path
|
||||||
//...mostly working
|
//...mostly working
|
||||||
@@ -144,12 +152,17 @@ impl WindowLike for Terminal {
|
|||||||
loop {
|
loop {
|
||||||
if let Ok(ci) = self.pty_outerr_rx.as_mut().unwrap().recv_timeout(Duration::from_millis(5)) {
|
if let Ok(ci) = self.pty_outerr_rx.as_mut().unwrap().recv_timeout(Duration::from_millis(5)) {
|
||||||
if char::from(ci) == '\n' {
|
if char::from(ci) == '\n' {
|
||||||
let pcl_len = self.process_current_line.len();
|
let append_line = strip_ansi_escape_codes(bytes_to_string(self.process_current_line.clone()));
|
||||||
self.lines.push(strip_ansi_escape_codes(String::from_utf8(self.process_current_line.clone()).unwrap_or("?".repeat(pcl_len))));
|
self.output += &append_line;
|
||||||
|
self.output += "\n";
|
||||||
|
self.lines.push(append_line);
|
||||||
self.process_current_line = Vec::new();
|
self.process_current_line = Vec::new();
|
||||||
} else if char::from(ci) == '\r' {
|
} else if char::from(ci) == '\r' {
|
||||||
//for now, ignore
|
//for now, ignore
|
||||||
//
|
//
|
||||||
|
} else if char::from(ci) == '\t' {
|
||||||
|
//for now, interpret as space
|
||||||
|
self.process_current_line.push(b' ');
|
||||||
} else {
|
} else {
|
||||||
self.process_current_line.push(ci);
|
self.process_current_line.push(ci);
|
||||||
}
|
}
|
||||||
@@ -163,7 +176,14 @@ impl WindowLike for Terminal {
|
|||||||
//process exited
|
//process exited
|
||||||
self.pty_outerr_rx = None;
|
self.pty_outerr_rx = None;
|
||||||
self.mode = Mode::Input;
|
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;
|
changed = true;
|
||||||
} else {
|
} else {
|
||||||
if key_press.key == 'i' {
|
if key_press.key == 'i' {
|
||||||
@@ -184,8 +204,9 @@ impl WindowLike for Terminal {
|
|||||||
} else if key_press.is_enter() {
|
} else if key_press.is_enter() {
|
||||||
let _ = self.pty_in_tx.as_mut().unwrap().send(self.current_stdin_input.clone());
|
let _ = self.pty_in_tx.as_mut().unwrap().send(self.current_stdin_input.clone());
|
||||||
self.mode = Mode::Running;
|
self.mode = Mode::Running;
|
||||||
let pcl_len = self.process_current_line.len();
|
let append_line = strip_ansi_escape_codes(bytes_to_string(self.process_current_line.clone()) + &self.current_stdin_input);
|
||||||
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.output += &append_line;
|
||||||
|
self.lines.push(append_line);
|
||||||
self.current_stdin_input = String::new();
|
self.current_stdin_input = String::new();
|
||||||
self.process_current_line = Vec::new();
|
self.process_current_line = Vec::new();
|
||||||
} else if key_press.is_backspace() {
|
} else if key_press.is_backspace() {
|
||||||
@@ -207,6 +228,12 @@ impl WindowLike for Terminal {
|
|||||||
//kills and running_process is now None
|
//kills and running_process is now None
|
||||||
let _ = self.running_process.take().unwrap().kill();
|
let _ = self.running_process.take().unwrap().kill();
|
||||||
self.mode = Mode::Input;
|
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
|
WindowMessageResponse::JustRedraw
|
||||||
} else if self.mode == Mode::Input && (key_press.key == 'p' || key_press.key == 'n') {
|
} 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
|
//only the last command is saved unlike other terminals. good enough for me
|
||||||
@@ -228,6 +255,7 @@ impl WindowLike for Terminal {
|
|||||||
},
|
},
|
||||||
WindowMessage::Shortcut(shortcut) => {
|
WindowMessage::Shortcut(shortcut) => {
|
||||||
match shortcut {
|
match shortcut {
|
||||||
|
ShortcutType::ClipboardCopy => WindowMessageResponse::Request(WindowManagerRequest::ClipboardCopy(self.output.clone())),
|
||||||
ShortcutType::ClipboardPaste(copy_string) => {
|
ShortcutType::ClipboardPaste(copy_string) => {
|
||||||
if self.mode == Mode::Input || self.mode == Mode::Stdin {
|
if self.mode == Mode::Input || self.mode == Mode::Stdin {
|
||||||
if self.mode == Mode::Input {
|
if self.mode == Mode::Input {
|
||||||
@@ -331,11 +359,13 @@ impl Terminal {
|
|||||||
}
|
}
|
||||||
Mode::Input
|
Mode::Input
|
||||||
} else {
|
} else {
|
||||||
let (pty, pts) = blocking::open().unwrap();
|
let (pty, pts) = open_pty().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 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();
|
let (tx1, rx1) = channel();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
for ci in pty.bytes() {
|
for ci in pty.file.bytes() {
|
||||||
if let Ok(ci) = ci {
|
if let Ok(ci) = ci {
|
||||||
tx1.send(ci).unwrap();
|
tx1.send(ci).unwrap();
|
||||||
} else {
|
} else {
|
||||||
@@ -378,8 +408,7 @@ impl Terminal {
|
|||||||
//must_add_current_line will be false
|
//must_add_current_line will be false
|
||||||
"$ ".to_string() + &self.current_input + "█"
|
"$ ".to_string() + &self.current_input + "█"
|
||||||
} else {
|
} else {
|
||||||
let pcl_len = self.process_current_line.len();
|
strip_ansi_escape_codes(bytes_to_string(self.process_current_line.clone()) + &self.current_stdin_input.clone() + "█")
|
||||||
strip_ansi_escape_codes(String::from_utf8(self.process_current_line.clone()).unwrap_or("?".repeat(pcl_len))) + &self.current_stdin_input.clone() + "█"
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.lines[line_num].clone()
|
self.lines[line_num].clone()
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
use std::process::{ Command, Stdio };
|
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::io::{ stdin, stdout, BufReader, BufRead, Write };
|
use std::io::{ stdin, stdout, Write };
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
use linux::fb::Framebuffer;
|
use linux::fb::Framebuffer;
|
||||||
use termion::input::TermRead;
|
use linux::raw::RawStdout;
|
||||||
use termion::raw::IntoRawMode;
|
use linux::keys::{ RawStdin, Key };
|
||||||
use termion::event::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::window_manager_types::KeyChar;
|
||||||
use ming_wm_lib::messages::*;
|
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 CLEAR_ALL: &'static str = "\x1b[2J";
|
||||||
const HIDE_CURSOR: &'static str = "\x1b[?25l";
|
const HIDE_CURSOR: &'static str = "\x1b[?25l";
|
||||||
@@ -30,10 +31,10 @@ fn key_to_char(key: Key) -> Option<KeyChar> {
|
|||||||
Key::Ctrl(c) => Some(KeyChar::Ctrl(c)),
|
Key::Ctrl(c) => Some(KeyChar::Ctrl(c)),
|
||||||
Key::Backspace => Some(KeyChar::Press('𐘁')),
|
Key::Backspace => Some(KeyChar::Press('𐘁')),
|
||||||
Key::Esc => Some(KeyChar::Press('𐘃')),
|
Key::Esc => Some(KeyChar::Press('𐘃')),
|
||||||
Key::Up => Some(KeyChar::Press('𐙘')),
|
Key::ArrowUp => Some(KeyChar::Press('𐙘')),
|
||||||
Key::Down => Some(KeyChar::Press('𐘞')),
|
Key::ArrowDown => Some(KeyChar::Press('𐘞')),
|
||||||
Key::Left => Some(KeyChar::Press('𐙣')),
|
Key::ArrowLeft => Some(KeyChar::Press('𐙣')),
|
||||||
Key::Right => Some(KeyChar::Press('𐙥')),
|
Key::ArrowRight => Some(KeyChar::Press('𐙥')),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,15 +72,16 @@ fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) {
|
|||||||
|
|
||||||
writer.init(framebuffer_info.clone());
|
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);
|
wm.draw(None, false);
|
||||||
|
|
||||||
@@ -89,9 +91,9 @@ fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) {
|
|||||||
|
|
||||||
//read key presses
|
//read key presses
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let stdin = stdin().lock();
|
let stdin = RawStdin::new(stdin());
|
||||||
for c in stdin.keys() {
|
for c in stdin {
|
||||||
if let Some(kc) = key_to_char(c.unwrap()) {
|
if let Some(kc) = key_to_char(c) {
|
||||||
//do not allow exit when locked unless debugging
|
//do not allow exit when locked unless debugging
|
||||||
//if kc == KeyChar::Alt('E') {
|
//if kc == KeyChar::Alt('E') {
|
||||||
if kc == KeyChar::Alt('E') {
|
if kc == KeyChar::Alt('E') {
|
||||||
@@ -110,34 +112,35 @@ fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) {
|
|||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
//spawn evtest, parse it for touch coords
|
//spawn evtest, parse it for touch coords
|
||||||
if touch {
|
if touch {
|
||||||
let mut evtest = Command::new("evtest").arg("/dev/input/by-path/first-touchscreen").stdout(Stdio::piped()).spawn().unwrap();
|
let mut events = Input::new("/dev/input/by-path/first-touchscreen").unwrap(); //panics in threads don't matter in this case
|
||||||
let reader = BufReader::new(evtest.stdout.as_mut().unwrap());
|
|
||||||
let mut x: Option<usize> = None;
|
let mut x: Option<usize> = None;
|
||||||
let mut y: Option<usize> = None;
|
let mut y: Option<usize> = None;
|
||||||
for line in reader.lines() {
|
loop {
|
||||||
let line = line.unwrap();
|
let event = events.next();
|
||||||
if line.contains("ABS_X), value ") || line.contains("ABS_Y), value ") {
|
if let Some(event) = event {
|
||||||
let value: Vec<_> = line.split("), value ").collect();
|
//ABS_X = 0, ABS_Y = 1
|
||||||
let value = value[value.len() - 1].parse::<usize>().unwrap();
|
if event.type_ == EventType::EV_ABS && (event.code == 0 || event.code == 1) {
|
||||||
if line.contains("ABS_X") {
|
if event.code == 0 {
|
||||||
x = Some(value);
|
x = Some(event.value as usize); //event.value is u16 so this should be fine. unless usize is u8, lmao
|
||||||
} else {
|
|
||||||
y = Some(value);
|
|
||||||
}
|
|
||||||
if x.is_some() && y.is_some() {
|
|
||||||
let (x2, y2) = if rotate {
|
|
||||||
(dimensions[0] - y.unwrap(), x.unwrap())
|
|
||||||
} else {
|
} else {
|
||||||
(x.unwrap(), y.unwrap())
|
y = Some(event.value as usize);
|
||||||
};
|
}
|
||||||
//top right, clear
|
if x.is_some() && y.is_some() {
|
||||||
//useful sometimes, I think.
|
let (x2, y2) = if rotate {
|
||||||
if x2 > dimensions[0] - 100 && y2 < 100 {
|
(dimensions[0] - y.unwrap(), x.unwrap())
|
||||||
tx1.send(ThreadMessage::Clear).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));
|
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::KeyChar(kc) => wm.handle_message(WindowManagerMessage::KeyChar(kc.clone())),
|
||||||
ThreadMessage::Touch(x, y) => wm.handle_message(WindowManagerMessage::Touch(x, y)),
|
ThreadMessage::Touch(x, y) => wm.handle_message(WindowManagerMessage::Touch(x, y)),
|
||||||
ThreadMessage::Clear => {
|
ThreadMessage::Clear => {
|
||||||
write!(stdout, "{}", CLEAR_ALL).unwrap();
|
write!(stdout.stdout, "{}", CLEAR_ALL).unwrap();
|
||||||
stdout.flush().unwrap();
|
stdout.stdout.flush().unwrap();
|
||||||
},
|
},
|
||||||
ThreadMessage::Exit => {
|
ThreadMessage::Exit => {
|
||||||
if !wm.locked {
|
if !wm.locked {
|
||||||
write!(stdout, "{}", SHOW_CURSOR).unwrap();
|
write!(stdout.stdout, "{}", SHOW_CURSOR).unwrap();
|
||||||
stdout.suspend_raw_mode().unwrap();
|
stdout.exit_raw_mode().unwrap();
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
12
wm/Cargo.toml
Normal file
@@ -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"
|
||||||
@@ -7,8 +7,8 @@ use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse };
|
|||||||
use ming_wm_lib::framebuffer_types::Dimensions;
|
use ming_wm_lib::framebuffer_types::Dimensions;
|
||||||
use ming_wm_lib::themes::ThemeInfo;
|
use ming_wm_lib::themes::ThemeInfo;
|
||||||
use ming_wm_lib::dirs::exe_dir;
|
use ming_wm_lib::dirs::exe_dir;
|
||||||
use crate::components::Component;
|
use ming_wm_lib::components::Component;
|
||||||
use crate::components::paragraph::Paragraph;
|
use ming_wm_lib::components::paragraph::Paragraph;
|
||||||
|
|
||||||
pub struct About {
|
pub struct About {
|
||||||
dimensions: Dimensions,
|
dimensions: Dimensions,
|
||||||
@@ -8,8 +8,8 @@ use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse };
|
|||||||
use ming_wm_lib::dirs::exe_dir;
|
use ming_wm_lib::dirs::exe_dir;
|
||||||
use ming_wm_lib::framebuffer_types::Dimensions;
|
use ming_wm_lib::framebuffer_types::Dimensions;
|
||||||
use ming_wm_lib::themes::ThemeInfo;
|
use ming_wm_lib::themes::ThemeInfo;
|
||||||
use crate::components::paragraph::Paragraph;
|
use ming_wm_lib::components::paragraph::Paragraph;
|
||||||
use crate::components::Component;
|
use ming_wm_lib::components::Component;
|
||||||
|
|
||||||
pub struct Help {
|
pub struct Help {
|
||||||
dimensions: Dimensions,
|
dimensions: Dimensions,
|
||||||
@@ -5,15 +5,14 @@ use ming_wm_lib::framebuffer_types::Dimensions;
|
|||||||
use ming_wm_lib::themes::ThemeInfo;
|
use ming_wm_lib::themes::ThemeInfo;
|
||||||
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest };
|
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest };
|
||||||
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType };
|
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType };
|
||||||
use blake2::{ Blake2b512, Digest };
|
use bitcoin_hashes::Sha512;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/password.rs"));
|
|
||||||
|
|
||||||
//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];
|
//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 {
|
pub struct LockScreen {
|
||||||
dimensions: Dimensions,
|
dimensions: Dimensions,
|
||||||
input_password: String,
|
input_password: String,
|
||||||
|
password_hash: [u8; 64],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowLike for LockScreen {
|
impl WindowLike for LockScreen {
|
||||||
@@ -26,9 +25,7 @@ impl WindowLike for LockScreen {
|
|||||||
WindowMessage::KeyPress(key_press) => {
|
WindowMessage::KeyPress(key_press) => {
|
||||||
if key_press.is_enter() {
|
if key_press.is_enter() {
|
||||||
//check password
|
//check password
|
||||||
let mut hasher = Blake2b512::new();
|
if Sha512::hash((self.input_password.clone() + "salt?sorrycryptographers!1!").as_bytes()).to_byte_array() == self.password_hash {
|
||||||
hasher.update((self.input_password.clone() + "salt?sorrycryptographers").as_bytes());
|
|
||||||
if hasher.finalize() == PASSWORD_HASH.into() {
|
|
||||||
WindowMessageResponse::Request(WindowManagerRequest::Unlock)
|
WindowMessageResponse::Request(WindowManagerRequest::Unlock)
|
||||||
} else {
|
} else {
|
||||||
self.input_password = String::new();
|
self.input_password = String::new();
|
||||||
@@ -73,10 +70,11 @@ impl WindowLike for LockScreen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl LockScreen {
|
impl LockScreen {
|
||||||
pub fn new() -> Self {
|
pub fn new(password_hash: [u8; 64]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
dimensions: [0, 0],
|
dimensions: [0, 0],
|
||||||
input_password: String::new(),
|
input_password: String::new(),
|
||||||
|
password_hash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,8 +7,8 @@ use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, WindowManager
|
|||||||
use ming_wm_lib::framebuffer_types::Dimensions;
|
use ming_wm_lib::framebuffer_types::Dimensions;
|
||||||
use ming_wm_lib::themes::ThemeInfo;
|
use ming_wm_lib::themes::ThemeInfo;
|
||||||
use ming_wm_lib::utils::point_inside;
|
use ming_wm_lib::utils::point_inside;
|
||||||
use crate::components::Component;
|
use ming_wm_lib::components::Component;
|
||||||
use crate::components::press_button::PressButton;
|
use ming_wm_lib::components::press_button::PressButton;
|
||||||
|
|
||||||
//seems like framebuffer only updates if (real) key is pressed...
|
//seems like framebuffer only updates if (real) key is pressed...
|
||||||
//on mobile, volume down button seems to work but is annoying
|
//on mobile, volume down button seems to work but is annoying
|
||||||
@@ -7,9 +7,9 @@ use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, WindowManager
|
|||||||
use ming_wm_lib::framebuffer_types::Dimensions;
|
use ming_wm_lib::framebuffer_types::Dimensions;
|
||||||
use ming_wm_lib::themes::ThemeInfo;
|
use ming_wm_lib::themes::ThemeInfo;
|
||||||
use ming_wm_lib::dirs::exe_dir;
|
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::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"];
|
static CATEGORIES: [&'static str; 9] = ["About", "Utils", "Games", "Editing", "Files", "Internet", "Misc", "Help", "Lock"];
|
||||||
|
|
||||||
@@ -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::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest, ShortcutType, InfoType, WindowsVec };
|
||||||
use ming_wm_lib::framebuffer_types::Dimensions;
|
use ming_wm_lib::framebuffer_types::Dimensions;
|
||||||
use ming_wm_lib::themes::ThemeInfo;
|
use ming_wm_lib::themes::ThemeInfo;
|
||||||
use crate::components::Component;
|
use ming_wm_lib::components::Component;
|
||||||
use crate::components::toggle_button::ToggleButton;
|
use ming_wm_lib::components::toggle_button::ToggleButton;
|
||||||
|
|
||||||
const PADDING: usize = 4;
|
const PADDING: usize = 4;
|
||||||
const META_WIDTH: usize = 175; //of the window button
|
const META_WIDTH: usize = 175; //of the window button
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
use std::fs::{ read_dir, File };
|
use std::fs::{ read_dir, File };
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
@@ -34,19 +33,6 @@ pub fn get_font_char_from_fonts(fonts: &[String], c: char) -> (char, Vec<Vec<u8>
|
|||||||
get_font_char(&p, '?').unwrap_or(('?', vec![vec![0]], 0))
|
get_font_char(&p, '?').unwrap_or(('?', vec![vec![0]], 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all_files(dir: PathBuf) -> Vec<PathBuf> {
|
|
||||||
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<Display name, file name>
|
//Category, Vec<Display name, file name>
|
||||||
pub type ExeWindowInfos = HashMap<String, Vec<(String, String)>>;
|
pub type ExeWindowInfos = HashMap<String, Vec<(String, String)>>;
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
|
pub use linux;
|
||||||
|
|
||||||
pub mod framebuffer;
|
pub mod framebuffer;
|
||||||
pub mod window_manager;
|
pub mod window_manager;
|
||||||
pub mod components;
|
|
||||||
pub mod fs;
|
pub mod fs;
|
||||||
mod proxy_window_like;
|
mod proxy_window_like;
|
||||||
mod essential;
|
mod essential;
|
||||||
@@ -24,7 +24,6 @@ use crate::essential::start_menu::StartMenu;
|
|||||||
use crate::essential::about::About;
|
use crate::essential::about::About;
|
||||||
use crate::essential::help::Help;
|
use crate::essential::help::Help;
|
||||||
use crate::essential::onscreen_keyboard::OnscreenKeyboard;
|
use crate::essential::onscreen_keyboard::OnscreenKeyboard;
|
||||||
//use crate::logging::log;
|
|
||||||
|
|
||||||
//todo: a lot of the usize should be changed to u16
|
//todo: a lot of the usize should be changed to u16
|
||||||
|
|
||||||
@@ -40,6 +39,7 @@ struct WindowLikeInfo {
|
|||||||
id: usize,
|
id: usize,
|
||||||
window_like: WindowBox,
|
window_like: WindowBox,
|
||||||
top_left: Point,
|
top_left: Point,
|
||||||
|
old_top_left: Point,
|
||||||
dimensions: Dimensions,
|
dimensions: Dimensions,
|
||||||
workspace: Workspace,
|
workspace: Workspace,
|
||||||
fullscreen: bool,
|
fullscreen: bool,
|
||||||
@@ -65,12 +65,13 @@ pub struct WindowManager {
|
|||||||
current_workspace: u8,
|
current_workspace: u8,
|
||||||
framebuffer: Framebuffer,
|
framebuffer: Framebuffer,
|
||||||
clipboard: Option<String>,
|
clipboard: Option<String>,
|
||||||
|
password_hash: [u8; 64],
|
||||||
}
|
}
|
||||||
|
|
||||||
//1 is up, 2 is down
|
//1 is up, 2 is down
|
||||||
|
|
||||||
impl WindowManager {
|
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);
|
//println!("bg: {}x{}", dimensions[0], dimensions[1] - TASKBAR_HEIGHT - INDICATOR_HEIGHT);
|
||||||
let mut wm = WindowManager {
|
let mut wm = WindowManager {
|
||||||
writer: RefCell::new(writer),
|
writer: RefCell::new(writer),
|
||||||
@@ -86,6 +87,7 @@ impl WindowManager {
|
|||||||
current_workspace: 0,
|
current_workspace: 0,
|
||||||
framebuffer,
|
framebuffer,
|
||||||
clipboard: None,
|
clipboard: None,
|
||||||
|
password_hash,
|
||||||
};
|
};
|
||||||
wm.lock();
|
wm.lock();
|
||||||
wm.change_theme();
|
wm.change_theme();
|
||||||
@@ -103,6 +105,7 @@ impl WindowManager {
|
|||||||
id,
|
id,
|
||||||
window_like,
|
window_like,
|
||||||
top_left,
|
top_left,
|
||||||
|
old_top_left: top_left,
|
||||||
dimensions,
|
dimensions,
|
||||||
workspace: if subtype == WindowLikeType::Window {
|
workspace: if subtype == WindowLikeType::Window {
|
||||||
Workspace::Workspace(self.current_workspace)
|
Workspace::Workspace(self.current_workspace)
|
||||||
@@ -136,7 +139,7 @@ impl WindowManager {
|
|||||||
fn lock(&mut self) {
|
fn lock(&mut self) {
|
||||||
self.locked = true;
|
self.locked = true;
|
||||||
self.window_infos = Vec::new();
|
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) {
|
fn unlock(&mut self) {
|
||||||
@@ -456,17 +459,20 @@ impl WindowManager {
|
|||||||
let window_like = &self.window_infos[focused_index].window_like;
|
let window_like = &self.window_infos[focused_index].window_like;
|
||||||
if window_like.subtype() == WindowLikeType::Window && window_like.resizable() {
|
if window_like.subtype() == WindowLikeType::Window && window_like.resizable() {
|
||||||
//toggle fullscreen
|
//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
|
//todo: send message to window about resize
|
||||||
let new_dimensions;
|
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];
|
new_dimensions = [self.dimensions[0], self.dimensions[1] - TASKBAR_HEIGHT - INDICATOR_HEIGHT];
|
||||||
self.window_infos[focused_index].top_left = [0, INDICATOR_HEIGHT];
|
window_info.old_top_left = window_info.top_left;
|
||||||
redraw_ids = Some(vec![self.window_infos[focused_index].id]);
|
window_info.top_left = [0, INDICATOR_HEIGHT];
|
||||||
|
redraw_ids = Some(vec![window_info.id]);
|
||||||
} else {
|
} 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;
|
press_response = WindowMessageResponse::JustRedraw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||