|
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,59 @@ 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 | +/// This command uses OSC control sequence `Pr = 5 2` (See |
| 417 | +/// [XTerm Control Sequences](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands) ) |
| 418 | +/// to copy data to the terminal host clipboard. |
| 419 | +/// |
| 420 | +/// This only works if it is enabled on the respective terminal emulator. If a terminal multiplexer |
| 421 | +/// is used, the multiplexer will likely need to support it, too. |
| 422 | +/// |
| 423 | +/// # Notes |
| 424 | +/// |
| 425 | +/// Commands must be executed/queued for execution otherwise they do nothing. |
| 426 | +#[cfg(feature = "clipboard")] |
| 427 | +#[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 428 | +pub struct CopyToClipboard<T>(pub T, pub ClipboardDestination); |
| 429 | + |
| 430 | +#[cfg(feature = "clipboard")] |
| 431 | +impl<T: AsRef<[u8]>> Command for CopyToClipboard<T> { |
| 432 | + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { |
| 433 | + write!( |
| 434 | + f, |
| 435 | + osc!("52;{destination};{encoded_text}"), |
| 436 | + destination = match self.1 { |
| 437 | + ClipboardDestination::Clipboard => 'c', |
| 438 | + ClipboardDestination::Primary => 'p', |
| 439 | + }, |
| 440 | + encoded_text = BASE64_STANDARD.encode(&self.0) |
| 441 | + ) |
| 442 | + } |
| 443 | + |
| 444 | + #[cfg(windows)] |
| 445 | + fn execute_winapi(&self) -> std::io::Result<()> { |
| 446 | + use std::io; |
| 447 | + |
| 448 | + Err(io::Error::new( |
| 449 | + io::ErrorKind::Unsupported, |
| 450 | + "Copying is not implemented for the Windows API.", |
| 451 | + )) |
| 452 | + } |
| 453 | + |
| 454 | +} |
| 455 | + |
399 | 456 | /// A command that instructs the terminal emulator to begin a synchronized frame. |
400 | 457 | /// |
401 | 458 | /// # Notes |
@@ -565,4 +622,20 @@ mod tests { |
565 | 622 | // check we're back to normal mode |
566 | 623 | assert!(!is_raw_mode_enabled().unwrap()); |
567 | 624 | } |
| 625 | + |
| 626 | + #[test] |
| 627 | + #[cfg(feature = "clipboard")] |
| 628 | + fn test_copy_string_osc52() { |
| 629 | + let mut buffer = String::new(); |
| 630 | + super::CopyToClipboard("foo", ClipboardDestination::Clipboard) |
| 631 | + .write_ansi(&mut buffer) |
| 632 | + .unwrap(); |
| 633 | + assert_eq!(buffer, "\x1b]52;c;Zm9v\x07"); |
| 634 | + |
| 635 | + buffer.clear(); |
| 636 | + super::CopyToClipboard("foo", ClipboardDestination::Primary) |
| 637 | + .write_ansi(&mut buffer) |
| 638 | + .unwrap(); |
| 639 | + assert_eq!(buffer, "\x1b]52;p;Zm9v\x07"); |
| 640 | + } |
568 | 641 | } |
0 commit comments