use turbo_vision::app::Application;
use turbo_vision::core::command::CM_QUIT;
use turbo_vision::core::event::{Event, EventType, KB_ALT_X, KB_ESC, KB_ESC_ESC};
use turbo_vision::core::geometry::Rect;
use turbo_vision::views::kitty_image::KittyImage;
use turbo_vision::views::label::LabelBuilder;
use turbo_vision::views::status_line::{StatusItem, StatusLine};
use turbo_vision::views::window::WindowBuilder;
use turbo_vision::views::View;
fn generate_gray_pattern(width: u32, height: u32) -> Vec<u8> {
let mut png_data: Vec<u8> = vec![0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
let mut ihdr_data = Vec::new();
ihdr_data.extend_from_slice(&width.to_be_bytes());
ihdr_data.extend_from_slice(&height.to_be_bytes());
ihdr_data.push(8); ihdr_data.push(2); ihdr_data.push(0); ihdr_data.push(0); ihdr_data.push(0);
write_png_chunk(&mut png_data, b"IHDR", &ihdr_data);
let mut raw_data = Vec::new();
for y in 0..height {
raw_data.push(0); for x in 0..width {
let pattern = create_gray_pattern(x, y);
raw_data.push(pattern); raw_data.push(pattern); raw_data.push(pattern); }
}
let compressed = deflate_compress(&raw_data);
write_png_chunk(&mut png_data, b"IDAT", &compressed);
write_png_chunk(&mut png_data, b"IEND", &[]);
png_data
}
fn create_gray_pattern(x: u32, y: u32) -> u8 {
let base_gray: u8 = 85;
let pattern1 = ((x / 2) % 2) ^ ((y / 2) % 2); let pattern2 = ((x / 4) % 2) ^ ((y / 4) % 2); let pattern3 = ((x + y) % 4 == 0) as u32;
let variation = (pattern1 * 3 + pattern2 * 2 + pattern3 * 2) as u8;
base_gray.saturating_add(variation).min(95)
}
fn write_png_chunk(output: &mut Vec<u8>, chunk_type: &[u8; 4], data: &[u8]) {
output.extend_from_slice(&(data.len() as u32).to_be_bytes());
output.extend_from_slice(chunk_type);
output.extend_from_slice(data);
let mut crc_data = Vec::new();
crc_data.extend_from_slice(chunk_type);
crc_data.extend_from_slice(data);
let crc = crc32(&crc_data);
output.extend_from_slice(&crc.to_be_bytes());
}
fn crc32(data: &[u8]) -> u32 {
static CRC_TABLE: [u32; 256] = {
let mut table = [0u32; 256];
let mut i = 0;
while i < 256 {
let mut c = i as u32;
let mut k = 0;
while k < 8 {
if c & 1 != 0 {
c = 0xEDB88320 ^ (c >> 1);
} else {
c >>= 1;
}
k += 1;
}
table[i] = c;
i += 1;
}
table
};
let mut crc = 0xFFFFFFFFu32;
for byte in data {
crc = CRC_TABLE[((crc ^ (*byte as u32)) & 0xFF) as usize] ^ (crc >> 8);
}
crc ^ 0xFFFFFFFF
}
fn deflate_compress(data: &[u8]) -> Vec<u8> {
let mut output = Vec::new();
output.push(0x78);
output.push(0x01);
let chunks = data.chunks(65535);
let chunk_count = data.chunks(65535).count();
for (i, chunk) in chunks.enumerate() {
let is_last = i == chunk_count - 1;
output.push(if is_last { 0x01 } else { 0x00 });
let len = chunk.len() as u16;
output.push((len & 0xFF) as u8);
output.push((len >> 8) as u8);
output.push((!len & 0xFF) as u8);
output.push((!len >> 8) as u8);
output.extend_from_slice(chunk);
}
let adler = adler32(data);
output.extend_from_slice(&adler.to_be_bytes());
output
}
fn adler32(data: &[u8]) -> u32 {
let mut a: u32 = 1;
let mut b: u32 = 0;
for byte in data {
a = (a + *byte as u32) % 65521;
b = (b + a) % 65521;
}
(b << 16) | a
}
fn main() -> turbo_vision::core::error::Result<()> {
let mut app = Application::new()?;
let (width, height) = app.terminal.size();
let status_line = StatusLine::new(
Rect::new(0, height - 1, width, height),
vec![
StatusItem::new("~Alt-X~ Exit", KB_ALT_X, CM_QUIT),
StatusItem::new("~Esc~ Exit", KB_ESC, CM_QUIT),
],
);
app.set_status_line(status_line);
let pattern_width = 256;
let pattern_height = 256;
let gray_pattern = generate_gray_pattern(pattern_width, pattern_height);
let background = KittyImage::from_bytes(
Rect::new(0, 1, width, height - 1),
gray_pattern,
).z_index(-1);
app.desktop.add(Box::new(background));
let window_width = 50;
let window_height = 12;
let window_x = (width - window_width) / 2;
let window_y = (height - window_height) / 2 - 3;
let mut window = WindowBuilder::new()
.bounds(Rect::new(window_x, window_y, window_x + window_width, window_y + window_height))
.title("Kitty Background Demo")
.build();
let label1 = LabelBuilder::new()
.bounds(Rect::new(2, 2, 46, 2))
.text("Desktop background using Kitty graphics!")
.build();
let label2 = LabelBuilder::new()
.bounds(Rect::new(2, 4, 46, 4))
.text("Try dragging the windows around.")
.build();
let label3 = LabelBuilder::new()
.bounds(Rect::new(2, 6, 46, 6))
.text("The gray pattern is a PNG image.")
.build();
let label4 = LabelBuilder::new()
.bounds(Rect::new(2, 8, 46, 8))
.text("Press Alt-X or Esc to exit.")
.build();
window.add(Box::new(label1));
window.add(Box::new(label2));
window.add(Box::new(label3));
window.add(Box::new(label4));
app.desktop.add(Box::new(window));
let about_width = 36;
let about_height = 9;
let about_x = width - about_width - 5;
let about_y = height - about_height - 3;
let mut about_window = WindowBuilder::new()
.bounds(Rect::new(about_x, about_y, about_x + about_width, about_y + about_height))
.title("About")
.build();
let about1 = LabelBuilder::new()
.bounds(Rect::new(2, 2, 32, 2))
.text("Turbo Vision for Rust")
.build();
let about2 = LabelBuilder::new()
.bounds(Rect::new(2, 3, 32, 3))
.text("Version 1.0.2")
.build();
let about3 = LabelBuilder::new()
.bounds(Rect::new(2, 5, 32, 5))
.text("Kitty Graphics Protocol Demo")
.build();
let about4 = LabelBuilder::new()
.bounds(Rect::new(2, 6, 32, 6))
.text("Drag me around!")
.build();
about_window.add(Box::new(about1));
about_window.add(Box::new(about2));
about_window.add(Box::new(about3));
about_window.add(Box::new(about4));
app.desktop.add(Box::new(about_window));
app.running = true;
while app.running {
app.desktop.draw(&mut app.terminal);
let _ = app.terminal.flush();
if let Ok(Some(mut event)) = app.terminal.poll_event(std::time::Duration::from_millis(50)) {
if event.what == EventType::Keyboard {
match event.key_code {
KB_ALT_X | KB_ESC | KB_ESC_ESC => {
event = Event::command(CM_QUIT);
}
_ => {}
}
}
app.desktop.handle_event(&mut event);
if event.what == EventType::Command && event.command == CM_QUIT {
let _ = app.terminal.clear_kitty_images();
app.running = false;
}
}
}
let _ = app.terminal.clear_kitty_images();
app.terminal.shutdown()?;
Ok(())
}