| 63 | |
| 64 | impl EventHandler { |
| 65 | pub fn new(tick_rate: Duration) -> Self { |
| 66 | let (tx, rx) = mpsc::unbounded_channel(); |
| 67 | let keepalive = tx.clone(); |
| 68 | let paused = Arc::new(AtomicBool::new(false)); |
| 69 | let paused_flag = paused.clone(); |
| 70 | |
| 71 | tokio::spawn(async move { |
| 72 | // Use a short poll interval so we check the paused flag frequently. |
| 73 | // The tick event fires when the full tick_rate elapses without input. |
| 74 | let poll_interval = Duration::from_millis(50); |
| 75 | let mut since_tick = std::time::Instant::now(); |
| 76 | |
| 77 | loop { |
| 78 | // When paused, sleep instead of polling stdin so the child |
| 79 | // process (e.g. SSH shell) gets uncontested access to stdin. |
| 80 | if paused_flag.load(Ordering::Relaxed) { |
| 81 | tokio::time::sleep(Duration::from_millis(50)).await; |
| 82 | since_tick = std::time::Instant::now(); |
| 83 | continue; |
| 84 | } |
| 85 | |
| 86 | if event::poll(poll_interval).unwrap_or(false) { |
| 87 | match event::read() { |
| 88 | Ok(TermEvent::Key(key)) if tx.send(Event::Key(key)).is_err() => { |
| 89 | return; |
| 90 | } |
| 91 | Ok(TermEvent::Mouse(mouse)) if tx.send(Event::Mouse(mouse)).is_err() => { |
| 92 | return; |
| 93 | } |
| 94 | Ok(TermEvent::Resize(w, h)) if tx.send(Event::Resize(w, h)).is_err() => { |
| 95 | return; |
| 96 | } |
| 97 | _ => {} |
| 98 | } |
| 99 | } else if since_tick.elapsed() >= tick_rate { |
| 100 | since_tick = std::time::Instant::now(); |
| 101 | if tx.send(Event::Tick).is_err() { |
| 102 | return; |
| 103 | } |
| 104 | } |
| 105 | } |
| 106 | }); |
| 107 | |
| 108 | Self { |
| 109 | rx, |
| 110 | keepalive, |
| 111 | paused, |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | pub async fn next(&mut self) -> Option<Event> { |
| 116 | self.rx.recv().await |