mirror of
https://github.com/wez/wezterm.git
synced 2024-12-23 21:32:13 +03:00
allow capturing the clipboard impl from the terminal host
This should allow asynchronous access to the clipboard, which in turn will allow the server to send the clipboard to the client unilaterally.
This commit is contained in:
parent
0b9c953446
commit
e993e5a625
58
src/frontend/guicommon/clipboard.rs
Normal file
58
src/frontend/guicommon/clipboard.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use clipboard::{ClipboardContext, ClipboardProvider};
|
||||
use failure::{format_err, Fallible};
|
||||
use std::sync::Mutex;
|
||||
use term::terminal::Clipboard;
|
||||
|
||||
pub struct SystemClipboard {
|
||||
inner: Mutex<Inner>,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
/// macOS gets unhappy if we set up the clipboard too early,
|
||||
/// so we use an Option to defer it until we use it
|
||||
clipboard: Option<ClipboardContext>,
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
fn new() -> Self {
|
||||
Self { clipboard: None }
|
||||
}
|
||||
|
||||
fn clipboard(&mut self) -> Fallible<&mut ClipboardContext> {
|
||||
if self.clipboard.is_none() {
|
||||
self.clipboard = Some(ClipboardContext::new().map_err(|e| format_err!("{}", e))?);
|
||||
}
|
||||
Ok(self.clipboard.as_mut().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl SystemClipboard {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: Mutex::new(Inner::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clipboard for SystemClipboard {
|
||||
fn get_contents(&self) -> Fallible<String> {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
inner
|
||||
.clipboard()?
|
||||
.get_contents()
|
||||
.map_err(|e| format_err!("{}", e))
|
||||
}
|
||||
|
||||
fn set_contents(&self, data: Option<String>) -> Fallible<()> {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
let clip = inner.clipboard()?;
|
||||
clip.set_contents(data.unwrap_or_else(|| "".into()))
|
||||
.map_err(|e| format_err!("{}", e))?;
|
||||
// Request the clipboard contents we just set; on some systems
|
||||
// if we copy and paste in wezterm, the clipboard isn't visible
|
||||
// to us again until the second call to get_clipboard.
|
||||
clip.get_contents()
|
||||
.map(|_| ())
|
||||
.map_err(|e| format_err!("{}", e))
|
||||
}
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
use super::window::TerminalWindow;
|
||||
use crate::font::{FontConfiguration, FontSystemSelection};
|
||||
use crate::frontend::guicommon::clipboard::SystemClipboard;
|
||||
use crate::frontend::guicommon::window::SpawnTabDomain;
|
||||
use crate::frontend::{front_end, gui_executor};
|
||||
use crate::mux::tab::{Tab, TabId};
|
||||
use crate::mux::Mux;
|
||||
use clipboard::{ClipboardContext, ClipboardProvider};
|
||||
use failure::Error;
|
||||
use failure::Fallible;
|
||||
use failure::{format_err, Error};
|
||||
use log::error;
|
||||
use portable_pty::PtySize;
|
||||
use promise::Future;
|
||||
@ -14,6 +14,7 @@ use std::collections::HashMap;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use term::terminal::Clipboard;
|
||||
use term::{KeyCode, KeyModifiers};
|
||||
use termwiz::hyperlink::Hyperlink;
|
||||
|
||||
@ -49,9 +50,7 @@ pub trait HostHelper {
|
||||
|
||||
pub struct HostImpl<H: HostHelper> {
|
||||
helper: H,
|
||||
/// macOS gets unhappy if we set up the clipboard too early,
|
||||
/// so we use an Option to defer it until we use it
|
||||
clipboard: Option<ClipboardContext>,
|
||||
clipboard: Arc<Clipboard>,
|
||||
keys: KeyMap,
|
||||
}
|
||||
|
||||
@ -174,32 +173,13 @@ impl<H: HostHelper> HostImpl<H> {
|
||||
pub fn new(helper: H) -> Self {
|
||||
Self {
|
||||
helper,
|
||||
clipboard: None,
|
||||
clipboard: Arc::new(SystemClipboard::new()),
|
||||
keys: key_bindings(),
|
||||
}
|
||||
}
|
||||
|
||||
fn clipboard(&mut self) -> Result<&mut ClipboardContext, Error> {
|
||||
if self.clipboard.is_none() {
|
||||
self.clipboard = Some(ClipboardContext::new().map_err(|e| format_err!("{}", e))?);
|
||||
}
|
||||
Ok(self.clipboard.as_mut().unwrap())
|
||||
}
|
||||
|
||||
pub fn get_clipboard(&mut self) -> Result<String, Error> {
|
||||
self.clipboard()?
|
||||
.get_contents()
|
||||
.map_err(|e| format_err!("{}", e))
|
||||
}
|
||||
|
||||
pub fn set_clipboard(&mut self, clip: Option<String>) -> Result<(), Error> {
|
||||
self.clipboard()?
|
||||
.set_contents(clip.unwrap_or_else(|| "".into()))
|
||||
.map_err(|e| format_err!("{}", e))?;
|
||||
// Request the clipboard contents we just set; on some systems
|
||||
// if we copy and paste in wezterm, the clipboard isn't visible
|
||||
// to us again until the second call to get_clipboard.
|
||||
self.get_clipboard().map(|_| ())
|
||||
pub fn get_clipboard(&mut self) -> Fallible<Arc<Clipboard>> {
|
||||
Ok(Arc::clone(&self.clipboard))
|
||||
}
|
||||
|
||||
pub fn spawn_new_window(&mut self) {
|
||||
@ -238,7 +218,7 @@ impl<H: HostHelper> HostImpl<H> {
|
||||
// Nominally copy, but that is implicit, so NOP
|
||||
}
|
||||
Paste => {
|
||||
let text = self.get_clipboard()?;
|
||||
let text = self.get_clipboard()?.get_contents()?;
|
||||
if text.len() <= PASTE_CHUNK_SIZE {
|
||||
// Send it all now
|
||||
tab.send_paste(&text)?;
|
||||
@ -375,14 +355,10 @@ impl<'a, H: HostHelper> term::TerminalHost for TabHost<'a, H> {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_clipboard(&mut self) -> Result<String, Error> {
|
||||
fn get_clipboard(&mut self) -> Fallible<Arc<Clipboard>> {
|
||||
self.host.get_clipboard()
|
||||
}
|
||||
|
||||
fn set_clipboard(&mut self, clip: Option<String>) -> Result<(), Error> {
|
||||
self.host.set_clipboard(clip)
|
||||
}
|
||||
|
||||
fn set_title(&mut self, _title: &str) {
|
||||
self.host.with_window(move |win| {
|
||||
win.update_title();
|
||||
|
@ -1,3 +1,4 @@
|
||||
pub mod clipboard;
|
||||
pub mod host;
|
||||
pub mod localtab;
|
||||
pub mod window;
|
||||
|
@ -4,9 +4,9 @@ use crate::mux::tab::{Tab, TabId};
|
||||
use crate::mux::window::{Window, WindowId};
|
||||
use crate::server::pollable::{pollable_channel, PollableReceiver, PollableSender};
|
||||
use domain::{Domain, DomainId};
|
||||
use failure::{format_err, Error, Fallible};
|
||||
use failure::{bail, format_err, Error, Fallible};
|
||||
use failure_derive::*;
|
||||
use log::{debug, error, warn};
|
||||
use log::{debug, error};
|
||||
use portable_pty::ExitStatus;
|
||||
use promise::{Executor, Future};
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
@ -16,6 +16,7 @@ use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use term::terminal::Clipboard;
|
||||
use term::TerminalHost;
|
||||
use termwiz::hyperlink::Hyperlink;
|
||||
|
||||
@ -101,13 +102,8 @@ impl<'a> TerminalHost for Host<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_clipboard(&mut self) -> Result<String, Error> {
|
||||
warn!("peer requested clipboard; ignoring");
|
||||
Ok("".into())
|
||||
}
|
||||
|
||||
fn set_clipboard(&mut self, _clip: Option<String>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
fn get_clipboard(&mut self) -> Fallible<Arc<Clipboard>> {
|
||||
bail!("peer requested clipboard; ignoring");
|
||||
}
|
||||
|
||||
fn set_title(&mut self, _title: &str) {}
|
||||
|
@ -8,7 +8,7 @@ use crossbeam_channel::TryRecvError;
|
||||
use failure::{bail, err_msg, format_err, Error, Fallible};
|
||||
#[cfg(unix)]
|
||||
use libc::{mode_t, umask};
|
||||
use log::{debug, error, warn};
|
||||
use log::{debug, error};
|
||||
use native_tls::{Identity, TlsAcceptor};
|
||||
use promise::{Executor, Future};
|
||||
use std::collections::HashMap;
|
||||
@ -23,6 +23,7 @@ use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
use std::time::Instant;
|
||||
use term::terminal::Clipboard;
|
||||
use termwiz::surface::{Change, Position, SequenceNo, Surface};
|
||||
|
||||
struct LocalListener {
|
||||
@ -312,16 +313,8 @@ impl<'a> term::TerminalHost for BufferedTerminalHost<'a> {
|
||||
error!("ignoring url open of {:?}", link.uri());
|
||||
}
|
||||
|
||||
fn get_clipboard(&mut self) -> Result<String, Error> {
|
||||
warn!("peer requested clipboard; ignoring");
|
||||
Ok("".into())
|
||||
}
|
||||
|
||||
fn set_clipboard(&mut self, clip: Option<String>) -> Result<(), Error> {
|
||||
if let Some(clip) = clip {
|
||||
self.clipboard.replace(clip);
|
||||
}
|
||||
Ok(())
|
||||
fn get_clipboard(&mut self) -> Fallible<Arc<Clipboard>> {
|
||||
bail!("peer requested clipboard; ignoring");
|
||||
}
|
||||
|
||||
fn set_title(&mut self, title: &str) {
|
||||
|
@ -1,8 +1,14 @@
|
||||
use super::*;
|
||||
use failure::Fallible;
|
||||
use std::sync::Arc;
|
||||
use termwiz::escape::parser::Parser;
|
||||
use termwiz::hyperlink::Rule as HyperlinkRule;
|
||||
|
||||
pub trait Clipboard {
|
||||
fn get_contents(&self) -> Fallible<String>;
|
||||
fn set_contents(&self, data: Option<String>) -> Fallible<()>;
|
||||
}
|
||||
|
||||
/// Represents the host of the terminal.
|
||||
/// Provides a means for sending data to the connected pty,
|
||||
/// and for operating on the clipboard
|
||||
@ -11,11 +17,8 @@ pub trait TerminalHost {
|
||||
/// slave end of the associated pty.
|
||||
fn writer(&mut self) -> &mut std::io::Write;
|
||||
|
||||
/// Returns the current clipboard contents
|
||||
fn get_clipboard(&mut self) -> Result<String, Error>;
|
||||
|
||||
/// Adjust the contents of the clipboard
|
||||
fn set_clipboard(&mut self, clip: Option<String>) -> Result<(), Error>;
|
||||
/// Returns the clipboard manager
|
||||
fn get_clipboard(&mut self) -> Fallible<Arc<Clipboard>>;
|
||||
|
||||
/// Change the title of the window
|
||||
fn set_title(&mut self, title: &str);
|
||||
|
@ -429,7 +429,7 @@ impl TerminalState {
|
||||
y: event.y as ScrollbackOrVisibleRowIndex
|
||||
- self.viewport_offset as ScrollbackOrVisibleRowIndex,
|
||||
});
|
||||
host.set_clipboard(None)
|
||||
host.get_clipboard()?.set_contents(None)
|
||||
}
|
||||
|
||||
/// Double click to select a word on the current line
|
||||
@ -506,7 +506,7 @@ impl TerminalState {
|
||||
"finish 2click selection {:?} '{}'",
|
||||
self.selection_range, text
|
||||
);
|
||||
host.set_clipboard(Some(text))
|
||||
host.get_clipboard()?.set_contents(Some(text))
|
||||
}
|
||||
|
||||
/// triple click to select the current line
|
||||
@ -531,7 +531,7 @@ impl TerminalState {
|
||||
"finish 3click selection {:?} '{}'",
|
||||
self.selection_range, text
|
||||
);
|
||||
host.set_clipboard(Some(text))
|
||||
host.get_clipboard()?.set_contents(Some(text))
|
||||
}
|
||||
|
||||
fn mouse_press_left(
|
||||
@ -555,7 +555,7 @@ impl TerminalState {
|
||||
_ => {
|
||||
self.selection_range = None;
|
||||
self.selection_start = None;
|
||||
host.set_clipboard(None)?;
|
||||
host.get_clipboard()?.set_contents(None)?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -578,7 +578,7 @@ impl TerminalState {
|
||||
"finish drag selection {:?} '{}'",
|
||||
self.selection_range, text
|
||||
);
|
||||
host.set_clipboard(Some(text))?;
|
||||
host.get_clipboard()?.set_contents(Some(text))?;
|
||||
} else if let Some(link) = self.current_highlight() {
|
||||
// If the button release wasn't a drag, consider
|
||||
// whether it was a click on a hyperlink
|
||||
@ -650,7 +650,7 @@ impl TerminalState {
|
||||
format!("\x1b[<{};{};{}M", button, event.x + 1, event.y + 1).as_bytes(),
|
||||
)?;
|
||||
} else if event.button == MouseButton::Middle {
|
||||
let clip = host.get_clipboard()?;
|
||||
let clip = host.get_clipboard()?.get_contents()?;
|
||||
self.send_paste(&clip, host.writer())?
|
||||
}
|
||||
}
|
||||
@ -2077,13 +2077,19 @@ impl<'a> Performer<'a> {
|
||||
}
|
||||
|
||||
OperatingSystemCommand::ClearSelection(_) => {
|
||||
self.host.set_clipboard(None).ok();
|
||||
if let Ok(clip) = self.host.get_clipboard() {
|
||||
clip.set_contents(None).ok();
|
||||
}
|
||||
}
|
||||
OperatingSystemCommand::QuerySelection(_) => {}
|
||||
OperatingSystemCommand::SetSelection(_, selection_data) => {
|
||||
match self.host.set_clipboard(Some(selection_data)) {
|
||||
if let Ok(clip) = self.host.get_clipboard() {
|
||||
match clip.set_contents(Some(selection_data)) {
|
||||
Ok(_) => (),
|
||||
Err(err) => error!("failed to set clipboard in response to OSC 52: {:?}", err),
|
||||
Err(err) => {
|
||||
error!("failed to set clipboard in response to OSC 52: {:?}", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
OperatingSystemCommand::ITermProprietary(iterm) => match iterm {
|
||||
|
Loading…
Reference in New Issue
Block a user