this repo has no description
0
fork

Configure Feed

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

gpt5-rust pt4

alice 588c96e1 ef13cc72

+137 -48
+137 -48
tic80_rust/src/main.rs
··· 1 + use std::cell::RefCell; 1 2 use std::rc::Rc; 2 3 use std::time::{Duration, Instant}; 3 - use std::cell::RefCell; 4 4 5 + use mlua::{Function, Lua, MultiValue, RegistryKey, Result as LuaResult, Value}; 5 6 use pixels::{Error, Pixels, SurfaceTexture}; 7 + use std::sync::OnceLock; 6 8 use winit::dpi::LogicalSize; 7 9 use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}; 8 10 use winit::event_loop::{ControlFlow, EventLoop}; 9 11 use winit::window::WindowBuilder; 10 - use mlua::{Lua, Function, RegistryKey, Result as LuaResult, MultiValue, Value}; 11 - use std::sync::OnceLock; 12 12 13 13 const WIDTH: u32 = 240; 14 14 const HEIGHT: u32 = 136; 15 15 16 16 // Default 16-color TIC-80 palette (sRGB) as RGBA8 17 17 const PALETTE: [(u8, u8, u8, u8); 16] = [ 18 - (0x00, 0x00, 0x00, 0xFF), (0x1D, 0x2B, 0x53, 0xFF), (0x7E, 0x25, 0x53, 0xFF), (0x00, 0x87, 0x51, 0xFF), 19 - (0xAB, 0x52, 0x36, 0xFF), (0x5F, 0x57, 0x4F, 0xFF), (0xC2, 0xC3, 0xC7, 0xFF), (0xFF, 0xF1, 0xE8, 0xFF), 20 - (0xFF, 0x00, 0x4D, 0xFF), (0xFF, 0xA3, 0x00, 0xFF), (0xFF, 0xEC, 0x27, 0xFF), (0x00, 0xE4, 0x36, 0xFF), 21 - (0x29, 0xAD, 0xFF, 0xFF), (0x83, 0x76, 0x9C, 0xFF), (0xFF, 0x77, 0xA8, 0xFF), (0xFF, 0xCC, 0xAA, 0xFF), 18 + (0x00, 0x00, 0x00, 0xFF), 19 + (0x1D, 0x2B, 0x53, 0xFF), 20 + (0x7E, 0x25, 0x53, 0xFF), 21 + (0x00, 0x87, 0x51, 0xFF), 22 + (0xAB, 0x52, 0x36, 0xFF), 23 + (0x5F, 0x57, 0x4F, 0xFF), 24 + (0xC2, 0xC3, 0xC7, 0xFF), 25 + (0xFF, 0xF1, 0xE8, 0xFF), 26 + (0xFF, 0x00, 0x4D, 0xFF), 27 + (0xFF, 0xA3, 0x00, 0xFF), 28 + (0xFF, 0xEC, 0x27, 0xFF), 29 + (0x00, 0xE4, 0x36, 0xFF), 30 + (0x29, 0xAD, 0xFF, 0xFF), 31 + (0x83, 0x76, 0x9C, 0xFF), 32 + (0xFF, 0x77, 0xA8, 0xFF), 33 + (0xFF, 0xCC, 0xAA, 0xFF), 22 34 ]; 23 35 24 36 // TIC-80 default 5x8 font as 8 rows per glyph, 5 bits per row (LSB masked with 0x1F), ASCII indexed. ··· 30 42 // Parse C-style hex list into bytes 31 43 let mut out = Vec::with_capacity(1024); 32 44 for tok in FONT_TEXT.split(|c: char| c.is_whitespace() || c == ',') { 33 - if tok.is_empty() { continue; } 45 + if tok.is_empty() { 46 + continue; 47 + } 34 48 let t = tok.trim(); 35 49 let val = if let Some(hex) = t.strip_prefix("0x").or_else(|| t.strip_prefix("0X")) { 36 50 u8::from_str_radix(hex, 16).ok() ··· 38 52 // fallback: decimal 39 53 t.parse::<u8>().ok() 40 54 }; 41 - if let Some(b) = val { out.push(b); } 55 + if let Some(b) = val { 56 + out.push(b); 57 + } 42 58 } 43 59 out 44 60 }) ··· 51 67 52 68 impl Framebuffer { 53 69 fn new() -> Self { 54 - Self { idx: vec![0; (WIDTH * HEIGHT) as usize] } 70 + Self { 71 + idx: vec![0; (WIDTH * HEIGHT) as usize], 72 + } 55 73 } 56 74 57 75 // cls(color): fill framebuffer with palette index ··· 98 116 let c = color & 0x0F; 99 117 loop { 100 118 let _ = self.pix(x0, y0, Some(c)); 101 - if x0 == x1 && y0 == y1 { break; } 119 + if x0 == x1 && y0 == y1 { 120 + break; 121 + } 102 122 let e2 = 2 * err; 103 - if e2 >= dy { err += dy; x0 += sx; } 104 - if e2 <= dx { err += dx; y0 += sy; } 123 + if e2 >= dy { 124 + err += dy; 125 + x0 += sx; 126 + } 127 + if e2 <= dx { 128 + err += dx; 129 + y0 += sy; 130 + } 105 131 } 106 132 } 107 133 108 134 // Filled rectangle 109 135 fn rect(&mut self, x: i32, y: i32, w: i32, h: i32, color: u8) { 110 - if w <= 0 || h <= 0 { return; } 136 + if w <= 0 || h <= 0 { 137 + return; 138 + } 111 139 let c = color & 0x0F; 112 - for yy in y..y+h { 113 - for xx in x..x+w { 140 + for yy in y..y + h { 141 + for xx in x..x + w { 114 142 let _ = self.pix(xx, yy, Some(c)); 115 143 } 116 144 } 117 145 } 118 146 119 147 // 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 { 148 + #[allow(clippy::too_many_arguments)] 149 + fn print_text( 150 + &mut self, 151 + text: &str, 152 + x: i32, 153 + mut y: i32, 154 + color: u8, 155 + fixed: bool, 156 + scale: i32, 157 + _small: bool, 158 + ) -> i32 { 121 159 // Match TIC-80 print/drawText: 122 160 // - draw from 8x8 tile 123 161 // - fixed: draw full 8 columns, advance by TIC_FONT_WIDTH (6) ··· 125 163 const GLYPH_W: usize = 8; 126 164 const GLYPH_H: usize = 8; 127 165 const ADV: i32 = 6; // TIC_FONT_WIDTH 128 - if scale <= 0 { return 0; } 166 + if scale <= 0 { 167 + return 0; 168 + } 129 169 let cidx = color & 0x0F; 130 170 let font = font_bytes(); 131 171 ··· 134 174 135 175 for ch in text.chars() { 136 176 if ch == '\n' { 137 - if pos > max_pos { max_pos = pos; } 177 + if pos > max_pos { 178 + max_pos = pos; 179 + } 138 180 pos = x; 139 181 y += ADV * scale; // TIC uses TIC_FONT_HEIGHT (6); same as ADV here 140 182 continue; ··· 154 196 let mask = font[base + row]; 155 197 if mask != 0 { 156 198 let mut l = 0; 157 - while l < GLYPH_W && ((mask >> l) & 1) == 0 { l += 1; } 199 + while l < GLYPH_W && ((mask >> l) & 1) == 0 { 200 + l += 1; 201 + } 158 202 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; } 203 + while r > 0 && ((mask >> (r - 1)) & 1) == 0 { 204 + r -= 1; 205 + } 206 + if l < left { 207 + left = l; 208 + } 209 + if r > right { 210 + right = r; 211 + } 162 212 } 163 213 } 164 214 let width = right.saturating_sub(left); ··· 196 246 } 197 247 } 198 248 199 - if pos > max_pos { pos - x } else { max_pos - x } 249 + if pos > max_pos { 250 + pos - x 251 + } else { 252 + max_pos - x 253 + } 200 254 } 201 255 } 202 256 ··· 208 262 209 263 impl Ticker { 210 264 fn new() -> Self { 211 - Self { last: Instant::now(), step: Duration::from_micros(16_667) } 265 + Self { 266 + last: Instant::now(), 267 + step: Duration::from_micros(16_667), 268 + } 212 269 } 213 270 fn should_tick(&mut self) -> bool { 214 271 let now = Instant::now(); ··· 268 325 269 326 // line(x0,y0,x1,y1,color) 270 327 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 - })?; 328 + let line_fn = lua.create_function( 329 + move |_, (x0, y0, x1, y1, color): (f32, f32, f32, f32, u8)| { 330 + fb_line 331 + .borrow_mut() 332 + .line(x0 as i32, y0 as i32, x1 as i32, y1 as i32, color); 333 + Ok(()) 334 + }, 335 + )?; 275 336 globals.set("line", line_fn)?; 276 337 277 338 // rect(x,y,w,h,color) 278 339 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 - })?; 340 + let rect_fn = 341 + lua.create_function(move |_, (x, y, w, h, color): (i32, i32, i32, i32, u8)| { 342 + fb_rect.borrow_mut().rect(x, y, w, h, color); 343 + Ok(()) 344 + })?; 283 345 globals.set("rect", rect_fn)?; 284 346 285 347 // print(text, x=0, y=0, color=15, fixed=false, scale=1, small=false) -> width ··· 297 359 // Parse arguments by position 298 360 for (i, v) in args.iter().enumerate() { 299 361 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; } 362 + (0, Value::String(s)) => { 363 + text = s.to_str()?.to_string(); 364 + } 365 + (1, Value::Integer(n)) => { 366 + x = *n as i32; 367 + } 368 + (2, Value::Integer(n)) => { 369 + y = *n as i32; 370 + } 371 + (3, Value::Integer(n)) => { 372 + color = (*n).clamp(0, 255) as u8; 373 + } 374 + (4, Value::Boolean(b)) => { 375 + fixed = *b; 376 + } 377 + (5, Value::Integer(n)) => { 378 + scale = (*n as i32).max(1); 379 + } 380 + (6, Value::Boolean(b)) => { 381 + small = *b; 382 + } 307 383 _ => {} 308 384 } 309 385 } 310 386 311 - let width = fb_print.borrow_mut().print_text(&text, x, y, color, fixed, scale, small); 387 + let width = fb_print 388 + .borrow_mut() 389 + .print_text(&text, x, y, color, fixed, scale, small); 312 390 Ok(width) 313 391 })?; 314 392 globals.set("print", print_fn)?; ··· 342 420 343 421 fn run() -> Result<(), Error> { 344 422 let event_loop = EventLoop::new(); 345 - let scale = 3.0f64; // default integer scaling 346 - let size = LogicalSize::new((WIDTH as f64) * scale, (HEIGHT as f64) * scale); 423 + const SCALE: f64 = 3.0; // default integer scaling 424 + let size = LogicalSize::new((WIDTH as f64) * SCALE, (HEIGHT as f64) * SCALE); 347 425 let window = WindowBuilder::new() 348 426 .with_title("tic80_rust – Milestone 1 (GUI + cls/pix)") 349 427 .with_inner_size(size) ··· 365 443 match event { 366 444 Event::WindowEvent { event, .. } => match event { 367 445 WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, 368 - WindowEvent::KeyboardInput { input: KeyboardInput { virtual_keycode: Some(VirtualKeyCode::Escape), state: ElementState::Pressed, .. }, .. } => { 369 - *control_flow = ControlFlow::Exit 370 - } 446 + WindowEvent::KeyboardInput { 447 + input: 448 + KeyboardInput { 449 + virtual_keycode: Some(VirtualKeyCode::Escape), 450 + state: ElementState::Pressed, 451 + .. 452 + }, 453 + .. 454 + } => *control_flow = ControlFlow::Exit, 371 455 WindowEvent::Resized(size) => { 372 456 let _ = pixels.resize_surface(size.width, size.height); 373 457 } ··· 375 459 }, 376 460 Event::MainEventsCleared => { 377 461 if ticker.should_tick() { 378 - if let Some(r) = &lua_runner { r.tick(); } 462 + if let Some(r) = &lua_runner { 463 + r.tick(); 464 + } 379 465 window.request_redraw(); 380 466 } 381 467 } 382 468 Event::RedrawRequested(_) => { 383 469 let frame = pixels.frame_mut(); 384 470 fb.borrow().blit_to_rgba(frame); 385 - let _ = pixels.render(); 471 + if let Err(err) = pixels.render() { 472 + eprintln!("Render error: {err}"); 473 + *control_flow = ControlFlow::Exit; 474 + } 386 475 } 387 476 _ => {} 388 477 }