3 Commits

Author SHA1 Message Date
c4876e5606 v1.2.3: add arg to fix framebuffer not redrawing
on some devices, the framebuffer will not redraw unless something is written to stdout, so the arg 'force-stdout' has been added to write spaces to stdout after every key press or touch event
2025-11-03 00:39:59 +00:00
Jon Dough
497beb7bb0 change video url 2025-09-28 04:41:06 +00:00
stjet
40f6795163 v1.2.2: text measuring improvements, minor code clean
fixed some clippy warnings, ignored many that I do not care about. also, added demo video and updated koxinga image
2025-09-28 04:39:41 +00:00
25 changed files with 198 additions and 121 deletions

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "ming-wm" name = "ming-wm"
version = "1.2.1" version = "1.2.3"
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"
@@ -11,6 +11,20 @@ default-run = "ming"
[workspace] [workspace]
members = [ "wm", "linux" ] members = [ "wm", "linux" ]
[workspace.lints.clippy]
len_zero = "allow"
comparison_to_empty = "allow"
manual_saturating_arithmetic = "allow"
result_unit_err = "allow"
needless_borrow = "allow"
needless_borrows_for_generic_args = "allow"
redundant_static_lifetimes = "allow"
collapsible_else_if = "allow"
too_many_arguments = "allow"
[lints]
workspace = true
[build-dependencies] [build-dependencies]
bmp-rust = "0.5.0" bmp-rust = "0.5.0"
bitcoin_hashes = { version = "0.16.0", default-features = false } bitcoin_hashes = { version = "0.16.0", default-features = false }

View File

@@ -1,8 +1,10 @@
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). 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.
![example 1](/docs/images/ws1.png) ![example 1](/docs/images/ws1.png)
![example 2](/docs/images/ws3.png) ![example 2](/docs/images/ws3.png)
https://github.com/user-attachments/assets/5a598287-27d5-40f2-9b5f-9c1e67cab2f5
The [Koxinga web browser](https://github.com/stjet/koxinga) can be separately installed. The [Koxinga web browser](https://github.com/stjet/koxinga) can be separately installed.
![koxinga browser example](/docs/images/koxinga.png) ![koxinga browser example](/docs/images/koxinga.png)
@@ -65,6 +67,20 @@ ming touch rotate
<image alt="mobile example" src="/docs/images/mobile.png" width="50%"> <image alt="mobile example" src="/docs/images/mobile.png" width="50%">
### Troubleshooting
If key presses do nothing, that is, if on the lockscreen, despite key presses (physical or of the OSK), nothing happens (no asterisk characters show up), try adding the `force-stdout` arg. For example:
```
ming force-stdout
```
Or:
```
ming touch force-stdout
```
## Philosophy ## Philosophy
See [/docs/philosophy.md](/docs/philosophy.md) for some hopefully interesting ramblings. See [/docs/philosophy.md](/docs/philosophy.md) for some hopefully interesting ramblings.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

After

Width:  |  Height:  |  Size: 122 KiB

View File

@@ -4,5 +4,18 @@ version = "0.1.0"
license = "GPL-3.0-or-later" license = "GPL-3.0-or-later"
edition = "2021" edition = "2021"
[lints.clippy]
len_zero = "allow"
comparison_to_empty = "allow"
manual_saturating_arithmetic = "allow"
result_unit_err = "allow"
needless_borrow = "allow"
needless_borrows_for_generic_args = "allow"
redundant_static_lifetimes = "allow"
collapsible_else_if = "allow"
too_many_arguments = "allow"
useless_conversion = "allow"
unnecessary_mut_passed = "allow"
[dependencies] [dependencies]
libc = "0.2" libc = "0.2"

View File

@@ -71,9 +71,9 @@ impl Iterator for RawStdin {
b'D' => Key::ArrowLeft, b'D' => Key::ArrowLeft,
_ => Key::Other(n), _ => Key::Other(n),
} }
} else if n.is_ok() { } else if let Ok(n) = n {
//Alt+<char> sends Esc+<char> //Alt+<char> sends Esc+<char>
Key::Alt(char::from(n.unwrap())) Key::Alt(char::from(n))
} else { } else {
Key::Esc Key::Esc
} }

View File

@@ -1,10 +1,21 @@
[package] [package]
name = "ming-wm-lib" name = "ming-wm-lib"
version = "0.2.2" version = "0.2.3"
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"
license = "GPL-3.0-or-later" license = "GPL-3.0-or-later"
edition = "2021" edition = "2021"
[lints.clippy]
len_zero = "allow"
comparison_to_empty = "allow"
manual_saturating_arithmetic = "allow"
result_unit_err = "allow"
needless_borrow = "allow"
needless_borrows_for_generic_args = "allow"
redundant_static_lifetimes = "allow"
collapsible_else_if = "allow"
too_many_arguments = "allow"
[dependencies] [dependencies]

