Fix Ctrl+C handling with proper tokio signal handler
Previous keyboard event polling didn't work for Ctrl+C in raw mode. Now using tokio::signal::ctrl_c() with a background task that signals the render loop through a channel when Ctrl+C is pressed. Changes: - Added "signal" and "sync" features to tokio dependency - Spawn background task to listen for Ctrl+C signal - Use mpsc channel to communicate signal to render loop - Keep 'q' and ESC keyboard shortcuts for convenience Ctrl+C now properly exits looping animations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -21,8 +21,8 @@ palette = "0.7"
|
|||||||
# Terminal manipulation
|
# Terminal manipulation
|
||||||
crossterm = "0.27"
|
crossterm = "0.27"
|
||||||
|
|
||||||
# Async runtime (for timing)
|
# Async runtime (for timing and signal handling)
|
||||||
tokio = { version = "1.35", features = ["time", "rt-multi-thread", "macros"] }
|
tokio = { version = "1.35", features = ["time", "rt-multi-thread", "macros", "signal", "sync"] }
|
||||||
|
|
||||||
# Process execution
|
# Process execution
|
||||||
which = "5.0"
|
which = "5.0"
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ use crate::animation::{easing::EasingFunction, effects::Effect, timeline::Timeli
|
|||||||
use crate::color::{apply, ColorEngine};
|
use crate::color::{apply, ColorEngine};
|
||||||
use crate::utils::{ansi, ascii::AsciiArt, terminal::TerminalManager};
|
use crate::utils::{ansi, ascii::AsciiArt, terminal::TerminalManager};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use crossterm::event::{self, Event, KeyCode, KeyModifiers};
|
use crossterm::event::{self, Event, KeyCode};
|
||||||
|
use std::time::Duration;
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
|
||||||
pub struct Renderer<'a> {
|
pub struct Renderer<'a> {
|
||||||
@@ -35,21 +36,30 @@ impl<'a> Renderer<'a> {
|
|||||||
let mut timeline = Timeline::new(self.timeline.duration_ms(), self.timeline.fps());
|
let mut timeline = Timeline::new(self.timeline.duration_ms(), self.timeline.fps());
|
||||||
timeline.start();
|
timeline.start();
|
||||||
|
|
||||||
|
// Setup Ctrl+C handler using a channel
|
||||||
|
let (tx, mut rx) = tokio::sync::mpsc::channel::<()>(1);
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let _ = tokio::signal::ctrl_c().await;
|
||||||
|
let _ = tx.send(()).await;
|
||||||
|
});
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let frame_start = std::time::Instant::now();
|
let frame_start = std::time::Instant::now();
|
||||||
|
|
||||||
// Check for Ctrl+C or 'q' to exit
|
// Check for keyboard input (q or ESC to exit)
|
||||||
if event::poll(std::time::Duration::from_millis(0))? {
|
if event::poll(Duration::from_millis(0))? {
|
||||||
if let Event::Key(key) = event::read()? {
|
if let Event::Key(key) = event::read()? {
|
||||||
if key.code == KeyCode::Char('c') && key.modifiers.contains(KeyModifiers::CONTROL) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if key.code == KeyCode::Char('q') || key.code == KeyCode::Esc {
|
if key.code == KeyCode::Char('q') || key.code == KeyCode::Esc {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for Ctrl+C signal
|
||||||
|
if rx.try_recv().is_ok() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate progress with easing
|
// Calculate progress with easing
|
||||||
let linear_progress = timeline.progress();
|
let linear_progress = timeline.progress();
|
||||||
let eased_progress = self.easing.ease(linear_progress);
|
let eased_progress = self.easing.ease(linear_progress);
|
||||||
|
|||||||
Reference in New Issue
Block a user