mirror of
https://github.com/wez/wezterm.git
synced 2024-11-23 15:04:36 +03:00
avoid deadlock while pasting large chunks of text
A big paste could saturate the input/output of the pty and lead to an effective deadlock; the application wants to send output to us but we are busy trying to write the paste to it. Break up large pastes into chunks and send them piece by piece. This does mean that a large bracketed paste is not an atomic unit any longer, but it seems to work out ok when pasting into vim.
This commit is contained in:
parent
c93f967bc4
commit
acc895f1cd
@ -1,9 +1,12 @@
|
||||
use super::window::TerminalWindow;
|
||||
use crate::mux::tab::Tab;
|
||||
use crate::frontend::gui_executor;
|
||||
use crate::mux::tab::{Tab, TabId};
|
||||
use crate::mux::Mux;
|
||||
use clipboard::{ClipboardContext, ClipboardProvider};
|
||||
use failure::Error;
|
||||
use promise::Future;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use term::{KeyCode, KeyModifiers};
|
||||
use termwiz::hyperlink::Hyperlink;
|
||||
|
||||
@ -22,6 +25,45 @@ pub struct HostImpl<H: HostHelper> {
|
||||
clipboard: Option<ClipboardContext>,
|
||||
}
|
||||
|
||||
const PASTE_CHUNK_SIZE: usize = 1024;
|
||||
|
||||
struct Paste {
|
||||
tab_id: TabId,
|
||||
text: String,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
fn schedule_next_paste(paste: &Arc<Mutex<Paste>>) {
|
||||
let paste = Arc::clone(paste);
|
||||
Future::with_executor(gui_executor().unwrap(), move || {
|
||||
let mut locked = paste.lock().unwrap();
|
||||
let mux = Mux::get().unwrap();
|
||||
let tab = mux.get_tab(locked.tab_id).unwrap();
|
||||
|
||||
let remain = locked.text.len() - locked.offset;
|
||||
let chunk = remain.min(PASTE_CHUNK_SIZE);
|
||||
let text_slice = &locked.text[locked.offset..locked.offset + chunk];
|
||||
tab.send_paste(text_slice).unwrap();
|
||||
|
||||
if chunk < remain {
|
||||
// There is more to send
|
||||
locked.offset += chunk;
|
||||
schedule_next_paste(&paste);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
fn trickle_paste(tab_id: TabId, text: String) {
|
||||
let paste = Arc::new(Mutex::new(Paste {
|
||||
tab_id,
|
||||
text,
|
||||
offset: PASTE_CHUNK_SIZE,
|
||||
}));
|
||||
schedule_next_paste(&paste);
|
||||
}
|
||||
|
||||
impl<H: HostHelper> HostImpl<H> {
|
||||
pub fn new(helper: H) -> Self {
|
||||
Self {
|
||||
@ -78,7 +120,15 @@ impl<H: HostHelper> HostImpl<H> {
|
||||
if (cfg!(target_os = "macos") && mods == KeyModifiers::SUPER && key == KeyCode::Char('v'))
|
||||
|| (mods == KeyModifiers::SHIFT && key == KeyCode::Insert)
|
||||
{
|
||||
tab.send_paste(&self.get_clipboard()?)?;
|
||||
let text = self.get_clipboard()?;
|
||||
if text.len() <= PASTE_CHUNK_SIZE {
|
||||
// Send it all now
|
||||
tab.send_paste(&text)?;
|
||||
return Ok(true);
|
||||
}
|
||||
// It's pretty heavy, so we trickle it into the pty
|
||||
tab.send_paste(&text[0..PASTE_CHUNK_SIZE])?;
|
||||
trickle_paste(tab.tab_id(), text);
|
||||
return Ok(true);
|
||||
}
|
||||
if mods == (KeyModifiers::SUPER | KeyModifiers::SHIFT)
|
||||
|
@ -5,6 +5,7 @@ use crate::mux::Mux;
|
||||
use failure::Error;
|
||||
use promise::Executor;
|
||||
use serde_derive::*;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -34,9 +35,23 @@ impl Default for FrontEndSelection {
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static EXECUTOR: RefCell<Option<Box<Executor>>> = RefCell::new(None);
|
||||
}
|
||||
|
||||
pub fn gui_executor() -> Option<Box<Executor>> {
|
||||
let mut res = None;
|
||||
EXECUTOR.with(|exec| {
|
||||
if let Some(exec) = &*exec.borrow() {
|
||||
res = Some(exec.clone_executor());
|
||||
}
|
||||
});
|
||||
res
|
||||
}
|
||||
|
||||
impl FrontEndSelection {
|
||||
pub fn try_new(self, mux: &Rc<Mux>) -> Result<Rc<FrontEnd>, Error> {
|
||||
match self {
|
||||
let front_end = match self {
|
||||
FrontEndSelection::Glutin => glium::glutinloop::GlutinFrontEnd::try_new(mux),
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
FrontEndSelection::X11 => xwindows::x11loop::X11FrontEnd::try_new(mux),
|
||||
@ -44,7 +59,13 @@ impl FrontEndSelection {
|
||||
FrontEndSelection::X11 => bail!("X11 not compiled in"),
|
||||
FrontEndSelection::MuxServer => muxserver::MuxServerFrontEnd::try_new(mux),
|
||||
FrontEndSelection::Null => muxserver::MuxServerFrontEnd::new_null(mux),
|
||||
}
|
||||
}?;
|
||||
|
||||
EXECUTOR.with(|exec| {
|
||||
*exec.borrow_mut() = Some(front_end.gui_executor());
|
||||
});
|
||||
|
||||
Ok(front_end)
|
||||
}
|
||||
|
||||
// TODO: find or build a proc macro for this
|
||||
|
@ -740,6 +740,10 @@ impl TerminalState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bracketed_paste_enabled(&self) -> bool {
|
||||
self.bracketed_paste
|
||||
}
|
||||
|
||||
/// Send text to the terminal that is the result of pasting.
|
||||
/// If bracketed paste mode is enabled, the paste is enclosed
|
||||
/// in the bracketing, otherwise it is fed to the pty as-is.
|
||||
|
Loading…
Reference in New Issue
Block a user