nonblocking/
nb_stdin.rs

1//! nonblocking stdin
2
3use std::{thread, time};
4use std::sync::mpsc;
5use std::error::Error;
6
7use futures::{select, pin_mut, future::FutureExt};
8use futures::StreamExt;
9use futures::executor::block_on;
10use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
11use crossterm::event; // event::{poll, read};
12use crossterm::event::{Event, EventStream, KeyCode, KeyModifiers};
13// use crossterm::execute;
14
15// use crossbeam_channel::{Sender, Receiver, unbounded, tick};
16
17/// NbStdin
18pub struct NbStdin {
19  reader: EventStream
20}
21
22/// NbStdin
23impl NbStdin {
24
25/// constructor
26pub fn start() -> Self {
27  let nb = NbStdin{reader: EventStream::new()};
28  enable_raw_mode().unwrap();
29  nb
30}
31
32/// destructor
33pub fn stop(&self) {
34  disable_raw_mode().unwrap();
35}
36
37/// default for async_stdin
38pub fn break_with_esc(e: Event) -> bool {
39  match e {
40  Event::Key(ke) => {
41    if ke.code == KeyCode::Esc { return true; }
42    if ke.modifiers == KeyModifiers::CONTROL {
43      if ke.code == KeyCode::Char('c') { return true; } // ^c
44    }
45    if ke.modifiers == KeyModifiers::CONTROL | KeyModifiers::SHIFT {
46      if ke.code == KeyCode::Char('C') { return true; } // ^c with shift
47    }
48    ()
49  },
50  _ => ()
51  }
52  false
53}
54
55/// async-await
56pub async fn async_stdin(&mut self, lambda: fn (e: Event) -> bool) -> Result<bool, Box<dyn Error>> {
57  match self.reader.next().await { // blocking
58  None => (),
59  Some(Err(e)) => { println!("Error: {:?}", e); () },
60  Some(Ok(e)) => { if lambda(e) { return Ok(true); } }
61  }
62  Ok(false)
63}
64
65/// select async-await
66pub async fn select_stdin(timeout: time::Duration) -> Result<Option<Event>, Box<dyn Error>> {
67  let now = time::Instant::now();
68  let mut reader = EventStream::new();
69  loop {
70    let c = reader.next().fuse();
71    pin_mut!(c);
72    select! {
73      e = c => {
74        match e {
75        None => (),
76        Some(Err(e)) => { println!("Error: {:?}", e); () },
77        Some(Ok(e)) => { return Ok(Some(e)); }
78        }
79      },
80      complete => break,
81      default => {
82        if now.elapsed() < timeout { () } else { break; } // or unreachable!()
83      }
84    }
85  }
86  Ok(None)
87}
88
89/// async another thread
90pub fn async_non_blocking_stdin(timeout: time::Duration, lambda: fn (e: Event) -> bool) -> bool {
91  let (tx, rx) = mpsc::channel();
92  let _handle = thread::spawn(move || {
93    loop {
94      match block_on(NbStdin::select_stdin(timeout)).unwrap() { // blocking
95      None => (),
96      Some(e) => {
97        match tx.send(e) {
98        Ok(()) => break,
99        Err(_) => ()
100        }
101      }
102      }
103    }
104    ()
105  });
106  match rx.recv_timeout(timeout) {
107  Ok(e) => { if lambda(e) { return true; } },
108  Err(mpsc::RecvTimeoutError::Timeout) => (),
109  Err(mpsc::RecvTimeoutError::Disconnected) => { println!("disconnected"); () } // unreachable!()
110  }
111  false
112}
113
114/// another thread
115pub fn non_blocking_stdin(timeout: time::Duration, lambda: fn (e: Event) -> bool) -> bool {
116  let (tx, rx) = mpsc::channel();
117  let _handle = thread::spawn(move || {
118    loop {
119      if !event::poll(timeout).unwrap() { break; } // nonblocking
120      else {
121        match event::read().unwrap() { // blocking
122        e => {
123          match tx.send(e) {
124          Ok(()) => (),
125          Err(_) => ()
126          }
127          break;
128        }
129        }
130      }
131    }
132    ()
133  });
134  match rx.recv_timeout(timeout) {
135  Ok(e) => { if lambda(e) { return true; } },
136  Err(mpsc::RecvTimeoutError::Timeout) => (),
137  Err(mpsc::RecvTimeoutError::Disconnected) => { println!("disconnected"); () } // unreachable!()
138  }
139  false
140}
141
142}