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 timeout_returns_none() {
317 let (rx, _tx) = make_pipe();
318 let mut src = new_reader(rx);
319 let start = Instant::now();
320 let res = src.poll(Some(Duration::from_millis(10))).unwrap();
321 let elapsed = start.elapsed();
322 assert!(!res);
323 assert!(elapsed >= Duration::from_millis(5));
324 }
325
326 #[test]
327 fn waker_interrupts_blocking_read() {
328 let (rx, _tx) = make_pipe();
329 let mut src = new_reader(rx);
330 let waker = src.waker();
331 let handle = thread::spawn(move || {
332 thread::sleep(Duration::from_millis(20));
333 waker.wake().unwrap();
334 });
335 let err = src.read().expect_err("should be Interrupted");
336 handle.join().unwrap();
337 assert_eq!(err.kind(), io::ErrorKind::Interrupted);
338 }
339
340 #[test]
341 fn esc_resolves_before_late_continuation_byte() {
342 let (rx, tx) = make_pipe();
346 let mut src = new_reader(rx); write_bytes(&tx, b"\x1b");
348 assert!(!src.poll(Some(Duration::from_millis(0))).unwrap());
351 thread::sleep(Duration::from_millis(80));
354 write_bytes(&tx, b"a");
355 let first = src.read().unwrap();
356 assert!(
357 matches!(&first, Event::KeyPress(k) if k.code == KeyCode::Escape),
358 "expected bare Esc, got {:?}",
359 first
360 );
361 let second = src.read().unwrap();
362 assert!(
363 matches!(&second, Event::KeyPress(k) if k.code == KeyCode::Char('a')),
364 "expected 'a', got {:?}",
365 second
366 );
367 }
368
369 #[test]
370 fn paste_idle_timeout_synthesizes_paste_end() {
371 let (rx, tx) = make_pipe();
372 let mut src = EventSource::new(rx)
373 .unwrap()
374 .with_paste_idle_timeout(Some(Duration::from_millis(40)));
375 write_bytes(&tx, b"\x1b[200~hello");
376 let mut got_start = false;
378 let mut got_chunk = false;
379 for _ in 0..4 {
380 if !src.poll(Some(Duration::from_millis(20))).unwrap() {
381 break;
382 }
383 while let Some(ev) = src.try_read() {
384 match ev {
385 Event::PasteStart => got_start = true,
386 Event::PasteChunk(b) => {
387 assert_eq!(b, b"hello".to_vec());
388 got_chunk = true;
389 }
390 other => panic!("unexpected pre-timeout event {:?}", other),
391 }
392 }
393 if got_start && got_chunk {
394 break;
395 }
396 }
397 assert!(got_start && got_chunk);
398
399 let start = Instant::now();
401 assert!(src.poll(Some(Duration::from_secs(5))).unwrap());
402 let ev = src.read().unwrap();
403 let elapsed = start.elapsed();
404 assert_eq!(ev, Event::PasteEnd);
405 assert!(
406 elapsed < Duration::from_millis(500),
407 "elapsed = {:?}",
408 elapsed
409 );
410 }
411
412 #[test]
413 fn paste_completes_when_terminator_arrives_within_idle_window() {
414 let (rx, tx) = make_pipe();
415 let mut src = EventSource::new(rx)
416 .unwrap()
417 .with_paste_idle_timeout(Some(Duration::from_millis(500)));
418 write_bytes(&tx, b"\x1b[200~hi");
419 let _ = src.poll(Some(Duration::from_millis(50))).unwrap();
420 while src.try_read().is_some() {}
421 thread::sleep(Duration::from_millis(50));
423 write_bytes(&tx, b"\x1b[201~");
424 assert!(src.poll(Some(Duration::from_secs(1))).unwrap());
425 let mut saw_end = false;
426 while let Some(ev) = src.try_read() {
427 if matches!(ev, Event::PasteEnd) {
428 saw_end = true;
429 }
430 }
431 if !saw_end {
432 let _ = src.poll(Some(Duration::from_millis(50))).unwrap();
434 while let Some(ev) = src.try_read() {
435 if matches!(ev, Event::PasteEnd) {
436 saw_end = true;
437 }
438 }
439 }
440 assert!(saw_end, "expected PasteEnd within the idle window");
441 }
442
443 #[test]
444 fn explicit_end_paste_recovers_stream() {
445 let (rx, tx) = make_pipe();
446 let mut src = EventSource::new(rx).unwrap().with_paste_idle_timeout(None);
447 write_bytes(&tx, b"\x1b[200~stuck");
448 let _ = src.poll(Some(Duration::from_millis(50))).unwrap();
449 while src.try_read().is_some() {}
450
451 src.end_paste();
453 let ev = src.try_read().expect("PasteEnd should be queued");
454 assert_eq!(ev, Event::PasteEnd);
455
456 write_bytes(&tx, b"a");
458 assert!(src.poll(Some(Duration::from_secs(1))).unwrap());
459 let ev = src.read().unwrap();
460 assert!(matches!(
461 ev,
462 Event::KeyPress(ref k) if k.code == KeyCode::Char('a')
463 ));
464 }
465
466 #[test]
467 fn paste_idle_timeout_disabled_blocks_indefinitely() {
468 let (rx, tx) = make_pipe();
469 let mut src = EventSource::new(rx).unwrap().with_paste_idle_timeout(None);
470 write_bytes(&tx, b"\x1b[200~partial");
471 let _ = src.poll(Some(Duration::from_millis(50))).unwrap();
472 while src.try_read().is_some() {}
473
474 let res = src.poll(Some(Duration::from_millis(80))).unwrap();
477 assert!(!res, "should time out, not synthesise PasteEnd");
478 assert!(src.try_read().is_none());
479 }
480
481 #[test]
482 fn esc_deadline_does_not_fire_during_paste() {
483 let (rx, tx) = make_pipe();
486 let mut src = EventSource::new(rx)
487 .unwrap()
488 .with_esc_timeout(Duration::from_millis(20))
489 .with_paste_idle_timeout(Some(Duration::from_secs(5)));
490 write_bytes(&tx, b"\x1b[200~body");
491 let _ = src.poll(Some(Duration::from_millis(50))).unwrap();
492 while src.try_read().is_some() {}
493
494 write_bytes(&tx, b"\x1b[20");
498 let _ = src.poll(Some(Duration::from_millis(80))).unwrap();
499 let mut saw_esc = false;
500 while let Some(ev) = src.try_read() {
501 if matches!(ev, Event::KeyPress(ref k) if k.code == KeyCode::Escape) {
502 saw_esc = true;
503 }
504 }
505 assert!(!saw_esc, "esc deadline must not fire during paste");
506
507 write_bytes(&tx, b"1~");
509 assert!(src.poll(Some(Duration::from_secs(1))).unwrap());
510 let mut saw_end = false;
511 while let Some(ev) = src.try_read() {
512 if matches!(ev, Event::PasteEnd) {
513 saw_end = true;
514 }
515 }
516 assert!(saw_end);
517 }
518
519 #[test]
520 fn esc_deadline_tightens_long_caller_timeout() {
521 let (rx, tx) = make_pipe();
522 let mut src = EventSource::new(rx)
523 .unwrap()
524 .with_esc_timeout(Duration::from_millis(20));
525 write_byte(&tx, 0x1b);
526 let _ = src.poll(Some(Duration::from_secs(60))).unwrap();
527 let start = Instant::now();
528 assert!(src.poll(Some(Duration::from_secs(60))).unwrap());
529 let ev = src.read().unwrap();
530 let elapsed = start.elapsed();
531 assert!(matches!(ev, Event::KeyPress(k) if k.code == KeyCode::Escape));
532 assert!(
533 elapsed < Duration::from_millis(500),
534 "elapsed = {:?}",
535 elapsed
536 );
537 }
538
539 #[test]
540 fn paste_end_after_chunk_is_delivered_without_extra_input() {
541 let (rx, tx) = make_pipe();
547 let mut src = new_reader(rx);
548 write_bytes(&tx, b"\x1b[200~hello\x1b[201~");
549 assert!(src.poll(Some(Duration::from_secs(1))).unwrap());
550 assert!(matches!(src.read().unwrap(), Event::PasteStart));
551 assert!(matches!(src.read().unwrap(), Event::PasteChunk(ref b) if b == b"hello"));
552 assert!(matches!(src.read().unwrap(), Event::PasteEnd));
553 }
554
555 #[test]
556 fn handle_resize_false_suppresses_sigwinch_resize_event() {
557 let stderr_fd = 2;
561 let ws: libc::winsize = unsafe { std::mem::zeroed() };
562 let probe = unsafe { libc::ioctl(stderr_fd, libc::TIOCGWINSZ, &ws as *const _) };
563 if probe < 0 {
564 return;
565 }
566 let stderr_dup = unsafe { libc::dup(stderr_fd) };
567 assert!(stderr_dup >= 0);
568 let stderr_file = unsafe { File::from_raw_fd(stderr_dup) };
569 let mut src = new_reader(stderr_file);
570 assert!(src.handle_resize());
571 src.set_handle_resize(false);
572 assert!(!src.handle_resize());
573 src.last_size = None;
574 unsafe { libc::raise(libc::SIGWINCH) };
575 assert!(!src.poll(Some(Duration::from_millis(50))).unwrap());
577 assert!(src.try_read().is_none());
578 }
579
580 #[cfg(not(target_os = "l4re"))]
584 #[test]
585 fn sigwinch_dedups_unchanged_size() {
586 let Some((master, _slave)) = crate::testutil::open_pty_pair() else {
587 return;
588 };
589 let ws: libc::winsize = unsafe { std::mem::zeroed() };
594 if unsafe { libc::ioctl(master.as_raw_fd(), libc::TIOCGWINSZ, &ws) } < 0 {
595 return;
596 }
597
598 let mut src = new_reader(master);
599 assert!(
600 src.last_size.is_some(),
601 "constructor did not seed the resize dedupe from the input fd"
602 );
603
604 unsafe { libc::raise(libc::SIGWINCH) };
605 assert!(
606 !src.poll(Some(Duration::from_millis(50))).unwrap(),
607 "an unchanged size surfaced a resize event"
608 );
609 assert!(src.try_read().is_none());
610 }
611
612 #[test]
613 fn sigwinch_surfaces_resize_event() {
614 let stderr_fd = 2;
618 let ws: libc::winsize = unsafe { std::mem::zeroed() };
619 let probe = unsafe { libc::ioctl(stderr_fd, libc::TIOCGWINSZ, &ws as *const _) };
620 if probe < 0 {
621 return;
622 }
623 let stderr_dup = unsafe { libc::dup(stderr_fd) };
624 assert!(stderr_dup >= 0);
625 let stderr_file = unsafe { File::from_raw_fd(stderr_dup) };
626 let mut src = new_reader(stderr_file);
627 src.last_size = None;
630 unsafe { libc::raise(libc::SIGWINCH) };
631 assert!(src.poll(Some(Duration::from_secs(1))).unwrap());
632 let ev = src.read().unwrap();
633 assert!(matches!(ev, Event::Resize(_)));
634 }
635}