Skip to main content

uncurses/program/
modes.rs

1//! Non-render terminal/input mode toggles for the [`Program`] facade —
2//! cursor style, mouse tracking, bracketed paste, focus reporting,
3//! color-scheme update reports, in-band resize reports, window title,
4//! progress reports, and the default foreground/background/cursor colors.
5//!
6//! Each setter emits its escape bytes through the owned renderer and
7//! flushes immediately, so the mode change takes effect on the terminal
8//! right away and the call returns [`io::Result<()>`](std::io::Result). A
9//! setter whose tracked value is unchanged is a no-op and performs no I/O.
10//!
11//! [`Program`]: super::Program
12
13use std::io::{self, Write};
14
15use crate::ansi::{self, color, cursor, kitty, mode, progress, status, xterm};
16use crate::color::Color;
17use crate::event::Input;
18
19use super::MouseTracking;
20use super::Program;
21use super::ProgressState;
22use crate::ansi::cursor::{CursorShape, CursorStyle};
23
24/// DEC private modes for the mouse tracking modes and encodings this
25/// library supports. Reset together to unconditionally turn mouse
26/// reporting off.
27const MOUSE_MODES: &[mode::Mode] = &[
28    mode::Mode::MOUSE_X10,
29    mode::Mode::MOUSE_NORMAL,
30    mode::Mode::MOUSE_BUTTON,
31    mode::Mode::MOUSE_ANY,
32    mode::Mode::MOUSE_SGR,
33    mode::Mode::MOUSE_SGR_PIXEL,
34];
35
36impl<I: Input, O: Write> Program<I, O> {
37    /// Send a complete OSC 7501 program status report and flush.
38    ///
39    /// Reports replace the addressed record. Include its app, title, and
40    /// message on every update when they should remain present. Use
41    /// [`ProgramStatusState::CLEAR`](super::ProgramStatusState::CLEAR) to
42    /// remove the record and its descendants; a root clear removes all records.
43    ///
44    /// The terminal owns record lifetime. Pause, resume, screen switches,
45    /// and finish leave these reports unchanged, so done and error records
46    /// can survive exit. Explicit discovery is optional; unsupported
47    /// terminals ignore reports.
48    ///
49    /// Invalid fields return [`io::ErrorKind::InvalidInput`] before output.
50    /// See [`write_program_status`](ansi::program_status::write_program_status)
51    /// for validation and protocol limits.
52    pub fn set_program_status(&mut self, report: &super::ProgramStatus<'_>) -> io::Result<()> {
53        ansi::program_status::write_program_status(&mut self.screen, report)?;
54        self.screen.flush()
55    }
56
57    /// Query OSC 7501 support and flush, without waiting for a reply.
58    ///
59    /// The reply is [`Event::ProgramStatus`](crate::event::Event::ProgramStatus),
60    /// with the accepted state and kind names. Event observation records
61    /// support and both lists in [`Capabilities`](super::Capabilities).
62    /// To include this query in [`Self::query_capabilities`], enable
63    /// [`ProgramOptions::query_program_status`](super::ProgramOptions::query_program_status).
64    /// Initialization sends no queries.
65    pub fn request_program_status(&mut self) -> io::Result<()> {
66        self.screen
67            .write_all(ansi::program_status::REQUEST_PROGRAM_STATUS)?;
68        self.screen.flush()
69    }
70
71    /// Set the cursor shape and blinking state (`DECSCUSR`) and flush.
72    ///
73    /// * `shape` — the visual cursor shape ([`Block`](CursorShape::Block),
74    ///   [`Underline`](CursorShape::Underline), or [`Bar`](CursorShape::Bar)).
75    /// * `blinking` — whether the cursor blinks.
76    pub fn set_cursor_style(&mut self, shape: CursorShape, blinking: bool) -> io::Result<()> {
77        let style = CursorStyle::new(shape, blinking);
78        cursor::write_cursor_style(&mut self.screen, style)?;
79        self.state.cursor_style = style;
80        self.screen.flush()
81    }
82
83    /// Hand the cursor style back to the terminal (`DECSCUSR` parameter 0)
84    /// and flush.
85    ///
86    /// Every terminal surveyed for this reads its own configuration here:
87    /// kitty, foot, rio, Alacritty, wezterm, VTE, Konsole and Windows
88    /// Terminal all restore the shape their user chose. xterm documents the
89    /// parameter as a blinking block instead, so what appears is the
90    /// terminal's to decide either way.
91    ///
92    /// That makes this the portable way to undo
93    /// [`set_cursor_style`](Self::set_cursor_style) without knowing what
94    /// came before. To put back the cursor *this session* found rather than
95    /// the one the user configured, read it with
96    /// [`request_cursor_style`](Self::request_cursor_style) before changing
97    /// it and set that style again.
98    pub fn reset_cursor_style(&mut self) -> io::Result<()> {
99        cursor::write_cursor_style(&mut self.screen, CursorStyle::Default)?;
100        self.state.cursor_style = CursorStyle::Default;
101        self.screen.flush()
102    }
103
104    /// Ring the terminal bell (`BEL`) and flush.
105    pub fn beep(&mut self) -> io::Result<()> {
106        self.screen.write_all(b"\x07")?;
107        self.screen.flush()
108    }
109
110    /// Set the pointer (mouse cursor) shape (`OSC 22`) and flush.
111    ///
112    /// `shape` is a pointer shape name such as `"default"`, `"text"`, or
113    /// `"pointer"`. The shape is recorded for save/restore.
114    pub fn set_pointer_shape(&mut self, shape: &str) -> io::Result<()> {
115        cursor::write_set_pointer_shape(&mut self.screen, shape)?;
116        self.state.pointer_shape = Some(shape.to_string());
117        self.screen.flush()
118    }
119
120    /// Reset the pointer (mouse cursor) shape to the terminal default
121    /// (`OSC 22 ; default`) and flush.
122    ///
123    /// Uses the explicit `"default"` shape name rather than an empty one: some
124    /// terminals don't treat an empty `OSC 22` as a reset.
125    pub fn reset_pointer_shape(&mut self) -> io::Result<()> {
126        cursor::write_set_pointer_shape(&mut self.screen, "default")?;
127        self.state.pointer_shape = None;
128        self.screen.flush()
129    }
130
131    /// Report progress to the terminal (`OSC 9;4`) and flush.
132    ///
133    /// Terminals that support it show the progress in the taskbar, tab, or
134    /// window chrome; the rest ignore the sequence. Percentages are clamped
135    /// to `0..=100`.
136    ///
137    /// The state is recorded for save/restore: it is removed on a shell
138    /// handoff ([`pause`](Self::pause), [`suspend`](Self::suspend),
139    /// [`finish`](Self::finish)) and re-reported by
140    /// [`resume`](Self::resume). Take it down with
141    /// [`reset_progress_state`](Self::reset_progress_state).
142    pub fn set_progress_state(&mut self, progress: ProgressState) -> io::Result<()> {
143        progress.write(&mut self.screen)?;
144        self.state.progress = Some(progress);
145        self.screen.flush()
146    }
147
148    /// Remove the progress report (`OSC 9;4;0`) and flush.
149    pub fn reset_progress_state(&mut self) -> io::Result<()> {
150        self.screen.write_all(progress::RESET_PROGRESS_BAR)?;
151        self.state.progress = None;
152        self.screen.flush()
153    }
154
155    /// Enable mouse tracking and flush.
156    ///
157    /// This emits exactly what is asked for and does not consult terminal
158    /// [`capabilities`](Self::capabilities). Unsupported modes are ignored by
159    /// the terminal, and because the mode requests are mutually exclusive, each
160    /// terminal settles on the most capable variant it understands:
161    ///
162    /// * Tracking: button (`1000`) and button-event (`1002`) are always
163    ///   requested, so a terminal reports drag where it can and plain clicks
164    ///   otherwise. With [`MouseTracking::MOTION`], any-event tracking (`1003`)
165    ///   is added on top, so motion without a button held is reported where
166    ///   supported.
167    /// * Encoding: SGR (`1006`) is always requested, since the legacy byte
168    ///   encoding caps coordinates at 223 and SGR is universally supported.
169    ///   With [`MouseTracking::PIXELS`], SGR-pixel (`1016`) is added; terminals
170    ///   that support it report pixel coordinates, and the rest fall back to
171    ///   SGR cell coordinates.
172    ///
173    /// Pass [`MouseTracking::empty()`] for basic button tracking with no
174    /// extras. To turn mouse tracking off, call [`disable_mouse`](Self::disable_mouse).
175    ///
176    /// To learn which variant a terminal actually chose, read
177    /// [`capabilities`](Self::capabilities) (for example
178    /// [`supports(Mode::MOUSE_SGR_PIXEL)`](super::Capabilities::supports) to
179    /// tell whether pixels or cells will arrive). When pixel reporting is active, a
180    /// [`Mouse`](crate::event::Mouse) event's pixel coordinates can be converted
181    /// to cells with [`mouse_pixels_to_cells`](Self::mouse_pixels_to_cells).
182    ///
183    /// Mouse coordinates are physical screen coordinates. Inline, follow this
184    /// with [`request_origin`](Self::request_origin) to learn where the managed
185    /// area sits, so [`mouse_to_origin`](Self::mouse_to_origin) can map them
186    /// into it.
187    ///
188    /// The request is recorded for save/restore.
189    pub fn enable_mouse(&mut self, tracking: MouseTracking) -> io::Result<()> {
190        // Drop any prior tracking first so modes don't stack ambiguously.
191        mode::write_reset_mode(&mut self.screen, MOUSE_MODES)?;
192        self.write_mouse_modes(tracking, true)?;
193        self.state.mouse = Some(tracking);
194        self.screen.flush()
195    }
196
197    /// Disable all mouse tracking modes and encodings, and flush.
198    pub fn disable_mouse(&mut self) -> io::Result<()> {
199        mode::write_reset_mode(&mut self.screen, MOUSE_MODES)?;
200        self.state.mouse = None;
201        self.screen.flush()
202    }
203
204    /// Set or reset the mouse tracking modes and encoding for the given
205    /// tracking flags. `enable` selects set vs reset. The modes are emitted in
206    /// ascending order so that, where the requests are mutually exclusive, the
207    /// most capable supported variant wins (`1003` over `1002`/`1000`, `1016`
208    /// over `1006`).
209    fn write_mouse_modes(&mut self, tracking: MouseTracking, enable: bool) -> io::Result<()> {
210        // Always request plain and button-event tracking as a fallback pair,
211        // adding any-event tracking on top when motion is requested.
212        let mut modes = vec![mode::Mode::MOUSE_NORMAL, mode::Mode::MOUSE_BUTTON];
213        if tracking.contains(MouseTracking::MOTION) {
214            modes.push(mode::Mode::MOUSE_ANY);
215        }
216        // Always request SGR encoding; add SGR-pixel on top when pixels are
217        // requested. Terminals without pixel support fall back to SGR cells.
218        modes.push(mode::Mode::MOUSE_SGR);
219        if tracking.contains(MouseTracking::PIXELS) {
220            modes.push(mode::Mode::MOUSE_SGR_PIXEL);
221        }
222        if enable {
223            mode::write_set_mode(&mut self.screen, &modes)
224        } else {
225            mode::write_reset_mode(&mut self.screen, &modes)
226        }
227    }
228
229    /// Enable bracketed paste mode (DEC private mode 2004) and flush.
230    pub fn enable_bracketed_paste(&mut self) -> io::Result<()> {
231        mode::Mode::BRACKETED_PASTE.set(&mut self.screen)?;
232        self.state.bracketed_paste = true;
233        self.screen.flush()
234    }
235
236    /// Disable bracketed paste mode (DEC private mode 2004) and flush.
237    pub fn disable_bracketed_paste(&mut self) -> io::Result<()> {
238        mode::Mode::BRACKETED_PASTE.reset(&mut self.screen)?;
239        self.state.bracketed_paste = false;
240        self.screen.flush()
241    }
242
243    /// Enable focus in/out reporting (DEC private mode 1004) and flush.
244    pub fn enable_focus_events(&mut self) -> io::Result<()> {
245        mode::Mode::FOCUS.set(&mut self.screen)?;
246        self.state.focus_events = true;
247        self.screen.flush()
248    }
249
250    /// Disable focus in/out reporting (DEC private mode 1004) and flush.
251    pub fn disable_focus_events(&mut self) -> io::Result<()> {
252        mode::Mode::FOCUS.reset(&mut self.screen)?;
253        self.state.focus_events = false;
254        self.screen.flush()
255    }
256
257    /// Enable color-scheme update notifications (DEC private mode 2031) and
258    /// flush. The terminal then sends a `CSI ? 997 ; {1|2} n` report
259    /// whenever the user or operating system switches between dark and
260    /// light schemes; these surface as [`Event::ColorScheme`]. The report
261    /// indicates only the dark/light preference, not the actual colors.
262    ///
263    /// [`Event::ColorScheme`]: crate::event::Event::ColorScheme
264    pub fn enable_color_scheme_updates(&mut self) -> io::Result<()> {
265        mode::Mode::LIGHT_DARK.set(&mut self.screen)?;
266        self.state.color_scheme_updates = true;
267        self.screen.flush()
268    }
269
270    /// Disable color-scheme update notifications (DEC private mode 2031) and
271    /// flush.
272    pub fn disable_color_scheme_updates(&mut self) -> io::Result<()> {
273        mode::Mode::LIGHT_DARK.reset(&mut self.screen)?;
274        self.state.color_scheme_updates = false;
275        self.screen.flush()
276    }
277
278    /// Enable terminal visibility reports (DEC private mode 2033) and flush.
279    /// The terminal then sends a `CSI ? 999 ; {1|2} n` report whenever the
280    /// view stops being observable or becomes observable again, which the
281    /// decoder surfaces as [`Event::Visibility`]. Being covered by another
282    /// window, scrolled out of a tab, or on a minimized window all count.
283    ///
284    /// The report is advisory and asymmetric: [`Visibility::Hidden`] means
285    /// the terminal knows nothing can be seen, so a render can be skipped,
286    /// while [`Visibility::Visible`] only means it may be observable. A
287    /// terminal that never reports is therefore treated as visible, which is
288    /// what makes ignoring this mode safe.
289    ///
290    /// Visibility is independent of focus: an unfocused window is usually
291    /// still visible.
292    ///
293    /// [`Event::Visibility`]: crate::event::Event::Visibility
294    /// [`Visibility::Hidden`]: crate::event::Visibility::Hidden
295    /// [`Visibility::Visible`]: crate::event::Visibility::Visible
296    pub fn enable_visibility_reports(&mut self) -> io::Result<()> {
297        mode::Mode::VISIBILITY_REPORTS.set(&mut self.screen)?;
298        self.state.visibility_reports = true;
299        self.screen.flush()
300    }
301
302    /// Disable terminal visibility reports (DEC private mode 2033) and flush.
303    pub fn disable_visibility_reports(&mut self) -> io::Result<()> {
304        mode::Mode::VISIBILITY_REPORTS.reset(&mut self.screen)?;
305        self.state.visibility_reports = false;
306        self.screen.flush()
307    }
308
309    /// Ask the terminal to report its visibility once and flush.
310    ///
311    /// The reply arrives as [`Event::Visibility`], the same event the
312    /// unsolicited reports use. This does not enable or disable
313    /// [`enable_visibility_reports`](Self::enable_visibility_reports), so it
314    /// is the way to read visibility without subscribing to changes. A
315    /// terminal that does not implement DEC private mode 2033 answers
316    /// nothing at all, so never block waiting for this reply.
317    ///
318    /// [`Event::Visibility`]: crate::event::Event::Visibility
319    pub fn request_visibility(&mut self) -> io::Result<()> {
320        self.screen
321            .write_all(status::REQUEST_VISIBILITY_REPORT)
322            .and_then(|()| self.screen.flush())
323    }
324
325    /// Enable in-band resize notifications (DEC private mode 2048) and
326    /// flush. The terminal then reports every surface size change in-band
327    /// as a `CSI 48 ; height ; width ; ypixel ; xpixel t` sequence, which
328    /// the decoder surfaces as [`Event::Resize`] — no `SIGWINCH` handler
329    /// required. The event source stops synthesizing resizes from `SIGWINCH`
330    /// while this is on, so a size change is reported once, not twice.
331    ///
332    /// Only call this after [`capabilities`](Self::capabilities) reports
333    /// [`supports(Mode::IN_BAND_RESIZE)`](super::Capabilities::supports); a
334    /// terminal that ignores the mode would otherwise leave you with no
335    /// resize events at all.
336    ///
337    /// [`Event::Resize`]: crate::event::Event::Resize
338    pub fn enable_in_band_resize(&mut self) -> io::Result<()> {
339        mode::Mode::IN_BAND_RESIZE.set(&mut self.screen)?;
340        self.state.in_band_resize = true;
341        self.state.chosen.insert(mode::Mode::IN_BAND_RESIZE);
342        self.source.lock().unwrap().set_handle_resize(false);
343        self.screen.flush()
344    }
345
346    /// Disable in-band resize notifications (DEC private mode 2048) and
347    /// flush, handing resize reporting back to the `SIGWINCH` path.
348    pub fn disable_in_band_resize(&mut self) -> io::Result<()> {
349        mode::Mode::IN_BAND_RESIZE.reset(&mut self.screen)?;
350        self.state.in_band_resize = false;
351        self.state.chosen.insert(mode::Mode::IN_BAND_RESIZE);
352        self.source.lock().unwrap().set_handle_resize(true);
353        self.screen.flush()
354    }
355
356    /// Set both the window title and icon name (`OSC 0`) and flush.
357    ///
358    /// An empty `title` clears both overrides, restoring the terminal's
359    /// defaults; the state is recorded as unset so teardown and resume skip
360    /// them. To set just one, use
361    /// [`set_window_title`](Self::set_window_title) (`OSC 2`) or
362    /// [`set_icon_title`](Self::set_icon_title) (`OSC 1`).
363    pub fn set_title(&mut self, title: &str) -> io::Result<()> {
364        ansi::title::write_window_title_and_icon(&mut self.screen, title)?;
365        let stored = (!title.is_empty()).then(|| title.to_string());
366        self.state.window_title = stored.clone();
367        self.state.icon_name = stored;
368        self.screen.flush()
369    }
370
371    /// Set the window title only (`OSC 2`) and flush.
372    ///
373    /// An empty `title` clears the override, restoring the terminal's default
374    /// window title. Unlike [`set_title`](Self::set_title) (`OSC 0`), this
375    /// leaves the icon name untouched.
376    pub fn set_window_title(&mut self, title: &str) -> io::Result<()> {
377        ansi::title::write_window_title(&mut self.screen, title)?;
378        self.state.window_title = (!title.is_empty()).then(|| title.to_string());
379        self.screen.flush()
380    }
381
382    /// Set the icon name only (`OSC 1`) and flush.
383    ///
384    /// An empty `title` clears the override, restoring the terminal's default
385    /// icon name. Unlike [`set_title`](Self::set_title) (`OSC 0`), this leaves
386    /// the window title untouched.
387    pub fn set_icon_title(&mut self, title: &str) -> io::Result<()> {
388        ansi::title::write_icon_name(&mut self.screen, title)?;
389        self.state.icon_name = (!title.is_empty()).then(|| title.to_string());
390        self.screen.flush()
391    }
392
393    /// Enter the alternate screen buffer (DECSET 1049) and flush.
394    ///
395    /// The managed area becomes the whole viewport: the screen switches to
396    /// absolute addressing and repaints in full on the next
397    /// [`render`](crate::screen::Screen::render). The normal buffer, its scrollback, and the
398    /// shell prompt are left untouched underneath and come back on
399    /// [`exit_alt_screen`](Self::exit_alt_screen).
400    ///
401    /// Cursor visibility and the Kitty keyboard stack are per-screen-buffer
402    /// on some terminals, so both are re-asserted on the newly active buffer.
403    pub fn enter_alt_screen(&mut self) -> io::Result<()> {
404        self.set_alt_screen(true)
405    }
406
407    /// Leave the alternate screen buffer (DECRST 1049) and flush, restoring
408    /// the normal buffer and its scrollback. The managed area becomes an
409    /// inline band again, addressed with relative moves.
410    pub fn exit_alt_screen(&mut self) -> io::Result<()> {
411        self.set_alt_screen(false)
412    }
413
414    /// Emit the alternate-screen switch and bring the screen and the
415    /// per-buffer modes with it. Always emits the mode; the bookkeeping runs
416    /// only on an actual transition.
417    pub(super) fn set_alt_screen(&mut self, enter: bool) -> io::Result<()> {
418        let changed = self.state.alt_screen != enter;
419        if changed && enter {
420            // Capture the inline anchor before the buffer switch hides it.
421            self.screen.save_cursor();
422        }
423        if enter {
424            mode::Mode::ALT_SCREEN_SAVE_CURSOR.set(&mut self.screen)?;
425        } else {
426            mode::Mode::ALT_SCREEN_SAVE_CURSOR.reset(&mut self.screen)?;
427        }
428        self.state.alt_screen = enter;
429        if changed {
430            self.screen.set_fullscreen(enter);
431            // Some terminals track DECTCEM and the Kitty keyboard stack per
432            // screen buffer, so the newly active one can disagree with what
433            // this program asked for. Re-assert both.
434            if !self.state.cursor_visible {
435                mode::Mode::CURSOR_VISIBLE.reset(&mut self.screen)?;
436            } else {
437                mode::Mode::CURSOR_VISIBLE.set(&mut self.screen)?;
438            }
439            if !self.state.kitty_keyboard.is_empty() {
440                kitty::write_set_kitty_keyboard(
441                    &mut self.screen,
442                    self.state.kitty_keyboard,
443                    kitty::KittyKeyboardMode::Set,
444                )?;
445            }
446        }
447        self.screen.flush()
448    }
449
450    /// Show the terminal cursor (DECSET 25) and flush.
451    ///
452    /// Also tells the screen, which hides a visible cursor around each frame's
453    /// cell diff so it does not dance across cells as the renderer
454    /// repositions it.
455    pub fn show_cursor(&mut self) -> io::Result<()> {
456        mode::Mode::CURSOR_VISIBLE.set(&mut self.screen)?;
457        self.state.cursor_visible = true;
458        self.screen.set_cursor_visible(true);
459        self.screen.flush()
460    }
461
462    /// Hide the terminal cursor (DECRST 25) and flush.
463    pub fn hide_cursor(&mut self) -> io::Result<()> {
464        mode::Mode::CURSOR_VISIBLE.reset(&mut self.screen)?;
465        self.state.cursor_visible = false;
466        self.screen.set_cursor_visible(false);
467        self.screen.flush()
468    }
469
470    /// Enable Unicode core / grapheme-cluster mode (DECSET 2027) and flush,
471    /// switching the screen to measure text per extended grapheme cluster so
472    /// it agrees with the terminal.
473    ///
474    /// Only call this after [`capabilities`](Self::capabilities) reports
475    /// [`supports(Mode::UNICODE_CORE)`](super::Capabilities::supports); a
476    /// terminal that ignores the mode still measures per code point, and the
477    /// two disagreeing misplaces every cell after the first cluster on a line.
478    pub fn enable_grapheme_clusters(&mut self) -> io::Result<()> {
479        mode::Mode::UNICODE_CORE.set(&mut self.screen)?;
480        self.state.grapheme_clusters = true;
481        self.state.chosen.insert(mode::Mode::UNICODE_CORE);
482        self.screen.set_grapheme_clusters(true);
483        self.screen.flush()
484    }
485
486    /// Disable grapheme-cluster mode (DECRST 2027) and flush, returning the
487    /// screen to per-code-point (wcwidth-style) measurement.
488    pub fn disable_grapheme_clusters(&mut self) -> io::Result<()> {
489        mode::Mode::UNICODE_CORE.reset(&mut self.screen)?;
490        self.state.grapheme_clusters = false;
491        self.state.chosen.insert(mode::Mode::UNICODE_CORE);
492        self.screen.set_grapheme_clusters(false);
493        self.screen.flush()
494    }
495
496    /// Return the decoder's current Backarrow mode (DECBKM).
497    ///
498    /// Reset (`false`) is the default assumption. [`Self::observe_event`]
499    /// applies recognized reports to future decoding. Already queued events
500    /// keep their decoded values.
501    pub fn backspace_mode(&self) -> bool {
502        self.source.lock().unwrap().backspace_mode()
503    }
504
505    /// Set Backarrow mode (DECBKM), flush, and update legacy key decoding.
506    ///
507    /// Set (`true`) selects BS (`0x08`) for ordinary Backspace; reset (`false`)
508    /// selects DEL (`0x7f`). [`DecoderFlags`](crate::event::DecoderFlags)
509    /// choose the ambiguous interpretations. Terminals can ignore the request.
510    ///
511    /// Teardown resets an application-selected mode. Resume reapplies this
512    /// selection. Received reports update decoding without changing that
513    /// lifecycle selection. The caller's options remain unchanged.
514    pub fn set_backspace_mode(&mut self, enabled: bool) -> io::Result<()> {
515        self.write_backspace_mode(enabled)?;
516        self.state.backspace_mode = enabled;
517        self.state.chosen.insert(mode::Mode::BACKARROW_KEY);
518        Ok(())
519    }
520
521    fn write_backspace_mode(&mut self, enabled: bool) -> io::Result<()> {
522        // Keep async decoding in step with the terminal mode change.
523        let mut source = self.source.lock().unwrap();
524        if enabled {
525            mode::Mode::BACKARROW_KEY.set(&mut self.screen)?;
526        } else {
527            mode::Mode::BACKARROW_KEY.reset(&mut self.screen)?;
528        }
529        self.screen.flush()?;
530        source.set_backspace_mode(enabled);
531        Ok(())
532    }
533
534    /// Set the per-screen-buffer Kitty keyboard enhancements and flush.
535    /// `Some(flags)` enables the selected progressive-enhancement bits;
536    /// `None` disables every enhancement.
537    ///
538    /// The Kitty stack is per-screen-buffer, so the flags are re-applied on
539    /// the newly active buffer by [`enter_alt_screen`](Self::enter_alt_screen)
540    /// / [`exit_alt_screen`](Self::exit_alt_screen), as part of the switch.
541    pub fn set_kitty_keyboard(
542        &mut self,
543        flags: Option<kitty::KittyKeyboardFlags>,
544    ) -> io::Result<()> {
545        let flags = flags.unwrap_or_else(kitty::KittyKeyboardFlags::empty);
546        kitty::write_set_kitty_keyboard(&mut self.screen, flags, kitty::KittyKeyboardMode::Set)?;
547        self.state.kitty_keyboard = flags;
548        self.screen.flush()
549    }
550
551    /// Set the xterm modifyOtherKeys mode (`CSI > 4 ; n m`) and flush.
552    /// Passing [`ModifyOtherKeysMode::Disabled`] resets it (`CSI > 4 m`).
553    /// The mode is recorded so [`Program::finish`](super::Program::finish)
554    /// can reset it and [`Program::resume`](super::Program::resume) re-apply
555    /// it.
556    ///
557    /// [`ModifyOtherKeysMode::Disabled`]: crate::event::ModifyOtherKeysMode::Disabled
558    pub fn set_modify_other_keys(
559        &mut self,
560        mode: crate::event::ModifyOtherKeysMode,
561    ) -> io::Result<()> {
562        use crate::event::ModifyOtherKeysMode;
563        match mode {
564            ModifyOtherKeysMode::Disabled => {
565                self.screen.write_all(xterm::RESET_MODIFY_OTHER_KEYS)?
566            }
567            ModifyOtherKeysMode::Mode1 => self.screen.write_all(xterm::SET_MODIFY_OTHER_KEYS_1)?,
568            ModifyOtherKeysMode::Mode2 => self.screen.write_all(xterm::SET_MODIFY_OTHER_KEYS_2)?,
569        }
570        self.state.modify_other_keys = mode;
571        self.screen.flush()
572    }
573
574    /// Set the default foreground color (`OSC 10`) and flush. The color is
575    /// converted to 24-bit RGB and emitted as `rgb:RRRR/GGGG/BBBB`, and is
576    /// recorded so [`Program::finish`](super::Program::finish) can restore
577    /// the terminal default and [`Program::resume`](super::Program::resume)
578    /// can re-apply it.
579    pub fn set_foreground_color(&mut self, color: Color) -> io::Result<()> {
580        let (r, g, b) = color.to_rgb();
581        color::write_set_foreground_color(&mut self.screen, &color::xparse_rgb(r, g, b))?;
582        self.state.foreground_color = Some(color);
583        self.screen.flush()
584    }
585
586    /// Restore the terminal's default foreground color (`OSC 110`) and
587    /// flush.
588    pub fn reset_foreground_color(&mut self) -> io::Result<()> {
589        self.screen.write_all(color::RESET_FOREGROUND_COLOR)?;
590        self.state.foreground_color = None;
591        self.screen.flush()
592    }
593
594    /// Set the default background color (`OSC 11`) and flush. See
595    /// [`set_foreground_color`](Self::set_foreground_color) for
596    /// state-tracking semantics.
597    pub fn set_background_color(&mut self, color: Color) -> io::Result<()> {
598        let (r, g, b) = color.to_rgb();
599        color::write_set_background_color(&mut self.screen, &color::xparse_rgb(r, g, b))?;
600        self.state.background_color = Some(color);
601        self.screen.flush()
602    }
603
604    /// Restore the terminal's default background color (`OSC 111`) and
605    /// flush.
606    pub fn reset_background_color(&mut self) -> io::Result<()> {
607        self.screen.write_all(color::RESET_BACKGROUND_COLOR)?;
608        self.state.background_color = None;
609        self.screen.flush()
610    }
611
612    /// Set the cursor color (`OSC 12`) and flush. See
613    /// [`set_foreground_color`](Self::set_foreground_color) for
614    /// state-tracking semantics.
615    pub fn set_cursor_color(&mut self, color: Color) -> io::Result<()> {
616        let (r, g, b) = color.to_rgb();
617        color::write_set_cursor_color(&mut self.screen, &color::xparse_rgb(r, g, b))?;
618        self.state.cursor_color = Some(color);
619        self.screen.flush()
620    }
621
622    /// Restore the terminal's default cursor color (`OSC 112`) and flush.
623    pub fn reset_cursor_color(&mut self) -> io::Result<()> {
624        self.screen.write_all(color::RESET_CURSOR_COLOR)?;
625        self.state.cursor_color = None;
626        self.screen.flush()
627    }
628
629    /// Set a terminal palette color by index (`OSC 4`) and flush. The
630    /// override is tracked so [`Program::finish`](super::Program::finish) can
631    /// restore it and [`Program::resume`](super::Program::resume) re-apply it.
632    pub fn set_palette_color(&mut self, index: u8, color: Color) -> io::Result<()> {
633        let (r, g, b) = color.to_rgb();
634        color::write_set_palette_color(&mut self.screen, index, &color::xparse_rgb(r, g, b))?;
635        self.state.palette.insert(index, color);
636        self.screen.flush()
637    }
638
639    /// Reset a single terminal palette color to its default
640    /// (`OSC 104 ; index`) and flush.
641    pub fn reset_palette_color(&mut self, index: u8) -> io::Result<()> {
642        color::write_reset_palette_color(&mut self.screen, index)?;
643        self.state.palette.remove(&index);
644        self.screen.flush()
645    }
646
647    /// Reset the entire terminal palette to its defaults (`OSC 104`) and
648    /// flush, clearing every tracked palette override.
649    pub fn reset_palette_colors(&mut self) -> io::Result<()> {
650        self.screen.write_all(color::RESET_PALETTE_COLORS)?;
651        self.state.palette.clear();
652        self.screen.flush()
653    }
654
655    /// Stage the teardown of every mode currently held — non-render modes
656    /// (cursor style, mouse, paste, focus, colors, title, …) followed by the
657    /// render-coupled modes (cursor visibility, alternate screen, Kitty
658    /// keyboard, Unicode core) — returning the terminal to a clean baseline
659    /// before handing control back to the shell. Preserves the selected
660    /// state, so [`restore`](Self::restore) re-applies the same modes.
661    /// Backarrow changes also update the decoder. The caller flushes.
662    pub(super) fn reset(&mut self) -> io::Result<()> {
663        // --- Non-render modes ---
664        if self.state.cursor_style != cursor::CursorStyle::Default {
665            cursor::write_cursor_style(&mut self.screen, cursor::CursorStyle::Default)?;
666        }
667        if self.state.bracketed_paste {
668            mode::Mode::BRACKETED_PASTE.reset(&mut self.screen)?;
669        }
670        if self.state.focus_events {
671            mode::Mode::FOCUS.reset(&mut self.screen)?;
672        }
673        if let Some(tracking) = self.state.mouse {
674            self.write_mouse_modes(tracking, false)?;
675        }
676        if self.state.color_scheme_updates {
677            mode::Mode::LIGHT_DARK.reset(&mut self.screen)?;
678        }
679        if self.state.visibility_reports {
680            mode::Mode::VISIBILITY_REPORTS.reset(&mut self.screen)?;
681        }
682        if self.state.in_band_resize {
683            mode::Mode::IN_BAND_RESIZE.reset(&mut self.screen)?;
684        }
685        if self.state.chosen.contains(&mode::Mode::BACKARROW_KEY) {
686            self.write_backspace_mode(false)?;
687        }
688        if self.state.modify_other_keys != crate::event::ModifyOtherKeysMode::Disabled {
689            self.screen.write_all(xterm::RESET_MODIFY_OTHER_KEYS)?;
690        }
691        if self.state.foreground_color.is_some() {
692            self.screen.write_all(color::RESET_FOREGROUND_COLOR)?;
693        }
694        if self.state.background_color.is_some() {
695            self.screen.write_all(color::RESET_BACKGROUND_COLOR)?;
696        }
697        if self.state.cursor_color.is_some() {
698            self.screen.write_all(color::RESET_CURSOR_COLOR)?;
699        }
700        if !self.state.palette.is_empty() {
701            self.screen.write_all(color::RESET_PALETTE_COLORS)?;
702        }
703        match (&self.state.window_title, &self.state.icon_name) {
704            // Both set to the same string (e.g. via `set_title`): clear both
705            // with a single `OSC 0`.
706            (Some(w), Some(i)) if w == i => {
707                ansi::title::write_window_title_and_icon(&mut self.screen, "")?;
708            }
709            (window_title, icon_name) => {
710                if window_title.is_some() {
711                    ansi::title::write_window_title(&mut self.screen, "")?;
712                }
713                if icon_name.is_some() {
714                    ansi::title::write_icon_name(&mut self.screen, "")?;
715                }
716            }
717        }
718        if self.state.pointer_shape.is_some() {
719            cursor::write_set_pointer_shape(&mut self.screen, "default")?;
720        }
721        if self.state.progress.is_some() {
722            self.screen.write_all(progress::RESET_PROGRESS_BAR)?;
723        }
724
725        // --- Render-coupled modes ---
726        // Park the cursor below the managed area before any teardown, so the
727        // shell prompt lands where the user expects.
728        self.screen.park_cursor()?;
729        if !self.state.cursor_visible {
730            mode::Mode::CURSOR_VISIBLE.set(&mut self.screen)?;
731        }
732        let fullscreen = self.state.alt_screen;
733        // Clear the alt screen's kitty keyboard frame *before* leaving the alt
734        // screen — the stack is per-screen-buffer.
735        if fullscreen && !self.state.kitty_keyboard.is_empty() {
736            kitty::write_set_kitty_keyboard(
737                &mut self.screen,
738                kitty::KittyKeyboardFlags::empty(),
739                kitty::KittyKeyboardMode::Set,
740            )?;
741        }
742        if fullscreen {
743            mode::Mode::ALT_SCREEN_SAVE_CURSOR.reset(&mut self.screen)?;
744            self.screen.restore_cursor();
745        }
746        // Now on the main screen — clear its frame too.
747        if !self.state.kitty_keyboard.is_empty() {
748            kitty::write_set_kitty_keyboard(
749                &mut self.screen,
750                kitty::KittyKeyboardFlags::empty(),
751                kitty::KittyKeyboardMode::Set,
752            )?;
753        }
754        if self.state.grapheme_clusters {
755            mode::Mode::UNICODE_CORE.reset(&mut self.screen)?;
756        }
757        self.screen.invalidate_cursor();
758        Ok(())
759    }
760
761    /// Re-emit every mode held in the tracked state — the render-coupled
762    /// modes (Kitty keyboard, alternate screen, Unicode core, cursor
763    /// visibility) first, then the non-render modes — for any scenario where
764    /// the terminal was temporarily handed back to the shell. Pairs with
765    /// [`reset`](Self::reset). Preserves the selected state and updates the
766    /// decoder's Backarrow mode. The caller flushes.
767    pub(super) fn restore(&mut self) -> io::Result<()> {
768        // --- Render-coupled modes ---
769        // Re-apply the desired kitty keyboard flags on the main screen
770        // *before* entering the alt screen — the stack is per-buffer.
771        if !self.state.kitty_keyboard.is_empty() {
772            kitty::write_set_kitty_keyboard(
773                &mut self.screen,
774                self.state.kitty_keyboard,
775                kitty::KittyKeyboardMode::Set,
776            )?;
777        }
778        let fullscreen = self.state.alt_screen;
779        if fullscreen {
780            self.screen.save_cursor();
781            mode::Mode::ALT_SCREEN_SAVE_CURSOR.set(&mut self.screen)?;
782        }
783        // Now on the alt screen (if alt was active) — re-apply on the alt
784        // buffer too, since its stack is independent.
785        if fullscreen && !self.state.kitty_keyboard.is_empty() {
786            kitty::write_set_kitty_keyboard(
787                &mut self.screen,
788                self.state.kitty_keyboard,
789                kitty::KittyKeyboardMode::Set,
790            )?;
791        }
792        if self.state.grapheme_clusters {
793            mode::Mode::UNICODE_CORE.set(&mut self.screen)?;
794        }
795        if !self.state.cursor_visible {
796            mode::Mode::CURSOR_VISIBLE.reset(&mut self.screen)?;
797        }
798
799        // --- Non-render modes ---
800        if self.state.cursor_style != cursor::CursorStyle::Default {
801            cursor::write_cursor_style(&mut self.screen, self.state.cursor_style)?;
802        }
803        if self.state.color_scheme_updates {
804            mode::Mode::LIGHT_DARK.set(&mut self.screen)?;
805        }
806        if self.state.visibility_reports {
807            mode::Mode::VISIBILITY_REPORTS.set(&mut self.screen)?;
808        }
809        if self.state.in_band_resize {
810            mode::Mode::IN_BAND_RESIZE.set(&mut self.screen)?;
811        }
812        if self.state.chosen.contains(&mode::Mode::BACKARROW_KEY) {
813            self.write_backspace_mode(self.state.backspace_mode)?;
814        }
815        match self.state.modify_other_keys {
816            crate::event::ModifyOtherKeysMode::Mode1 => {
817                self.screen.write_all(xterm::SET_MODIFY_OTHER_KEYS_1)?;
818            }
819            crate::event::ModifyOtherKeysMode::Mode2 => {
820                self.screen.write_all(xterm::SET_MODIFY_OTHER_KEYS_2)?;
821            }
822            crate::event::ModifyOtherKeysMode::Disabled => {}
823        }
824        if self.state.bracketed_paste {
825            mode::Mode::BRACKETED_PASTE.set(&mut self.screen)?;
826        }
827        if self.state.focus_events {
828            mode::Mode::FOCUS.set(&mut self.screen)?;
829        }
830        if let Some(pref) = self.state.mouse {
831            self.write_mouse_modes(pref, true)?;
832        }
833        if let Some(c) = self.state.foreground_color {
834            let (r, g, b) = c.to_rgb();
835            color::write_set_foreground_color(&mut self.screen, &color::xparse_rgb(r, g, b))?;
836        }
837        if let Some(c) = self.state.background_color {
838            let (r, g, b) = c.to_rgb();
839            color::write_set_background_color(&mut self.screen, &color::xparse_rgb(r, g, b))?;
840        }
841        if let Some(c) = self.state.cursor_color {
842            let (r, g, b) = c.to_rgb();
843            color::write_set_cursor_color(&mut self.screen, &color::xparse_rgb(r, g, b))?;
844        }
845        for (&index, &c) in &self.state.palette {
846            let (r, g, b) = c.to_rgb();
847            color::write_set_palette_color(&mut self.screen, index, &color::xparse_rgb(r, g, b))?;
848        }
849        match (
850            self.state.window_title.clone(),
851            self.state.icon_name.clone(),
852        ) {
853            // Both set to the same string (e.g. via `set_title`): restore both
854            // with a single `OSC 0`.
855            (Some(w), Some(i)) if w == i => {
856                ansi::title::write_window_title_and_icon(&mut self.screen, &w)?;
857            }
858            (window_title, icon_name) => {
859                if let Some(title) = window_title {
860                    ansi::title::write_window_title(&mut self.screen, &title)?;
861                }
862                if let Some(name) = icon_name {
863                    ansi::title::write_icon_name(&mut self.screen, &name)?;
864                }
865            }
866        }
867        if let Some(shape) = self.state.pointer_shape.clone() {
868            cursor::write_set_pointer_shape(&mut self.screen, &shape)?;
869        }
870        if let Some(progress) = self.state.progress {
871            progress.write(&mut self.screen)?;
872        }
873        Ok(())
874    }
875
876    // --- Request delegates -----------------------------------------------
877    //
878    // Each writes a terminal query and flushes; the reply arrives later
879    // through the event flow. Replies that double as capability
880    // reports (mode, kitty keyboard) are recorded into
881    // [`capabilities`](Self::capabilities); value replies (cursor
882    // position, colors, pixel sizes) surface to the caller as events.
883
884    /// Request the window size in pixels (XTWINOPS `CSI 14 t`). Reply:
885    /// [`Event::WindowPixelSize`](crate::event::Event::WindowPixelSize).
886    pub fn request_window_pixel_size(&mut self) -> io::Result<()> {
887        self.screen
888            .write_all(crate::ansi::winop::REQUEST_WINDOW_PIXEL_SIZE)?;
889        self.screen.flush()
890    }
891
892    /// Request the character cell size in pixels (XTWINOPS `CSI 16 t`).
893    /// Reply: [`Event::CellPixelSize`](crate::event::Event::CellPixelSize).
894    pub fn request_cell_pixel_size(&mut self) -> io::Result<()> {
895        self.screen
896            .write_all(crate::ansi::winop::REQUEST_CELL_PIXEL_SIZE)?;
897        self.screen.flush()
898    }
899
900    /// Request the physical screen coordinate of the managed area's top-left
901    /// cell: park the cursor there and ask the terminal where it landed
902    /// (`CSI 6n`).
903    ///
904    /// The reply arrives asynchronously as a
905    /// [`CursorPosition`](crate::event::Event::CursorPosition) event and is
906    /// recorded (and clipped to keep the managed area on screen) by
907    /// [`observe_event`](Self::observe_event); read the result with
908    /// [`origin`](Self::origin). The event is still delivered to you.
909    ///
910    /// Call this once mouse mapping starts, again whenever the terminal
911    /// resizes, and again after [`resume`](Self::resume), each of which can
912    /// move the managed area. Without it the origin stays at `(0, 0)` and
913    /// [`mouse_to_origin`](Self::mouse_to_origin) is an identity.
914    ///
915    /// A no-op in fullscreen, where the origin is always `(0, 0)`.
916    pub fn request_origin(&mut self) -> io::Result<()> {
917        if self.screen.fullscreen() {
918            return Ok(());
919        }
920        // Park the cursor at the surface top-left; in relative-cursor inline
921        // mode that is the physical origin, so the reply *is* the origin.
922        // Stage the move rather than using move_cursor_to, which would flush
923        // before the query is written.
924        self.screen
925            .stage_move_cursor_to(crate::layout::Position::ORIGIN);
926        self.screen
927            .write_all(crate::ansi::status::REQUEST_CURSOR_POSITION)?;
928        self.origin_queries_pending = self.origin_queries_pending.saturating_add(1);
929        self.screen.flush()
930    }
931
932    /// Request the terminal's active Kitty keyboard flags (`CSI ? u`).
933    /// The reply is recorded in [`capabilities`](Self::capabilities).
934    pub fn request_kitty_keyboard(&mut self) -> io::Result<()> {
935        self.screen
936            .write_all(crate::ansi::kitty::REQUEST_KITTY_KEYBOARD)?;
937        self.screen.flush()
938    }
939
940    /// Request the terminal's modifyOtherKeys state (`CSI ? 4 m`). The
941    /// reply is recorded in [`capabilities`](Self::capabilities).
942    pub fn request_modify_other_keys(&mut self) -> io::Result<()> {
943        self.screen
944            .write_all(crate::ansi::xterm::QUERY_MODIFY_OTHER_KEYS)?;
945        self.screen.flush()
946    }
947
948    /// Request the default foreground color (`OSC 10 ; ? ST`). Reply:
949    /// [`Event::ForegroundColor`](crate::event::Event::ForegroundColor).
950    pub fn request_foreground_color(&mut self) -> io::Result<()> {
951        self.screen
952            .write_all(crate::ansi::color::REQUEST_FOREGROUND_COLOR)?;
953        self.screen.flush()
954    }
955
956    /// Request the default background color (`OSC 11 ; ? ST`). Reply:
957    /// [`Event::BackgroundColor`](crate::event::Event::BackgroundColor).
958    pub fn request_background_color(&mut self) -> io::Result<()> {
959        self.screen
960            .write_all(crate::ansi::color::REQUEST_BACKGROUND_COLOR)?;
961        self.screen.flush()
962    }
963
964    /// Request the cursor color (`OSC 12 ; ? ST`). Reply:
965    /// [`Event::CursorColor`](crate::event::Event::CursorColor).
966    pub fn request_cursor_color(&mut self) -> io::Result<()> {
967        self.screen
968            .write_all(crate::ansi::color::REQUEST_CURSOR_COLOR)?;
969        self.screen.flush()
970    }
971
972    /// Request a terminal palette color by index (`OSC 4 ; index ; ? ST`).
973    /// Reply: `OSC 4 ; index ; rgb:... ST`.
974    pub fn request_palette_color(&mut self, index: u8) -> io::Result<()> {
975        crate::ansi::color::write_request_palette_color(&mut self.screen, index)?;
976        self.screen.flush()
977    }
978
979    /// Request a terminal mode's current setting (DECRQM). Reply:
980    /// [`Event::ModeReport`](crate::event::Event::ModeReport).
981    ///
982    /// The reply's [`ModeSetting`](crate::ansi::mode::ModeSetting) reports whether
983    /// the mode is set, reset, or permanently fixed. A permanently reset mode
984    /// is recognized but can never be enabled, so check
985    /// [`ModeSetting::is_available`](crate::ansi::mode::ModeSetting::is_available)
986    /// before relying on it.
987    pub fn request_mode(&mut self, mode: crate::ansi::mode::Mode) -> io::Result<()> {
988        mode.request(&mut self.screen)?;
989        self.screen.flush()
990    }
991
992    /// Request the cursor position (`CSI 6 n`). Reply:
993    /// [`Event::CursorPosition`](crate::event::Event::CursorPosition).
994    ///
995    /// For a reply distinct from modified F3, use
996    /// [`request_extended_cursor_position`](Self::request_extended_cursor_position)
997    /// on terminals that support DECXCPR.
998    pub fn request_cursor_position(&mut self) -> io::Result<()> {
999        self.screen
1000            .write_all(crate::ansi::status::REQUEST_CURSOR_POSITION)?;
1001        self.screen.flush()
1002    }
1003
1004    /// Request the extended cursor position (`CSI ? 6 n`) and flush.
1005    ///
1006    /// DECXCPR replies produce [`Event::CursorPosition`](crate::event::Event::CursorPosition)
1007    /// with zero-based cell coordinates and a one-based page number when
1008    /// reported. An absent page is zero. The private reply distinguishes cursor
1009    /// reports from modified F3.
1010    pub fn request_extended_cursor_position(&mut self) -> io::Result<()> {
1011        self.screen
1012            .write_all(crate::ansi::status::REQUEST_EXTENDED_CURSOR_POSITION)?;
1013        self.screen.flush()
1014    }
1015
1016    /// Request the current cursor style (DECRQSS for `DECSCUSR`). Reply:
1017    /// [`Event::SettingReport`](crate::event::Event::SettingReport) carrying
1018    /// [`SettingReport::CursorStyle`](crate::event::SettingReport::CursorStyle).
1019    ///
1020    /// The reply says what the cursor is *now*, so ask before changing it if
1021    /// you mean to put it back. Teardown writes [`CursorStyle::Default`],
1022    /// which returns the cursor the terminal's user configured; keeping the
1023    /// one this session found is yours to do, because only you know when you
1024    /// asked.
1025    ///
1026    /// A terminal that does not implement DECRQSS may answer
1027    /// [`SettingReport::Unrecognized`](crate::event::SettingReport::Unrecognized)
1028    /// or stay silent, so treat the reply as optional.
1029    ///
1030    /// Read the answer with [`CursorStyle::shape`] and
1031    /// [`CursorStyle::blinking`], the same terms
1032    /// [`set_cursor_style`](Self::set_cursor_style) takes.
1033    pub fn request_cursor_style(&mut self) -> io::Result<()> {
1034        crate::ansi::status::write_decrqss(&mut self.screen, " q")?;
1035        self.screen.flush()
1036    }
1037
1038    /// Request the current color scheme (`CSI ? 996 n`): whether the
1039    /// terminal's scheme is dark or light. This reports only the dark/light
1040    /// preference, not the actual colors. Reply:
1041    /// [`Event::ColorScheme`](crate::event::Event::ColorScheme).
1042    pub fn request_color_scheme(&mut self) -> io::Result<()> {
1043        self.screen
1044            .write_all(crate::ansi::status::REQUEST_LIGHT_DARK_REPORT)?;
1045        self.screen.flush()
1046    }
1047
1048    /// Set the system clipboard contents (`OSC 52 ; c`). `data` is
1049    /// base64-encoded for transport.
1050    pub fn set_system_clipboard(&mut self, data: &[u8]) -> io::Result<()> {
1051        crate::ansi::clipboard::write_set_clipboard(
1052            &mut self.screen,
1053            crate::ansi::clipboard::SYSTEM_CLIPBOARD,
1054            data,
1055        )?;
1056        self.screen.flush()
1057    }
1058
1059    /// Set the primary selection contents (`OSC 52 ; p`). `data` is
1060    /// base64-encoded for transport.
1061    pub fn set_primary_clipboard(&mut self, data: &[u8]) -> io::Result<()> {
1062        crate::ansi::clipboard::write_set_clipboard(
1063            &mut self.screen,
1064            crate::ansi::clipboard::PRIMARY_CLIPBOARD,
1065            data,
1066        )?;
1067        self.screen.flush()
1068    }
1069
1070    /// Request the system clipboard contents (`OSC 52 ; c ; ?`). Reply:
1071    /// [`Event::Clipboard`](crate::event::Event::Clipboard).
1072    pub fn request_system_clipboard(&mut self) -> io::Result<()> {
1073        crate::ansi::clipboard::write_request_clipboard(
1074            &mut self.screen,
1075            crate::ansi::clipboard::SYSTEM_CLIPBOARD,
1076        )?;
1077        self.screen.flush()
1078    }
1079
1080    /// Request the primary selection contents (`OSC 52 ; p ; ?`). Reply:
1081    /// [`Event::Clipboard`](crate::event::Event::Clipboard).
1082    pub fn request_primary_clipboard(&mut self) -> io::Result<()> {
1083        crate::ansi::clipboard::write_request_clipboard(
1084            &mut self.screen,
1085            crate::ansi::clipboard::PRIMARY_CLIPBOARD,
1086        )?;
1087        self.screen.flush()
1088    }
1089}