View File

@@ -20,9 +20,9 @@ fn get_font_char(dir: &str, c: char) -> Option<FontCharInfo> {
let mut contents = String::new(); let mut contents = String::new();
file.read_to_string(&mut contents).unwrap(); file.read_to_string(&mut contents).unwrap();
let lines: Vec<&str> = contents.split("\n").collect(); let lines: Vec<&str> = contents.split("\n").collect();
for ln in 1..lines.len() { for ln in lines.iter().skip(1) {
//.unwrap_or(0) is important because zeroes are just empty //.unwrap_or(0) is important because zeroes are just empty
ch.push(lines[ln].replace(":", ",,,,").replace(";", ",,,").replace(".", ",,").split(",").map(|n| n.parse().unwrap_or(0)).collect()); ch.push(ln.replace(":", ",,,,").replace(";", ",,,").replace(".", ",,").split(",").map(|n| n.parse().unwrap_or(0)).collect());
} }
return Some(FontCharInfo { return Some(FontCharInfo {
c, c,
@@ -58,8 +58,7 @@ pub struct MeasureInfo {
pub width: usize, pub width: usize,
} }
/// Doesn't take into account `horiz_spacing`, which defaults to 1 per character pub fn measure_text(fonts: &[String], text: &str, horiz_spacing: Option<usize>) -> MeasureInfo {
pub fn measure_text(fonts: &[String], text: String) -> MeasureInfo {
let mut height = 0; let mut height = 0;
let mut width = 0; let mut width = 0;
for c in text.chars() { for c in text.chars() {
@@ -68,8 +67,27 @@ pub fn measure_text(fonts: &[String], text: String) -> MeasureInfo {
if c_height > height { if c_height > height {
height = c_height; height = c_height;
} }
width += i.width; width += i.width + horiz_spacing.unwrap_or(1);
} }
width -= horiz_spacing.unwrap_or(1);
MeasureInfo {
height,
width,
}
}
pub fn measure_text_with_cache(fc_getter: &mut CachedFontCharGetter, fonts: &[String], text: &str, horiz_spacing: Option<usize>) -> MeasureInfo {
let mut height = 0;
let mut width = 0;
for c in text.chars() {
let i = fc_getter.get(fonts, c);
let c_height = i.top_offset as usize + i.height;
if c_height > height {
height = c_height;
}
width += i.width + horiz_spacing.unwrap_or(1);
}
width -= horiz_spacing.unwrap_or(1);
MeasureInfo { MeasureInfo {
height, height,
width, width,
@@ -85,9 +103,10 @@ pub struct CachedFontCharGetter {
impl CachedFontCharGetter { impl CachedFontCharGetter {
pub fn new(max_cache_size: usize) -> Self { pub fn new(max_cache_size: usize) -> Self {
let mut s: Self = Default::default(); Self {
s.max_cache_size = max_cache_size; max_cache_size,
s ..Default::default()
}
} }
pub fn get(&mut self, fonts: &[String], c: char) -> FontCharInfo { pub fn get(&mut self, fonts: &[String], c: char) -> FontCharInfo {

View File

@@ -44,11 +44,7 @@ pub enum WindowMessageResponse {
impl WindowMessageResponse { impl WindowMessageResponse {
pub fn is_key_char_request(&self) -> bool { pub fn is_key_char_request(&self) -> bool {
if let WindowMessageResponse::Request(WindowManagerRequest::DoKeyChar(_)) = self { matches!(self, WindowMessageResponse::Request(WindowManagerRequest::DoKeyChar(_)))
true
} else {
false
}
} }
} }

View File

@@ -83,7 +83,7 @@ impl Serializable for ThemeInfo {
} }
fn deserialize(serialized: &str) -> Result<Self, ()> { fn deserialize(serialized: &str) -> Result<Self, ()> {
//strip newline at the end //strip newline at the end
let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized }; let serialized = serialized.strip_suffix("\n").unwrap_or(serialized);
let mut theme_info: ThemeInfo = Default::default(); let mut theme_info: ThemeInfo = Default::default();
let arrays = serialized.split(":"); let arrays = serialized.split(":");
//won't error or panic if less than 9... rest will just be black by default I guess //won't error or panic if less than 9... rest will just be black by default I guess
@@ -162,7 +162,7 @@ impl Serializable for WindowMessageResponse {
} }
fn deserialize(serialized: &str) -> Result<Self, ()> { fn deserialize(serialized: &str) -> Result<Self, ()> {
//strip newline at the end //strip newline at the end
let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized }; let serialized = serialized.strip_suffix("\n").unwrap_or(serialized);
let mut parts = serialized.split("/"); let mut parts = serialized.split("/");
match parts.next().unwrap_or("Invalid") { match parts.next().unwrap_or("Invalid") {
"JustRedraw" => Ok(WindowMessageResponse::JustRedraw), "JustRedraw" => Ok(WindowMessageResponse::JustRedraw),
@@ -444,12 +444,12 @@ impl Serializable for DrawInstructionsVec {
if self.len() == 0 { if self.len() == 0 {
return "empty".to_string(); return "empty".to_string();
} }
let collected: Vec<_> = self.into_iter().map(|ins| ins.serialize()).collect(); let collected: Vec<_> = self.iter().map(|ins| ins.serialize()).collect();
collected.join("\x1D") collected.join("\x1D")
} }
fn deserialize(serialized: &str) -> Result<Self, ()> { fn deserialize(serialized: &str) -> Result<Self, ()> {
//strip newline at the end //strip newline at the end
let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized }; let serialized = serialized.strip_suffix("\n").unwrap_or(serialized);
if serialized == "empty" { if serialized == "empty" {
return Ok(Vec::new()); return Ok(Vec::new());
} }
@@ -504,7 +504,7 @@ impl Serializable for WindowLikeType {
} }
} }
fn deserialize(serialized: &str) -> Result<Self, ()> { fn deserialize(serialized: &str) -> Result<Self, ()> {
let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized }; let serialized = serialized.strip_suffix("\n").unwrap_or(serialized);
match serialized { match serialized {
"LockScreen" => Ok(WindowLikeType::LockScreen), "LockScreen" => Ok(WindowLikeType::LockScreen),
"Window" => Ok(WindowLikeType::Window), "Window" => Ok(WindowLikeType::Window),
@@ -531,7 +531,7 @@ impl Serializable for Dimensions {
} }
fn deserialize(serialized: &str) -> Result<Self, ()> { fn deserialize(serialized: &str) -> Result<Self, ()> {
//strip newline at the end //strip newline at the end
let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized }; let serialized = serialized.strip_suffix("\n").unwrap_or(serialized);
let d = get_two_array(serialized)?; let d = get_two_array(serialized)?;
Ok(d) Ok(d)
} }
@@ -592,7 +592,7 @@ impl Serializable for WindowMessage {
} }
} }
fn deserialize(serialized: &str) -> Result<Self, ()> { fn deserialize(serialized: &str) -> Result<Self, ()> {
let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized }; let serialized = serialized.strip_suffix("\n").unwrap_or(serialized);
let mut parts = serialized.split("/"); let mut parts = serialized.split("/");
match parts.next().unwrap_or("Invalid") { match parts.next().unwrap_or("Invalid") {
"Init" => { "Init" => {
@@ -716,9 +716,9 @@ impl Serializable for WindowMessage {
return Err(()); return Err(());
} }
if let Ok(n) = arg2.unwrap().parse() { if let Ok(n) = arg2.unwrap().parse() {
return Ok(WindowMessage::Info(InfoType::WindowsInWorkspace(w_vec, n))); Ok(WindowMessage::Info(InfoType::WindowsInWorkspace(w_vec, n)))
} else { } else {
return Err(()); Err(())
} }
}, },
"Focus" => Ok(WindowMessage::Focus), "Focus" => Ok(WindowMessage::Focus),

View File

@@ -11,15 +11,17 @@ pub enum Themes {
//Parchment, //Parchment,
} }
impl Themes { impl std::str::FromStr for Themes {
pub fn from_str(name: &str) -> Option<Self> { type Err = ();
fn from_str(name: &str) -> Result<Self, Self::Err> {
match name { match name {
"Standard" => Some(Themes::Standard), "Standard" => Ok(Themes::Standard),
"Night" => Some(Themes::Night), "Night" => Ok(Themes::Night),
"Industrial" => Some(Themes::Industrial), "Industrial" => Ok(Themes::Industrial),
"Forest" => Some(Themes::Forest), "Forest" => Ok(Themes::Forest),
"Royal" => Some(Themes::Royal), "Royal" => Ok(Themes::Royal),
_ => None, _ => Err(()),
} }
} }
} }

View File

@@ -1,6 +1,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::fs::read_dir; use std::fs::read_dir;
use crate::fonts::measure_text;
use crate::framebuffer_types::{ Dimensions, Point }; use crate::framebuffer_types::{ Dimensions, Point };
pub fn min(one: usize, two: usize) -> usize { pub fn min(one: usize, two: usize) -> usize {
@@ -96,6 +97,23 @@ pub fn calc_actual_lines<'a>(lines: impl Iterator<Item = &'a String>, max_chars_
actual_lines actual_lines
} }
/// truncate to ... if too long (uses `measure_text`)
pub fn trunc_words(fonts: &[String], to_measure: String, horiz_spacing: Option<usize>, max_text_width: usize) -> String {
if measure_text(fonts, &to_measure, horiz_spacing).width > max_text_width {
let mut current = String::new();
for c in to_measure.chars() {
let to_measure = current.clone() + &c.to_string() + "...";
if measure_text(fonts, &to_measure, horiz_spacing).width > max_text_width {
break;
}
current += &c.to_string();
}
current + "..."
} else {
to_measure.clone()
}
}
pub fn concat_paths(current_path: &str, add_path: &str) -> Result<PathBuf, ()> { pub fn concat_paths(current_path: &str, add_path: &str) -> Result<PathBuf, ()> {
let mut new_path = PathBuf::from(current_path); let mut new_path = PathBuf::from(current_path);
//if current_path is a file, automatically uses it's parent (a directory) //if current_path is a file, automatically uses it's parent (a directory)

View File

@@ -152,11 +152,11 @@ impl WindowLike for AudioPlayer {
let current = &queue[queue.len() - sink_len]; let current = &queue[queue.len() - sink_len];
let current_name = current.0.file_name().unwrap().to_string_lossy().into_owned(); let current_name = current.0.file_name().unwrap().to_string_lossy().into_owned();
let fonts = ["nimbus-roman".to_string(), "shippori-mincho".to_string()]; let fonts = ["nimbus-roman".to_string(), "shippori-mincho".to_string()];
let cn_width = measure_text(&fonts, current_name.clone()).width; let cn_width = measure_text(&fonts, &current_name, None).width;
instructions.push(DrawInstructions::Text([self.dimensions[0] / 2 - cn_width / 2, 2], fonts.to_vec(), current_name.clone(), theme_info.text, theme_info.background, Some(0), None)); instructions.push(DrawInstructions::Text([self.dimensions[0] / 2 - cn_width / 2, 2], fonts.to_vec(), current_name.clone(), theme_info.text, theme_info.background, Some(0), None));
if let Some(artist) = &current.2 { if let Some(artist) = &current.2 {
let artist_string = "by ".to_string() + &artist; let artist_string = "by ".to_string() + &artist;
let as_width = measure_text(&fonts, artist_string.clone()).width; let as_width = measure_text(&fonts, &artist_string, None).width;
instructions.push(DrawInstructions::Text([self.dimensions[0] / 2 - as_width / 2, LINE_HEIGHT + 2], fonts.to_vec(), artist_string, theme_info.text, theme_info.background, Some(0), None)); instructions.push(DrawInstructions::Text([self.dimensions[0] / 2 - as_width / 2, LINE_HEIGHT + 2], fonts.to_vec(), artist_string, theme_info.text, theme_info.background, Some(0), None));
} }
//in this case no chance of mincho so MONO_WIDTH method of calculating width is ok //in this case no chance of mincho so MONO_WIDTH method of calculating width is ok

View File

@@ -241,9 +241,11 @@ impl WindowLike for Draw {
impl Draw { impl Draw {
pub fn new() -> Self { pub fn new() -> Self {
let mut d: Self = Default::default(); //apparently this is legal. thanks clippy
d.current_linewidth = 1; Self {
d current_linewidth: 1,
..Default::default() //no comma here allowed though??
}
} }
} }

View File

@@ -111,14 +111,13 @@ impl WindowLike for FileExplorer {
self.metadata = Some(metadata(&selected_entry.path).unwrap()); self.metadata = Some(metadata(&selected_entry.path).unwrap());
} }
WindowMessageResponse::JustRedraw WindowMessageResponse::JustRedraw
} else { } else if self.state == State::Info {
if self.state == State::Info { //pressing any key to exit info mode
self.state = State::List; self.state = State::List;
WindowMessageResponse::JustRedraw WindowMessageResponse::JustRedraw
} else { } else {
WindowMessageResponse::DoNothing WindowMessageResponse::DoNothing
} }
}
}, },
_ => WindowMessageResponse::DoNothing, _ => WindowMessageResponse::DoNothing,
} }
@@ -128,7 +127,7 @@ impl WindowLike for FileExplorer {
let mut instructions = Vec::new(); let mut instructions = Vec::new();
if self.state == State::List { if self.state == State::List {
//top bar with path name //top bar with path name
instructions.push(DrawInstructions::Text([5, 0], vec!["nimbus-roman".to_string(), "shippori-mincho".to_string()], "Current: ".to_string() + &self.current_path.to_string_lossy().to_string(), theme_info.text, theme_info.background, None, None)); instructions.push(DrawInstructions::Text([5, 0], vec!["nimbus-roman".to_string(), "shippori-mincho".to_string()], "Current: ".to_string() + self.current_path.to_string_lossy().as_ref(), theme_info.text, theme_info.background, None, None));
//the actual files and directories //the actual files and directories
let mut start_y = HEIGHT; let mut start_y = HEIGHT;
let mut i = self.top_position; let mut i = self.top_position;
@@ -142,10 +141,10 @@ impl WindowLike for FileExplorer {
} }
//unwrap_or not used because "Arguments passed to unwrap_or are eagerly evaluated", apparently //unwrap_or not used because "Arguments passed to unwrap_or are eagerly evaluated", apparently
let name = entry.override_name.clone(); let name = entry.override_name.clone();
let name = if name.is_none() { let name = if let Some(name) = name {
entry.path.file_name().unwrap().to_os_string().into_string().unwrap() name
} else { } else {
name.unwrap() entry.path.file_name().unwrap().to_os_string().into_string().unwrap()
}; };
instructions.push(DrawInstructions::Text([5, start_y + 4], vec!["nimbus-roman".to_string(), "shippori-mincho".to_string()], name, if is_selected { theme_info.top_text } else { theme_info.text }, if is_selected { theme_info.top } else { theme_info.background }, None, None)); instructions.push(DrawInstructions::Text([5, start_y + 4], vec!["nimbus-roman".to_string(), "shippori-mincho".to_string()], name, if is_selected { theme_info.top_text } else { theme_info.text }, if is_selected { theme_info.top } else { theme_info.background }, None, None));
start_y += HEIGHT; start_y += HEIGHT;

View File

@@ -138,8 +138,8 @@ impl WindowLike for Malvim {
if key_press.is_enter() { if key_press.is_enter() {
let mut line: Vec<char> = line.chars().collect(); let mut line: Vec<char> = line.chars().collect();
let (left, right) = line.split_at_mut(current_file.cursor_pos); let (left, right) = line.split_at_mut(current_file.cursor_pos);
let left = left.into_iter().map(|c| c.to_string()).collect::<Vec<String>>().join(""); let left = left.iter_mut().map(|c| c.to_string()).collect::<Vec<String>>().join("");
let right = right.into_iter().map(|c| c.to_string()).collect::<Vec<String>>().join(""); let right = right.iter_mut().map(|c| c.to_string()).collect::<Vec<String>>().join("");
current_file.content[current_file.line_pos] = left.to_string(); current_file.content[current_file.line_pos] = left.to_string();
let spaces = Malvim::calc_spaces(self.autoindent, &left); let spaces = Malvim::calc_spaces(self.autoindent, &left);
current_file.content.insert(current_file.line_pos + 1, " ".repeat(spaces) + &right); current_file.content.insert(current_file.line_pos + 1, " ".repeat(spaces) + &right);
@@ -495,7 +495,7 @@ impl WindowLike for Malvim {
if i == 0 { if i == 0 {
//modify current line //modify current line
let line = &current_file.content[current_file.line_pos]; let line = &current_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.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(); current_file.cursor_pos += copy_string.len();
} else { } else {
//insert a new line //insert a new line
@@ -750,7 +750,7 @@ impl Malvim {
} }
} else if self.files.len() == 0 { } else if self.files.len() == 0 {
self.bottom_message = Some("No files are open, so can only do :e(dit)".to_string()); self.bottom_message = Some("No files are open, so can only do :e(dit)".to_string());
} else if first.starts_with("/") { } else if let Some(s_first) = first.strip_prefix("/") {
let current_file = &mut self.files[self.current_file_index]; let current_file = &mut self.files[self.current_file_index];
if current_file.content.len() > 0 { if current_file.content.len() > 0 {
let p1 = if arg == "" { let p1 = if arg == "" {
@@ -764,7 +764,7 @@ impl Malvim {
} else { } else {
" ".to_string() + &rest " ".to_string() + &rest
}; };
let query = first[1..].to_string() + &p1 + &rest; let query = s_first.to_string() + &p1 + &rest;
let mut lines = current_file.content.iter().skip(current_file.line_pos); let mut lines = current_file.content.iter().skip(current_file.line_pos);
for i in 0..(current_file.content.len() - current_file.line_pos) { for i in 0..(current_file.content.len() - current_file.line_pos) {
let line = if i == 0 { let line = if i == 0 {

View File

@@ -256,7 +256,7 @@ impl Minesweeper {
self.random_seed as usize % 16 self.random_seed as usize % 16
} }
pub fn on_adjacent_tiles(&self, x: usize, y: usize, mut action: impl FnMut(usize, usize) -> (), if_mine: bool) { pub fn on_adjacent_tiles(&self, x: usize, y: usize, mut action: impl FnMut(usize, usize), if_mine: bool) {
if y > 0 { if y > 0 {
//above //above
if self.tiles[y - 1][x].mine == if_mine { if self.tiles[y - 1][x].mine == if_mine {

View File

@@ -1,5 +1,6 @@
use std::vec::Vec; use std::vec::Vec;
use std::vec; use std::vec;
use std::cmp::Ordering;
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 };
@@ -101,13 +102,11 @@ impl WindowLike for Reversi {
} }
} }
} }
if white_tiles == black_tiles { self.state = match white_tiles.cmp(&black_tiles) {
self.state = State::Tie; Ordering::Equal => State::Tie,
} else if white_tiles > black_tiles { Ordering::Greater => State::WhiteWin,
self.state = State::WhiteWin; Ordering::Less => State::BlackWin,
} else { };
self.state = State::BlackWin;
}
} }
} }
self.current_number = None; self.current_number = None;

View File

@@ -149,8 +149,7 @@ impl WindowLike for Terminal {
Mode::Running => { Mode::Running => {
//update //update
let mut changed = false; let mut changed = false;
loop { while 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 append_line = strip_ansi_escape_codes(bytes_to_string(self.process_current_line.clone())); let append_line = strip_ansi_escape_codes(bytes_to_string(self.process_current_line.clone()));
self.output += &append_line; self.output += &append_line;
@@ -167,9 +166,6 @@ impl WindowLike for Terminal {
self.process_current_line.push(ci); self.process_current_line.push(ci);
} }
changed = true; changed = true;
} else {
break;
}
} }
let running_process = self.running_process.as_mut().unwrap(); let running_process = self.running_process.as_mut().unwrap();
if let Some(_status) = running_process.try_wait().unwrap() { if let Some(_status) = running_process.try_wait().unwrap() {
@@ -377,13 +373,9 @@ impl Terminal {
let mut stdin = self.running_process.as_mut().unwrap().stdin.take().unwrap(); let mut stdin = self.running_process.as_mut().unwrap().stdin.take().unwrap();
let (tx2, rx2) = channel(); let (tx2, rx2) = channel();
thread::spawn(move || { thread::spawn(move || {
loop { while let Ok(write_line) = rx2.recv() {
if let Ok(write_line) = rx2.recv() {
let write_line: String = write_line + "\n"; let write_line: String = write_line + "\n";
stdin.write(write_line.as_bytes()).unwrap(); stdin.write_all(write_line.as_bytes()).unwrap();
} else {
break;
}
} }
}); });
self.pty_outerr_rx = Some(rx1); self.pty_outerr_rx = Some(rx1);

View File

@@ -107,6 +107,7 @@ fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) {
}); });
let touch = args.contains(&"touch".to_string()); let touch = args.contains(&"touch".to_string());
let force_stdout = args.contains(&"force-stdout".to_string()); //write to stdout to force framebuffer redraw
//read touchscreen presses (hopefully) //read touchscreen presses (hopefully)
thread::spawn(move || { thread::spawn(move || {
@@ -152,10 +153,17 @@ fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) {
for message in rx { for message in rx {
match message { match message {
ThreadMessage::KeyChar(kc) => wm.handle_message(WindowManagerMessage::KeyChar(kc.clone())), ThreadMessage::KeyChar(kc) => {
wm.handle_message(WindowManagerMessage::KeyChar(kc.clone()));
if force_stdout {
println!(" "); //without any stdout, on some user's devices, for some reason the framebuffer doesn't get redrawn to the screen
}
},
ThreadMessage::Touch(x, y) => { ThreadMessage::Touch(x, y) => {
wm.handle_message(WindowManagerMessage::Touch(x, y)); wm.handle_message(WindowManagerMessage::Touch(x, y));
if force_stdout {
println!(" "); //without any stdout, on my phone, for some reason the framebuffer doesn't get redrawn to the screen println!(" "); //without any stdout, on my phone, for some reason the framebuffer doesn't get redrawn to the screen
}
}, },
ThreadMessage::Clear => { ThreadMessage::Clear => {
write!(stdout.stdout, "{}", CLEAR_ALL).unwrap(); write!(stdout.stdout, "{}", CLEAR_ALL).unwrap();

View File

@@ -5,6 +5,9 @@ repository = "https://github.com/stjet/ming-wm"
license = "GPL-3.0-or-later" license = "GPL-3.0-or-later"
edition = "2021" edition = "2021"
[lints]
workspace = true
[dependencies] [dependencies]
ming-wm-lib = { path = "../ming-wm-lib" } ming-wm-lib = { path = "../ming-wm-lib" }
linux = { path = "../linux" } linux = { path = "../linux" }

View File

@@ -53,7 +53,7 @@ impl WindowLike for DesktopBackground {
} }
} else if line.len() > 1 { } else if line.len() > 1 {
//first character of line is either r or any other character, but is not part of the path //first character of line is either r or any other character, but is not part of the path
return vec![DrawInstructions::Bmp([0, 0], line[1..].to_string(), line.chars().next().unwrap() == 'r')]; return vec![DrawInstructions::Bmp([0, 0], line[1..].to_string(), line.starts_with('r'))];
} }
} }
} }

View File

@@ -6,7 +6,7 @@ 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 ming_wm_lib::fonts::measure_text; use ming_wm_lib::utils::trunc_words;
use ming_wm_lib::components::Component; use ming_wm_lib::components::Component;
use ming_wm_lib::components::toggle_button::ToggleButton; use ming_wm_lib::components::toggle_button::ToggleButton;
@@ -80,24 +80,7 @@ impl WindowLike for Taskbar {
} }
let info = &self.windows_in_workspace[wi]; let info = &self.windows_in_workspace[wi];
let max_text_width = META_WIDTH - PADDING * 2; let max_text_width = META_WIDTH - PADDING * 2;
//horiz_spacing is by default 1 per char, which measure_text doesn't take into account let name = trunc_words(&["nimbus-roman".to_string()], info.1.clone(), None, max_text_width);
let to_measure = info.1.clone();
let to_measure_len = to_measure.chars().count();
let name = if measure_text(&["nimbus-roman".to_string()], to_measure).width + to_measure_len > max_text_width {
let mut current = String::new();
for c in info.1.chars() {
//horiz_spacing is 1 by default
let to_measure = current.clone() + &c.to_string() + "...";
let to_measure_len = to_measure.chars().count();
if measure_text(&["nimbus-roman".to_string()], to_measure).width + to_measure_len > max_text_width {
break;
}
current += &c.to_string();
}
current + "..."
} else {
info.1.clone()
};
let mut b = ToggleButton::new(name.to_string() + "-window", [PADDING * 2 + 44 + (META_WIDTH + PADDING) * wi, PADDING], [META_WIDTH, self.dimensions[1] - (PADDING * 2)], name.to_string(), TaskbarMessage::Nothing, TaskbarMessage::Nothing); let mut b = ToggleButton::new(name.to_string() + "-window", [PADDING * 2 + 44 + (META_WIDTH + PADDING) * wi, PADDING], [META_WIDTH, self.dimensions[1] - (PADDING * 2)], name.to_string(), TaskbarMessage::Nothing, TaskbarMessage::Nothing);
b.inverted = info.0 == self.focused_id; b.inverted = info.0 == self.focused_id;
instructions.extend(b.draw(theme_info)); instructions.extend(b.draw(theme_info));

View File

@@ -133,11 +133,9 @@ impl FramebufferWriter {
for row in 0..char_info.height { for row in 0..char_info.height {
start_pos = ((top_left[1] + row + char_info.top_offset as usize) * self.info.stride + top_left[0]) * self.info.bytes_per_pixel; start_pos = ((top_left[1] + row + char_info.top_offset as usize) * self.info.stride + top_left[0]) * self.info.bytes_per_pixel;
for col in &char_info.data[row] { for col in &char_info.data[row] {
if col > &0 { if col > &0 && start_pos + 3 < self.info.byte_len {
if start_pos + 3 < self.info.byte_len {
self._draw_pixel(start_pos, color_with_alpha(color, bg_color, *col)); self._draw_pixel(start_pos, color_with_alpha(color, bg_color, *col));
} }
}
start_pos += self.info.bytes_per_pixel; start_pos += self.info.bytes_per_pixel;
} }
} }

View File

@@ -88,7 +88,7 @@ impl ProxyWindowLike {
if let Some(buffer) = buffer.stdout.as_mut() { if let Some(buffer) = buffer.stdout.as_mut() {
let mut output = String::new(); let mut output = String::new();
let mut reader = BufReader::new(buffer); let mut reader = BufReader::new(buffer);
if let Ok(_) = reader.read_line(&mut output) { if reader.read_line(&mut output).is_ok() {
output output
} else { } else {
String::new() String::new()

View File

@@ -6,11 +6,12 @@ use std::boxed::Box;
use std::cell::RefCell; use std::cell::RefCell;
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use std::str::FromStr;
use linux::fb::Framebuffer; use linux::fb::Framebuffer;
use ming_wm_lib::framebuffer_types::{ Point, Dimensions }; use ming_wm_lib::framebuffer_types::{ Point, Dimensions };
use ming_wm_lib::themes::{ Themes, get_theme_info }; use ming_wm_lib::themes::{ Themes, get_theme_info };
use ming_wm_lib::utils::{ min, point_inside }; use ming_wm_lib::utils::{ min, point_inside, trunc_words };
use ming_wm_lib::messages::*; use ming_wm_lib::messages::*;
use ming_wm_lib::dirs::config_dir; use ming_wm_lib::dirs::config_dir;
use ming_wm_lib::window_manager_types::*; use ming_wm_lib::window_manager_types::*;
@@ -168,8 +169,9 @@ impl WindowManager {
//if off_only is true, also handle request //if off_only is true, also handle request
//written confusingly but it works I promise //written confusingly but it works I promise
fn toggle_start_menu(&mut self, off_only: bool) -> WindowMessageResponse { fn toggle_start_menu(&mut self, off_only: bool) -> WindowMessageResponse {
let start_menu_exists = self.window_infos.iter().find(|w| w.window_like.subtype() == WindowLikeType::StartMenu).is_some(); let start_menu_exists = self.window_infos.iter().any(|w| w.window_like.subtype() == WindowLikeType::StartMenu);
if (start_menu_exists && off_only) || !off_only { //if (start_menu_exists && off_only) || !off_only {
if start_menu_exists || !off_only {
let taskbar_index = self.window_infos.iter().position(|w| w.window_like.subtype() == WindowLikeType::Taskbar).unwrap(); let taskbar_index = self.window_infos.iter().position(|w| w.window_like.subtype() == WindowLikeType::Taskbar).unwrap();
self.focused_id = self.window_infos[taskbar_index].id; self.focused_id = self.window_infos[taskbar_index].id;
if off_only { if off_only {
@@ -710,13 +712,15 @@ impl WindowManager {
//draw window background //draw window background
instructions.push_front(DrawInstructions::Rect([0, 0], window_dimensions, theme_info.background)); instructions.push_front(DrawInstructions::Rect([0, 0], window_dimensions, theme_info.background));
//draw window top decorations and what not //draw window top decorations and what not
let title = trunc_words(&["nimbus-roman".to_string()], window_info.window_like.title(), None, window_dimensions[0]);
instructions.extend(vec![ instructions.extend(vec![
//left top border //left top border
DrawInstructions::Rect([0, 0], [window_dimensions[0], 1], theme_info.border_left_top), DrawInstructions::Rect([0, 0], [window_dimensions[0], 1], theme_info.border_left_top),
DrawInstructions::Rect([0, 0], [1, window_dimensions[1]], theme_info.border_left_top), DrawInstructions::Rect([0, 0], [1, window_dimensions[1]], theme_info.border_left_top),
//top //top
DrawInstructions::Rect([1, 1], [window_dimensions[0] - 2, WINDOW_TOP_HEIGHT - 3], theme_info.top), DrawInstructions::Rect([1, 1], [window_dimensions[0] - 2, WINDOW_TOP_HEIGHT - 3], theme_info.top),
DrawInstructions::Text([4, 4], vec!["nimbus-roman".to_string()], window_info.window_like.title().to_string(), theme_info.top_text, theme_info.top, None, None), //window title
DrawInstructions::Text([4, 4], vec!["nimbus-roman".to_string()], title, theme_info.top_text, theme_info.top, None, None),
//top bottom border //top bottom border
DrawInstructions::Rect([1, WINDOW_TOP_HEIGHT - 2], [window_dimensions[0] - 2, 2], theme_info.border_left_top), DrawInstructions::Rect([1, WINDOW_TOP_HEIGHT - 2], [window_dimensions[0] - 2, 2], theme_info.border_left_top),
//right bottom border //right bottom border