use crossterm::cursor::{Hide, Show};
use crossterm::execute;
use crossterm::terminal::{
disable_raw_mode, enable_raw_mode, Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen,
};
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
use std::io::{stdout, Write};
use std::sync::{Arc, Mutex};
use crate::domain::events::presentation_events::ExitRequested;
use crate::domain::events::EventBus;
use crate::infrastructure::logging::{log_error_to_file, log_panic_to_file};
use crate::presentation::tui::screens::PanicScreen;
use crate::presentation::tui::{Screen, ScreenManagerImpl};
use crate::GitTypeError;
pub fn setup_signal_handlers(
screen_manager: Arc<Mutex<ScreenManagerImpl<CrosstermBackend<std::io::Stdout>>>>,
) {
let manager_for_panic = screen_manager.clone();
std::panic::set_hook(Box::new(move |panic_info| {
let _ = disable_raw_mode();
let _ = execute!(std::io::stderr(), LeaveAlternateScreen, Show);
let message = if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
s.to_string()
} else if let Some(s) = panic_info.payload().downcast_ref::<String>() {
s.clone()
} else {
"Unknown panic occurred".to_string()
};
let full_message = if let Some(location) = panic_info.location() {
format!(
"{}\n\nLocation: {}:{}:{}",
message,
location.file(),
location.line(),
location.column()
)
} else {
message
};
log_panic_to_file(panic_info);
let error = GitTypeError::PanicError(full_message.clone());
log_error_to_file(&error);
if show_panic_screen(&full_message, &manager_for_panic).is_err() {
ScreenManagerImpl::<CrosstermBackend<std::io::Stdout>>::cleanup_terminal_static();
eprintln!("\\n💥 GitType encountered an unexpected error:");
eprintln!("{}", full_message);
eprintln!("\\nThe error has been logged. Please report this issue at:");
eprintln!("https://github.com/unhappychoice/gittype/issues");
std::process::exit(1);
}
}));
ctrlc::set_handler(move || {
screen_manager
.lock()
.ok()
.map(|manager| {
manager
.get_event_bus()
.as_event_bus()
.publish(ExitRequested)
})
.unwrap_or_else(|| {
ScreenManagerImpl::<CrosstermBackend<std::io::Stdout>>::cleanup_terminal_static();
std::process::exit(0);
});
})
.expect("Error setting Ctrl-C handler");
}
fn show_panic_screen(
error_message: &str,
screen_manager: &Arc<Mutex<ScreenManagerImpl<CrosstermBackend<std::io::Stdout>>>>,
) -> anyhow::Result<()> {
let mut raw_mode_enabled = false;
let mut terminal_initialized = false;
if enable_raw_mode().is_ok() {
raw_mode_enabled = true;
if execute!(stdout(), EnterAlternateScreen, Hide, Clear(ClearType::All)).is_ok() {
terminal_initialized = true;
}
}
let backend = CrosstermBackend::new(stdout());
let mut terminal = Terminal::new(backend)?;
let event_bus = screen_manager
.lock()
.ok()
.map(|mgr| mgr.get_event_bus())
.unwrap_or_else(|| Arc::new(EventBus::default()));
use crate::domain::services::theme_service::ThemeServiceInterface;
use crate::presentation::di::AppModule;
use shaku::HasComponent;
let container = AppModule::builder().build();
let theme_service: Arc<dyn ThemeServiceInterface> = container.resolve();
let mut panic_screen =
PanicScreen::with_error_message(error_message.to_string(), event_bus, theme_service, None);
let result = panic_screen_loop_ratatui(&mut terminal, &mut panic_screen, error_message);
cleanup_panic_terminal(terminal_initialized, raw_mode_enabled);
result
}
fn panic_screen_loop_ratatui(
terminal: &mut Terminal<CrosstermBackend<std::io::Stdout>>,
panic_screen: &mut PanicScreen,
error_message: &str,
) -> anyhow::Result<()> {
use crossterm::event::{poll, read, Event};
use std::time::Duration;
let mut needs_render = true;
loop {
if needs_render {
terminal.draw(|frame| {
if panic_screen.render_ratatui(frame).is_err() {
}
})?;
needs_render = false; }
match poll(Duration::from_millis(100)) {
Ok(true) => {
if let Ok(Event::Key(key_event)) = read() {
match panic_screen.handle_key_event(key_event) {
Ok(()) => {
needs_render = true;
continue;
}
Err(_) => break,
}
}
}
Ok(false) => continue, Err(_) => {
cleanup_panic_terminal(true, true);
eprintln!("\\n💥 PANIC SCREEN (simplified due to terminal limitations)");
eprintln!("Error: {}", error_message);
eprintln!("\\nPress Enter to exit...");
let _ = std::io::stdin().read_line(&mut String::new());
break;
}
}
}
Ok(())
}
fn cleanup_panic_terminal(terminal_initialized: bool, raw_mode_enabled: bool) {
if terminal_initialized {
let _ = execute!(std::io::stdout(), LeaveAlternateScreen, Show);
}
if raw_mode_enabled {
let _ = disable_raw_mode();
}
let _ = std::io::stdout().flush();
}