1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-10 06:34:17 +03:00

use the clipboard crate

This removes some tricksy code in favor of a third party crate,
which unlocks the clipboard on windows.  I'm not sure how well
this will work on the pure x11 impl yet.
This commit is contained in:
Wez Furlong 2019-02-17 18:48:18 -08:00
parent 3a4673f22c
commit d69c718a73
11 changed files with 35 additions and 623 deletions

View File

@ -28,6 +28,7 @@ addons:
- binutils-dev
- cmake
- gcc
- xorg-dev
- libcurl4-openssl-dev
- libdw-dev
- libegl1-mesa-dev

View File

@ -20,6 +20,7 @@ unicode-width = "~0.1"
directories = "~1.0"
font-loader = "0.8"
rusttype = "0.7"
clipboard = "0.2"
[target.'cfg(unix)'.dependencies]
harfbuzz-sys = "~0.2"

View File

@ -19,6 +19,7 @@ case `lsb_release -ds` in
Ubuntu*|Debian*)
apt-get install -y \
cmake \
xorg-dev \
bsdutils \
libxcb-icccm4-dev \
libxcb-ewmh-dev \

View File

@ -1,35 +0,0 @@
use failure::Error;
use guiloop::{GuiSender, WindowId};
mod none;
#[cfg(any(target_os = "macos", windows))]
pub use self::none::NoClipboard as Clipboard;
#[cfg(all(unix, not(target_os = "macos")))]
mod x11;
#[cfg(all(unix, not(target_os = "macos")))]
pub use self::x11::Clipboard;
/// A fragment of the clipboard data received from another
/// app during paste.
#[derive(Debug)]
pub enum Paste {
/// The whole content of the paste is available
All(String),
/// Someone else now owns the selection. You should
/// clear the selection locally.
Cleared,
/// The clipboard window has initialized successfully
Running,
}
/// Abstracts away system specific clipboard implementation details.
pub trait ClipboardImpl {
fn new(wakeup: GuiSender<WindowId>, window_id: WindowId) -> Result<Self, Error>
where
Self: Sized;
fn set_clipboard(&self, text: Option<String>) -> Result<(), Error>;
fn get_clipboard(&self) -> Result<String, Error>;
fn try_get_paste(&self) -> Result<Option<Paste>, Error>;
}

View File

@ -1,30 +0,0 @@
use clipboard::{ClipboardImpl, Paste};
use failure::Error;
use guiloop::{GuiSender, WindowId};
use std::sync::mpsc::{channel, Receiver, Sender};
/// A no-op clipboard implementation
#[allow(dead_code)]
pub struct NoClipboard {
receiver: Receiver<Paste>,
sender: Sender<Paste>,
}
impl ClipboardImpl for NoClipboard {
fn new(_wakeup: GuiSender<WindowId>, _window_id: WindowId) -> Result<Self, Error> {
let (sender, receiver) = channel();
Ok(Self { sender, receiver })
}
fn set_clipboard(&self, _text: Option<String>) -> Result<(), Error> {
Ok(())
}
fn get_clipboard(&self) -> Result<String, Error> {
Ok("".into())
}
fn try_get_paste(&self) -> Result<Option<Paste>, Error> {
Ok(None)
}
}

View File

