Skip to main content

uncurses/event/
source.rs

1//! Wakeable event source shared by synchronous and asynchronous readers.
2//!
3//! ## Purpose
4//!
5//! [`EventSource`] owns the input handle, waits for platform readiness, feeds a
6//! [`Decoder`], and queues typed [`Event`] values. It is the entry
7//! point for applications that want blocking, timeout-based, or wakeable event
8//! reads.
9//!
10//! ```text
11//! input fd / HANDLE ─┬─▶ Poller ── ready ──▶ read bytes ──▶ Decoder
12//! wake handle ───────┤                         │              │
13//! SIGWINCH pipe ─────┘                         └──────────────┴─▶ queue
14//!      (Unix only)        deadlines: ESC timeout and paste idle timeout
15//! ```
16//!
17//! ## Key types
18//!
19//! * [`Input`] describes the platform capabilities required from an input
20//!   handle.
21//! * [`EventSource`] stores the decoder, bounded pending-byte buffer, readiness
22//!   poller, event queue, timeout deadlines, and resize state.
23//! * [`Waker`] is a cloneable handle that interrupts an in-progress wait from
24//!   another thread.
25//!
26//! ## Lifecycle
27//!
28//! Construct with [`EventSource::new`] on the platform-specific impl. Call
29//! [`EventSource::poll`] to wait up to a timeout, drain queued events with
30//! [`EventSource::try_read`], or call [`EventSource::read`] to block until one
31//! event arrives. [`EventSource::unread`] can put an unrelated event back while
32//! code waits for a specific terminal reply.
33//!
34//! ## Gotchas
35//!
36//! [`EventSource::try_read`] is purely non-blocking: it only pops the queue.
37//! [`EventSource::poll`] performs I/O and timeout handling. The effective poll
38//! wait is shortened when a partial `ESC` sequence or open paste has an internal
39//! deadline, so a long caller timeout does not delay disambiguation.
40use std::collections::VecDeque;
41use std::io;
42use std::io::Read;
43use std::sync::Arc;
44use std::time::{Duration, Instant};
45
46#[cfg(unix)]
47use std::os::fd::AsFd;
48#[cfg(windows)]
49use std::os::windows::io::AsHandle;
50
51use super::decode::{Decoder, DecoderFlags, is_c1_introducer};
52use super::pending::Pending;
53#[cfg(any(unix, windows))]
54use super::poll::Poller;
55#[cfg(unix)]
56use super::sigwinch as winch;
57#[cfg(unix)]
58use super::source_unix::UnixWakerInner;
59#[cfg(windows)]
60use super::source_windows::WindowsWakerInner;
61use crate::event::Event;
62#[cfg(unix)]
63use crate::terminal::Winsize;
64
65/// Platform-specific capabilities required from a Unix event input handle.
66///
67/// The handle must implement [`Read`], expose an fd through [`AsFd`], and be
68/// safe to move to the helper thread used by async streams. Any type satisfying
69/// those bounds implements this trait automatically.
70///
71/// Users normally do not implement this trait manually; pass the input half
72/// returned by the terminal API to [`EventSource::new`].
73#[cfg(unix)]
74pub trait Input: Read + AsFd + Send {}
75#[cfg(unix)]
76impl<T: Read + AsFd + Send> Input for T {}
77
78/// Platform-specific capabilities required from a Windows event input handle.
79///
80/// The handle must implement [`Read`], expose a console `HANDLE` through
81/// [`AsHandle`], and be safe to move to the helper thread used by async streams.
82/// Any type satisfying those bounds implements this trait automatically.
83///
84/// Users normally do not implement this trait manually; pass the input half
85/// returned by the terminal API to [`EventSource::new`].
86#[cfg(windows)]
87pub trait Input: Read + AsHandle + Send {}
88#[cfg(windows)]
89impl<T: Read + AsHandle + Send> Input for T {}
90
91/// Default read buffer capacity. This is the hard cap on any single
92/// sequence; the backing buffer is allocated once at construction.
93pub(super) const DEFAULT_BUFFER_CAPACITY: usize = 4096;
94
95/// Readiness slots the platform poller reports, in fixed index order.
96/// Unix watches `[input, wake, winch]`; Windows watches `[input, wake]`
97/// (resize arrives in-band as a console record, so there is no winch fd).
98#[cfg(unix)]
99pub(super) const READY_SLOTS: usize = 3;
100#[cfg(windows)]
101pub(super) const READY_SLOTS: usize = 2;
102/// Index of the input handle in the readiness slice.
103#[cfg(any(unix, windows))]
104pub(super) const READY_INPUT: usize = 0;
105/// Index of the wake handle in the readiness slice.
106#[cfg(any(unix, windows))]
107pub(super) const READY_WAKE: usize = 1;
108/// Index of the SIGWINCH pipe in the readiness slice (Unix only).
109#[cfg(unix)]
110pub(super) const READY_WINCH: usize = 2;
111
112/// Default escape-sequence timeout.
113///
114/// When the pending buffer holds a partial `ESC`-prefixed sequence or 8-bit C1
115/// introducer and no continuation arrives within this window, the source asks
116/// the decoder to resolve the buffered bytes as best-effort events. This is how
117/// a physical Escape key is distinguished from an Alt-prefixed key or CSI-style
118/// control sequence.
119///
120/// Applications that prefer more responsive Escape handling can lower this
121/// value with [`EventSource::with_esc_timeout`]; applications that need to
122/// tolerate slow byte delivery can raise it.
123pub const DEFAULT_ESC_TIMEOUT: Duration = Duration::from_millis(50);
124
125/// Default bracketed-paste idle timeout.
126///
127/// When a paste has been opened (a `PasteStart` was emitted) and no
128/// further input arrives within this window, the source synthesises a
129/// `PasteEnd`, flushes any held-back bytes as a final `PasteChunk`,
130/// and clears the decoder's paste state. Guards against terminators
131/// that never arrive (truncated stream, malformed input).
132pub const DEFAULT_PASTE_IDLE_TIMEOUT: Duration = Duration::from_secs(2);
133
134/// Cloneable handle that interrupts an in-progress [`EventSource::poll`] or
135/// [`EventSource::read`].
136///
137/// A waker is bound to one source at construction time. Calling [`Waker::wake`]
138/// makes the source's readiness wait return `Interrupted`; multiple wake calls
139/// may coalesce into one interruption. The handle is cheap to clone and can be
140/// sent to other threads.
141#[derive(Clone)]
142pub struct Waker {
143    #[cfg(unix)]
144    inner: Arc<UnixWakerInner>,
145    #[cfg(windows)]
146    inner: Arc<WindowsWakerInner>,
147    #[cfg(not(any(unix, windows)))]
148    _phantom: std::marker::PhantomData<()>,
149}
150
151impl Waker {
152    #[cfg(unix)]
153    pub(super) fn from_unix_inner(inner: Arc<UnixWakerInner>) -> Self {
154        Self { inner }
155    }
156
157    #[cfg(windows)]
158    pub(super) fn from_windows_inner(inner: Arc<WindowsWakerInner>) -> Self {
159        Self { inner }
160    }
161
162    /// Read end of the wake self-pipe. Lives behind the same `Arc` as the
163    /// write end so it is never closed while a writer survives.
164    #[cfg(unix)]
165    pub(super) fn pipe_read_fd(&self) -> std::os::fd::RawFd {
166        self.inner.read_fd()
167    }
168
169    /// Interrupt the [`EventSource`] this waker is bound to.
170    ///
171    /// A blocked [`EventSource::poll`] returns `Ok(false)` and a blocked
172    /// [`EventSource::read`] returns an [`io::ErrorKind::Interrupted`] error.
173    /// The wake does not enqueue an [`Event`] and does not mutate decoder state.
174    ///
175    /// Returns any platform error produced while signalling the wake handle.
176    pub fn wake(&self) -> io::Result<()> {
177        #[cfg(any(unix, windows))]
178        {
179            self.inner.wake()
180        }
181        #[cfg(not(any(unix, windows)))]
182        {
183            Err(io::Error::new(
184                io::ErrorKind::Unsupported,
185                "waker not supported on this platform",
186            ))
187        }
188    }
189}
190
191/// Wakeable event source backed by a platform readiness primitive.
192///
193/// `EventSource` is the synchronous owner of terminal input. It stores pending
194/// bytes, a `Decoder`, an event queue, deadline state for ambiguous `ESC`
195/// prefixes and open bracketed pastes, and the platform handles needed for
196/// wakeups and resize notifications.
197///
198/// Construct it with [`EventSource::new`]. Use [`EventSource::poll`] to perform
199/// I/O and wait for queued events, [`EventSource::try_read`] to pop an already
200/// queued event, or [`EventSource::read`] to block until one event is available.
201/// The type is generic over the platform [`Input`] handle.
202pub struct EventSource<I>
203where
204    I: Input,
205{
206    /// Owned input handle. Used both as the byte source (`Read`) and, on
207    /// Unix, as the readiness target (its fd is registered with the
208    /// [`super::poll::Poller`]).
209    #[cfg_attr(windows, allow(dead_code))]
210    pub(super) input: I,
211
212    pub(super) parser: Decoder,
213    /// Bounded read buffer. The unread slice is `pending.slice()`; new
214    /// input is written into `pending.spare_mut()`. `pending.capacity()`
215    /// is the hard cap on any single sequence and is never resized.
216    pub(super) pending: Pending,
217    pub(super) esc_timeout: Duration,
218    /// Wall-clock instant at which a buffered partial escape sequence
219    /// should be force-resolved (typically as a bare `Esc` keypress).
220    pub(super) esc_deadline: Option<Instant>,
221    /// Idle timeout applied while the decoder is in a bracketed paste.
222    /// `None` disables the safety net.
223    pub(super) paste_idle_timeout: Option<Duration>,
224    /// Wall-clock instant at which an open paste should be
225    /// force-closed (synthesised `PasteEnd`).
226    pub(super) paste_deadline: Option<Instant>,
227    pub(super) queue: VecDeque<Event>,
228    pub(super) waker: Waker,
229    /// Whether the source delivers [`Event::Resize`] from the
230    /// out-of-band kernel resize notification (`SIGWINCH` on Unix).
231    /// Defaults to `true`. Set to `false` when in-band resize reports
232    /// (DEC mode 2048) are enabled so resizes arrive solely through the
233    /// decoder and are not duplicated. No effect on Windows, where resize
234    /// is always delivered in-band through the decoder.
235    pub(super) handle_resize: bool,
236
237    // --- Unix-only state ---
238    /// Shared readiness poller watching `[input, wake_rx, winch_rx]` (in
239    /// that index order). Held behind `Arc` so an [`super::EventStream`]
240    /// reader thread can wait on it lock-free while the source's decode
241    /// state is mutated under a separate lock.
242    #[cfg(unix)]
243    pub(super) poller: Arc<dyn Poller>,
244    /// Active SIGWINCH subscription, and owner of the pipe the handler wakes.
245    /// The pipe is leased from a process-lifetime pool rather than owned here,
246    /// so no descriptor the handler may be about to write to can be closed —
247    /// see the `PIPES` pool in the sigwinch module.
248    #[cfg(unix)]
249    pub(super) winch_sub: winch::Subscription,
250    #[cfg(unix)]
251    pub(super) last_size: Option<Winsize>,
252
253    // --- Windows-only state ---
254    /// Shared readiness poller watching `[input_handle, wake_event]` (in
255    /// that index order). Held behind `Arc` so an [`super::EventStream`]
256    /// reader thread can wait on it lock-free while the source's decode
257    /// state is mutated under a separate lock.
258    #[cfg(windows)]
259    pub(super) poller: Arc<dyn Poller>,
260    #[cfg(windows)]
261    pub(super) wake_event: windows_sys::Win32::Foundation::HANDLE,
262    #[cfg(windows)]
263    pub(super) vt_input: bool,
264    /// Pending high surrogate per key direction (0 = up, 1 = down).
265    /// VT input delivers astral code points as two consecutive
266    /// `KEY_EVENT` records, one per UTF-16 unit.
267    #[cfg(windows)]
268    pub(super) pending_high_surrogate: [Option<u16>; 2],
269    #[cfg(windows)]
270    pub(super) last_size: Option<(i16, i16)>,
271    #[cfg(windows)]
272    pub(super) last_mouse_buttons: u32,
273}
274
275// Drop ordering note for Unix: the winch pipe is leased from a
276// process-lifetime pool and is never closed, so dropping the subscription
277// only frees the slot. Field drop order is therefore not load-bearing here.
278
279// ---------------------------------------------------------------------------
280// Shared methods (platform-agnostic)
281// ---------------------------------------------------------------------------
282
283impl<I> EventSource<I>
284where
285    I: Input,
286{
287    /// Return the configured escape-sequence timeout.
288    ///
289    /// This is the duration used to disambiguate a physical Escape key from an
290    /// Alt-prefixed key or a partial control sequence. Reading it has no side
291    /// effects and never performs I/O.
292    pub fn esc_timeout(&self) -> Duration {
293        self.esc_timeout
294    }
295
296    /// Set the escape-sequence timeout.
297    ///
298    /// `timeout` is how long the source waits for a continuation byte before a
299    /// buffered partial escape sequence is force-resolved. The default is
300    /// [`DEFAULT_ESC_TIMEOUT`]. A zero duration makes ambiguous prefixes expire
301    /// on the next poll cycle.
302    ///
303    /// This is a consuming builder intended to be chained after
304    /// [`EventSource::new`]. It does not inspect or clear any already-buffered
305    /// input and never panics.
306    pub fn with_esc_timeout(mut self, timeout: Duration) -> Self {
307        self.esc_timeout = timeout;
308        self
309    }
310
311    /// Choose how the ambiguous legacy keys are read.
312    ///
313    /// LF reads as Ctrl+J by default. Set [`DecoderFlags::LF_IS_ENTER`] to
314    /// read it as Enter instead. See [`DecoderFlags`] for all mappings.
315    ///
316    /// This is a consuming builder intended to be chained after
317    /// [`EventSource::new`]. It never panics.
318    pub fn with_decoder_flags(mut self, flags: DecoderFlags) -> Self {
319        self.set_decoder_flags(flags);
320        self
321    }
322
323    /// Choose how the ambiguous legacy keys are read, on a source already in
324    /// use.
325    ///
326    /// The same choice [`with_decoder_flags`](Self::with_decoder_flags)
327    /// makes, for a caller holding the source rather than building it.
328    /// Buffered input is left alone: the flags decide how a byte is named,
329    /// not how it is framed.
330    pub fn set_decoder_flags(&mut self, flags: DecoderFlags) {
331        self.parser.set_flags(flags);
332    }
333
334    /// How the ambiguous legacy keys are being read.
335    pub fn decoder_flags(&self) -> DecoderFlags {
336        self.parser.flags()
337    }
338
339    /// Return the decoder's current Backarrow mode (DECBKM).
340    ///
341    /// Defaults to reset (`false`).
342    pub fn backspace_mode(&self) -> bool {
343        self.parser.backspace_mode()
344    }
345
346    /// Select Backarrow mode for future legacy input without terminal I/O.
347    ///
348    /// Set (`true`) selects BS (`0x08`) for Backspace; reset (`false`) selects
349    /// DEL (`0x7f`). [`DecoderFlags`] choose the ambiguous interpretations.
350    /// Mode reports are returned to the caller for handling. Already queued
351    /// events keep their decoded values.
352    ///
353    /// Use [`Program::set_backspace_mode`](crate::program::Program::set_backspace_mode)
354    /// to change both the terminal and decoder.
355    pub fn set_backspace_mode(&mut self, enabled: bool) {
356        self.parser.set_backspace_mode(enabled);
357    }
358
359    /// Set the idle timeout for an open bracketed paste.
360    ///
361    /// If no further input arrives before `timeout`, the source flushes any
362    /// held bytes as a final [`Event::PasteChunk`], synthesizes
363    /// [`Event::PasteEnd`], and leaves paste mode. Passing `None` disables this
364    /// safety net and waits indefinitely for a real terminator. The default is
365    /// `Some(`[`DEFAULT_PASTE_IDLE_TIMEOUT`]`)`.
366    ///
367    /// This is a consuming builder intended to be chained after
368    /// [`EventSource::new`]. It never panics.
369    pub fn with_paste_idle_timeout(mut self, timeout: Option<Duration>) -> Self {
370        self.paste_idle_timeout = timeout;
371        self
372    }
373
374    /// Return a cloneable [`Waker`] bound to this source.
375    ///
376    /// Use the returned handle from another thread to interrupt a blocking
377    /// [`EventSource::poll`] or [`EventSource::read`]. Cloning the waker does
378    /// not clone the source or its input handle.
379    pub fn waker(&self) -> Waker {
380        self.waker.clone()
381    }
382
383    /// Return a clone of the shared [`Poller`] handle.
384    ///
385    /// The poller is `Arc`-wrapped so a waiter thread can block on readiness
386    /// without holding the source mutex. Both the underlying epoll and kqueue
387    /// registrations are level-triggered, so a concurrent poll from a waiter
388    /// and a subsequent drain from the owner both observe the same readiness.
389    #[cfg(feature = "async")]
390    pub(super) fn poller(&self) -> Arc<dyn Poller> {
391        Arc::clone(&self.poller)
392    }
393
394    /// Return whether out-of-band resize handling is enabled.
395    ///
396    /// On Unix, `true` means a readable SIGWINCH pipe can produce
397    /// [`Event::Resize`]. On Windows, resize records are delivered in-band and
398    /// this flag has no practical effect.
399    pub fn handle_resize(&self) -> bool {
400        self.handle_resize
401    }
402
403    /// Control whether the source delivers [`Event::Resize`] from the
404    /// kernel's out-of-band window-resize notification (`SIGWINCH` on
405    /// Unix). Defaults to `true`.
406    ///
407    /// Set this to `false` after enabling in-band resize reports (DEC
408    /// mode 2048): the terminal then reports size changes in-band as
409    /// `CSI 48 t`, which the decoder surfaces as [`Event::Resize`], so
410    /// leaving the `SIGWINCH` path on would deliver each resize twice.
411    /// Restore it to `true` when in-band reporting is disabled again.
412    ///
413    /// No effect on Windows, where resize is always delivered in-band
414    /// through the decoder.
415    pub fn set_handle_resize(&mut self, enable: bool) {
416        self.handle_resize = enable;
417    }
418
419    /// Wait up to `timeout` for at least one event to become available.
420    ///
421    /// This method performs I/O, drains decoder output into the internal queue,
422    /// handles resize notifications, and resolves any expired ESC or paste
423    /// deadlines. `None` means block until an event or wake; `Some(Duration::ZERO)`
424    /// means perform a non-blocking readiness pass.
425    ///
426    /// A platform whose wait takes a whole number of milliseconds rounds a
427    /// fractional millisecond up, so such a wait can return up to a
428    /// millisecond after the deadline.
429    ///
430    /// Returns:
431    ///
432    /// * `Ok(true)` when the queue has at least one event;
433    /// * `Ok(false)` when the timeout elapsed or a paired [`Waker`] interrupted
434    ///   the wait without producing an event;
435    /// * `Err(_)` for fatal input or platform readiness errors.
436    pub fn poll(&mut self, timeout: Option<Duration>) -> io::Result<bool> {
437        if !self.queue.is_empty() {
438            return Ok(true);
439        }
440        let deadline = timeout.map(|t| Instant::now() + t);
441        loop {
442            let remaining = deadline.map(|d| d.saturating_duration_since(Instant::now()));
443            match self.fill(remaining) {
444                Ok(()) => {
445                    if !self.queue.is_empty() {
446                        return Ok(true);
447                    }
448                    if let Some(left) = remaining
449                        && left.is_zero()
450                    {
451                        return Ok(false);
452                    }
453                }
454                Err(e) if e.kind() == io::ErrorKind::Interrupted => return Ok(false),
455                Err(e) => return Err(e),
456            }
457        }
458    }
459
460    /// Return the next queued event without performing I/O.
461    ///
462    /// This only pops the internal queue. Call [`EventSource::poll`] first when
463    /// the queue may be empty but input could be ready. Returns `None` when no
464    /// event is currently queued.
465    pub fn try_read(&mut self) -> Option<Event> {
466        self.queue.pop_front()
467    }
468
469    /// One read-decode cycle: resolve any overdue decode deadline, wait up
470    /// to `timeout` for readiness, then service it. Winch surfaces a
471    /// [`Event::Resize`]; a [`Waker`] surfaces `Err(Interrupted)` without
472    /// touching decode state; ready input is drained and decoded; a wait
473    /// that returns with no input ready resolves the decode deadline it was
474    /// tightened to. Fills [`Self::queue`].
475    pub(super) fn fill(&mut self, timeout: Option<Duration>) -> io::Result<()> {
476        // Resolve an already-overdue deadline before reading, so a late
477        // continuation byte cannot merge with a sequence that has expired.
478        self.expire_elapsed();
479        if !self.queue.is_empty() {
480            return Ok(());
481        }
482
483        let effective = self.effective_timeout(timeout);
484        let mut ready = [false; READY_SLOTS];
485        self.poller.poll(&mut ready, effective)?;
486
487        #[cfg(unix)]
488        if ready[READY_WINCH] {
489            self.handle_winch();
490        }
491
492        if ready[READY_WAKE] {
493            self.drain_wake();
494            return Err(io::Error::new(io::ErrorKind::Interrupted, "wake"));
495        }
496
497        if ready[READY_INPUT] {
498            self.drain_input()?;
499        } else {
500            // The wait was tightened to a decode deadline that has elapsed.
501            self.expire_elapsed();
502        }
503        Ok(())
504    }
505
506    /// Block until the next event is available, then return it.
507    ///
508    /// This repeatedly checks the queue and calls [`EventSource::poll`] with no
509    /// caller timeout. It returns the next [`Event`] on success.
510    ///
511    /// Returns [`io::ErrorKind::Interrupted`] if a paired [`Waker`] fired while
512    /// waiting, and propagates fatal input/readiness errors.
513    pub fn read(&mut self) -> io::Result<Event> {
514        loop {
515            if let Some(ev) = self.queue.pop_front() {
516                return Ok(ev);
517            }
518            if !self.poll(None)? {
519                return Err(io::Error::new(io::ErrorKind::Interrupted, "wake"));
520            }
521        }
522    }
523
524    /// Return an event to the front of the queue, so the next
525    /// [`read`](Self::read) / [`try_read`](Self::try_read) yields it before
526    /// anything already queued. Use to put back an event read while waiting
527    /// for a specific reply (e.g. a cursor-position report), preserving it
528    /// for normal delivery. Restore a batch in original order by unreading
529    /// in reverse.
530    pub fn unread(&mut self, event: Event) {
531        self.queue.push_front(event);
532    }
533
534    /// Push a freshly produced event onto the queue.
535    pub(super) fn emit(&mut self, ev: Event) {
536        self.queue.push_back(ev);
537    }
538
539    /// Drive the parser as far as it will go against the bytes
540    /// currently in `pending`, pushing extracted events onto the
541    /// queue and arming the appropriate timeout deadline.
542    ///
543    /// While the decoder is in bracketed paste, the paste-idle
544    /// deadline governs (and is reset on every drain, since drain is
545    /// only called after fresh input arrived). Otherwise the ESC
546    /// disambiguation deadline arms when a partial sequence sits at
547    /// the head of `pending`.
548    pub(super) fn drain_parser(&mut self) {
549        loop {
550            let (n, ev) = self.parser.parse_one(self.pending.slice());
551            if n == 0 && ev.is_none() {
552                break;
553            }
554            if n > 0 {
555                self.pending.consume(n);
556            }
557            if let Some(ev) = ev {
558                self.emit(ev);
559            }
560        }
561
562        if self.parser.in_paste() {
563            // In paste: only the paste-idle timer applies. Reset on
564            // every drain (input has just arrived).
565            self.esc_deadline = None;
566            self.paste_deadline = self.paste_idle_timeout.map(|t| Instant::now() + t);
567            return;
568        }
569
570        // Not in paste: clear paste deadline; arm esc deadline if a
571        // partial sequence sits at the head.
572        self.paste_deadline = None;
573        let Some(b0) = self.pending.first() else {
574            self.esc_deadline = None;
575            return;
576        };
577        let armable = b0 == 0x1b || is_c1_introducer(b0);
578        if armable {
579            if self.esc_deadline.is_none() {
580                self.esc_deadline = Some(Instant::now() + self.esc_timeout);
581            }
582        } else {
583            self.esc_deadline = None;
584        }
585    }
586
587    /// Force-resolve a buffered partial sequence whose ESC deadline
588    /// elapsed.
589    pub(super) fn expire_partial(&mut self) {
590        self.esc_deadline = None;
591        if self.pending.first().is_none() {
592            return;
593        }
594        // Flip the decoder into expired mode so its recursive ESC
595        // handler can commit buffered partial sequences (e.g. resolving
596        // `\x1b\x1b` to `Alt+Esc`). Anything the decoder still can't
597        // consume falls back to the single-byte fallback below.
598        self.parser.set_expired(true);
599        self.drain_parser();
600        while let Some(b0) = self.pending.first() {
601            let ev = self
602                .parser
603                .expire_leading(b0)
604                .unwrap_or_else(|| Event::Unknown(vec![b0]));
605            self.pending.consume(1);
606            self.emit(ev);
607            self.drain_parser();
608        }
609        self.parser.set_expired(false);
610    }
611
612    /// Force-close a stuck bracketed paste. Flushes any leftover
613    /// pending bytes as a final `PasteChunk`, then enqueues
614    /// `PasteEnd` and clears the decoder's paste state.
615    ///
616    /// Called from `pump` when the paste-idle deadline elapses, and
617    /// from the public [`EventSource::end_paste`] escape hatch.
618    pub(super) fn expire_paste(&mut self) {
619        self.paste_deadline = None;
620        if !self.parser.in_paste() {
621            return;
622        }
623        if !self.pending.is_empty() {
624            let bytes = self.pending.slice().to_vec();
625            self.pending.clear();
626            self.emit(Event::PasteChunk(bytes));
627        }
628        if let Some(ev) = self.parser.end_paste() {
629            self.emit(ev);
630        }
631    }
632
633    /// Force-exit bracketed paste mode.
634    ///
635    /// If the decoder is currently inside a paste, this flushes any pending
636    /// bytes as a [`Event::PasteChunk`], queues [`Event::PasteEnd`], and clears
637    /// paste state so subsequent bytes parse as ordinary input. Use it when an
638    /// embedding application enforces a paste size cap, user cancellation, or a
639    /// custom watchdog. It has no effect outside paste mode and never panics.
640    pub fn end_paste(&mut self) {
641        self.expire_paste();
642    }
643
644    /// Resolve any decode deadline that has already elapsed, before more
645    /// bytes are read. A buffered partial `ESC` past its window becomes a
646    /// bare `Esc`; an idle bracketed paste past its window is force-closed.
647    /// Run before draining input so a late continuation byte can't merge
648    /// with a sequence whose deadline already passed, and again after a
649    /// wait that returned no input. A no-op when no deadline is overdue;
650    /// the two cases are mutually exclusive (either mid-paste or holding a
651    /// partial escape, never both).
652    pub(super) fn expire_elapsed(&mut self) {
653        let now = Instant::now();
654        if self.parser.in_paste() {
655            if self.paste_deadline.is_some_and(|d| d <= now) {
656                self.expire_paste();
657            }
658        } else if self.esc_deadline.is_some_and(|d| d <= now) {
659            self.expire_partial();
660        }
661    }
662
663    /// Effective wait for the next readiness poll: the caller's `timeout`
664    /// tightened to the nearest decode deadline (ESC or paste-idle,
665    /// whichever is sooner) so a buffered partial sequence resolves
666    /// promptly even when the caller asked to block longer.
667    pub(super) fn effective_timeout(&self, timeout: Option<Duration>) -> Option<Duration> {
668        let now = Instant::now();
669        let deadline = match (self.esc_deadline, self.paste_deadline) {
670            (Some(a), Some(b)) => Some(a.min(b)),
671            (a, b) => a.or(b),
672        };
673        let internal = deadline.map(|d| d.saturating_duration_since(now));
674        match (timeout, internal) {
675            (Some(t), Some(i)) => Some(t.min(i)),
676            (None, i) => i,
677            (t, None) => t,
678        }
679    }
680}