1#![cfg(unix)]
32
33use std::collections::VecDeque;
34use std::io;
35use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
36use std::sync::Arc;
37
38use super::decode::{Decoder, DecoderFlags};
39use super::pending::Pending;
40use super::poll::PollFd;
41use super::sigwinch as winch;
42use super::source::{
43 DEFAULT_BUFFER_CAPACITY, DEFAULT_ESC_TIMEOUT, DEFAULT_PASTE_IDLE_TIMEOUT, EventSource, Input,
44 Waker,
45};
46use crate::event::Event;
47use crate::terminal::get_window_size;
48
49pub(super) struct UnixWakerInner {
50 tx: OwnedFd,
52 rx: OwnedFd,
61}
62
63impl UnixWakerInner {
64 pub(super) fn read_fd(&self) -> std::os::fd::RawFd {
65 self.rx.as_raw_fd()
66 }
67
68 pub(super) fn wake(&self) -> io::Result<()> {
69 let buf = b"w";
70 loop {
71 let n = unsafe { libc::write(self.tx.as_raw_fd(), buf.as_ptr() as *const _, 1) };
72 if n < 0 {
73 let err = io::Error::last_os_error();
74 match err.kind() {
75 io::ErrorKind::Interrupted => continue,
76 io::ErrorKind::WouldBlock => return Ok(()),
79 _ => return Err(err),
80 }
81 }
82 return Ok(());
83 }
84 }
85}
86
87impl<I> EventSource<I>
92where
93 I: Input,
94{
95 pub fn new(input: I) -> io::Result<Self> {
115 let (pipe_rx, pipe_tx) = make_self_pipe()?;
116 let winch_sub = winch::subscribe()?;
117 let waker = Waker::from_unix_inner(Arc::new(UnixWakerInner {
118 tx: pipe_tx,
119 rx: pipe_rx,
120 }));
121
122 let input_fd = input.as_fd().as_raw_fd();
127 let input_is_tty = unsafe { libc::isatty(input_fd) } == 1;
128 let last_size = get_window_size(input.as_fd()).ok();
134 let fds: [PollFd; 3] = [input_fd, waker.pipe_read_fd(), winch_sub.read_fd()];
135 let poller = super::poll::new_poller(&fds, input_is_tty)?;
136
137 Ok(Self {
138 input,
139 parser: Decoder::new(DecoderFlags::empty()),
140 pending: Pending::with_capacity(DEFAULT_BUFFER_CAPACITY),
141 esc_timeout: DEFAULT_ESC_TIMEOUT,
142 esc_deadline: None,
143 paste_idle_timeout: Some(DEFAULT_PASTE_IDLE_TIMEOUT),
144 paste_deadline: None,
145 queue: VecDeque::with_capacity(16),
146 waker,
147 handle_resize: true,
148 poller,
149 winch_sub,
150 last_size,
151 })
152 }
153
154 pub(super) fn drain_wake(&mut self) {
159 drain_pipe(self.waker.pipe_read_fd());
160 }
161
162 pub(super) fn drain_input(&mut self) -> io::Result<()> {
170 if self.pending.is_full() {
174 self.pending.clear();
175 self.esc_deadline = None;
176 }
177 let n = match self.input.read(self.pending.spare_mut()) {
178 Ok(n) => n,
179 Err(e) => {
180 if matches!(
181 e.kind(),
182 io::ErrorKind::Interrupted | io::ErrorKind::WouldBlock
183 ) {
184 return Ok(());
185 }
186 return Err(e);
187 }
188 };
189 if n == 0 {
190 return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "input closed"));
191 }
192 self.pending.advance_written(n);
193 #[cfg(debug_assertions)]
194 {
195 let s = self.pending.slice();
196 crate::trace::tee_input(&s[s.len() - n..]);
197 }
198 self.drain_parser();
199 Ok(())
200 }
201
202 pub(super) fn handle_winch(&mut self) {
203 drain_pipe(self.winch_sub.read_fd());
204 if !self.handle_resize {
208 return;
209 }
210 let new_size = match get_window_size(self.input.as_fd()) {
211 Ok(sz) => sz,
212 Err(_) => return,
213 };
214 if Some(new_size) == self.last_size {
215 return;
216 }
217 self.last_size = Some(new_size);
218 self.emit(Event::Resize(new_size));
219 }
220}
221
222pub(super) fn make_self_pipe() -> io::Result<(OwnedFd, OwnedFd)> {
223 let mut fds = [0i32; 2];
224 let rc = unsafe { libc::pipe(fds.as_mut_ptr()) };
225 if rc != 0 {
226 return Err(io::Error::last_os_error());
227 }
228 let rx = unsafe { OwnedFd::from_raw_fd(fds[0]) };
230 let tx = unsafe { OwnedFd::from_raw_fd(fds[1]) };
231 set_nonblock_cloexec(rx.as_raw_fd())?;
232 set_nonblock_cloexec(tx.as_raw_fd())?;
233 Ok((rx, tx))
234}
235
236fn set_nonblock_cloexec(fd: i32) -> io::Result<()> {
237 unsafe {
238 let flags = libc::fcntl(fd, libc::F_GETFL);
239 if flags < 0 {
240 return Err(io::Error::last_os_error());
241 }
242 if libc::fcntl(fd, libc::F_SETFL, flags | libc::O_NONBLOCK) < 0 {
243 return Err(io::Error::last_os_error());
244 }
245 let fd_flags = libc::fcntl(fd, libc::F_GETFD);
246 if fd_flags < 0 {
247 return Err(io::Error::last_os_error());
248 }
249 if libc::fcntl(fd, libc::F_SETFD, fd_flags | libc::FD_CLOEXEC) < 0 {
250 return Err(io::Error::last_os_error());
251 }
252 }
253 Ok(())
254}
255
256pub(super) fn drain_pipe(fd: i32) {
257 let mut buf = [0u8; 32];
258 loop {
259 let n = unsafe { libc::read(fd, buf.as_mut_ptr() as *mut _, buf.len()) };
260 if n <= 0 {
261 break;
262 }
263 }
264}
265
266#[cfg(test)]
267mod tests {
268 use super::*;
269 use crate::event::KeyCode;
270 use std::fs::File;
271 use std::os::fd::FromRawFd;
272 use std::thread;
273 use std::time::Duration;
274 use std::time::Instant;
275
276 fn make_pipe() -> (File, File) {
277 let mut fds = [0i32; 2];
278 let rc = unsafe { libc::pipe(fds.as_mut_ptr()) };
279 assert_eq!(rc, 0, "pipe() failed");
280 let rx = unsafe { File::from_raw_fd(fds[0]) };
282 let tx = unsafe { File::from_raw_fd(fds[1]) };
283 (rx, tx)
284 }
285
286 fn write_byte(f: &File, byte: u8) {
287 let n = unsafe { libc::write(f.as_raw_fd(), &byte as *const _ as *const _, 1) };
288 assert_eq!(n, 1);
289 }
290
291 fn write_bytes(f: &File, bytes: &[u8]) {
292 let n = unsafe { libc::write(f.as_raw_fd(), bytes.as_ptr() as *const _, bytes.len()) };
293 assert_eq!(n, bytes.len() as isize);
294 }
295
296 fn new_reader(input: File) -> EventSource<File> {
297 EventSource::new(input)
298 .unwrap()
299 .with_esc_timeout(Duration::from_millis(50))
300 }
301
302 #[test]
303 fn reads_event_from_input_fd() {
304 let (rx, tx) = make_pipe();
305 let mut src = new_reader(rx);
306 write_byte(&tx, b'a');
307 assert!(src.poll(Some(Duration::from_secs(1))).unwrap());
308 let ev = src.read().unwrap();
309 match ev {
310 Event::KeyPress(k) => assert_eq!(k.code, KeyCode::Char('a')),
311 other => panic!("unexpected event {:?}", other),
312 }
313 }
314
315 #[test]
316 fn cursor_reports_and_f3_preserve_event_order() {
317 use crate::event::{Key, KeyModifiers};
318 use crate::layout::Position;
319
320 let cpr = |x, y, page| Event::CursorPosition {
321 position: Position::new(x, y),
322 page,
323 };
324 let f3 = |modifiers| Event::KeyPress(Key::new(KeyCode::F(3), modifiers));
325 for prefix in [b"\x1b[".as_slice(), b"\x9b".as_slice()] {
326 for (body, expected) in [
327 ("R", Some(vec![f3(KeyModifiers::empty())])),
328 ("1R", Some(vec![f3(KeyModifiers::empty())])),
329 ("1;5R", Some(vec![f3(KeyModifiers::CTRL), cpr(4, 0, 0)])),
330 ("5;10R", Some(vec![cpr(9, 4, 0)])),
331 ("1;17R", Some(vec![cpr(16, 0, 0)])),
332 ("?1;5R", Some(vec![cpr(4, 0, 0)])),
333 ("?5;10;2R", Some(vec![cpr(9, 4, 2)])),
334 ("?5;10;R", Some(vec![cpr(9, 4, 0)])),
335 ("?1;5;2;3R", Some(vec![cpr(4, 0, 2)])),
336 ("1;2;3R", None),
337 ("?R", None),
338 (">1;5R", None),
339 ("?1;5 R", None),
340 ] {
341 let mut wire = [prefix, body.as_bytes()].concat();
342 let mut expected =
343 expected.unwrap_or_else(|| vec![Event::UnknownCsi(wire.clone())]);
344 wire.push(b'x');
345 expected.push(Event::KeyPress(Key::new(
346 KeyCode::Char('x'),
347 KeyModifiers::empty(),
348 )));
349 let (rx, tx) = make_pipe();
350 let mut src = new_reader(rx);
351 write_bytes(&tx, &wire);
352 for event in expected {
353 assert!(src.poll(Some(Duration::from_secs(1))).unwrap(), "{wire:?}");
354 assert_eq!(src.read().unwrap(), event, "{wire:?}");
355 }
356 assert!(src.try_read().is_none(), "{wire:?}");
357 }
358 }
359 }
360
361 #[test]
362 fn timeout_returns_none() {
363 let (rx, _tx) = make_pipe();
364 let mut src = new_reader(rx);
365 let start = Instant::now();
366 let res = src.poll(Some(Duration::from_millis(10))).unwrap();
367 let elapsed = start.elapsed();
368 assert!(!res);
369 assert!(elapsed >= Duration::from_millis(5));
370 }
371
372 #[test]
373 fn waker_interrupts_blocking_read() {
374 let (rx, _tx) = make_pipe();
375 let mut src = new_reader(rx);
376 let waker = src.waker();
377 let handle = thread::spawn(move || {
378 thread::sleep(Duration::from_millis(20));
379 waker.wake().unwrap();
380 });
381 let err = src.read().expect_err("should be Interrupted");
382 handle.join().unwrap();
383 assert_eq!(err.kind(), io::ErrorKind::Interrupted);
384 }
385
386 #[test]
387 fn esc_resolves_before_late_continuation_byte() {
388 let (rx, tx) = make_pipe();
392 let mut src = new_reader(rx); write_bytes(&tx, b"\x1b");
394 assert!(!src.poll(Some(Duration::from_millis(0))).unwrap());
397 thread::sleep(Duration::from_millis(80));
400 write_bytes(&tx, b"a");
401 let first = src.read().unwrap();
402 assert!(
403 matches!(&first, Event::KeyPress(k) if k.code == KeyCode::Escape),
404 "expected bare Esc, got {:?}",
405 first
406 );
407 let second = src.read().unwrap();
408 assert!(
409 matches!(&second, Event::KeyPress(k) if k.code == KeyCode::Char('a')),
410 "expected 'a', got {:?}",
411 second
412 );
413 }
414
415 #[test]
416 fn paste_idle_timeout_synthesizes_paste_end() {
417 let (rx, tx) = make_pipe();
418 let mut src = EventSource::new(rx)
419 .unwrap()
420 .with_paste_idle_timeout(Some(Duration::from_millis(40)));
421 write_bytes(&tx, b"\x1b[200~hello");
422 let mut got_start = false;
424 let mut got_chunk = false;
425 for _ in 0..4 {
426 if !src.poll(Some(Duration::from_millis(20))).unwrap() {
427 break;
428 }
429 while let Some(ev) = src.try_read() {
430 match ev {
431 Event::PasteStart => got_start = true,
432 Event::PasteChunk(b) => {
433 assert_eq!(b, b"hello".to_vec());
434 got_chunk = true;
435 }
436 other => panic!("unexpected pre-timeout event {:?}", other),
437 }
438 }
439 if got_start && got_chunk {
440 break;
441 }
442 }
443 assert!(got_start && got_chunk);
444
445 let start = Instant::now();
447 assert!(src.poll(Some(Duration::from_secs(5))).unwrap());
448 let ev = src.read().unwrap();
449 let elapsed = start.elapsed();
450 assert_eq!(ev, Event::PasteEnd);
451 assert!(
452 elapsed < Duration::from_millis(500),
453 "elapsed = {:?}",
454 elapsed
455 );
456 }
457
458 #[test]
459 fn paste_completes_when_terminator_arrives_within_idle_window() {
460 let (rx, tx) = make_pipe();
461 let mut src = EventSource::new(rx)
462 .unwrap()
463 .with_paste_idle_timeout(Some(Duration::from_millis(500)));
464 write_bytes(&tx, b"\x1b[200~hi");
465 let _ = src.poll(Some(Duration::from_millis(50))).unwrap();
466 while src.try_read().is_some() {}
467 thread::sleep(Duration::from_millis(50));
469 write_bytes(&tx, b"\x1b[201~");
470 assert!(src.poll(Some(Duration::from_secs(1))).unwrap());
471 let mut saw_end = false;
472 while let Some(ev) = src.try_read() {
473 if matches!(ev, Event::PasteEnd) {
474 saw_end = true;
475 }
476 }
477 if !saw_end {
478 let _ = src.poll(Some(Duration::from_millis(50))).unwrap();
480 while let Some(ev) = src.try_read() {
481 if matches!(ev, Event::PasteEnd) {
482 saw_end = true;
483 }
484 }
485 }
486 assert!(saw_end, "expected PasteEnd within the idle window");
487 }
488
489 #[test]
490 fn explicit_end_paste_recovers_stream() {
491 let (rx, tx) = make_pipe();
492 let mut src = EventSource::new(rx).unwrap().with_paste_idle_timeout(None);
493 write_bytes(&tx, b"\x1b[200~stuck");
494 let _ = src.poll(Some(Duration::from_millis(50))).unwrap();
495 while src.try_read().is_some() {}
496
497 src.end_paste();
499 let ev = src.try_read().expect("PasteEnd should be queued");
500 assert_eq!(ev, Event::PasteEnd);
501
502 write_bytes(&tx, b"a");
504 assert!(src.poll(Some(Duration::from_secs(1))).unwrap());
505 let ev = src.read().unwrap();
506 assert!(matches!(
507 ev,
508 Event::KeyPress(ref k) if k.code == KeyCode::Char('a')
509 ));
510 }
511
512 #[test]
513 fn paste_idle_timeout_disabled_blocks_indefinitely() {
514 let (rx, tx) = make_pipe();
515 let mut src = EventSource::new(rx).unwrap().with_paste_idle_timeout(None);
516 write_bytes(&tx, b"\x1b[200~partial");
517 let _ = src.poll(Some(Duration::from_millis(50))).unwrap();
518 while src.try_read().is_some() {}
519
520 let res = src.poll(Some(Duration::from_millis(80))).unwrap();
523 assert!(!res, "should time out, not synthesise PasteEnd");
524 assert!(src.try_read().is_none());
525 }
526
527 #[test]
531 fn the_decoder_flags_a_caller_chose_are_the_ones_used() {
532 use crate::event::DecoderFlags;
533
534 let (rx, tx) = make_pipe();
535 let mut src = EventSource::new(rx).unwrap();
536 write_byte(&tx, 0x0d);
537 assert!(src.poll(Some(Duration::from_secs(1))).unwrap());
538 let Event::KeyPress(k) = src.read().unwrap() else {
539 panic!("expected a key")
540 };
541 assert_eq!(k.code, KeyCode::Enter, "the reading a terminal means");
542
543 let (rx, tx) = make_pipe();
544 let mut src = EventSource::new(rx)
545 .unwrap()
546 .with_decoder_flags(DecoderFlags::CTRL_M);
547 assert_eq!(src.decoder_flags(), DecoderFlags::CTRL_M);
548 write_byte(&tx, 0x0d);
549 assert!(src.poll(Some(Duration::from_secs(1))).unwrap());
550 let Event::KeyPress(k) = src.read().unwrap() else {
551 panic!("expected a key")
552 };
553 assert_eq!(k.code, KeyCode::Char('m'));
554 assert_eq!(k.modifiers, crate::event::KeyModifiers::CTRL);
555 }
556
557 #[test]
562 fn a_lone_escape_from_the_terminal_follows_the_flag() {
563 use crate::event::DecoderFlags;
564
565 let (rx, tx) = make_pipe();
566 let mut src = EventSource::new(rx)
567 .unwrap()
568 .with_esc_timeout(Duration::from_millis(20))
569 .with_decoder_flags(DecoderFlags::CTRL_OPEN_BRACKET);
570 write_byte(&tx, 0x1b);
571 let _ = src.poll(Some(Duration::from_millis(50))).unwrap();
574 assert!(src.poll(Some(Duration::from_secs(1))).unwrap());
575 let Event::KeyPress(k) = src.read().unwrap() else {
576 panic!("expected a key")
577 };
578 assert_eq!(k.code, KeyCode::Char('['));
579 assert_eq!(k.modifiers, crate::event::KeyModifiers::CTRL);
580 }
581
582 #[test]
586 fn the_flags_can_be_chosen_on_a_source_already_built() {
587 use crate::event::DecoderFlags;
588
589 let (rx, tx) = make_pipe();
590 let mut src = EventSource::new(rx).unwrap();
591 assert!(
592 src.decoder_flags().is_empty(),
593 "a source reads each as the named key until told otherwise"
594 );
595 src.set_decoder_flags(DecoderFlags::CTRL_I);
596 write_byte(&tx, 0x09);
597 assert!(src.poll(Some(Duration::from_secs(1))).unwrap());
598 let Event::KeyPress(k) = src.read().unwrap() else {
599 panic!("expected a key")
600 };
601 assert_eq!(k.code, KeyCode::Char('i'));
602 assert_eq!(k.modifiers, crate::event::KeyModifiers::CTRL);
603 }
604
605 #[test]
606 fn esc_deadline_does_not_fire_during_paste() {
607 let (rx, tx) = make_pipe();
610 let mut src = EventSource::new(rx)
611 .unwrap()
612 .with_esc_timeout(Duration::from_millis(20))
613 .with_paste_idle_timeout(Some(Duration::from_secs(5)));
614 write_bytes(&tx, b"\x1b[200~body");
615 let _ = src.poll(Some(Duration::from_millis(50))).unwrap();
616 while src.try_read().is_some() {}
617
618 write_bytes(&tx, b"\x1b[20");
622 let _ = src.poll(Some(Duration::from_millis(80))).unwrap();
623 let mut saw_esc = false;
624 while let Some(ev) = src.try_read() {
625 if matches!(ev, Event::KeyPress(ref k) if k.code == KeyCode::Escape) {
626 saw_esc = true;
627 }
628 }
629 assert!(!saw_esc, "esc deadline must not fire during paste");
630
631 write_bytes(&tx, b"1~");
633 assert!(src.poll(Some(Duration::from_secs(1))).unwrap());
634 let mut saw_end = false;
635 while let Some(ev) = src.try_read() {
636 if matches!(ev, Event::PasteEnd) {
637 saw_end = true;
638 }
639 }
640 assert!(saw_end);
641 }
642
643 #[test]
644 fn esc_deadline_tightens_long_caller_timeout() {
645 let (rx, tx) = make_pipe();
646 let mut src = EventSource::new(rx)
647 .unwrap()
648 .with_esc_timeout(Duration::from_millis(20));
649 write_byte(&tx, 0x1b);
650 let _ = src.poll(Some(Duration::from_secs(60))).unwrap();
651 let start = Instant::now();
652 assert!(src.poll(Some(Duration::from_secs(60))).unwrap());
653 let ev = src.read().unwrap();
654 let elapsed = start.elapsed();
655 assert!(matches!(ev, Event::KeyPress(k) if k.code == KeyCode::Escape));
656 assert!(
657 elapsed < Duration::from_millis(500),
658 "elapsed = {:?}",
659 elapsed
660 );
661 }
662
663 #[test]
664 fn paste_end_after_chunk_is_delivered_without_extra_input() {
665 let (rx, tx) = make_pipe();
671 let mut src = new_reader(rx);
672 write_bytes(&tx, b"\x1b[200~hello\x1b[201~");
673 assert!(src.poll(Some(Duration::from_secs(1))).unwrap());
674 assert!(matches!(src.read().unwrap(), Event::PasteStart));
675 assert!(matches!(src.read().unwrap(), Event::PasteChunk(ref b) if b == b"hello"));
676 assert!(matches!(src.read().unwrap(), Event::PasteEnd));
677 }
678
679 #[test]
680 fn handle_resize_false_suppresses_sigwinch_resize_event() {
681 let stderr_fd = 2;
685 let ws: libc::winsize = unsafe { std::mem::zeroed() };
686 let probe = unsafe { libc::ioctl(stderr_fd, libc::TIOCGWINSZ, &ws as *const _) };
687 if probe < 0 {
688 return;
689 }
690 let stderr_dup = unsafe { libc::dup(stderr_fd) };
691 assert!(stderr_dup >= 0);
692 let stderr_file = unsafe { File::from_raw_fd(stderr_dup) };
693 let mut src = new_reader(stderr_file);
694 assert!(src.handle_resize());
695 src.set_handle_resize(false);
696 assert!(!src.handle_resize());
697 src.last_size = None;
698 unsafe { libc::raise(libc::SIGWINCH) };
699 assert!(!src.poll(Some(Duration::from_millis(50))).unwrap());
701 assert!(src.try_read().is_none());
702 }
703
704 #[cfg(not(target_os = "l4re"))]
708 #[test]
709 fn sigwinch_dedups_unchanged_size() {
710 let Some((master, _slave)) = crate::testutil::open_pty_pair() else {
711 return;
712 };
713 let ws: libc::winsize = unsafe { std::mem::zeroed() };
718 if unsafe { libc::ioctl(master.as_raw_fd(), libc::TIOCGWINSZ, &ws) } < 0 {
719 return;
720 }
721
722 let mut src = new_reader(master);
723 assert!(
724 src.last_size.is_some(),
725 "constructor did not seed the resize dedupe from the input fd"
726 );
727
728 unsafe { libc::raise(libc::SIGWINCH) };
729 assert!(
730 !src.poll(Some(Duration::from_millis(50))).unwrap(),
731 "an unchanged size surfaced a resize event"
732 );
733 assert!(src.try_read().is_none());
734 }
735
736 #[test]
737 fn sigwinch_surfaces_resize_event() {
738 let stderr_fd = 2;
742 let ws: libc::winsize = unsafe { std::mem::zeroed() };
743 let probe = unsafe { libc::ioctl(stderr_fd, libc::TIOCGWINSZ, &ws as *const _) };
744 if probe < 0 {
745 return;
746 }
747 let stderr_dup = unsafe { libc::dup(stderr_fd) };
748 assert!(stderr_dup >= 0);
749 let stderr_file = unsafe { File::from_raw_fd(stderr_dup) };
750 let mut src = new_reader(stderr_file);
751 src.last_size = None;
754 unsafe { libc::raise(libc::SIGWINCH) };
755 assert!(src.poll(Some(Duration::from_secs(1))).unwrap());
756 let ev = src.read().unwrap();
757 assert!(matches!(ev, Event::Resize(_)));
758 }
759}