@ -1,459 +0,0 @@
//! Interface with the X11 clipboard/selection
//! Check out <https://tronche.com/gui/x/icccm/sec-2.html> for some deep and complex
//! background on what's happening in here.
use failure::{self, Error};
use guiloop::{GuiSender, WindowId};
use mio::unix::EventedFd;
use mio::{Events, Poll, PollOpt, Ready, Token};
use mio_extras::channel::{channel as mio_channel, Receiver as MioReceiver, Sender as MioSender};
use std::os::unix::io::AsRawFd;
use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError};
use std::thread::{self, JoinHandle};
use std::time::Duration;
use xcb;
use xcb_util;
use clipboard::{ClipboardImpl, Paste};
#[derive(Debug, Fail)]
#[fail(display = "ClipTerminated")]
struct ClipTerminated;
#[derive(Debug)]
enum ClipRequest {
/// tell the system that we want to set (or clear) the
/// selection to the supplied parameter.
SetClipboard(Option<String>),
/// Ask the system to send us the clipboard contents
RequestClipboard,
/// Ask the clipboard manager to shutdown
Terminate,
}
struct Inner {
/// if we own the clipboard, here's its string content
owned: Option<String>,
receiver: MioReceiver<ClipRequest>,
sender: Sender<Paste>,
conn: xcb::Connection,
window_id: xcb::xproto::Window,
atom_utf8_string: xcb::Atom,
atom_xsel_data: xcb::Atom,
atom_targets: xcb::Atom,
atom_clipboard: xcb::Atom,
wakeup: GuiSender<WindowId>,
wakeup_window_id: WindowId,
}
impl Inner {
fn new(
receiver: MioReceiver<ClipRequest>,
sender: Sender<Paste>,
wakeup: GuiSender<WindowId>,
wakeup_window_id: WindowId,
) -> Result<Self, Error> {
let (conn, screen) = xcb::Connection::connect(None)?;
let atom_utf8_string = xcb::intern_atom(&conn, false, "UTF8_STRING")
.get_reply()?
.atom();
let atom_xsel_data = xcb::intern_atom(&conn, false, "XSEL_DATA")
.get_reply()?
.atom();
let atom_targets = xcb::intern_atom(&conn, false, "TARGETS")
.get_reply()?
.atom();
let atom_clipboard = xcb::intern_atom(&conn, false, "CLIPBOARD")
.get_reply()?
.atom();
let window_id = conn.generate_id();
{
let setup = conn.get_setup();
let screen = setup
.roots()
.nth(screen as usize)
.ok_or_else(|| failure::err_msg("no screen?"))?;
xcb::create_window_checked(
&conn,
xcb::COPY_FROM_PARENT as u8,
window_id,
screen.root(),
// x, y
0,
0,
// width, height
1,
1,
// border width
0,
xcb::WINDOW_CLASS_INPUT_ONLY as u16,
screen.root_visual(),
&[(xcb::CW_EVENT_MASK, 0)],
)
.request_check()?;
}
Ok(Inner {
conn,
owned: None,
receiver,
sender,
window_id,
atom_utf8_string,
atom_xsel_data,
atom_targets,
atom_clipboard,
wakeup,
wakeup_window_id,
})
}
fn send(&mut self, packet: Paste) -> Result<(), Error> {
match self.sender.send(packet) {
Ok(_) => {
self.wakeup.send(self.wakeup_window_id)?;
Ok(())
}
Err(err) => {
self.wakeup.send(self.wakeup_window_id)?;
bail!("clipboard: error sending to channel: {:?}", err);
}
}
}
/// Inform X that we either own the selection or that we no longer own the
/// selection.
fn update_owner(&self) {
let owner = if self.owned.is_some() {
self.window_id
} else {
xcb::NONE
};
xcb::set_selection_owner(&self.conn, owner, xcb::ATOM_PRIMARY, xcb::CURRENT_TIME);
// Also set the CLIPBOARD atom, not just the PRIMARY selection.
// TODO: make xterm clipboard selection configurable
xcb::set_selection_owner(&self.conn, owner, self.atom_clipboard, xcb::CURRENT_TIME);
}
fn selection_clear(&mut self) -> Result<(), Error> {
// Someone else now owns the selection
debug!("SELECTION_CLEAR received");
self.owned = None;
self.send(Paste::Cleared)
}
fn selection_notify(&mut self, selection: &xcb::SelectionNotifyEvent) -> Result<(), Error> {
debug!("SELECTION_NOTIFY received");
if (selection.selection() == xcb::ATOM_PRIMARY
|| selection.selection() == self.atom_clipboard)
&& selection.property() != xcb::NONE
{
match xcb_util::icccm::get_text_property(
&self.conn,
selection.requestor(),
selection.property(),
)
.get_reply()
{
Ok(prop) => self.send(Paste::All(prop.name().into())),
Err(err) => {
eprintln!("clipboard: err while getting clipboard property: {:?}", err);
self.send(Paste::All("".into()))
}
}
} else {
self.send(Paste::All("".into()))
}
}
fn selection_request(&mut self, request: &xcb::SelectionRequestEvent) -> Result<(), Error> {
// Someone is asking for our selected text
debug!(
"SEL: time={} owner={} requestor={} selection={} target={} property={}",
request.time(),
request.owner(),
request.requestor(),
request.selection(),
request.target(),
request.property()
);
debug!(
"XSEL={}, UTF8={} PRIMARY={} clip={}",
self.atom_xsel_data,
self.atom_utf8_string,
xcb::ATOM_PRIMARY,
self.atom_clipboard,
);
// I'd like to use `match` here, but the atom values are not
// known at compile time so we have to `if` like a caveman :-p
let selprop = if request.target() == self.atom_targets {
// They want to know which targets we support
debug!("responding with targets list");
let atoms: [u32; 1] = [self.atom_utf8_string];
xcb::xproto::change_property(
&self.conn,
xcb::xproto::PROP_MODE_REPLACE as u8,
request.requestor(),
request.property(),
xcb::xproto::ATOM_ATOM,
32, /* 32-bit atom value */
&atoms,
);
// let the requestor know that we set their property
request.property()
} else if request.target() == self.atom_utf8_string
|| request.target() == xcb::xproto::ATOM_STRING
{
// We'll accept requests for UTF-8 or STRING data.
// We don't and won't do any conversion from UTF-8 to
// whatever STRING represents; let's just assume that
// the other end is going to handle it correctly.
if let Some(ref text) = self.owned {
debug!("going to respond with clip text because we own it");
xcb::xproto::change_property(
&self.conn,
xcb::xproto::PROP_MODE_REPLACE as u8,
request.requestor(),
request.property(),
request.target(),
8, /* 8-bit string data */
text.as_bytes(),
);
// let the requestor know that we set their property
request.property()
} else {
debug!("we don't own the clipboard");
// We have no clipboard so there is nothing to report
xcb::NONE
}
} else {
debug!("don't know what to do");
// We didn't support their request, so there is nothing
// we can report back to them.
xcb::NONE
};
debug!("responding with selprop={}", selprop);
xcb::xproto::send_event(
&self.conn,
true,
request.requestor(),
0,
&xcb::xproto::SelectionNotifyEvent::new(
request.time(),
request.requestor(),
request.selection(),
request.target(),
selprop, // the disposition from the operation above
),
);
Ok(())
}
fn process_xcb_event(&mut self, event: &xcb::GenericEvent) -> Result<(), Error> {
match event.response_type() & 0x7f {
xcb::SELECTION_CLEAR => self.selection_clear(),
xcb::SELECTION_NOTIFY => self.selection_notify(unsafe { xcb::cast_event(event) }),
xcb::SELECTION_REQUEST => self.selection_request(unsafe { xcb::cast_event(event) }),
xcb::MAPPING_NOTIFY => {
// Nothing to do here; just don't want to print an error
// in the case below.
Ok(())
}
_ => {
eprintln!(
"clipboard: got unhandled XCB event type {}",
event.response_type() & 0x7f
);
// Don't bail here; we just want to log the event and carry on.
Ok(())
}
}
}
fn process_queued_xcb(&mut self) -> Result<(), Error> {
match self.conn.poll_for_event() {
None => match self.conn.has_error() {
Ok(_) => (),
Err(err) => {
bail!("clipboard window connection is broken: {:?}", err);
}
},
Some(event) => match self.process_xcb_event(&event) {
Ok(_) => (),
Err(err) => return Err(err),
},
}
self.conn.flush();
loop {
match self.conn.poll_for_queued_event() {
None => return Ok(()),
Some(event) => self.process_xcb_event(&event)?,
}
self.conn.flush();
}
}
fn process_receiver(&mut self) -> Result<(), Error> {
match self.receiver.try_recv() {
Err(TryRecvError::Empty) => Ok(()),
Err(err) => bail!("clipboard: Error while reading channel: {:?}", err),
Ok(request) => self.process_one_cliprequest(request),
}
}
fn process_one_cliprequest(&mut self, request: ClipRequest) -> Result<(), Error> {
match request {
ClipRequest::Terminate => return Err(ClipTerminated.into()),
ClipRequest::SetClipboard(clip) => {
self.owned = clip;
self.update_owner();
}
ClipRequest::RequestClipboard => {
// Find the owner and ask them to send us the buffer
xcb::convert_selection(
&self.conn,
self.window_id,
xcb::ATOM_PRIMARY,
self.atom_utf8_string,
self.atom_xsel_data,
xcb::CURRENT_TIME,
);
}
}
Ok(())
}
/// Waits for events from either the X server or the main thread of the
/// application.
fn clip_thread(&mut self) {
let poll = Poll::new().expect("mio Poll failed to init");
poll.register(
&EventedFd(&self.conn.as_raw_fd()),
Token(0),
Ready::readable(),
PollOpt::level(),
)
.expect("failed to register xcb conn for clipboard with mio");
poll.register(
&self.receiver,
Token(1),
Ready::readable(),
PollOpt::level(),
)
.expect("failed to register receiver for clipboard with mio");
let mut events = Events::with_capacity(2);
self.sender
.send(Paste::Running)
.expect("failed to send Running notice");
loop {
match poll.poll(&mut events, None) {
Ok(_) => {
for event in &events {
if event.token() == Token(0) {
if let Err(err) = self.process_queued_xcb() {
eprintln!("clipboard: {:?}", err);
return;
}
}
if event.token() == Token(1) {
if let Err(err) = self.process_receiver() {
// No need to print the error trace if we were shutdown gracefully
match err.downcast_ref::<ClipTerminated>() {
Some(_) => return,
_ => {
eprintln!("clipboard: {:?}", err);
return;
}
}
}
self.conn.flush();
}
}
}
Err(err) => {
eprintln!("clipboard: {:?}", err);
return;
}
}
}
}
}
/// A clipboard client allows getting or setting the clipboard.
pub struct Clipboard {
sender: MioSender<ClipRequest>,
receiver: Receiver<Paste>,
clip_thread: Option<JoinHandle<()>>,
}
impl ClipboardImpl for Clipboard {
/// Create a new clipboard instance. `ping` is
fn new(wakeup: GuiSender<WindowId>, window_id: WindowId) -> Result<Self, Error> {
let (sender_clip, receiver_clip) = mio_channel();
let (sender_paste, receiver_paste) = channel();
let clip_thread = thread::spawn(move || {
match Inner::new(receiver_clip, sender_paste, wakeup, window_id) {
Ok(mut clip) => clip.clip_thread(),
Err(err) => eprintln!("failed to init clipboard window: {:?}", err),
}
});
// Make sure that it started up ok
match receiver_paste.recv_timeout(Duration::from_secs(10)) {
Ok(Paste::Running) => {}
other => bail!("failed to init clipboard window: {:?}", other),
};
Ok(Self {
sender: sender_clip,
receiver: receiver_paste,
clip_thread: Some(clip_thread),
})
}
/// Tell X that we own the selection and its contents are `text`
fn set_clipboard(&self, text: Option<String>) -> Result<(), Error> {
self.sender.send(ClipRequest::SetClipboard(text))?;
Ok(())
}
/// Blocks until the clipboard contents have been retrieved
fn get_clipboard(&self) -> Result<String, Error> {
self.sender.send(ClipRequest::RequestClipboard)?;
match self.receiver.recv_timeout(Duration::from_secs(10)) {
Ok(Paste::All(result)) => Ok(result),
Ok(Paste::Cleared) => Ok("".into()),
other => bail!("unexpected result while waiting for paste: {:?}", other),
}
}
fn try_get_paste(&self) -> Result<Option<Paste>, Error> {
match self.receiver.try_recv() {
Err(TryRecvError::Empty) => Ok(None),
Err(err) => bail!("{:?}", err),
Ok(paste) => Ok(Some(paste)),
}
}
}
impl Drop for Clipboard {
fn drop(&mut self) {
self.sender.send(ClipRequest::Terminate).ok();
if let Some(thread) = self.clip_thread.take() {
thread.join().ok();
}
}
}

