Bugfix: Console locked

This commit is contained in:
Jan-Peter von Hunnius 2025-09-29 20:40:06 +02:00
parent 0278a96a8d
commit 77ede95bce
2 changed files with 90 additions and 68 deletions

View file

@ -16,5 +16,3 @@ crossterm = "0.27"
ratatui = "0.24"
[dev-dependencies]
# Property-based testing
proptest = "1.0"

View file

@ -84,64 +84,76 @@ impl ConsoleApp {
pub fn run(&mut self) -> Result<(), Box<dyn std::error::Error>> {
// Setup terminal
enable_raw_mode()?;
enable_raw_mode().map_err(|e| format!("Failed to enable raw mode: {}", e))?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
execute!(stdout, EnterAlternateScreen).map_err(|e| format!("Failed to enter alternate screen: {}", e))?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
self.running = true;
let mut last_draw = Instant::now();
// Main event loop
while self.running {
let now = Instant::now();
// Handle input
if event::poll(Duration::from_millis(50))? {
if let Event::Key(key) = event::read()? {
if let Ok(true) = event::poll(Duration::from_millis(10)) {
if let Ok(Event::Key(key)) = event::read() {
self.handle_key_event(key.code);
}
}
// Update display if needed
if self.last_update.elapsed() >= Duration::from_millis(self.config.refresh_rate_ms) {
terminal.draw(|f| self.draw_ui(f))?;
self.last_update = Instant::now();
// Update display at regular intervals
if now.duration_since(last_draw) >= Duration::from_millis(self.config.refresh_rate_ms) {
if let Err(e) = terminal.draw(|f| self.draw_ui(f)) {
eprintln!("DEBUG: Failed to draw UI: {}", e);
break;
}
last_draw = now;
}
// Check if system should still be running
if let Ok(system) = self.system.lock() {
if !system.is_running() && !self.show_help {
// System stopped, show final state
terminal.draw(|f| self.draw_ui(f))?;
thread::sleep(Duration::from_millis(1000));
// System stopped, show final state briefly
if let Err(e) = terminal.draw(|f| self.draw_ui(f)) {
eprintln!("DEBUG: Failed to draw final UI: {}", e);
}
thread::sleep(Duration::from_millis(500));
break;
}
}
// Small delay to prevent busy waiting
thread::sleep(Duration::from_millis(1));
}
// Restore terminal
disable_raw_mode()?;
disable_raw_mode().map_err(|e| format!("Failed to disable raw mode: {}", e))?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
terminal.show_cursor()?;
LeaveAlternateScreen
).map_err(|e| format!("Failed to leave alternate screen: {}", e))?;
terminal.show_cursor().map_err(|e| format!("Failed to show cursor: {}", e))?;
Ok(())
}
fn handle_key_event(&mut self, key: KeyCode) {
match key {
KeyCode::Char('q') | KeyCode::Esc => {
KeyCode::Char('q') | KeyCode::Char('Q') | KeyCode::Esc => {
println!("DEBUG: Quit key pressed, stopping console");
self.running = false;
if let Ok(mut system) = self.system.lock() {
system.stop();
}
}
KeyCode::Char('h') => {
KeyCode::Char('h') | KeyCode::Char('H') => {
self.show_help = !self.show_help;
}
KeyCode::Char('r') => {
KeyCode::Char('r') | KeyCode::Char('R') => {
if let Ok(mut system) = self.system.lock() {
if system.is_running() {
system.stop();
@ -150,7 +162,7 @@ impl ConsoleApp {
}
}
}
KeyCode::Char('s') => {
KeyCode::Char('s') | KeyCode::Char('S') => {
if let Ok(mut system) = self.system.lock() {
system.stop();
}
@ -170,8 +182,10 @@ impl ConsoleApp {
self.command_buffer.clear();
}
KeyCode::Char(c) => {
if c.is_ascii_alphabetic() || c.is_ascii_digit() {
self.command_buffer.push(c);
}
}
_ => {}
}
}
@ -180,6 +194,7 @@ impl ConsoleApp {
let cmd = self.command_buffer.trim().to_lowercase();
match cmd.as_str() {
"quit" | "exit" | "q" => {
println!("DEBUG: Executing quit command");
self.running = false;
if let Ok(mut system) = self.system.lock() {
system.stop();
@ -206,6 +221,7 @@ impl ConsoleApp {
}
_ => {
// Unknown command - could show error message
println!("DEBUG: Unknown command: {}", cmd);
}
}
}
@ -218,36 +234,37 @@ impl ConsoleApp {
return;
}
// Create main layout
// Create main layout with proper constraints
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(3), // Title bar
Constraint::Min(10), // Main content
Constraint::Length(4), // Title bar
Constraint::Min(8), // Main content
Constraint::Length(3), // Command bar
])
.split(size);
// Title bar
let title = Paragraph::new(vec![
let title_text = vec![
Line::from(vec![
Span::styled("Intel MCS-4 Emulator Console", Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD)),
]),
Line::from(vec![
Span::raw("Commands: "),
Span::styled("q", Style::default().fg(Color::Yellow)),
Span::raw("uit, "),
Span::styled("r", Style::default().fg(Color::Yellow)),
Span::raw("un, "),
Span::styled("s", Style::default().fg(Color::Yellow)),
Span::raw("top, "),
Span::styled("h", Style::default().fg(Color::Yellow)),
Span::raw("elp, "),
Span::styled("Tab", Style::default().fg(Color::Yellow)),
Span::raw(" to switch panes"),
Span::styled("q/Q", Style::default().fg(Color::Yellow)),
Span::raw("=quit, "),
Span::styled("r/R", Style::default().fg(Color::Yellow)),
Span::raw("=run, "),
Span::styled("s/S", Style::default().fg(Color::Yellow)),
Span::raw("=stop, "),
Span::styled("h/H", Style::default().fg(Color::Yellow)),
Span::raw("=help"),
]),
])
.block(Block::default().borders(Borders::ALL).title("Status"));
];
let title = Paragraph::new(title_text)
.block(Block::default().borders(Borders::ALL).title("Status"))
.wrap(Wrap { trim: true });
f.render_widget(title, chunks[0]);
// Main content area
@ -315,9 +332,10 @@ impl ConsoleApp {
.split(area);
// System information
let system_info = if let Ok(system) = self.system.lock() {
let info = system.get_system_info();
vec![
let system_info = match self.system.lock() {
Ok(system) => {
match system.get_system_info() {
info => vec![
Line::from(vec![Span::raw(format!("System: {}", info.name))]),
Line::from(vec![Span::raw(format!("Description: {}", info.description))]),
Line::from(vec![Span::raw(format!("Components: {}", info.component_count))]),
@ -326,8 +344,11 @@ impl ConsoleApp {
Line::from(vec![Span::raw(format!("RAM Size: {} nibbles", info.ram_size))]),
Line::from(vec![Span::raw(format!("Running: {}", system.is_running()))]),
]
} else {
}
}
Err(_) => {
vec![Line::from(vec![Span::raw("System information unavailable")])]
}
};
let system_widget = Paragraph::new(system_info)
@ -354,7 +375,8 @@ impl ConsoleApp {
let mut ram_info = vec![Line::from(vec![Span::raw("RAM Contents:")])];
// Try to get actual RAM data from the system
if let Ok(_system) = self.system.lock() {
match self.system.lock() {
Ok(_system) => {
// This is a simplified version - in a real implementation,
// we would need to access the actual RAM components
// For now, show a more informative placeholder
@ -372,9 +394,11 @@ impl ConsoleApp {
bank_data.push(']');
ram_info.push(Line::from(vec![Span::raw(bank_data)]));
}
} else {
}
Err(_) => {
ram_info.push(Line::from(vec![Span::raw("Unable to access RAM data")]));
}
}
let ram_widget = Paragraph::new(ram_info)
.block(Block::default().borders(Borders::ALL).title("RAM Contents"))