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