1#![cfg(unix)]
30
31use std::collections::VecDeque;
32use std::io;
33use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
34use std::sync::Arc;
35
36use super::decode::{Decoder, DecoderFlags};
37use super::pending::Pending;
38use super::poll::PollFd;
39use super::sigwinch as winch;
40use super::source::{
41 DEFAULT_BUFFER_CAPACITY, DEFAULT_ESC_TIMEOUT, DEFAULT_PASTE_IDLE_TIMEOUT, EventSource, Input,
42 Waker,
43};
44use crate::event::Event;
45use crate::terminal::get_window_size;
46
47pub(super) struct UnixWakerInner {
48 tx: OwnedFd,
50}
51
52impl UnixWakerInner {
53 pub(super) fn wake(&self) -> io::Result<()> {
54 let buf = [b'w'];
55 loop {
56 let n = unsafe { libc::write(self.tx.as_raw_fd(), buf.as_ptr() as *const _, 1) };
57 if n < 0 {
58 let err = io::Error::last_os_error();
59 match err.kind() {
60 io::ErrorKind::Interrupted => continue,
61 io::ErrorKind::WouldBlock => return Ok(()),
64 _ => return Err(err),
65 }
66 }
67 return Ok(());
68 }
69 }
70}
71
72impl<I> EventSource<I>
77where
78 I: Input,
79{
80 pub fn new(input: I) -> io::Result<Self> {
96 let (pipe_rx, pipe_tx) = make_self_pipe()?;
97 let (winch_rx, winch_tx) = make_self_pipe()?;
98 let winch_sub = winch::subscribe(winch_tx.as_raw_fd())?;
99 let waker = Waker::from_unix_inner(Arc::new(UnixWakerInner { tx: pipe_tx }));
100
101 let input_fd = input.as_fd().as_raw_fd();
106 let input_is_tty = unsafe { libc::isatty(input_fd) } == 1;
107 let fds: [PollFd; 3] = [input_fd, pipe_rx.as_raw_fd(), winch_rx.as_raw_fd()];
108 let poller = super::poll::new_poller(&fds, input_is_tty)?;
109
110 Ok(Self {
111 input,
112 parser: Decoder::new(DecoderFlags::empty()),
113 pending: Pending::with_capacity(DEFAULT_BUFFER_CAPACITY),
114 esc_timeout: DEFAULT_ESC_TIMEOUT,
115 esc_deadline: None,
116 paste_idle_timeout: Some(DEFAULT_PASTE_IDLE_TIMEOUT),
117 paste_deadline: None,
118 queue: VecDeque::with_capacity(16),
119 waker,
120 handle_resize: true,
121 poller,
122 pipe_rx,
123 winch_rx,
124 _winch_tx: winch_tx,
125 _winch_sub: winch_sub,
126 last_size: None,
127 })
128 }
129
130 pub(super) fn drain_wake(&mut self) {
135 drain_pipe(self.pipe_rx.as_raw_fd());
136 }
137
138 pub(super) fn drain_input(&mut self) -> io::Result<()> {
146 if self.pending.is_full() {
150 self.pending.clear();
151 self.esc_deadline = None;
152 }
153 let n = match self.input.read(self.pending.spare_mut()) {
154 Ok(n) => n,
155 Err(e) => {
156 if matches!(
157 e.kind(),
158 io::ErrorKind::Interrupted | io::ErrorKind::WouldBlock
159 ) {
160 return Ok(());
161 }
162 return Err(e);
163 }
164 };
165 if n == 0 {
166 return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "input closed"));
167 }
168 self.pending.advance_written(n);
169 #[cfg(debug_assertions)]
170 {
171 let s = self.pending.slice();
172 crate::trace::tee_input(&s[s.len() - n..]);
173 }
174 self.drain_parser();
175 Ok(())
176 }
177
178 pub(super) fn handle_winch(&mut self) {
179 drain_pipe(self.winch_rx.as_raw_fd());
180 if !self.handle_resize {
184 return;
185 }
186 let new_size = match get_window_size(self.input.as_fd()) {
187 Ok(sz) => sz,
188 Err(_) => return,
189 };
190 if Some(new_size) == self.last_size {
191 return;
192 }
193 self.last_size = Some(new_size);
194 self.emit(Event::Resize(new_size));
195 }
196}
197
198fn make_self_pipe() -> io::Result<(OwnedFd, OwnedFd)> {
199 let mut fds = [0i32; 2];
200 let rc = unsafe { libc::pipe(fds.as_mut_ptr()) };
201 if rc != 0 {
202 return Err(io::Error::last_os_error());
203 }
204 let rx = unsafe { OwnedFd::from_raw_fd(fds[0]) };
206 let tx = unsafe { OwnedFd::from_raw_fd(fds[1]) };
207 set_nonblock_cloexec(rx.as_raw_fd())?;
208 set_nonblock_cloexec(tx.as_raw_fd())?;
209 Ok((rx, tx))
210}
211
212fn set_nonblock_cloexec(fd: i32) -> io::Result<()> {
213 unsafe {
214 let flags = libc::fcntl(fd, libc::F_GETFL);
215 if flags < 0 {
216 return Err(io::Error::last_os_error());
217 }
218 if libc::fcntl(fd, libc::F_SETFL, flags | libc::O_NONBLOCK) < 0 {
219 return Err(io::Error::last_os_error());
220 }
221 let fd_flags = libc::fcntl(fd, libc::F_GETFD);
222 if fd_flags < 0 {
223 return Err(io::Error::last_os_error());
224 }
225 if libc::fcntl(fd, libc::F_SETFD, fd_flags | libc::FD_CLOEXEC) < 0 {
226 return Err(io::Error::last_os_error());
227 }
228 }
229 Ok(())
230}
231
232fn drain_pipe(fd: i32) {
233 let mut buf = [0u8; 32];
234 loop {
235 let n = unsafe { libc::read(fd, buf.as_mut_ptr() as *mut _, buf.len()) };
236 if n <= 0 {
237 break;
238 }
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245 use crate::event::KeyCode;
246 use std::fs::File;
247 use std::os::fd::FromRawFd;
248 use std::thread;
249 use std::time::Duration;
250 use std::time::Instant;
251
252 fn make_pipe() -> (File, File) {
253 let mut fds = [0i32; 2];
254 let rc = unsafe { libc::pipe(fds.as_mut_ptr()) };
255 assert_eq!(rc, 0, "pipe() failed");
256 let rx = unsafe { File::from_raw_fd(fds[0]) };
258 let tx = unsafe { File::from_raw_fd(fds[1]) };
259 (rx, tx)
260 }
261
262 fn write_byte(f: &File, byte: u8) {
263 let n = unsafe { libc::write(f.as_raw_fd(), &byte as *const _ as *const _, 1) };
264 assert_eq!(n, 1);
265 }
266
267 fn write_bytes(f: &File, bytes: &[u8]) {
268 let n = unsafe { libc::write(f.as_raw_fd(), bytes.as_ptr() as *const _, bytes.len()) };
269 assert_eq!(n, bytes.len() as isize);
270 }
271
272 fn new_reader(input: File) -> EventSource<File> {
273 EventSource::new(input)
274 .unwrap()
275 .with_esc_timeout(Duration::from_millis(50))
276 }
277
278 #[test]
279 fn reads_event_from_input_fd() {
280 let (rx, tx) = make_pipe();
281 let mut src = new_reader(rx);
282 write_byte(&tx, b'a');
283 assert!(src.poll(Some(Duration::from_secs(1))).unwrap());
284 let ev = src.read().unwrap();
285 match ev {
286 Event::KeyPress(k) => assert_eq!(k.code, KeyCode::Char('a')),
287 other => panic!("unexpected event {:?}", other),
288 }
289 }
290
291 #[test]
292 fn timeout_returns_none() {
293 let (rx, _tx) = make_pipe();
294 let mut src = new_reader(rx);
295 let start = Instant::now();
296 let res = src.poll(Some(Duration::from_millis(10))).unwrap();
297 let elapsed = start.elapsed();
298 assert!(!res);
299 assert!(elapsed >= Duration::from_millis(5));
300 }
301
302 #[test]
303 fn waker_interrupts_blocking_read() {
304 let (rx, _tx) = make_pipe();
305 let mut src = new_reader(rx);
306 let waker = src.waker();
307 let handle = thread::spawn(move || {
308 thread::sleep(Duration::from_millis(20));
309 waker.wake().unwrap();
310 });
311 let err = src.read().expect_err("should be Interrupted");
312 handle.join().unwrap();
313 assert_eq!(err.kind(), io::ErrorKind::Interrupted);
314 }
315
316 #[test]
317 fn esc_resolves_before_late_continuation_byte() {
318 let (rx, tx) = make_pipe();
322 let mut src = new_reader(rx); write_bytes(&tx, b"\x1b");
324 assert!(!src.poll(Some(Duration::from_millis(0))).unwrap());
327 thread::sleep(Duration::from_millis(80));
330 write_bytes(&tx, b"a");
331 let first = src.read().unwrap();
332 assert!(
333 matches!(&first, Event::KeyPress(k) if k.code == KeyCode::Escape),
334 "expected bare Esc, got {:?}",
335 first
336 );
337 let second = src.read().unwrap();
338 assert!(
339 matches!(&second, Event::KeyPress(k) if k.code == KeyCode::Char('a')),
340 "expected 'a', got {:?}",
341 second
342 );
343 }
344
345 #[test]
346 fn paste_idle_timeout_synthesizes_paste_end() {
347 let (rx, tx) = make_pipe();
348 let mut src = EventSource::new(rx)
349 .unwrap()
350 .with_paste_idle_timeout(Some(Duration::from_millis(40)));
351 write_bytes(&tx, b"\x1b[200~hello");
352 let mut got_start = false;
354 let mut got_chunk = false;
355 for _ in 0..4 {
356 if !src.poll(Some(Duration::from_millis(20))).unwrap() {
357 break;
358 }
359 while let Some(ev) = src.try_read() {
360 match ev {
361 Event::PasteStart => got_start = true,
362 Event::PasteChunk(b) => {
363 assert_eq!(b, b"hello".to_vec());
364 got_chunk = true;
365 }
366 other => panic!("unexpected pre-timeout event {:?}", other),
367 }
368 }
369 if got_start && got_chunk {
370 break;
371 }
372 }
373 assert!(got_start && got_chunk);
374
375 let start = Instant::now();
377 assert!(src.poll(Some(Duration::from_secs(5))).unwrap());
378 let ev = src.read().unwrap();
379 let elapsed = start.elapsed();
380 assert_eq!(ev, Event::PasteEnd);
381 assert!(
382 elapsed < Duration::from_millis(500),
383 "elapsed = {:?}",
384 elapsed
385 );
386 }
387
388 #[test]
389 fn paste_completes_when_terminator_arrives_within_idle_window() {
390 let (rx, tx) = make_pipe();
391 let mut src = EventSource::new(rx)
392 .unwrap()
393 .with_paste_idle_timeout(Some(Duration::from_millis(500)));
394 write_bytes(&tx, b"\x1b[200~hi");
395 let _ = src.poll(Some(Duration::from_millis(50))).unwrap();
396 while src.try_read().is_some() {}
397 thread::sleep(Duration::from_millis(50));
399 write_bytes(&tx, b"\x1b[201~");
400 assert!(src.poll(Some(Duration::from_secs(1))).unwrap());
401 let mut saw_end = false;
402 while let Some(ev) = src.try_read() {
403 if matches!(ev, Event::PasteEnd) {
404 saw_end = true;
405 }
406 }
407 if !saw_end {
408 let _ = src.poll(Some(Duration::from_millis(50))).unwrap();
410 while let Some(ev) = src.try_read() {
411 if matches!(ev, Event::PasteEnd) {
412 saw_end = true;
413 }
414 }
415 }
416 assert!(saw_end, "expected PasteEnd within the idle window");
417 }
418
419 #[test]
420 fn explicit_end_paste_recovers_stream() {
421 let (rx, tx) = make_pipe();
422 let mut src = EventSource::new(rx).unwrap().with_paste_idle_timeout(None);
423 write_bytes(&tx, b"\x1b[200~stuck");
424 let _ = src.poll(Some(Duration::from_millis(50))).unwrap();
425 while src.try_read().is_some() {}
426
427 src.end_paste();
429 let ev = src.try_read().expect("PasteEnd should be queued");
430 assert_eq!(ev, Event::PasteEnd);
431
432 write_bytes(&tx, b"a");
434 assert!(src.poll(Some(Duration::from_secs(1))).unwrap());
435 let ev = src.read().unwrap();
436 assert!(matches!(
437 ev,
438 Event::KeyPress(ref k) if k.code == KeyCode::Char('a')
439 ));
440 }
441
442 #[test]
443 fn paste_idle_timeout_disabled_blocks_indefinitely() {
444 let (rx, tx) = make_pipe();
445 let mut src = EventSource::new(rx).unwrap().with_paste_idle_timeout(None);
446 write_bytes(&tx, b"\x1b[200~partial");
447 let _ = src.poll(Some(Duration::from_millis(50))).unwrap();
448 while src.try_read().is_some() {}
449
450 let res = src.poll(Some(Duration::from_millis(80))).unwrap();
453 assert!(!res, "should time out, not synthesise PasteEnd");
454 assert!(src.try_read().is_none());
455 }
456
457 #[test]
458 fn esc_deadline_does_not_fire_during_paste() {
459 let (rx, tx) = make_pipe();
462 let mut src = EventSource::new(rx)
463 .unwrap()
464 .with_esc_timeout(Duration::from_millis(20))
465 .with_paste_idle_timeout(Some(Duration::from_secs(5)));
466 write_bytes(&tx, b"\x1b[200~body");
467 let _ = src.poll(Some(Duration::from_millis(50))).unwrap();
468 while src.try_read().is_some() {}
469
470 write_bytes(&tx, b"\x1b[20");
474 let _ = src.poll(Some(Duration::from_millis(80))).unwrap();
475 let mut saw_esc = false;
476 while let Some(ev) = src.try_read() {
477 if matches!(ev, Event::KeyPress(ref k) if k.code == KeyCode::Escape) {
478 saw_esc = true;
479 }
480 }
481 assert!(!saw_esc, "esc deadline must not fire during paste");
482
483 write_bytes(&tx, b"1~");
485 assert!(src.poll(Some(Duration::from_secs(1))).unwrap());
486 let mut saw_end = false;
487 while let Some(ev) = src.try_read() {
488 if matches!(ev, Event::PasteEnd) {
489 saw_end = true;
490 }
491 }
492 assert!(saw_end);
493 }
494
495 #[test]
496 fn esc_deadline_tightens_long_caller_timeout() {
497 let (rx, tx) = make_pipe();
498 let mut src = EventSource::new(rx)
499 .unwrap()
500 .with_esc_timeout(Duration::from_millis(20));
501 write_byte(&tx, 0x1b);
502 let _ = src.poll(Some(Duration::from_secs(60))).unwrap();
503 let start = Instant::now();
504 assert!(src.poll(Some(Duration::from_secs(60))).unwrap());
505 let ev = src.read().unwrap();
506 let elapsed = start.elapsed();
507 assert!(matches!(ev, Event::KeyPress(k) if k.code == KeyCode::Escape));
508 assert!(
509 elapsed < Duration::from_millis(500),
510 "elapsed = {:?}",
511 elapsed
512 );
513 }
514
515 #[test]
516 fn paste_end_after_chunk_is_delivered_without_extra_input() {
517 let (rx, tx) = make_pipe();
523 let mut src = new_reader(rx);
524 write_bytes(&tx, b"\x1b[200~hello\x1b[201~");
525 assert!(src.poll(Some(Duration::from_secs(1))).unwrap());
526 assert!(matches!(src.read().unwrap(), Event::PasteStart));
527 assert!(matches!(src.read().unwrap(), Event::PasteChunk(ref b) if b == b"hello"));
528 assert!(matches!(src.read().unwrap(), Event::PasteEnd));
529 }
530
531 #[test]
532 fn handle_resize_false_suppresses_sigwinch_resize_event() {
533 let stderr_fd = 2;
537 let ws: libc::winsize = unsafe { std::mem::zeroed() };
538 let probe = unsafe { libc::ioctl(stderr_fd, libc::TIOCGWINSZ, &ws as *const _) };
539 if probe < 0 {
540 return;
541 }
542 let stderr_dup = unsafe { libc::dup(stderr_fd) };
543 assert!(stderr_dup >= 0);
544 let stderr_file = unsafe { File::from_raw_fd(stderr_dup) };
545 let mut src = new_reader(stderr_file);
546 assert!(src.handle_resize());
547 src.set_handle_resize(false);
548 assert!(!src.handle_resize());
549 src.last_size = None;
550 unsafe { libc::raise(libc::SIGWINCH) };
551 assert!(!src.poll(Some(Duration::from_millis(50))).unwrap());
553 assert!(src.try_read().is_none());
554 }
555
556 #[test]
557 fn sigwinch_surfaces_resize_event() {
558 let stderr_fd = 2;
562 let ws: libc::winsize = unsafe { std::mem::zeroed() };
563 let probe = unsafe { libc::ioctl(stderr_fd, libc::TIOCGWINSZ, &ws as *const _) };
564 if probe < 0 {
565 return;
566 }
567 let stderr_dup = unsafe { libc::dup(stderr_fd) };
568 assert!(stderr_dup >= 0);
569 let stderr_file = unsafe { File::from_raw_fd(stderr_dup) };
570 let mut src = new_reader(stderr_file);
571 src.last_size = None;
574 unsafe { libc::raise(libc::SIGWINCH) };
575 assert!(src.poll(Some(Duration::from_secs(1))).unwrap());
576 let ev = src.read().unwrap();
577 assert!(matches!(ev, Event::Resize(_)));
578 }
579}