status bar
git clone https://git.lucas.co/cce-status-interface.git
Update system configuration and interface modules
src/bin/check_stride.rs | 10 ++++++++
src/bin/detect_text.rs | 37 +++++++++++++++++++++++++++
src/bin/draw_ascii.rs | 37 +++++++++++++++++++++++++++
src/bin/draw_ascii_grim.rs | 32 ++++++++++++++++++++++++
src/bin/find_text_box.rs | 51 +++++++++++++++++++++++++++++++++++++
src/bin/inspect_image_corners.rs | 24 ++++++++++++++++++
src/bin/inspect_pixels.rs | 27 ++++++++++++++++++++
src/bin/list_unique_colors.rs | 36 +++++++++++++++++++++++++++
src/bin/verify_bg.rs | 33 ++++++++++++++++++++++++
src/bin/verify_pixels.rs | 54 ++++++++++++++++++++++++++++++++++++++++
src/main.rs | 27 ++++++++++++++++++--
11 files changed, 366 insertions(+), 2 deletions(-)
diff --git a/src/bin/check_stride.rs b/src/bin/check_stride.rs
new file mode 100644
index 0000000..9f15a42
--- /dev/null
+++ b/src/bin/check_stride.rs
@@ -0,0 +1,10 @@
+use std::fs::File;
+
+fn main() {
+ let file = File::open("/home/lsgalante/.gemini/antigravity/brain/2aeef5fd-2989-49f9-a513-b732668c813b/current_screen_unified_2.png").unwrap();
+ let decoder = png::Decoder::new(file);
+ let reader = decoder.read_info().unwrap();
+ let info = reader.info();
+ println!("PNG Info: width={}, height={}, color_type={:?}, bit_depth={:?}, raw_bytes={}",
+ info.width, info.height, info.color_type, info.bit_depth, info.raw_bytes());
+}
diff --git a/src/bin/detect_text.rs b/src/bin/detect_text.rs
new file mode 100644
index 0000000..3198920
--- /dev/null
+++ b/src/bin/detect_text.rs
@@ -0,0 +1,37 @@
+use std::fs::File;
+
+fn main() {
+ let file = File::open("/home/lsgalante/.gemini/antigravity/brain/2aeef5fd-2989-49f9-a513-b732668c813b/current_screen_unified_2.png").unwrap();
+ let decoder = png::Decoder::new(file);
+ let mut reader = decoder.read_info().unwrap();
+ let mut buf = vec![0; reader.output_buffer_size()];
+ let info = reader.next_frame(&mut buf).unwrap();
+ let width = info.width as usize;
+ let height = info.height as usize;
+ println!("Image size: {}x{}", width, height);
+
+ // Let's print ASCII representation of the region x = 3200..3840, y = 10..46 using horizontal difference
+ for y in 10..46 {
+ let mut row_chars = String::new();
+ for x in (3200..3840).step_by(2) {
+ let idx = (y * width + x) * 4;
+ let idx_prev = (y * width + (x - 2)) * 4;
+
+ let r = buf[idx] as i32;
+ let g = buf[idx + 1] as i32;
+ let b = buf[idx + 2] as i32;
+
+ let r_p = buf[idx_prev] as i32;
+ let g_p = buf[idx_prev + 1] as i32;
+ let b_p = buf[idx_prev + 2] as i32;
+
+ let diff = (r - r_p).abs() + (g - g_p).abs() + (b - b_p).abs();
+ if diff > 15 {
+ row_chars.push('#');
+ } else {
+ row_chars.push('.');
+ }
+ }
+ println!("{:2}: {}", y, row_chars);
+ }
+}
diff --git a/src/bin/draw_ascii.rs b/src/bin/draw_ascii.rs
new file mode 100644
index 0000000..fd24b60
--- /dev/null
+++ b/src/bin/draw_ascii.rs
@@ -0,0 +1,37 @@
+use std::fs::File;
+
+fn main() {
+ let file = File::open("/home/lsgalante/.gemini/antigravity/brain/2aeef5fd-2989-49f9-a513-b732668c813b/current_screen_unified.png").unwrap();
+ let decoder = png::Decoder::new(file);
+ let mut reader = decoder.read_info().unwrap();
+ let mut buf = vec![0; reader.output_buffer_size()];
+ let info = reader.next_frame(&mut buf).unwrap();
+ let width = info.width as usize;
+ let height = info.height as usize;
+ println!("Image size: {}x{}", width, height);
+
+ // Sample background pixel
+ let bg_idx = (20 * width + 100) * 4;
+ let bg_r = buf[bg_idx] as i32;
+ let bg_g = buf[bg_idx + 1] as i32;
+ let bg_b = buf[bg_idx + 2] as i32;
+ println!("Background color sampled at (100, 20): R={}, G={}, B={}", bg_r, bg_g, bg_b);
+
+ for y in (10..46).step_by(2) {
+ let mut row_chars = String::new();
+ for x in (2800..3840).step_by(2) {
+ let idx = (y * width + x) * 4;
+ let r = buf[idx] as i32;
+ let g = buf[idx + 1] as i32;
+ let b = buf[idx + 2] as i32;
+
+ let diff = (r - bg_r).abs() + (g - bg_g).abs() + (b - bg_b).abs();
+ if diff > 20 {
+ row_chars.push('#');
+ } else {
+ row_chars.push('.');
+ }
+ }
+ println!("{:2}: {}", y, row_chars);
+ }
+}
diff --git a/src/bin/draw_ascii_grim.rs b/src/bin/draw_ascii_grim.rs
new file mode 100644
index 0000000..a47bd8b
--- /dev/null
+++ b/src/bin/draw_ascii_grim.rs
@@ -0,0 +1,32 @@
+use std::fs::File;
+
+fn main() {
+ let file = File::open("/tmp/current_screen.png").unwrap();
+ let decoder = png::Decoder::new(file);
+ let mut reader = decoder.read_info().unwrap();
+ let mut buf = vec![0; reader.output_buffer_size()];
+ let info = reader.next_frame(&mut buf).unwrap();
+ let width = info.width as usize;
+ let height = info.height as usize;
+ let color_type = info.color_type;
+ println!("Image size: {}x{}, color_type: {:?}", width, height, color_type);
+
+ let bytes_per_pixel = match color_type {
+ png::ColorType::Rgb => 3,
+ png::ColorType::Rgba => 4,
+ png::ColorType::Grayscale => 1,
+ png::ColorType::GrayscaleAlpha => 2,
+ _ => panic!("Unsupported color type"),
+ };
+
+ // The actual status bar background color is R=69, G=69, B=89
+ let bg_r = 69;
+ let bg_g = 69;
+ let bg_b = 89;
+
+ println!("Strikethrough line pixels at y=30, x=3266..3344 (step by 8):");
+ for x in (3266..3344).step_by(8) {
+ let idx = (30 * width + x) * bytes_per_pixel;
+ println!(" x={}: R={}, G={}, B={}", x, buf[idx], buf[idx+1], buf[idx+2]);
+ }
+}
diff --git a/src/bin/find_text_box.rs b/src/bin/find_text_box.rs
new file mode 100644
index 0000000..5dc6617
--- /dev/null
+++ b/src/bin/find_text_box.rs
@@ -0,0 +1,51 @@
+use std::fs::File;
+
+fn main() {
+ let file = File::open("/home/lsgalante/.gemini/antigravity/brain/2aeef5fd-2989-49f9-a513-b732668c813b/current_screen_unified_2.png").unwrap();
+ let decoder = png::Decoder::new(file);
+ let mut reader = decoder.read_info().unwrap();
+ let mut buf = vec![0; reader.output_buffer_size()];
+ let info = reader.next_frame(&mut buf).unwrap();
+ let width = info.width as usize;
+ let bg_idx = (20 * width + 100) * 3;
+ let bg_r = buf[bg_idx] as i32;
+ let bg_g = buf[bg_idx + 1] as i32;
+ let bg_b = buf[bg_idx + 2] as i32;
+ println!("Background color sampled at (100, 20): R={}, G={}, B={}", bg_r, bg_g, bg_b);
+
+ // Status bar is in y = 0..80
+ // Let's count non-background pixels in each column for y = 10..48
+ let mut col_intensity = vec![0; width];
+ for x in 0..width {
+ for y in 10..48 {
+ let idx = (y * width + x) * 3;
+ let r = buf[idx] as i32;
+ let g = buf[idx + 1] as i32;
+ let b = buf[idx + 2] as i32;
+
+ let diff = (r - bg_r).abs() + (g - bg_g).abs() + (b - bg_b).abs();
+ if diff > 15 {
+ col_intensity[x] += 1;
+ }
+ }
+ }
+
+ // Group columns into contiguous active segments
+ let mut in_segment = false;
+ let mut start_x = 0;
+ println!("Active segments in status bar (x from left to right):");
+ for x in 0..width {
+ let active = col_intensity[x] > 2; // threshold of 2 pixels
+ if active && !in_segment {
+ start_x = x;
+ in_segment = true;
+ } else if !active && in_segment {
+ let end_x = x - 1;
+ println!(" Segment at x = {}..{} (width = {})", start_x, end_x, end_x - start_x + 1);
+ in_segment = false;
+ }
+ }
+ if in_segment {
+ println!(" Segment at x = {}..{} (width = {})", start_x, width - 1, width - start_x);
+ }
+}
diff --git a/src/bin/inspect_image_corners.rs b/src/bin/inspect_image_corners.rs
new file mode 100644
index 0000000..fc0b3f0
--- /dev/null
+++ b/src/bin/inspect_image_corners.rs
@@ -0,0 +1,24 @@
+use std::fs::File;
+
+fn main() {
+ let file = File::open("/home/lsgalante/.gemini/antigravity/brain/2aeef5fd-2989-49f9-a513-b732668c813b/current_screen_unified.png").unwrap();
+ let decoder = png::Decoder::new(file);
+ let mut reader = decoder.read_info().unwrap();
+ let mut buf = vec![0; reader.output_buffer_size()];
+ let info = reader.next_frame(&mut buf).unwrap();
+ let width = info.width as usize;
+ let height = info.height as usize;
+ println!("Width: {}, Height: {}", width, height);
+
+ println!("Top row y=4 colors (x = 0, 100, 500, 1000, 2000, 3000, 3800):");
+ for x in &[0, 100, 500, 1000, 2000, 3000, 3800] {
+ let idx = (4 * width + x) * 4;
+ println!("x={}: R={}, G={}, B={}, A={}", x, buf[idx], buf[idx+1], buf[idx+2], buf[idx+3]);
+ }
+
+ println!("Row y=20 colors (x = 0, 100, 500, 1000, 2000, 3000, 3800):");
+ for x in &[0, 100, 500, 1000, 2000, 3000, 3800] {
+ let idx = (20 * width + x) * 4;
+ println!("x={}: R={}, G={}, B={}, A={}", x, buf[idx], buf[idx+1], buf[idx+2], buf[idx+3]);
+ }
+}
diff --git a/src/bin/inspect_pixels.rs b/src/bin/inspect_pixels.rs
new file mode 100644
index 0000000..043e37c
--- /dev/null
+++ b/src/bin/inspect_pixels.rs
@@ -0,0 +1,27 @@
+use std::fs::File;
+
+fn main() {
+ let file = File::open("/home/lsgalante/.gemini/antigravity/brain/2aeef5fd-2989-49f9-a513-b732668c813b/current_screen_3.png").unwrap();
+ let decoder = png::Decoder::new(file);
+ let mut reader = decoder.read_info().unwrap();
+ let mut buf = vec![0; reader.output_buffer_size()];
+ let info = reader.next_frame(&mut buf).unwrap();
+
+ println!("Truly bright pixels for x = 3310..3390, y = 10..50:");
+ let mut found = 0;
+ for y in 10..50 {
+ for x in 3310..3390 {
+ let idx = (y * info.width as usize + x) * 4;
+ let r = buf[idx];
+ let g = buf[idx + 1];
+ let b = buf[idx + 2];
+ let a = buf[idx + 3];
+
+ if r > 100 && g > 100 && b > 100 {
+ found += 1;
+ println!("y={}, x={}: R={}, G={}, B={}, A={}", y, x, r, g, b, a);
+ }
+ }
+ }
+ println!("Total truly bright pixels: {}", found);
+}
diff --git a/src/bin/list_unique_colors.rs b/src/bin/list_unique_colors.rs
new file mode 100644
index 0000000..884660e
--- /dev/null
+++ b/src/bin/list_unique_colors.rs
@@ -0,0 +1,36 @@
+use std::fs::File;
+use std::collections::HashMap;
+
+fn main() {
+ let file = File::open("/tmp/current_screen.png").unwrap();
+ let decoder = png::Decoder::new(file);
+ let mut reader = decoder.read_info().unwrap();
+ let mut buf = vec![0; reader.output_buffer_size()];
+ let info = reader.next_frame(&mut buf).unwrap();
+ let width = info.width as usize;
+ let bytes_per_pixel = match info.color_type {
+ png::ColorType::Rgb => 3,
+ png::ColorType::Rgba => 4,
+ _ => panic!("Unsupported color type"),
+ };
+
+ let mut color_counts = HashMap::new();
+ // Scan the window area: x from 12 to 1800 (physical), y from 68 to 2300 (physical)
+ for y in (68..2300).step_by(2) {
+ for x in (12..1800).step_by(2) {
+ let idx = (y * width + x) * bytes_per_pixel;
+ let r = buf[idx];
+ let g = buf[idx + 1];
+ let b = buf[idx + 2];
+ *color_counts.entry((r, g, b)).or_insert(0) += 1;
+ }
+ }
+
+ let mut sorted_colors: Vec<_> = color_counts.into_iter().collect();
+ sorted_colors.sort_by(|a, b| b.1.cmp(&a.1));
+
+ println!("Top 30 most common colors in the system interface window area:");
+ for (i, ((r, g, b), count)) in sorted_colors.iter().take(30).enumerate() {
+ println!(" #{}: R={}, G={}, B={} (count={})", i + 1, r, g, b, count);
+ }
+}
diff --git a/src/bin/verify_bg.rs b/src/bin/verify_bg.rs
new file mode 100644
index 0000000..38114e7
--- /dev/null
+++ b/src/bin/verify_bg.rs
@@ -0,0 +1,33 @@
+use std::fs::File;
+
+fn main() {
+ let file = File::open("/tmp/current_screen.png").expect("Failed to open screenshot");
+ let decoder = png::Decoder::new(file);
+ let mut reader = decoder.read_info().expect("Failed to read PNG info");
+ let mut buf = vec![0; reader.output_buffer_size()];
+ let info = reader.next_frame(&mut buf).expect("Failed to decode PNG frame");
+ let width = info.width as usize;
+ let height = info.height as usize;
+ let bytes_per_pixel = match info.color_type {
+ png::ColorType::Rgb => 3,
+ png::ColorType::Rgba => 4,
+ _ => panic!("Unsupported color type"),
+ };
+
+ let mut found = 0;
+ for y in 0..height {
+ for x in 0..width {
+ let idx = (y * width + x) * bytes_per_pixel;
+ let r = buf[idx];
+ let g = buf[idx + 1];
+ let b = buf[idx + 2];
+ if r < 30 && g < 30 && b > 50 {
+ found += 1;
+ if found <= 20 {
+ println!("Pixel at ({}, {}): R={}, G={}, B={}", x, y, r, g, b);
+ }
+ }
+ }
+ }
+ println!("Total pixels matching: {}", found);
+}
diff --git a/src/bin/verify_pixels.rs b/src/bin/verify_pixels.rs
new file mode 100644
index 0000000..24bcf43
--- /dev/null
+++ b/src/bin/verify_pixels.rs
@@ -0,0 +1,54 @@
+use std::fs::File;
+
+fn main() {
+ let file = File::open("/home/lsgalante/.gemini/antigravity/brain/2aeef5fd-2989-49f9-a513-b732668c813b/current_screen_unified_2.png").unwrap();
+ let decoder = png::Decoder::new(file);
+ let mut reader = decoder.read_info().unwrap();
+ let mut buf = vec![0; reader.output_buffer_size()];
+ let info = reader.next_frame(&mut buf).unwrap();
+ let width = info.width as usize;
+
+ // Sample background at (100, 20)
+ let bg_idx = (20 * width + 100) * 3;
+ let bg_r = buf[bg_idx] as i32;
+ let bg_g = buf[bg_idx + 1] as i32;
+ let bg_b = buf[bg_idx + 2] as i32;
+ println!("Background color sampled at (100, 20): R={}, G={}, B={}", bg_r, bg_g, bg_b);
+
+ println!("Background color sampled at (3200, 20): R={}, G={}, B={}", buf[(20 * width + 3200) * 3], buf[(20 * width + 3200) * 3 + 1], buf[(20 * width + 3200) * 3 + 2]);
+ println!("Background color sampled at (1500, 20): R={}, G={}, B={}", buf[(20 * width + 1500) * 3], buf[(20 * width + 1500) * 3 + 1], buf[(20 * width + 1500) * 3 + 2]);
+
+ println!("Checking volume text region (x = 3264..3345, y = 12..44):");
+ let mut text_pixels = 0;
+ for y in 12..44 {
+ for x in 3264..3345 {
+ let idx = (y * width + x) * 3;
+ let r = buf[idx] as i32;
+ let g = buf[idx + 1] as i32;
+ let b = buf[idx + 2] as i32;
+
+ let diff = (r - bg_r).abs() + (g - bg_g).abs() + (b - bg_b).abs();
+ if diff > 15 {
+ text_pixels += 1;
+ }
+ }
+ }
+ println!("Found {} active pixels in volume region.", text_pixels);
+
+ // Let's also check a region that should be empty (e.g. x = 3200..3250, y = 12..44)
+ let mut empty_pixels = 0;
+ for y in 12..44 {
+ for x in 3200..3250 {
+ let idx = (y * width + x) * 3;
+ let r = buf[idx] as i32;
+ let g = buf[idx + 1] as i32;
+ let b = buf[idx + 2] as i32;
+
+ let diff = (r - bg_r).abs() + (g - bg_g).abs() + (b - bg_b).abs();
+ if diff > 15 {
+ empty_pixels += 1;
+ }
+ }
+ }
+ println!("Found {} active pixels in empty region.", empty_pixels);
+}
diff --git a/src/main.rs b/src/main.rs
index 0ecf64f..9d924d7 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -504,7 +504,7 @@ impl StatusApp {
right_x -= 16.0;
let is_muted = stats.volume_muted;
let color_val = if is_muted {
- color::TEXT_DIM
+ read_disabled_color_from_config().unwrap_or(color::TEXT_DIM)
} else {
color::TEXT_ACCENT
};
@@ -1918,9 +1918,32 @@ fn main() {
}
}
+fn read_disabled_color_from_config() -> Option<[f32; 4]> {
+ let content = std::fs::read_to_string("/home/lsgalante/.config/clearwm/config.toml").ok()?;
+ parse_srgb_color_from_key(&content, "disabled_color")
+}
+
+fn parse_srgb_color_from_key(content: &str, key: &str) -> Option<[f32; 4]> {
+ for line in content.lines() {
+ let trimmed = line.trim();
+ if let Some(rest) = trimmed.strip_prefix(key) {
+ let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+ let hex = rest.trim_end_matches('"').trim();
+ if let Some(rgb) = parse_hex(hex) {
+ let r = rgb[0] as f32 / 255.0;
+ let g = rgb[1] as f32 / 255.0;
+ let b = rgb[2] as f32 / 255.0;
+ return Some([r, g, b, 1.0]);
+ }
+ }
+ }
+ None
+}
+
fn read_bg_color_from_config() -> Option<[f32; 4]> {
let content = std::fs::read_to_string("/home/lsgalante/.config/clearwm/config.toml").ok()?;
- parse_color_from_key(&content, "background_color")
+ parse_color_from_key(&content, "low_color")
+ .or_else(|| parse_color_from_key(&content, "background_color"))
}
fn parse_color_from_key(content: &str, key: &str) -> Option<[f32; 4]> {