diff --git a/src/main.rs b/src/main.rs index acd1c8c..e92043d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,10 +4,14 @@ mod signals; mod terminal; fn main() { + // ── PRE-TERMINAL PHASE ──────────────────────────────────────────────────── + // Errors here use normal eprintln! — the terminal is not yet in raw mode. + // 1. Detect login shell BEFORE stripping argv[0] + // POSIX: kernel prefixes argv[0] with '-' for login shells. let is_login_shell = config::detect_login_shell(); - // 2. Parse CLI (strips dash from argv[0] internally) + // 2. Parse CLI (strips the leading dash from argv[0] before clap sees it) let cli = config::parse_cli(); // 3. Resolve config path and load config @@ -20,8 +24,65 @@ fn main() { } }; - // Phase 1 placeholder: print loaded config for verification - // This will be replaced by terminal init + event loop in Plan 03 - println!("Config loaded: {:?}", app_config); - println!("Login shell: {}", is_login_shell); + // ── TERMINAL PHASE ──────────────────────────────────────────────────────── + // Install safety envelope BEFORE terminal init so panics during init are caught. + + // 4. Install panic hook first — covers panics during terminal init itself + terminal::install_panic_hook(); + + // 5. Register signal handlers before terminal init — covers early SIGHUP + // (e.g. SSH disconnect between login shell launch and first draw) + let signal_flags = match signals::register_signals() { + Ok(s) => s, + Err(e) => { + eprintln!("SYSTEM ERROR: Cannot register signal handlers: {}", e); + std::process::exit(1); + } + }; + + // 6. Initialize terminal (enables raw mode, clears screen, sets Viewport::Fullscreen) + let mut term = match terminal::init_terminal() { + Ok(t) => t, + Err(e) => { + eprintln!("SYSTEM ERROR: Cannot initialize terminal: {}", e); + std::process::exit(1); + } + }; + + // ── EVENT LOOP PHASE ────────────────────────────────────────────────────── + + // 7. Create app state and run the event loop + // app_config is stored in App for Phase 2+ use (vault_path, theme). + let mut app_state = app::App::new(is_login_shell, app_config); + let shutdown_reason = app_state.run_event_loop(&mut term, &signal_flags); + + // ── SHUTDOWN PHASE ──────────────────────────────────────────────────────── + // Terminal must be restored in EVERY exit path below. + + // 8. Restore terminal — always, regardless of how we got here. + // Must happen BEFORE show_goodbye() since goodbye prints to stdout. + terminal::restore_terminal(); + + // 9. Handle the shutdown reason + match shutdown_reason { + Ok(app::ShutdownReason::UserQuit) => { + // User deliberately exited — show BBS goodbye message + app::show_goodbye(); + } + Ok(app::ShutdownReason::Signal) => { + // SIGHUP or SIGTERM — SSH disconnect or graceful OS shutdown. + // Exit silently: there may be nobody on the other end to see a message. + } + Err(e) => { + // I/O error from the event loop + if e.kind() == std::io::ErrorKind::BrokenPipe { + // SSH connection closed while we were writing — silent exit (LIFE-04). + // Terminal is already restored above; just exit quietly. + } else { + // Unexpected I/O error — log to stderr after terminal restore + eprintln!("SYSTEM ERROR: {}", e); + std::process::exit(1); + } + } + } }