2020-10-03 09:35:18 +03:00
|
|
|
use crate::PKI;
|
2022-01-11 03:55:11 +03:00
|
|
|
use anyhow::{anyhow, Context};
|
2020-10-03 08:39:15 +03:00
|
|
|
use codec::*;
|
2022-01-12 08:39:19 +03:00
|
|
|
use mux::client::ClientId;
|
2020-10-03 03:42:49 +03:00
|
|
|
use mux::pane::{Pane, PaneId};
|
|
|
|
use mux::renderable::{RenderableDimensions, StableCursorPosition};
|
|
|
|
use mux::tab::TabId;
|
|
|
|
use mux::Mux;
|
2020-02-03 18:46:52 +03:00
|
|
|
use promise::spawn::spawn_into_main_thread;
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::rc::Rc;
|
|
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
use std::time::Instant;
|
2021-08-08 22:45:08 +03:00
|
|
|
use termwiz::surface::SequenceNo;
|
2020-02-03 18:46:52 +03:00
|
|
|
use url::Url;
|
2021-05-30 20:18:30 +03:00
|
|
|
use wezterm_term::terminal::{Alert, Clipboard, ClipboardSelection};
|
2020-06-13 19:04:13 +03:00
|
|
|
use wezterm_term::StableRowIndex;
|
2020-02-03 18:46:52 +03:00
|
|
|
|
2020-10-03 20:36:23 +03:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct PduSender {
|
|
|
|
func: Arc<dyn Fn(DecodedPdu) -> anyhow::Result<()> + Send + Sync>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PduSender {
|
|
|
|
pub fn send(&self, pdu: DecodedPdu) -> anyhow::Result<()> {
|
|
|
|
(self.func)(pdu)
|
|
|
|
}
|
|
|
|
|
2020-10-05 07:07:40 +03:00
|
|
|
pub fn new<T>(f: T) -> Self
|
|
|
|
where
|
|
|
|
T: Fn(DecodedPdu) -> anyhow::Result<()> + Send + Sync + 'static,
|
|
|
|
{
|
|
|
|
Self { func: Arc::new(f) }
|
2020-10-03 20:36:23 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-03 18:46:52 +03:00
|
|
|
#[derive(Default, Debug)]
|
2021-05-30 20:18:30 +03:00
|
|
|
pub(crate) struct PerPane {
|
2020-02-03 18:46:52 +03:00
|
|
|
cursor_position: StableCursorPosition,
|
|
|
|
title: String,
|
|
|
|
working_dir: Option<Url>,
|
|
|
|
dimensions: RenderableDimensions,
|
|
|
|
mouse_grabbed: bool,
|
2021-05-30 20:18:30 +03:00
|
|
|
sent_initial_palette: bool,
|
2021-08-08 22:45:08 +03:00
|
|
|
seqno: SequenceNo,
|
2022-01-11 04:13:42 +03:00
|
|
|
config_generation: usize,
|
2021-05-30 20:18:30 +03:00
|
|
|
pub(crate) notifications: Vec<Alert>,
|
2020-02-03 18:46:52 +03:00
|
|
|
}
|
|
|
|
|
2020-06-27 02:49:07 +03:00
|
|
|
impl PerPane {
|
2020-03-08 18:36:52 +03:00
|
|
|
fn compute_changes(
|
|
|
|
&mut self,
|
2020-06-27 02:49:07 +03:00
|
|
|
pane: &Rc<dyn Pane>,
|
2020-03-10 18:34:47 +03:00
|
|
|
force_with_input_serial: Option<InputSerial>,
|
2020-06-27 02:49:07 +03:00
|
|
|
) -> Option<GetPaneRenderChangesResponse> {
|
2020-02-03 18:46:52 +03:00
|
|
|
let mut changed = false;
|
2020-06-27 02:49:07 +03:00
|
|
|
let mouse_grabbed = pane.is_mouse_grabbed();
|
2020-02-03 18:46:52 +03:00
|
|
|
if mouse_grabbed != self.mouse_grabbed {
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
2020-11-02 21:32:16 +03:00
|
|
|
let dims = pane.get_dimensions();
|
2020-02-03 18:46:52 +03:00
|
|
|
if dims != self.dimensions {
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
2020-11-02 21:32:16 +03:00
|
|
|
let cursor_position = pane.get_cursor_position();
|
2020-02-03 18:46:52 +03:00
|
|
|
if cursor_position != self.cursor_position {
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
2020-06-27 02:49:07 +03:00
|
|
|
let title = pane.get_title();
|
2020-02-03 18:46:52 +03:00
|
|
|
if title != self.title {
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
2020-06-27 02:49:07 +03:00
|
|
|
let working_dir = pane.get_current_working_dir();
|
2020-02-03 18:46:52 +03:00
|
|
|
if working_dir != self.working_dir {
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
2021-08-08 22:45:08 +03:00
|
|
|
let mut all_dirty_lines = pane.get_changed_since(
|
|
|
|
0..dims.physical_top + dims.viewport_rows as StableRowIndex,
|
|
|
|
self.seqno,
|
|
|
|
);
|
|
|
|
if !all_dirty_lines.is_empty() {
|
2020-02-03 18:46:52 +03:00
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
2020-03-10 18:34:47 +03:00
|
|
|
if !changed && !force_with_input_serial.is_some() {
|
2020-02-03 18:46:52 +03:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Figure out what we're going to send as dirty lines vs bonus lines
|
|
|
|
let viewport_range =
|
|
|
|
dims.physical_top..dims.physical_top + dims.viewport_rows as StableRowIndex;
|
|
|
|
|
2020-11-02 21:32:16 +03:00
|
|
|
let (first_line, lines) = pane.get_lines(viewport_range);
|
2020-02-03 18:46:52 +03:00
|
|
|
let mut bonus_lines = lines
|
|
|
|
.into_iter()
|
|
|
|
.enumerate()
|
2022-01-11 03:55:11 +03:00
|
|
|
.filter_map(|(idx, line)| {
|
|
|
|
if line.changed_since(self.seqno) {
|
|
|
|
let stable_row = first_line + idx as StableRowIndex;
|
|
|
|
all_dirty_lines.remove(stable_row);
|
|
|
|
Some((stable_row, line))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
2020-02-03 18:46:52 +03:00
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
// Always send the cursor's row, as that tends to the busiest and we don't
|
|
|
|
// have a sequencing concept for our idea of the remote state.
|
2020-11-02 21:32:16 +03:00
|
|
|
let (cursor_line, lines) = pane.get_lines(cursor_position.y..cursor_position.y + 1);
|
2020-02-03 18:46:52 +03:00
|
|
|
bonus_lines.push((cursor_line, lines[0].clone()));
|
|
|
|
|
|
|
|
self.cursor_position = cursor_position;
|
|
|
|
self.title = title.clone();
|
|
|
|
self.working_dir = working_dir.clone();
|
|
|
|
self.dimensions = dims;
|
|
|
|
self.mouse_grabbed = mouse_grabbed;
|
2021-08-08 22:45:08 +03:00
|
|
|
self.seqno = pane.get_current_seqno();
|
2020-02-03 18:46:52 +03:00
|
|
|
|
|
|
|
let bonus_lines = bonus_lines.into();
|
2020-06-27 02:49:07 +03:00
|
|
|
Some(GetPaneRenderChangesResponse {
|
|
|
|
pane_id: pane.pane_id(),
|
2020-02-03 18:46:52 +03:00
|
|
|
mouse_grabbed,
|
2021-08-08 22:45:08 +03:00
|
|
|
dirty_lines: all_dirty_lines.iter().cloned().collect(),
|
2020-02-03 18:46:52 +03:00
|
|
|
dimensions: dims,
|
|
|
|
cursor_position,
|
|
|
|
title,
|
|
|
|
bonus_lines,
|
|
|
|
working_dir: working_dir.map(Into::into),
|
2020-03-10 18:34:47 +03:00
|
|
|
input_serial: force_with_input_serial,
|
2021-08-08 22:45:08 +03:00
|
|
|
seqno: self.seqno,
|
2020-02-03 18:46:52 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-27 02:49:07 +03:00
|
|
|
fn maybe_push_pane_changes(
|
|
|
|
pane: &Rc<dyn Pane>,
|
2020-10-03 20:36:23 +03:00
|
|
|
sender: PduSender,
|
2020-06-27 02:49:07 +03:00
|
|
|
per_pane: Arc<Mutex<PerPane>>,
|
2020-02-03 18:46:52 +03:00
|
|
|
) -> anyhow::Result<()> {
|
2020-06-27 02:49:07 +03:00
|
|
|
let mut per_pane = per_pane.lock().unwrap();
|
|
|
|
if let Some(resp) = per_pane.compute_changes(pane, None) {
|
2020-02-03 18:46:52 +03:00
|
|
|
sender.send(DecodedPdu {
|
2020-06-27 02:49:07 +03:00
|
|
|
pdu: Pdu::GetPaneRenderChangesResponse(resp),
|
2020-02-03 18:46:52 +03:00
|
|
|
serial: 0,
|
|
|
|
})?;
|
|
|
|
}
|
2022-01-11 04:13:42 +03:00
|
|
|
|
|
|
|
let config = config::configuration();
|
|
|
|
if per_pane.config_generation != config.generation() {
|
|
|
|
per_pane.config_generation = config.generation();
|
|
|
|
// If the config changed, it may have changed colors
|
|
|
|
// in the palette that we need to push down, so we
|
|
|
|
// synthesize a palette change notification to let
|
|
|
|
// the client know
|
|
|
|
per_pane.notifications.push(Alert::PaletteChanged);
|
|
|
|
per_pane.sent_initial_palette = true;
|
|
|
|
}
|
|
|
|
|
2021-05-30 20:18:30 +03:00
|
|
|
if !per_pane.sent_initial_palette {
|
|
|
|
per_pane.notifications.push(Alert::PaletteChanged);
|
|
|
|
per_pane.sent_initial_palette = true;
|
|
|
|
}
|
|
|
|
for alert in per_pane.notifications.drain(..) {
|
|
|
|
match alert {
|
|
|
|
Alert::PaletteChanged => {
|
|
|
|
sender.send(DecodedPdu {
|
|
|
|
pdu: Pdu::SetPalette(SetPalette {
|
|
|
|
pane_id: pane.pane_id(),
|
|
|
|
palette: pane.palette(),
|
|
|
|
}),
|
|
|
|
serial: 0,
|
|
|
|
})?;
|
|
|
|
}
|
|
|
|
alert => {
|
|
|
|
sender.send(DecodedPdu {
|
|
|
|
pdu: Pdu::NotifyAlert(NotifyAlert {
|
|
|
|
pane_id: pane.pane_id(),
|
|
|
|
alert,
|
|
|
|
}),
|
|
|
|
serial: 0,
|
|
|
|
})?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-02-03 18:46:52 +03:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct SessionHandler {
|
2020-10-03 20:36:23 +03:00
|
|
|
to_write_tx: PduSender,
|
2020-06-27 02:49:07 +03:00
|
|
|
per_pane: HashMap<TabId, Arc<Mutex<PerPane>>>,
|
2022-01-12 08:39:19 +03:00
|
|
|
client_id: Option<ClientId>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for SessionHandler {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
if let Some(client_id) = self.client_id.take() {
|
|
|
|
let mux = Mux::get().unwrap();
|
|
|
|
mux.unregister_client(&client_id);
|
|
|
|
}
|
|
|
|
}
|
2020-02-03 18:46:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl SessionHandler {
|
2020-10-03 20:36:23 +03:00
|
|
|
pub fn new(to_write_tx: PduSender) -> Self {
|
2021-05-30 17:40:00 +03:00
|
|
|
// Fixup the clipboard on the empty initial pane that is
|
|
|
|
// spawned into the mux
|
|
|
|
let mux = Mux::get().unwrap();
|
|
|
|
for pane in mux.iter_panes() {
|
|
|
|
let clip: Arc<dyn Clipboard> = Arc::new(RemoteClipboard {
|
|
|
|
pane_id: pane.pane_id(),
|
|
|
|
sender: to_write_tx.clone(),
|
|
|
|
});
|
|
|
|
pane.set_clipboard(&clip);
|
|
|
|
}
|
|
|
|
|
2020-02-03 18:46:52 +03:00
|
|
|
Self {
|
|
|
|
to_write_tx,
|
2020-06-27 02:49:07 +03:00
|
|
|
per_pane: HashMap::new(),
|
2022-01-12 08:39:19 +03:00
|
|
|
client_id: None,
|
2020-02-03 18:46:52 +03:00
|
|
|
}
|
|
|
|
}
|
2021-05-30 20:18:30 +03:00
|
|
|
|
|
|
|
pub(crate) fn per_pane(&mut self, pane_id: PaneId) -> Arc<Mutex<PerPane>> {
|
2020-02-03 18:46:52 +03:00
|
|
|
Arc::clone(
|
2020-06-27 02:49:07 +03:00
|
|
|
self.per_pane
|
|
|
|
.entry(pane_id)
|
|
|
|
.or_insert_with(|| Arc::new(Mutex::new(PerPane::default()))),
|
2020-02-03 18:46:52 +03:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-06-27 02:49:07 +03:00
|
|
|
pub fn schedule_pane_push(&mut self, pane_id: PaneId) {
|
2020-02-03 18:46:52 +03:00
|
|
|
let sender = self.to_write_tx.clone();
|
2020-06-27 02:49:07 +03:00
|
|
|
let per_pane = self.per_pane(pane_id);
|
2020-02-03 18:46:52 +03:00
|
|
|
spawn_into_main_thread(async move {
|
|
|
|
let mux = Mux::get().unwrap();
|
2020-06-27 02:49:07 +03:00
|
|
|
let pane = mux
|
|
|
|
.get_pane(pane_id)
|
|
|
|
.ok_or_else(|| anyhow!("no such pane {}", pane_id))?;
|
|
|
|
maybe_push_pane_changes(&pane, sender, per_pane)?;
|
2020-02-03 18:46:52 +03:00
|
|
|
Ok::<(), anyhow::Error>(())
|
2020-10-05 10:06:01 +03:00
|
|
|
})
|
|
|
|
.detach();
|
2020-02-03 18:46:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn process_one(&mut self, decoded: DecodedPdu) {
|
|
|
|
let start = Instant::now();
|
|
|
|
let sender = self.to_write_tx.clone();
|
|
|
|
let serial = decoded.serial;
|
|
|
|
|
2022-01-12 08:39:19 +03:00
|
|
|
if let Some(client_id) = &self.client_id {
|
|
|
|
Mux::get().unwrap().client_had_input(client_id);
|
|
|
|
}
|
|
|
|
|
2020-02-03 18:46:52 +03:00
|
|
|
let send_response = move |result: anyhow::Result<Pdu>| {
|
|
|
|
let pdu = match result {
|
|
|
|
Ok(pdu) => pdu,
|
|
|
|
Err(err) => Pdu::ErrorResponse(ErrorResponse {
|
|
|
|
reason: format!("Error: {}", err),
|
|
|
|
}),
|
|
|
|
};
|
|
|
|
log::trace!("{} processing time {:?}", serial, start.elapsed());
|
2020-02-23 21:38:46 +03:00
|
|
|
sender.send(DecodedPdu { pdu, serial }).ok();
|
2020-02-03 18:46:52 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
fn catch<F, SND>(f: F, send_response: SND)
|
|
|
|
where
|
|
|
|
F: FnOnce() -> anyhow::Result<Pdu>,
|
|
|
|
SND: Fn(anyhow::Result<Pdu>),
|
|
|
|
{
|
|
|
|
send_response(f());
|
|
|
|
}
|
|
|
|
|
|
|
|
match decoded.pdu {
|
|
|
|
Pdu::Ping(Ping {}) => send_response(Ok(Pdu::Pong(Pong {}))),
|
2022-01-13 02:28:32 +03:00
|
|
|
Pdu::SetWindowWorkspace(SetWindowWorkspace {
|
|
|
|
window_id,
|
|
|
|
workspace,
|
|
|
|
}) => {
|
|
|
|
spawn_into_main_thread(async move {
|
|
|
|
catch(
|
|
|
|
move || {
|
|
|
|
let mux = Mux::get().unwrap();
|
|
|
|
let mut window = mux
|
|
|
|
.get_window_mut(window_id)
|
|
|
|
.ok_or_else(|| anyhow!("window {} is invalid", window_id))?;
|
|
|
|
window.set_workspace(&workspace);
|
|
|
|
Ok(Pdu::UnitResponse(UnitResponse {}))
|
|
|
|
},
|
|
|
|
send_response,
|
|
|
|
)
|
|
|
|
})
|
|
|
|
.detach();
|
|
|
|
}
|
2022-01-12 08:39:19 +03:00
|
|
|
Pdu::SetClientId(SetClientId { client_id }) => {
|
|
|
|
self.client_id.replace(client_id.clone());
|
|
|
|
spawn_into_main_thread(async move {
|
|
|
|
let mux = Mux::get().unwrap();
|
|
|
|
mux.register_client(&client_id);
|
|
|
|
})
|
|
|
|
.detach();
|
|
|
|
send_response(Ok(Pdu::UnitResponse(UnitResponse {})))
|
|
|
|
}
|
|
|
|
Pdu::GetClientList(GetClientList) => {
|
|
|
|
spawn_into_main_thread(async move {
|
|
|
|
catch(
|
|
|
|
move || {
|
|
|
|
let mux = Mux::get().unwrap();
|
|
|
|
let clients = mux.iter_clients();
|
|
|
|
Ok(Pdu::GetClientListResponse(GetClientListResponse {
|
|
|
|
clients,
|
|
|
|
}))
|
|
|
|
},
|
|
|
|
send_response,
|
|
|
|
)
|
|
|
|
})
|
|
|
|
.detach();
|
|
|
|
}
|
2020-09-27 06:14:28 +03:00
|
|
|
Pdu::ListPanes(ListPanes {}) => {
|
2020-02-03 18:46:52 +03:00
|
|
|
spawn_into_main_thread(async move {
|
|
|
|
catch(
|
|
|
|
move || {
|
|
|
|
let mux = Mux::get().unwrap();
|
|
|
|
let mut tabs = vec![];
|
|
|
|
for window_id in mux.iter_windows().into_iter() {
|
|
|
|
let window = mux.get_window(window_id).unwrap();
|
|
|
|
for tab in window.iter() {
|
2020-09-27 06:14:28 +03:00
|
|
|
tabs.push(tab.codec_pane_tree());
|
2020-02-03 18:46:52 +03:00
|
|
|
}
|
|
|
|
}
|
2020-12-30 03:33:58 +03:00
|
|
|
log::trace!("ListPanes {:#?}", tabs);
|
2020-09-27 06:14:28 +03:00
|
|
|
Ok(Pdu::ListPanesResponse(ListPanesResponse { tabs }))
|
2020-02-03 18:46:52 +03:00
|
|
|
},
|
|
|
|
send_response,
|
|
|
|
)
|
2020-10-05 10:06:01 +03:00
|
|
|
})
|
|
|
|
.detach();
|
2020-02-03 18:46:52 +03:00
|
|
|
}
|
|
|
|
|
2020-06-27 02:49:07 +03:00
|
|
|
Pdu::WriteToPane(WriteToPane { pane_id, data }) => {
|
2020-02-03 18:46:52 +03:00
|
|
|
let sender = self.to_write_tx.clone();
|
2020-06-27 02:49:07 +03:00
|
|
|
let per_pane = self.per_pane(pane_id);
|
2020-02-03 18:46:52 +03:00
|
|
|
spawn_into_main_thread(async move {
|
|
|
|
catch(
|
|
|
|
move || {
|
|
|
|
let mux = Mux::get().unwrap();
|
2020-06-27 02:49:07 +03:00
|
|
|
let pane = mux
|
|
|
|
.get_pane(pane_id)
|
|
|
|
.ok_or_else(|| anyhow!("no such pane {}", pane_id))?;
|
|
|
|
pane.writer().write_all(&data)?;
|
|
|
|
maybe_push_pane_changes(&pane, sender, per_pane)?;
|
2020-02-03 18:46:52 +03:00
|
|
|
Ok(Pdu::UnitResponse(UnitResponse {}))
|
|
|
|
},
|
|
|
|
send_response,
|
|
|
|
);
|
2020-10-05 10:06:01 +03:00
|
|
|
})
|
|
|
|
.detach();
|
2020-02-03 18:46:52 +03:00
|
|
|
}
|
2021-03-28 18:07:51 +03:00
|
|
|
Pdu::KillPane(KillPane { pane_id }) => {
|
|
|
|
let sender = self.to_write_tx.clone();
|
|
|
|
let per_pane = self.per_pane(pane_id);
|
|
|
|
spawn_into_main_thread(async move {
|
|
|
|
catch(
|
|
|
|
move || {
|
|
|
|
let mux = Mux::get().unwrap();
|
|
|
|
let pane = mux
|
|
|
|
.get_pane(pane_id)
|
|
|
|
.ok_or_else(|| anyhow!("no such pane {}", pane_id))?;
|
|
|
|
pane.kill();
|
|
|
|
mux.remove_pane(pane_id);
|
|
|
|
maybe_push_pane_changes(&pane, sender, per_pane)?;
|
|
|
|
Ok(Pdu::UnitResponse(UnitResponse {}))
|
|
|
|
},
|
|
|
|
send_response,
|
|
|
|
);
|
|
|
|
})
|
|
|
|
.detach();
|
|
|
|
}
|
2020-06-27 02:49:07 +03:00
|
|
|
Pdu::SendPaste(SendPaste { pane_id, data }) => {
|
2020-02-03 18:46:52 +03:00
|
|
|
let sender = self.to_write_tx.clone();
|
2020-06-27 02:49:07 +03:00
|
|
|
let per_pane = self.per_pane(pane_id);
|
2020-02-03 18:46:52 +03:00
|
|
|
spawn_into_main_thread(async move {
|
|
|
|
catch(
|
|
|
|
move || {
|
|
|
|
let mux = Mux::get().unwrap();
|
2020-06-27 02:49:07 +03:00
|
|
|
let pane = mux
|
|
|
|
.get_pane(pane_id)
|
|
|
|
.ok_or_else(|| anyhow!("no such pane {}", pane_id))?;
|
|
|
|
pane.send_paste(&data)?;
|
|
|
|
maybe_push_pane_changes(&pane, sender, per_pane)?;
|
2020-02-03 18:46:52 +03:00
|
|
|
Ok(Pdu::UnitResponse(UnitResponse {}))
|
|
|
|
},
|
|
|
|
send_response,
|
|
|
|
)
|
2020-10-05 10:06:01 +03:00
|
|
|
})
|
|
|
|
.detach();
|
2020-02-03 18:46:52 +03:00
|
|
|
}
|
|
|
|
|
2020-06-27 02:49:07 +03:00
|
|
|
Pdu::SearchScrollbackRequest(SearchScrollbackRequest { pane_id, pattern }) => {
|
2020-10-03 03:42:49 +03:00
|
|
|
use mux::pane::Pattern;
|
2020-05-30 19:40:44 +03:00
|
|
|
|
2020-06-27 02:49:07 +03:00
|
|
|
async fn do_search(pane_id: TabId, pattern: Pattern) -> anyhow::Result<Pdu> {
|
2020-05-30 19:40:44 +03:00
|
|
|
let mux = Mux::get().unwrap();
|
2020-06-27 02:49:07 +03:00
|
|
|
let pane = mux
|
|
|
|
.get_pane(pane_id)
|
|
|
|
.ok_or_else(|| anyhow!("no such pane {}", pane_id))?;
|
2020-05-30 19:40:44 +03:00
|
|
|
|
2020-06-27 02:49:07 +03:00
|
|
|
pane.search(pattern).await.map(|results| {
|
|
|
|
Pdu::SearchScrollbackResponse(SearchScrollbackResponse { results })
|
2020-05-30 19:40:44 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
spawn_into_main_thread(async move {
|
|
|
|
promise::spawn::spawn(async move {
|
2020-06-27 02:49:07 +03:00
|
|
|
let result = do_search(pane_id, pattern).await;
|
2020-05-30 19:40:44 +03:00
|
|
|
send_response(result);
|
2020-10-05 10:06:01 +03:00
|
|
|
})
|
|
|
|
.detach();
|
|
|
|
})
|
|
|
|
.detach();
|
2020-05-30 19:40:44 +03:00
|
|
|
}
|
|
|
|
|
2020-09-27 06:14:28 +03:00
|
|
|
Pdu::SetPaneZoomed(SetPaneZoomed {
|
|
|
|
containing_tab_id,
|
|
|
|
pane_id,
|
|
|
|
zoomed,
|
|
|
|
}) => {
|
|
|
|
spawn_into_main_thread(async move {
|
|
|
|
catch(
|
|
|
|
move || {
|
|
|
|
let mux = Mux::get().unwrap();
|
|
|
|
let pane = mux
|
|
|
|
.get_pane(pane_id)
|
|
|
|
.ok_or_else(|| anyhow!("no such pane {}", pane_id))?;
|
|
|
|
let tab = mux
|
|
|
|
.get_tab(containing_tab_id)
|
|
|
|
.ok_or_else(|| anyhow!("no such tab {}", containing_tab_id))?;
|
|
|
|
tab.set_active_pane(&pane);
|
|
|
|
tab.set_zoomed(zoomed);
|
|
|
|
Ok(Pdu::UnitResponse(UnitResponse {}))
|
|
|
|
},
|
|
|
|
send_response,
|
|
|
|
)
|
2020-10-05 10:06:01 +03:00
|
|
|
})
|
|
|
|
.detach();
|
2020-09-27 06:14:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
Pdu::Resize(Resize {
|
|
|
|
containing_tab_id,
|
|
|
|
pane_id,
|
|
|
|
size,
|
|
|
|
}) => {
|
2020-02-03 18:46:52 +03:00
|
|
|
spawn_into_main_thread(async move {
|
|
|
|
catch(
|
|
|
|
move || {
|
|
|
|
let mux = Mux::get().unwrap();
|
2020-06-27 02:49:07 +03:00
|
|
|
let pane = mux
|
|
|
|
.get_pane(pane_id)
|
|
|
|
.ok_or_else(|| anyhow!("no such pane {}", pane_id))?;
|
|
|
|
pane.resize(size)?;
|
2020-09-27 06:14:28 +03:00
|
|
|
let tab = mux
|
|
|
|
.get_tab(containing_tab_id)
|
|
|
|
.ok_or_else(|| anyhow!("no such tab {}", containing_tab_id))?;
|
|
|
|
tab.rebuild_splits_sizes_from_contained_panes();
|
2020-02-03 18:46:52 +03:00
|
|
|
Ok(Pdu::UnitResponse(UnitResponse {}))
|
|
|
|
},
|
|
|
|
send_response,
|
|
|
|
)
|
2020-10-05 10:06:01 +03:00
|
|
|
})
|
|
|
|
.detach();
|
2020-02-03 18:46:52 +03:00
|
|
|
}
|
|
|
|
|
2020-03-10 18:34:47 +03:00
|
|
|
Pdu::SendKeyDown(SendKeyDown {
|
2020-06-27 02:49:07 +03:00
|
|
|
pane_id,
|
2020-03-10 18:34:47 +03:00
|
|
|
event,
|
|
|
|
input_serial,
|
|
|
|
}) => {
|
2020-02-03 18:46:52 +03:00
|
|
|
let sender = self.to_write_tx.clone();
|
2020-06-27 02:49:07 +03:00
|
|
|
let per_pane = self.per_pane(pane_id);
|
2020-02-03 18:46:52 +03:00
|
|
|
spawn_into_main_thread(async move {
|
|
|
|
catch(
|
|
|
|
move || {
|
|
|
|
let mux = Mux::get().unwrap();
|
2020-06-27 02:49:07 +03:00
|
|
|
let pane = mux
|
|
|
|
.get_pane(pane_id)
|
|
|
|
.ok_or_else(|| anyhow!("no such pane {}", pane_id))?;
|
|
|
|
pane.key_down(event.key, event.modifiers)?;
|
2020-03-08 18:36:52 +03:00
|
|
|
|
|
|
|
// For a key press, we want to always send back the
|
|
|
|
// cursor position so that the predictive echo doesn't
|
|
|
|
// leave the cursor in the wrong place
|
2020-06-27 02:49:07 +03:00
|
|
|
let mut per_pane = per_pane.lock().unwrap();
|
|
|
|
if let Some(resp) = per_pane.compute_changes(&pane, Some(input_serial))
|
|
|
|
{
|
2020-03-08 18:36:52 +03:00
|
|
|
sender.send(DecodedPdu {
|
2020-06-27 02:49:07 +03:00
|
|
|
pdu: Pdu::GetPaneRenderChangesResponse(resp),
|
2020-03-08 18:36:52 +03:00
|
|
|
serial: 0,
|
|
|
|
})?;
|
|
|
|
}
|
2020-02-03 18:46:52 +03:00
|
|
|
Ok(Pdu::UnitResponse(UnitResponse {}))
|
|
|
|
},
|
|
|
|
send_response,
|
|
|
|
)
|
2020-10-05 10:06:01 +03:00
|
|
|
})
|
|
|
|
.detach();
|
2020-02-03 18:46:52 +03:00
|
|
|
}
|
2020-06-27 02:49:07 +03:00
|
|
|
Pdu::SendMouseEvent(SendMouseEvent { pane_id, event }) => {
|
2020-02-03 18:46:52 +03:00
|
|
|
let sender = self.to_write_tx.clone();
|
2020-06-27 02:49:07 +03:00
|
|
|
let per_pane = self.per_pane(pane_id);
|
2020-02-03 18:46:52 +03:00
|
|
|
spawn_into_main_thread(async move {
|
|
|
|
catch(
|
|
|
|
move || {
|
|
|
|
let mux = Mux::get().unwrap();
|
2020-06-27 02:49:07 +03:00
|
|
|
let pane = mux
|
|
|
|
.get_pane(pane_id)
|
|
|
|
.ok_or_else(|| anyhow!("no such pane {}", pane_id))?;
|
|
|
|
pane.mouse_event(event)?;
|
|
|
|
maybe_push_pane_changes(&pane, sender, per_pane)?;
|
2020-02-03 18:46:52 +03:00
|
|
|
Ok(Pdu::UnitResponse(UnitResponse {}))
|
|
|
|
},
|
|
|
|
send_response,
|
|
|
|
)
|
2020-10-05 10:06:01 +03:00
|
|
|
})
|
|
|
|
.detach();
|
2020-02-03 18:46:52 +03:00
|
|
|
}
|
|
|
|
|
2021-03-28 23:57:29 +03:00
|
|
|
Pdu::SpawnV2(spawn) => {
|
|
|
|
let sender = self.to_write_tx.clone();
|
|
|
|
spawn_into_main_thread(async move {
|
|
|
|
schedule_domain_spawn_v2(spawn, sender, send_response);
|
|
|
|
})
|
|
|
|
.detach();
|
|
|
|
}
|
|
|
|
|
2020-09-28 09:07:33 +03:00
|
|
|
Pdu::SplitPane(split) => {
|
|
|
|
let sender = self.to_write_tx.clone();
|
|
|
|
spawn_into_main_thread(async move {
|
|
|
|
schedule_split_pane(split, sender, send_response);
|
2020-10-05 10:06:01 +03:00
|
|
|
})
|
|
|
|
.detach();
|
2020-09-28 09:07:33 +03:00
|
|
|
}
|
|
|
|
|
2020-06-27 02:49:07 +03:00
|
|
|
Pdu::GetPaneRenderChanges(GetPaneRenderChanges { pane_id, .. }) => {
|
2020-02-03 18:46:52 +03:00
|
|
|
let sender = self.to_write_tx.clone();
|
2020-06-27 02:49:07 +03:00
|
|
|
let per_pane = self.per_pane(pane_id);
|
2020-02-03 18:46:52 +03:00
|
|
|
spawn_into_main_thread(async move {
|
|
|
|
catch(
|
|
|
|
move || {
|
|
|
|
let mux = Mux::get().unwrap();
|
2020-06-27 02:49:07 +03:00
|
|
|
let is_alive = match mux.get_pane(pane_id) {
|
|
|
|
Some(pane) => {
|
|
|
|
maybe_push_pane_changes(&pane, sender, per_pane)?;
|
2020-03-06 19:44:46 +03:00
|
|
|
true
|
|
|
|
}
|
|
|
|
None => false,
|
|
|
|
};
|
2020-06-27 02:49:07 +03:00
|
|
|
Ok(Pdu::LivenessResponse(LivenessResponse {
|
|
|
|
pane_id,
|
|
|
|
is_alive,
|
2020-03-06 19:44:46 +03:00
|
|
|
}))
|
2020-02-03 18:46:52 +03:00
|
|
|
},
|
|
|
|
send_response,
|
|
|
|
)
|
2020-10-05 10:06:01 +03:00
|
|
|
})
|
|
|
|
.detach();
|
2020-02-03 18:46:52 +03:00
|
|
|
}
|
|
|
|
|
2020-06-27 02:49:07 +03:00
|
|
|
Pdu::GetLines(GetLines { pane_id, lines }) => {
|
2020-02-03 18:46:52 +03:00
|
|
|
spawn_into_main_thread(async move {
|
|
|
|
catch(
|
|
|
|
move || {
|
|
|
|
let mux = Mux::get().unwrap();
|
2020-06-27 02:49:07 +03:00
|
|
|
let pane = mux
|
|
|
|
.get_pane(pane_id)
|
|
|
|
.ok_or_else(|| anyhow!("no such pane {}", pane_id))?;
|
2020-02-03 18:46:52 +03:00
|
|
|
let mut lines_and_indices = vec![];
|
|
|
|
|
|
|
|
for range in lines {
|
2020-11-02 21:32:16 +03:00
|
|
|
let (first_row, lines) = pane.get_lines(range);
|
2020-02-03 18:46:52 +03:00
|
|
|
for (idx, line) in lines.into_iter().enumerate() {
|
|
|
|
let stable_row = first_row + idx as StableRowIndex;
|
|
|
|
lines_and_indices.push((stable_row, line));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(Pdu::GetLinesResponse(GetLinesResponse {
|
2020-06-27 02:49:07 +03:00
|
|
|
pane_id,
|
2020-02-03 18:46:52 +03:00
|
|
|
lines: lines_and_indices.into(),
|
|
|
|
}))
|
|
|
|
},
|
|
|
|
send_response,
|
|
|
|
)
|
2020-10-05 10:06:01 +03:00
|
|
|
})
|
|
|
|
.detach();
|
2020-02-03 18:46:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
Pdu::GetCodecVersion(_) => {
|
2022-01-11 03:50:32 +03:00
|
|
|
match std::env::current_exe().context("resolving current_exe") {
|
|
|
|
Err(err) => send_response(Err(err)),
|
|
|
|
Ok(executable_path) => {
|
|
|
|
send_response(Ok(Pdu::GetCodecVersionResponse(GetCodecVersionResponse {
|
|
|
|
codec_vers: CODEC_VERSION,
|
|
|
|
version_string: config::wezterm_version().to_owned(),
|
|
|
|
executable_path,
|
|
|
|
config_file_path: std::env::var_os("WEZTERM_CONFIG_FILE")
|
|
|
|
.map(Into::into),
|
|
|
|
})))
|
|
|
|
}
|
|
|
|
}
|
2020-02-03 18:46:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
Pdu::GetTlsCreds(_) => {
|
|
|
|
catch(
|
|
|
|
move || {
|
|
|
|
let client_cert_pem = PKI.generate_client_cert()?;
|
|
|
|
let ca_cert_pem = PKI.ca_pem_string()?;
|
|
|
|
Ok(Pdu::GetTlsCredsResponse(GetTlsCredsResponse {
|
|
|
|
client_cert_pem,
|
|
|
|
ca_cert_pem,
|
|
|
|
}))
|
|
|
|
},
|
|
|
|
send_response,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Pdu::Invalid { .. } => send_response(Err(anyhow!("invalid PDU {:?}", decoded.pdu))),
|
|
|
|
Pdu::Pong { .. }
|
2020-09-27 06:14:28 +03:00
|
|
|
| Pdu::ListPanesResponse { .. }
|
2020-02-03 18:46:52 +03:00
|
|
|
| Pdu::SetClipboard { .. }
|
2021-05-30 20:18:30 +03:00
|
|
|
| Pdu::NotifyAlert { .. }
|
|
|
|
| Pdu::SetPalette { .. }
|
2020-02-03 18:46:52 +03:00
|
|
|
| Pdu::SpawnResponse { .. }
|
2020-06-27 02:49:07 +03:00
|
|
|
| Pdu::GetPaneRenderChangesResponse { .. }
|
2020-02-03 18:46:52 +03:00
|
|
|
| Pdu::UnitResponse { .. }
|
2020-06-27 02:49:07 +03:00
|
|
|
| Pdu::LivenessResponse { .. }
|
|
|
|
| Pdu::SearchScrollbackResponse { .. }
|
2020-02-03 18:46:52 +03:00
|
|
|
| Pdu::GetLinesResponse { .. }
|
|
|
|
| Pdu::GetCodecVersionResponse { .. }
|
2022-01-13 02:28:32 +03:00
|
|
|
| Pdu::WindowWorkspaceChanged { .. }
|
2020-02-03 18:46:52 +03:00
|
|
|
| Pdu::GetTlsCredsResponse { .. }
|
2022-01-12 08:39:19 +03:00
|
|
|
| Pdu::GetClientListResponse { .. }
|
2021-05-29 10:16:27 +03:00
|
|
|
| Pdu::PaneRemoved { .. }
|
2020-02-03 18:46:52 +03:00
|
|
|
| Pdu::ErrorResponse { .. } => {
|
|
|
|
send_response(Err(anyhow!("expected a request, got {:?}", decoded.pdu)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dancing around a little bit here; we can't directly spawn_into_main_thread the domain_spawn
|
|
|
|
// function below because the compiler thinks that all of its locals then need to be Send.
|
|
|
|
// We need to shimmy through this helper to break that aspect of the compiler flow
|
|
|
|
// analysis and allow things to compile.
|
2021-03-28 23:57:29 +03:00
|
|
|
fn schedule_domain_spawn_v2<SND>(spawn: SpawnV2, sender: PduSender, send_response: SND)
|
|
|
|
where
|
|
|
|
SND: Fn(anyhow::Result<Pdu>) + 'static,
|
|
|
|
{
|
|
|
|
promise::spawn::spawn(async move { send_response(domain_spawn_v2(spawn, sender).await) })
|
|
|
|
.detach();
|
|
|
|
}
|
|
|
|
|
2020-10-03 20:36:23 +03:00
|
|
|
fn schedule_split_pane<SND>(split: SplitPane, sender: PduSender, send_response: SND)
|
|
|
|
where
|
2020-09-28 09:07:33 +03:00
|
|
|
SND: Fn(anyhow::Result<Pdu>) + 'static,
|
|
|
|
{
|
2020-10-05 10:06:01 +03:00
|
|
|
promise::spawn::spawn(async move { send_response(split_pane(split, sender).await) }).detach();
|
2020-09-28 09:07:33 +03:00
|
|
|
}
|
|
|
|
|
2020-02-03 18:46:52 +03:00
|
|
|
struct RemoteClipboard {
|
2020-10-03 20:36:23 +03:00
|
|
|
sender: PduSender,
|
2021-05-31 18:21:19 +03:00
|
|
|
pane_id: PaneId,
|
2020-02-03 18:46:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Clipboard for RemoteClipboard {
|
2021-02-08 19:54:14 +03:00
|
|
|
fn set_contents(
|
|
|
|
&self,
|
|
|
|
selection: ClipboardSelection,
|
|
|
|
clipboard: Option<String>,
|
|
|
|
) -> anyhow::Result<()> {
|
2020-02-03 18:46:52 +03:00
|
|
|
self.sender.send(DecodedPdu {
|
|
|
|
serial: 0,
|
|
|
|
pdu: Pdu::SetClipboard(SetClipboard {
|
2020-06-27 02:49:07 +03:00
|
|
|
pane_id: self.pane_id,
|
2020-02-03 18:46:52 +03:00
|
|
|
clipboard,
|
2021-02-08 19:54:14 +03:00
|
|
|
selection,
|
2020-02-03 18:46:52 +03:00
|
|
|
}),
|
|
|
|
})?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-03 20:36:23 +03:00
|
|
|
async fn split_pane(split: SplitPane, sender: PduSender) -> anyhow::Result<Pdu> {
|
2020-09-28 09:07:33 +03:00
|
|
|
let mux = Mux::get().unwrap();
|
2022-01-14 20:59:20 +03:00
|
|
|
let (_pane_domain_id, window_id, tab_id) = mux
|
2020-09-28 09:07:33 +03:00
|
|
|
.resolve_pane_id(split.pane_id)
|
|
|
|
.ok_or_else(|| anyhow!("pane_id {} invalid", split.pane_id))?;
|
|
|
|
|
2022-01-14 20:59:20 +03:00
|
|
|
let (pane, size) = mux
|
|
|
|
.split_pane(
|
|
|
|
split.pane_id,
|
|
|
|
split.direction,
|
|
|
|
split.command,
|
|
|
|
split.command_dir,
|
|
|
|
split.domain,
|
|
|
|
)
|
2020-09-28 09:07:33 +03:00
|
|
|
.await?;
|
|
|
|
|
|
|
|
let clip: Arc<dyn Clipboard> = Arc::new(RemoteClipboard {
|
|
|
|
pane_id: pane.pane_id(),
|
|
|
|
sender,
|
|
|
|
});
|
|
|
|
pane.set_clipboard(&clip);
|
|
|
|
|
|
|
|
Ok::<Pdu, anyhow::Error>(Pdu::SpawnResponse(SpawnResponse {
|
|
|
|
pane_id: pane.pane_id(),
|
|
|
|
tab_id: tab_id,
|
|
|
|
window_id,
|
|
|
|
size,
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
2021-03-28 23:57:29 +03:00
|
|
|
async fn domain_spawn_v2(spawn: SpawnV2, sender: PduSender) -> anyhow::Result<Pdu> {
|
|
|
|
let mux = Mux::get().unwrap();
|
|
|
|
|
2022-01-14 20:30:15 +03:00
|
|
|
let (tab, pane, window_id) = mux
|
|
|
|
.spawn_tab_or_window(
|
|
|
|
spawn.window_id,
|
|
|
|
spawn.domain,
|
|
|
|
spawn.command,
|
|
|
|
spawn.command_dir,
|
|
|
|
spawn.size,
|
|
|
|
)
|
2021-03-28 23:57:29 +03:00
|
|
|
.await?;
|
|
|
|
|
|
|
|
let clip: Arc<dyn Clipboard> = Arc::new(RemoteClipboard {
|
|
|
|
pane_id: pane.pane_id(),
|
|
|
|
sender,
|
|
|
|
});
|
|
|
|
pane.set_clipboard(&clip);
|
|
|
|
|
|
|
|
Ok::<Pdu, anyhow::Error>(Pdu::SpawnResponse(SpawnResponse {
|
|
|
|
pane_id: pane.pane_id(),
|
|
|
|
tab_id: tab.tab_id(),
|
|
|
|
window_id,
|
|
|
|
size: tab.get_size(),
|
|
|
|
}))
|
|
|
|
}
|