View File

@ -2,7 +2,7 @@
use super::MasterPty;
use super::{Child, Command};
use clipboard::{Clipboard, ClipboardImpl, Paste};
use clipboard::ClipboardContext;
use config::Config;
use failure::Error;
use font::FontConfiguration;
@ -22,7 +22,7 @@ use termwiz::hyperlink::Hyperlink;
struct Host {
display: glium::Display,
pty: MasterPty,
clipboard: Clipboard,
clipboard: ClipboardContext,
window_position: Option<(i32, i32)>,
/// is is_some, holds position to be restored after exiting
/// fullscreen mode.
@ -44,12 +44,21 @@ impl term::TerminalHost for Host {
}
fn get_clipboard(&mut self) -> Result<String, Error> {
self.clipboard.get_clipboard()
self.clipboard
.get_contents()
.map_err(|e| format_err!("{}", e))
}
fn set_clipboard(&mut self, clip: Option<String>) -> Result<(), Error> {
self.clipboard.set_clipboard(clip)
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.
.and_then(|_| self.get_clipboard().map(|_| ()))
}
fn set_title(&mut self, title: &str) {
self.display.gl_window().set_title(title);
}
@ -142,13 +151,12 @@ impl TerminalWindow {
glium::Display::new(window, pref_context, &*mut_loop)
.map_err(|e| format_err!("{:?}", e))?
};
let window_id = display.gl_window().id();
let window_position = display.gl_window().get_position();
let host = Host {
display,
pty,
clipboard: Clipboard::new(event_loop.paster.clone(), window_id)?,
clipboard: ClipboardContext::new().map_err(|e| format_err!("{}", e))?,
window_position,
is_fullscreen: None,
};
@ -581,18 +589,6 @@ impl TerminalWindow {
Ok(())
}
pub fn process_clipboard(&mut self) -> Result<(), Error> {
match self.host.clipboard.try_get_paste() {
Ok(Some(Paste::Cleared)) => {
self.terminal.clear_selection();
}
Ok(_) => {}
Err(err) => bail!("clipboard thread died? {:?}", err),
}
self.paint_if_needed()?;
Ok(())
}
pub fn test_for_child_exit(&mut self) -> Result<(), SessionTerminated> {
match self.process.try_wait() {
Ok(Some(status)) => Err(SessionTerminated::ProcessStatus { status }),

View File

@ -63,8 +63,6 @@ pub struct GuiEventLoop {
pub core: futurecore::Core,
poll_tx: GuiSender<IOEvent>,
poll_rx: Receiver<IOEvent>,
pub paster: GuiSender<WindowId>,
paster_rx: Receiver<WindowId>,
#[cfg(unix)]
sigchld_rx: Receiver<()>,
tick_rx: Receiver<()>,
@ -78,7 +76,6 @@ impl GuiEventLoop {
let core = futurecore::Core::new(fut_tx, fut_rx);
let (poll_tx, poll_rx) = channel(event_loop.create_proxy());
let (paster, paster_rx) = channel(event_loop.create_proxy());
#[cfg(unix)]
let (sigchld_tx, sigchld_rx) = channel(event_loop.create_proxy());
@ -102,8 +99,6 @@ impl GuiEventLoop {
core,
poll_tx,
poll_rx,
paster,
paster_rx,
tick_rx,
#[cfg(unix)]
sigchld_rx,
@ -225,23 +220,6 @@ impl GuiEventLoop {
}
}
/// Process paste notifications and route them to their owning windows.
fn process_paste(&self) -> Result<(), Error> {
loop {
match self.paster_rx.try_recv() {
Ok(window_id) => {
self.windows
.borrow_mut()
.by_id
.get_mut(&window_id)
.map(|w| w.process_clipboard());
}
Err(TryRecvError::Empty) => return Ok(()),
Err(err) => bail!("paster_rx disconnected {:?}", err),
}
}
}
fn process_tick(&self) -> Result<(), Error> {
loop {
match self.tick_rx.try_recv() {
@ -266,7 +244,7 @@ impl GuiEventLoop {
self.test_for_child_exit();
}
Err(TryRecvError::Empty) => return Ok(()),
Err(err) => bail!("paster_rx disconnected {:?}", err),
Err(err) => bail!("sigchld_rx disconnected {:?}", err),
}
}
}
@ -342,7 +320,6 @@ impl GuiEventLoop {
self.run_event_loop()?;
self.process_poll()?;
self.process_paste()?;
#[cfg(unix)]
self.process_sigchld()?;
self.process_tick()?;

View File

@ -59,8 +59,6 @@ struct Windows {
pub struct GuiEventLoop {
poll: Poll,
pub conn: Rc<Connection>,
pub paster: GuiSender<WindowId>,
paster_rx: GuiReceiver<WindowId>,
sigchld_rx: GuiReceiver<()>,
pub core: futurecore::Core,
windows: Rc<RefCell<Windows>>,
@ -68,7 +66,6 @@ pub struct GuiEventLoop {
}
const TOK_CORE: usize = 0xffff_ffff;
const TOK_PASTER: usize = 0xffff_fffe;
const TOK_CHLD: usize = 0xffff_fffd;
const TOK_XCB: usize = 0xffff_fffc;
@ -89,14 +86,6 @@ impl GuiEventLoop {
)?;
let core = futurecore::Core::new(fut_tx, fut_rx);
let (paster, paster_rx) = channel();
poll.register(
&paster_rx,
Token(TOK_PASTER),
Ready::readable(),
PollOpt::level(),
)?;
let (sigchld_tx, sigchld_rx) = channel();
poll.register(
&sigchld_rx,
@ -110,8 +99,6 @@ impl GuiEventLoop {
conn,
poll,
core,
paster,
paster_rx,
sigchld_rx,
interval: Duration::from_millis(50),
windows: Rc::new(RefCell::new(Default::default())),
@ -122,7 +109,6 @@ impl GuiEventLoop {
let mut events = Events::with_capacity(8);
let tok_core = Token(TOK_CORE);
let tok_paster = Token(TOK_PASTER);
let tok_chld = Token(TOK_CHLD);
let tok_xcb = Token(TOK_XCB);
@ -146,8 +132,6 @@ impl GuiEventLoop {
let t = event.token();
if t == tok_core {
self.process_futures();
} else if t == tok_paster {
self.process_paste()?;
} else if t == tok_chld {
self.process_sigchld()?;
} else if t == tok_xcb {
@ -412,23 +396,6 @@ impl GuiEventLoop {
self.conn.flush();
}
/// Process paste notifications and route them to their owning windows.
fn process_paste(&self) -> Result<(), Error> {
loop {
match self.paster_rx.try_recv() {
Ok(window_id) => {
self.windows
.borrow_mut()
.by_id
.get_mut(&window_id)
.map(|w| w.process_clipboard());
}
Err(TryRecvError::Empty) => return Ok(()),
Err(err) => bail!("paster_rx disconnected {:?}", err),
}
}
}
/// If we were signalled by a child process completion, zip through
/// the windows and have then notice and prepare to close.
fn process_sigchld(&self) -> Result<(), Error> {
@ -451,7 +418,7 @@ impl GuiEventLoop {
}
}
Err(TryRecvError::Empty) => return Ok(()),
Err(err) => bail!("paster_rx disconnected {:?}", err),
Err(err) => bail!("sigchld_rx disconnected {:?}", err),
}
}
}

View File

@ -26,6 +26,7 @@ extern crate palette;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate clipboard;
extern crate term;
extern crate termwiz;
extern crate toml;
@ -60,7 +61,6 @@ mod config;
mod futurecore;
mod opengl;
mod clipboard;
#[cfg(any(windows, feature = "force-glutin", target_os = "macos"))]
mod gliumwindows;
mod guiloop;

View File

@ -3,7 +3,7 @@ use super::super::{get_shell, spawn_window};
use super::super::{Child, Command};
use super::xkeysyms;
use super::{Connection, Window};
use clipboard::{Clipboard, ClipboardImpl, Paste};
use clipboard::ClipboardContext;
use config::Config;
use failure::Error;
use font::FontConfiguration;
@ -98,7 +98,7 @@ struct TabHost<'a> {
/// Holds most of the information we need to implement `TerminalHost`
struct Host {
window: Window,
clipboard: Clipboard,
clipboard: ClipboardContext,
event_loop: Rc<GuiEventLoop>,
fonts: Rc<FontConfiguration>,
config: Rc<Config>,
@ -131,11 +131,21 @@ impl<'a> term::TerminalHost for TabHost<'a> {
}
fn get_clipboard(&mut self) -> Result<String, Error> {
self.host.clipboard.get_clipboard()
self.host
.clipboard
.get_contents()
.map_err(|e| format_err!("{}", e))
}
fn set_clipboard(&mut self, clip: Option<String>) -> Result<(), Error> {
self.host.clipboard.set_clipboard(clip)
self.host
.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.
.and_then(|_| self.get_clipboard().map(|_| ()))
}
fn set_title(&mut self, _title: &str) {
@ -244,11 +254,10 @@ impl TerminalWindow {
let window = Window::new(&event_loop.conn, width, height)?;
window.set_title("wezterm");
let window_id = window.window.window_id;
let host = Host {
window,
clipboard: Clipboard::new(event_loop.paster.clone(), window_id)?,
clipboard: ClipboardContext::new().map_err(|e| format_err!("{}", e))?,
event_loop: Rc::clone(event_loop),
config: Rc::clone(config),
fonts: Rc::clone(fonts),
@ -365,22 +374,6 @@ impl TerminalWindow {
Ok(())
}
pub fn process_clipboard(&mut self) -> Result<(), Error> {
match self.host.clipboard.try_get_paste() {
Ok(Some(Paste::Cleared)) => {
self.tabs
.get_active()
.terminal
.borrow_mut()
.clear_selection();
}
Ok(_) => {}
Err(err) => bail!("clipboard thread died? {:?}", err),
}
self.paint_if_needed()?;
Ok(())
}
pub fn test_for_child_exit(&mut self) -> Result<(), SessionTerminated> {
match self.tabs.get_active().process.borrow_mut().try_wait() {
Ok(Some(status)) => Err(SessionTerminated::ProcessStatus { status }),