use anyhow::{ensure, Result}; use crossterm::event; use ratatui::{backend::CrosstermBackend, Terminal}; use std::{ io::{self, IsTerminal}, path::PathBuf, }; use tb_ide::{ app::{App, Execution}, options, terminal::TerminalGuard, }; fn main() -> Result<()> { let args: Vec<_> = std::env::args().skip(1).collect(); if args.iter().any(|s| s == "--help" || s == "-h") { println!("tb [Projekt.mak|Modul.bas|Formular.frm]\nTerminal Basic IDE · mindestens 80×25\nF11: Menü · Alt+F4: Beenden"); return Ok(()); } ensure!(args.len() <= 1, "Aufruf: tb [Projektdatei]"); ensure!( io::stdin().is_terminal() && io::stdout().is_terminal(), "tb benötigt ein interaktives Terminal; --help zeigt den Aufruf" ); let base = std::env::current_dir()?; let mut app = App::new(&base, options::config_path()?, crossterm::terminal::size()?)?; if let Some(path) = args.first() { app.load_initial_project(PathBuf::from(path))?; } let mut guard = TerminalGuard::enter(io::stdout())?; let mut terminal = Terminal::new(CrosstermBackend::new(io::stdout()))?; let start = std::time::Instant::now(); let mut last_draw = start - std::time::Duration::from_millis(16); let signals = tb_ui::signale::Signalquelle::neu(); while !app.quit { if signals.abholen().is_some() { app.pause_execution(); } let shell = if app.session.file_shell || matches!(app.execution, Execution::Running | Execution::Waiting) { app.session.host.shell_request.take() } else { None }; if let Some(command) = shell { let result = guard.shell(&command, &signals); terminal.clear()?; let (cols, rows) = crossterm::terminal::size()?; app.handle(event::Event::Resize(cols, rows)); if app.session.file_shell { app.session.file_shell = false; app.message = match result { Ok(code) => format!("Shell beendet: {code}"), Err(e) => format!("Shell: {e}"), }; } else { app.session.host.shell_result = Some(result.map_err(|_| tb_runtime::errors::RuntimeError(53))); } } if last_draw.elapsed() >= std::time::Duration::from_millis(16) { terminal.draw(|f| app.render(f))?; last_draw = std::time::Instant::now(); } // Bounded input batch: both keyboard and VM make progress under load. for _ in 0..64 { if !event::poll(std::time::Duration::ZERO)? { break; } app.handle(event::read()?); } app.tick_execution(start.elapsed().as_millis() as u64); let timeout = if app.execution == Execution::Running { std::time::Duration::ZERO } else { std::time::Duration::from_millis(10) }; if event::poll(timeout)? { app.handle(event::read()?); } } Ok(()) }