this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

gpt5-rust pt3

alice ef13cc72 b7bf5559

+188 -1
+188 -1
tic80_rust/src/main.rs
··· 7 7 use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}; 8 8 use winit::event_loop::{ControlFlow, EventLoop}; 9 9 use winit::window::WindowBuilder; 10 - use mlua::{Lua, Function, RegistryKey, Result as LuaResult}; 10 + use mlua::{Lua, Function, RegistryKey, Result as LuaResult, MultiValue, Value}; 11 + use std::sync::OnceLock; 11 12 12 13 const WIDTH: u32 = 240; 13 14 const HEIGHT: u32 = 136; ··· 20 21 (0x29, 0xAD, 0xFF, 0xFF), (0x83, 0x76, 0x9C, 0xFF), (0xFF, 0x77, 0xA8, 0xFF), (0xFF, 0xCC, 0xAA, 0xFF), 21 22 ]; 22 23 24 + // TIC-80 default 5x8 font as 8 rows per glyph, 5 bits per row (LSB masked with 0x1F), ASCII indexed. 25 + static FONT_TEXT: &str = include_str!("../../src/core/font.inl"); 26 + static FONT_BYTES: OnceLock<Vec<u8>> = OnceLock::new(); 27 + 28 + fn font_bytes() -> &'static [u8] { 29 + FONT_BYTES.get_or_init(|| { 30 + // Parse C-style hex list into bytes 31 + let mut out = Vec::with_capacity(1024); 32 + for tok in FONT_TEXT.split(|c: char| c.is_whitespace() || c == ',') { 33 + if tok.is_empty() { continue; } 34 + let t = tok.trim(); 35 + let val = if let Some(hex) = t.strip_prefix("0x").or_else(|| t.strip_prefix("0X")) { 36 + u8::from_str_radix(hex, 16).ok() 37 + } else { 38 + // fallback: decimal 39 + t.parse::<u8>().ok() 40 + }; 41 + if let Some(b) = val { out.push(b); } 42 + } 43 + out 44 + }) 45 + } 46 + 23 47 struct Framebuffer { 24 48 // 240x136 palette indices (0..15) 25 49 idx: Vec<u8>, ··· 61 85 rgba[o + 3] = a; 62 86 } 63 87 } 88 + 89 + // Draw a line using integer Bresenham 90 + fn line(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, color: u8) { 91 + let mut x0 = x0; 92 + let mut y0 = y0; 93 + let dx = (x1 - x0).abs(); 94 + let sx = if x0 < x1 { 1 } else { -1 }; 95 + let dy = -(y1 - y0).abs(); 96 + let sy = if y0 < y1 { 1 } else { -1 }; 97 + let mut err = dx + dy; 98 + let c = color & 0x0F; 99 + loop { 100 + let _ = self.pix(x0, y0, Some(c)); 101 + if x0 == x1 && y0 == y1 { break; } 102 + let e2 = 2 * err; 103 + if e2 >= dy { err += dy; x0 += sx; } 104 + if e2 <= dx { err += dx; y0 += sy; } 105 + } 106 + } 107 + 108 + // Filled rectangle 109 + fn rect(&mut self, x: i32, y: i32, w: i32, h: i32, color: u8) { 110 + if w <= 0 || h <= 0 { return; } 111 + let c = color & 0x0F; 112 + for yy in y..y+h { 113 + for xx in x..x+w { 114 + let _ = self.pix(xx, yy, Some(c)); 115 + } 116 + } 117 + } 118 + 119 + // Print text using TIC-80 default font (5x8 glyphs, 1px spacing) 120 + fn print_text(&mut self, text: &str, x: i32, mut y: i32, color: u8, fixed: bool, scale: i32, _small: bool) -> i32 { 121 + // Match TIC-80 print/drawText: 122 + // - draw from 8x8 tile 123 + // - fixed: draw full 8 columns, advance by TIC_FONT_WIDTH (6) 124 + // - non-fixed: trim empty columns [start,end) and advance by (width+1) if width>0 else TIC_FONT_WIDTH 125 + const GLYPH_W: usize = 8; 126 + const GLYPH_H: usize = 8; 127 + const ADV: i32 = 6; // TIC_FONT_WIDTH 128 + if scale <= 0 { return 0; } 129 + let cidx = color & 0x0F; 130 + let font = font_bytes(); 131 + 132 + let mut pos = x; 133 + let mut max_pos = x; 134 + 135 + for ch in text.chars() { 136 + if ch == '\n' { 137 + if pos > max_pos { max_pos = pos; } 138 + pos = x; 139 + y += ADV * scale; // TIC uses TIC_FONT_HEIGHT (6); same as ADV here 140 + continue; 141 + } 142 + 143 + let code = (ch as u32 & 0x7F) as usize; 144 + let base = code * GLYPH_H; 145 + if base + GLYPH_H > font.len() { 146 + pos += ADV * scale; 147 + continue; 148 + } 149 + 150 + let (start_col, width_cols) = if !fixed { 151 + let mut left = GLYPH_W; 152 + let mut right = 0; 153 + for row in 0..GLYPH_H { 154 + let mask = font[base + row]; 155 + if mask != 0 { 156 + let mut l = 0; 157 + while l < GLYPH_W && ((mask >> l) & 1) == 0 { l += 1; } 158 + let mut r = GLYPH_W; 159 + while r > 0 && ((mask >> (r - 1)) & 1) == 0 { r -= 1; } 160 + if l < left { left = l; } 161 + if r > right { right = r; } 162 + } 163 + } 164 + let width = right.saturating_sub(left); 165 + (left, width) 166 + } else { 167 + (0, GLYPH_W) 168 + }; 169 + 170 + // Draw glyph 171 + for row in 0..GLYPH_H { 172 + let mask = font[base + row]; 173 + for col in 0..width_cols { 174 + let bit_idx = start_col + col; 175 + if bit_idx < GLYPH_W && ((mask >> bit_idx) & 1) != 0 { 176 + let px = pos + (col as i32) * scale; 177 + let py = y + (row as i32) * scale; 178 + for sy in 0..scale { 179 + for sx in 0..scale { 180 + let _ = self.pix(px + sx, py + sy, Some(cidx)); 181 + } 182 + } 183 + } 184 + } 185 + } 186 + 187 + // Advance 188 + if !fixed { 189 + if width_cols > 0 { 190 + pos += ((width_cols as i32) + 1) * scale; 191 + } else { 192 + pos += ADV * scale; 193 + } 194 + } else { 195 + pos += ADV * scale; 196 + } 197 + } 198 + 199 + if pos > max_pos { pos - x } else { max_pos - x } 200 + } 64 201 } 65 202 66 203 // Simple fixed-step ticker at ~60 FPS ··· 95 232 local cx, cy = 120, 68 96 233 for dx = -10, 10 do pix(cx + dx, cy, 15) end 97 234 for dy = -10, 10 do pix(cx, cy + dy, 15) end 235 + print("Hello", 10, 10, 15) 236 + line(0,0,239,135, 14) 237 + rect(20, 20, 40, 20, 9) 98 238 t = t + 1 99 239 end 100 240 "#; ··· 125 265 Ok(res) 126 266 })?; 127 267 globals.set("pix", pix_fn)?; 268 + 269 + // line(x0,y0,x1,y1,color) 270 + let fb_line = fb.clone(); 271 + let line_fn = lua.create_function(move |_, (x0, y0, x1, y1, color): (f32, f32, f32, f32, u8)| { 272 + fb_line.borrow_mut().line(x0 as i32, y0 as i32, x1 as i32, y1 as i32, color); 273 + Ok(()) 274 + })?; 275 + globals.set("line", line_fn)?; 276 + 277 + // rect(x,y,w,h,color) 278 + let fb_rect = fb.clone(); 279 + let rect_fn = lua.create_function(move |_, (x, y, w, h, color): (i32, i32, i32, i32, u8)| { 280 + fb_rect.borrow_mut().rect(x, y, w, h, color); 281 + Ok(()) 282 + })?; 283 + globals.set("rect", rect_fn)?; 284 + 285 + // print(text, x=0, y=0, color=15, fixed=false, scale=1, small=false) -> width 286 + let fb_print = fb.clone(); 287 + let print_fn = lua.create_function(move |_, args: MultiValue| { 288 + // Defaults 289 + let mut text = String::new(); 290 + let mut x: i32 = 0; 291 + let mut y: i32 = 0; 292 + let mut color: u8 = 15; 293 + let mut fixed = false; 294 + let mut scale: i32 = 1; 295 + let mut small = false; 296 + 297 + // Parse arguments by position 298 + for (i, v) in args.iter().enumerate() { 299 + match (i, v) { 300 + (0, Value::String(s)) => { text = s.to_str()?.to_string(); } 301 + (1, Value::Integer(n)) => { x = *n as i32; } 302 + (2, Value::Integer(n)) => { y = *n as i32; } 303 + (3, Value::Integer(n)) => { color = (*n as i64).clamp(0, 255) as u8; } 304 + (4, Value::Boolean(b)) => { fixed = *b; } 305 + (5, Value::Integer(n)) => { scale = (*n as i32).max(1); } 306 + (6, Value::Boolean(b)) => { small = *b; } 307 + _ => {} 308 + } 309 + } 310 + 311 + let width = fb_print.borrow_mut().print_text(&text, x, y, color, fixed, scale, small); 312 + Ok(width) 313 + })?; 314 + globals.set("print", print_fn)?; 128 315 129 316 // Load script 130 317 lua.load(script_src).set_name("cart").exec()?;