|
85 | 85 |
|
86 | 86 | use std::{fmt, io}; |
87 | 87 |
|
| 88 | +#[cfg(feature = "clipboard")] |
| 89 | +use base64::prelude::{Engine, BASE64_STANDARD}; |
88 | 90 | #[cfg(windows)] |
89 | 91 | use crossterm_winapi::{ConsoleMode, Handle, ScreenBuffer}; |
90 | 92 | #[cfg(feature = "serde")] |
91 | 93 | use serde::{Deserialize, Serialize}; |
92 | 94 | #[cfg(windows)] |
93 | 95 | use winapi::um::wincon::ENABLE_WRAP_AT_EOL_OUTPUT; |
94 | 96 |
|
| 97 | +#[cfg(feature = "clipboard")] |
| 98 | +use crate::osc; |
95 | 99 | #[doc(no_inline)] |
96 | 100 | use crate::Command; |
97 | 101 | use crate::{csi, impl_display}; |
@@ -396,6 +400,63 @@ impl<T: fmt::Display> Command for SetTitle<T> { |
396 | 400 | } |
397 | 401 | } |
398 | 402 |
|
| 403 | +/// Different clipboard classes |
| 404 | +#[cfg(feature = "clipboard")] |
| 405 | +#[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 406 | +pub enum ClipboardDestination { |
| 407 | + /// Default clipboard when using Ctrl+C or Ctrl+V |
| 408 | + Clipboard, |
| 409 | + |
| 410 | + /// Clipboard on Linux/X/Wayland when using selection and middle mouse button |
| 411 | + Primary, |
| 412 | +} |
| 413 | + |
| 414 | +/// A command that copies to clipboard |
| 415 | +/// |
| 416 | +/// # Notes |
| 417 | +/// |
| 418 | +/// This command uses OSC control sequence `Pr = 5 2` (See |
| 419 | +/// [XTerm Control Sequences](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands) ) |
| 420 | +/// to copy data to the terminal host clipboard. |
| 421 | +/// |
| 422 | +/// This only works if it is enabled on the respective terminal emulator. If a terminal multiplexer |
| 423 | +/// is used, the multiplexer will likely need to support it, too. |
| 424 | +/// |
| 425 | +/// Commands must be executed/queued for execution otherwise they do nothing. |
| 426 | +/// |
| 427 | +/// # Examples |
| 428 | +/// |
| 429 | +/// See examples/copy.rs for a working example. |
| 430 | +#[cfg(feature = "clipboard")] |
| 431 | +#[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 432 | +pub struct CopyToClipboard<T>(pub T, pub ClipboardDestination); |
| 433 | + |
| 434 | +#[cfg(feature = "clipboard")] |
| 435 | +impl<T: AsRef<[u8]>> Command for CopyToClipboard<T> { |
| 436 | + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { |
| 437 | + write!( |
| 438 | + f, |
| 439 | + osc!("52;{destination};{encoded_text}"), |
| 440 | + destination = match self.1 { |
| 441 | + ClipboardDestination::Clipboard => 'c', |
| 442 | + ClipboardDestination::Primary => 'p', |
| 443 | + }, |
| 444 | + encoded_text = BASE64_STANDARD.encode(&self.0) |
| 445 | + ) |
| 446 | + } |
| 447 | + |
| 448 | + #[cfg(windows)] |
| 449 | + fn execute_winapi(&self) -> std::io::Result<()> { |
| 450 | + use std::io; |
| 451 | + |
| 452 | + Err(io::Error::new( |
| 453 | + io::ErrorKind::Unsupported, |
| 454 | + "Copying is not implemented for the Windows API.", |
| 455 | + )) |
| 456 | + } |
| 457 | + |
| 458 | +} |
| 459 | + |
399 | 460 | /// A command that instructs the terminal emulator to begin a synchronized frame. |
400 | 461 | /// |
401 | 462 | /// # Notes |
@@ -565,4 +626,20 @@ mod tests { |
565 | 626 | // check we're back to normal mode |
566 | 627 | assert!(!is_raw_mode_enabled().unwrap()); |
567 | 628 | } |
| 629 | + |
| 630 | + #[test] |
| 631 | + #[cfg(feature = "clipboard")] |
| 632 | + fn test_copy_string_osc52() { |
| 633 | + let mut buffer = String::new(); |
| 634 | + super::CopyToClipboard("foo", ClipboardDestination::Clipboard) |
| 635 | + .write_ansi(&mut buffer) |
| 636 | + .unwrap(); |
| 637 | + assert_eq!(buffer, "\x1b]52;c;Zm9v\x07"); |
| 638 | + |
| 639 | + buffer.clear(); |
| 640 | + super::CopyToClipboard("foo", ClipboardDestination::Primary) |
| 641 | + .write_ansi(&mut buffer) |
| 642 | + .unwrap(); |
| 643 | + assert_eq!(buffer, "\x1b]52;p;Zm9v\x07"); |
| 644 | + } |
568 | 645 | } |
0 commit comments