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}