2019-11-09 06:55:12 +03:00
|
|
|
//! a tab hosting a termwiz terminal applet
|
|
|
|
//! The idea is to use these when wezterm needs to request
|
|
|
|
//! input from the user as part of eg: setting up an ssh
|
|
|
|
//! session.
|
|
|
|
|
|
|
|
use crate::font::FontConfiguration;
|
2020-01-16 21:31:53 +03:00
|
|
|
use crate::frontend::front_end;
|
2019-11-09 06:55:12 +03:00
|
|
|
use crate::mux::domain::{alloc_domain_id, Domain, DomainId, DomainState};
|
2020-06-03 17:58:20 +03:00
|
|
|
use crate::mux::renderable::Renderable;
|
2020-06-27 02:49:07 +03:00
|
|
|
use crate::mux::tab::{alloc_pane_id, Pane, PaneId, Tab};
|
2019-11-09 06:55:12 +03:00
|
|
|
use crate::mux::window::WindowId;
|
|
|
|
use crate::mux::Mux;
|
2019-12-15 08:43:05 +03:00
|
|
|
use anyhow::{bail, Error};
|
2020-01-17 02:55:15 +03:00
|
|
|
use async_trait::async_trait;
|
2020-01-26 00:00:16 +03:00
|
|
|
use crossbeam::channel::{unbounded as channel, Receiver, Sender};
|
2020-06-03 17:58:20 +03:00
|
|
|
use filedescriptor::{FileDescriptor, Pipe};
|
2019-11-09 06:55:12 +03:00
|
|
|
use portable_pty::*;
|
|
|
|
use std::cell::RefCell;
|
|
|
|
use std::cell::RefMut;
|
2020-06-06 04:03:19 +03:00
|
|
|
use std::io::BufWriter;
|
2020-06-03 17:58:20 +03:00
|
|
|
use std::io::Write;
|
2019-11-09 06:55:12 +03:00
|
|
|
use std::rc::Rc;
|
2020-01-05 19:35:50 +03:00
|
|
|
use std::sync::Arc;
|
2019-11-09 06:55:12 +03:00
|
|
|
use std::time::Duration;
|
2020-06-03 17:58:20 +03:00
|
|
|
use termwiz::caps::{Capabilities, ColorLevel, ProbeHints};
|
2020-03-02 20:12:58 +03:00
|
|
|
use termwiz::input::{InputEvent, KeyEvent, MouseEvent as TermWizMouseEvent};
|
2019-11-09 08:11:22 +03:00
|
|
|
use termwiz::lineedit::*;
|
2020-06-03 17:58:20 +03:00
|
|
|
use termwiz::render::terminfo::TerminfoRenderer;
|
|
|
|
use termwiz::surface::Change;
|
2019-11-09 08:11:22 +03:00
|
|
|
use termwiz::terminal::{ScreenSize, Terminal, TerminalWaker};
|
2020-01-15 09:06:13 +03:00
|
|
|
use url::Url;
|
2020-06-13 19:04:13 +03:00
|
|
|
use wezterm_term::color::ColorPalette;
|
|
|
|
use wezterm_term::{KeyCode, KeyModifiers, MouseEvent};
|
2019-11-09 06:55:12 +03:00
|
|
|
|
|
|
|
struct TermWizTerminalDomain {
|
|
|
|
domain_id: DomainId,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TermWizTerminalDomain {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
let domain_id = alloc_domain_id();
|
|
|
|
Self { domain_id }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-17 02:55:15 +03:00
|
|
|
#[async_trait(?Send)]
|
2019-11-09 06:55:12 +03:00
|
|
|
impl Domain for TermWizTerminalDomain {
|
2020-01-17 03:36:13 +03:00
|
|
|
async fn spawn(
|
2019-11-09 06:55:12 +03:00
|
|
|
&self,
|
|
|
|
_size: PtySize,
|
|
|
|
_command: Option<CommandBuilder>,
|
2020-01-11 10:18:59 +03:00
|
|
|
_command_dir: Option<String>,
|
2019-11-09 06:55:12 +03:00
|
|
|
_window: WindowId,
|
2020-06-27 02:49:07 +03:00
|
|
|
) -> anyhow::Result<Rc<Tab>> {
|
|
|
|
bail!("cannot spawn tabs in a TermWizTerminalPane");
|
2019-11-09 06:55:12 +03:00
|
|
|
}
|
|
|
|
|
2020-03-02 20:12:58 +03:00
|
|
|
fn spawnable(&self) -> bool {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
2019-11-09 06:55:12 +03:00
|
|
|
fn domain_id(&self) -> DomainId {
|
|
|
|
self.domain_id
|
|
|
|
}
|
|
|
|
|
|
|
|
fn domain_name(&self) -> &str {
|
|
|
|
"TermWizTerminalDomain"
|
|
|
|
}
|
2020-01-17 02:55:15 +03:00
|
|
|
async fn attach(&self) -> anyhow::Result<()> {
|
|
|
|
Ok(())
|
2019-11-09 06:55:12 +03:00
|
|
|
}
|
|
|
|
|
2019-12-15 08:43:05 +03:00
|
|
|
fn detach(&self) -> anyhow::Result<()> {
|
2019-11-09 06:55:12 +03:00
|
|
|
bail!("detach not implemented for TermWizTerminalDomain");
|
|
|
|
}
|
|
|
|
|
|
|
|
fn state(&self) -> DomainState {
|
|
|
|
DomainState::Attached
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-27 02:49:07 +03:00
|
|
|
pub struct TermWizTerminalPane {
|
|
|
|
pane_id: PaneId,
|
2019-11-09 06:55:12 +03:00
|
|
|
domain_id: DomainId,
|
2020-06-13 19:04:13 +03:00
|
|
|
terminal: RefCell<wezterm_term::Terminal>,
|
2020-06-03 17:58:20 +03:00
|
|
|
input_tx: Sender<InputEvent>,
|
|
|
|
dead: RefCell<bool>,
|
|
|
|
writer: RefCell<Vec<u8>>,
|
|
|
|
render_rx: FileDescriptor,
|
2019-11-09 06:55:12 +03:00
|
|
|
}
|
|
|
|
|
2020-06-27 02:49:07 +03:00
|
|
|
impl TermWizTerminalPane {
|
2020-01-18 12:37:08 +03:00
|
|
|
fn new(
|
|
|
|
domain_id: DomainId,
|
|
|
|
width: usize,
|
|
|
|
height: usize,
|
|
|
|
input_tx: Sender<InputEvent>,
|
2020-06-03 17:58:20 +03:00
|
|
|
render_rx: FileDescriptor,
|
2020-01-18 12:37:08 +03:00
|
|
|
) -> Self {
|
2020-06-27 02:49:07 +03:00
|
|
|
let pane_id = alloc_pane_id();
|
2020-01-18 12:37:08 +03:00
|
|
|
|
2020-06-13 19:04:13 +03:00
|
|
|
let terminal = RefCell::new(wezterm_term::Terminal::new(
|
2020-06-03 17:58:20 +03:00
|
|
|
height,
|
|
|
|
width,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
std::sync::Arc::new(crate::config::TermConfig {}),
|
|
|
|
"WezTerm",
|
|
|
|
crate::wezterm_version(),
|
|
|
|
Box::new(Vec::new()), // FIXME: connect to something?
|
|
|
|
));
|
2020-01-18 12:37:08 +03:00
|
|
|
|
2019-11-09 06:55:12 +03:00
|
|
|
Self {
|
2020-06-27 02:49:07 +03:00
|
|
|
pane_id,
|
2019-11-09 06:55:12 +03:00
|
|
|
domain_id,
|
2020-06-03 17:58:20 +03:00
|
|
|
terminal,
|
|
|
|
writer: RefCell::new(Vec::new()),
|
|
|
|
render_rx,
|
|
|
|
input_tx,
|
|
|
|
dead: RefCell::new(false),
|
2019-11-09 06:55:12 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-27 02:49:07 +03:00
|
|
|
impl Pane for TermWizTerminalPane {
|
|
|
|
fn pane_id(&self) -> PaneId {
|
|
|
|
self.pane_id
|
2019-11-09 06:55:12 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn renderer(&self) -> RefMut<dyn Renderable> {
|
2020-06-03 17:58:20 +03:00
|
|
|
RefMut::map(self.terminal.borrow_mut(), |t| &mut *t)
|
2019-11-09 06:55:12 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get_title(&self) -> String {
|
2020-06-03 17:58:20 +03:00
|
|
|
self.terminal.borrow_mut().get_title().to_string()
|
2019-11-09 06:55:12 +03:00
|
|
|
}
|
|
|
|
|
2019-12-15 08:43:05 +03:00
|
|
|
fn send_paste(&self, text: &str) -> anyhow::Result<()> {
|
2019-11-09 06:55:12 +03:00
|
|
|
let paste = InputEvent::Paste(text.to_string());
|
2020-06-03 17:58:20 +03:00
|
|
|
self.input_tx.send(paste)?;
|
2019-11-09 06:55:12 +03:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-12-15 08:43:05 +03:00
|
|
|
fn reader(&self) -> anyhow::Result<Box<dyn std::io::Read + Send>> {
|
2020-06-03 17:58:20 +03:00
|
|
|
Ok(Box::new(self.render_rx.try_clone()?))
|
2019-11-09 06:55:12 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn writer(&self) -> RefMut<dyn std::io::Write> {
|
2020-03-02 20:12:58 +03:00
|
|
|
self.writer.borrow_mut()
|
2019-11-09 06:55:12 +03:00
|
|
|
}
|
|
|
|
|
2019-12-15 08:43:05 +03:00
|
|
|
fn resize(&self, size: PtySize) -> anyhow::Result<()> {
|
2020-06-03 17:58:20 +03:00
|
|
|
self.input_tx.send(InputEvent::Resized {
|
2020-03-08 19:45:39 +03:00
|
|
|
rows: size.rows as usize,
|
|
|
|
cols: size.cols as usize,
|
|
|
|
})?;
|
2020-06-03 17:58:20 +03:00
|
|
|
|
|
|
|
self.terminal.borrow_mut().resize(
|
|
|
|
size.rows as usize,
|
|
|
|
size.cols as usize,
|
|
|
|
size.pixel_width as usize,
|
|
|
|
size.pixel_height as usize,
|
|
|
|
);
|
|
|
|
|
2019-11-09 06:55:12 +03:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-12-15 08:43:05 +03:00
|
|
|
fn key_down(&self, key: KeyCode, modifiers: KeyModifiers) -> anyhow::Result<()> {
|
2019-11-09 06:55:12 +03:00
|
|
|
let event = InputEvent::Key(KeyEvent { key, modifiers });
|
2020-06-03 17:58:20 +03:00
|
|
|
if let Err(e) = self.input_tx.send(event) {
|
|
|
|
*self.dead.borrow_mut() = true;
|
|
|
|
return Err(e.into());
|
|
|
|
}
|
2019-11-09 06:55:12 +03:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-05-30 17:44:53 +03:00
|
|
|
fn mouse_event(&self, event: MouseEvent) -> anyhow::Result<()> {
|
2020-03-02 20:12:58 +03:00
|
|
|
use termwiz::input::MouseButtons as Buttons;
|
2020-06-13 19:04:13 +03:00
|
|
|
use wezterm_term::input::MouseButton;
|
2020-03-02 20:12:58 +03:00
|
|
|
|
|
|
|
let mouse_buttons = match event.button {
|
|
|
|
MouseButton::Left => Buttons::LEFT,
|
|
|
|
MouseButton::Middle => Buttons::MIDDLE,
|
|
|
|
MouseButton::Right => Buttons::RIGHT,
|
|
|
|
MouseButton::WheelUp(_) => Buttons::VERT_WHEEL | Buttons::WHEEL_POSITIVE,
|
|
|
|
MouseButton::WheelDown(_) => Buttons::VERT_WHEEL,
|
|
|
|
MouseButton::None => Buttons::NONE,
|
|
|
|
};
|
|
|
|
|
|
|
|
let event = InputEvent::Mouse(TermWizMouseEvent {
|
|
|
|
x: event.x as u16,
|
|
|
|
y: event.y as u16,
|
|
|
|
mouse_buttons,
|
|
|
|
modifiers: event.modifiers,
|
|
|
|
});
|
2020-06-03 17:58:20 +03:00
|
|
|
if let Err(e) = self.input_tx.send(event) {
|
|
|
|
*self.dead.borrow_mut() = true;
|
|
|
|
return Err(e.into());
|
|
|
|
}
|
2019-11-09 06:55:12 +03:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-06-03 17:58:20 +03:00
|
|
|
fn advance_bytes(&self, buf: &[u8]) {
|
|
|
|
self.terminal.borrow_mut().advance_bytes(buf)
|
2019-11-09 06:55:12 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn is_dead(&self) -> bool {
|
2020-06-03 17:58:20 +03:00
|
|
|
*self.dead.borrow()
|
2019-11-09 06:55:12 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn palette(&self) -> ColorPalette {
|
2020-06-03 17:58:20 +03:00
|
|
|
self.terminal.borrow().palette()
|
2019-11-09 06:55:12 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn domain_id(&self) -> DomainId {
|
|
|
|
self.domain_id
|
|
|
|
}
|
|
|
|
|
2020-01-03 07:02:59 +03:00
|
|
|
fn is_mouse_grabbed(&self) -> bool {
|
2020-06-03 17:58:20 +03:00
|
|
|
self.terminal.borrow().is_mouse_grabbed()
|
2020-01-03 07:02:59 +03:00
|
|
|
}
|
2020-01-11 10:18:15 +03:00
|
|
|
|
2020-01-15 09:06:13 +03:00
|
|
|
fn get_current_working_dir(&self) -> Option<Url> {
|
2020-06-03 17:58:20 +03:00
|
|
|
self.terminal.borrow().get_current_dir().cloned()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn erase_scrollback(&self) {
|
|
|
|
self.terminal.borrow_mut().erase_scrollback();
|
2020-01-11 10:18:15 +03:00
|
|
|
}
|
2019-11-09 06:55:12 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct TermWizTerminal {
|
2020-06-03 17:58:20 +03:00
|
|
|
render_tx: TermWizTerminalRenderTty,
|
2019-11-09 06:55:12 +03:00
|
|
|
input_rx: Receiver<InputEvent>,
|
2020-06-03 17:58:20 +03:00
|
|
|
renderer: TerminfoRenderer,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct TermWizTerminalRenderTty {
|
2020-06-06 04:03:19 +03:00
|
|
|
render_tx: BufWriter<FileDescriptor>,
|
2019-11-09 06:55:12 +03:00
|
|
|
screen_size: ScreenSize,
|
|
|
|
}
|
|
|
|
|
2020-06-03 17:58:20 +03:00
|
|
|
impl std::io::Write for TermWizTerminalRenderTty {
|
|
|
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
|
|
|
self.render_tx.write(buf)
|
|
|
|
}
|
|
|
|
fn flush(&mut self) -> std::io::Result<()> {
|
|
|
|
self.render_tx.flush()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl termwiz::render::RenderTty for TermWizTerminalRenderTty {
|
|
|
|
fn get_size_in_cells(&mut self) -> anyhow::Result<(usize, usize)> {
|
|
|
|
Ok((self.screen_size.cols, self.screen_size.rows))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-09 06:55:12 +03:00
|
|
|
impl TermWizTerminal {
|
2019-12-15 08:43:05 +03:00
|
|
|
fn do_input_poll(&mut self, wait: Option<Duration>) -> anyhow::Result<Option<InputEvent>> {
|
2019-11-09 06:55:12 +03:00
|
|
|
if let Some(timeout) = wait {
|
|
|
|
match self.input_rx.recv_timeout(timeout) {
|
|
|
|
Ok(input) => Ok(Some(input)),
|
|
|
|
Err(err) => {
|
|
|
|
if err.is_timeout() {
|
|
|
|
Ok(None)
|
|
|
|
} else {
|
|
|
|
Err(err.into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
let input = self.input_rx.recv()?;
|
|
|
|
Ok(Some(input))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl termwiz::terminal::Terminal for TermWizTerminal {
|
2019-12-15 08:43:05 +03:00
|
|
|
fn set_raw_mode(&mut self) -> anyhow::Result<()> {
|
2020-06-03 17:58:20 +03:00
|
|
|
use termwiz::escape::csi::{DecPrivateMode, DecPrivateModeCode, Mode, CSI};
|
|
|
|
|
|
|
|
macro_rules! decset {
|
|
|
|
($variant:ident) => {
|
|
|
|
write!(
|
|
|
|
self.render_tx,
|
|
|
|
"{}",
|
|
|
|
CSI::Mode(Mode::SetDecPrivateMode(DecPrivateMode::Code(
|
|
|
|
DecPrivateModeCode::$variant
|
|
|
|
)))
|
|
|
|
)?;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
decset!(BracketedPaste);
|
|
|
|
decset!(AnyEventMouse);
|
|
|
|
decset!(SGRMouse);
|
|
|
|
self.flush()?;
|
|
|
|
|
2019-11-09 06:55:12 +03:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-12-15 08:43:05 +03:00
|
|
|
fn set_cooked_mode(&mut self) -> anyhow::Result<()> {
|
2019-11-09 06:55:12 +03:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-12-15 08:43:05 +03:00
|
|
|
fn enter_alternate_screen(&mut self) -> anyhow::Result<()> {
|
2020-06-27 02:49:07 +03:00
|
|
|
bail!("TermWizTerminalPane has no alt screen");
|
2019-11-09 06:55:12 +03:00
|
|
|
}
|
|
|
|
|
2019-12-15 08:43:05 +03:00
|
|
|
fn exit_alternate_screen(&mut self) -> anyhow::Result<()> {
|
2020-06-27 02:49:07 +03:00
|
|
|
bail!("TermWizTerminalPane has no alt screen");
|
2019-11-09 06:55:12 +03:00
|
|
|
}
|
|
|
|
|
2019-12-15 08:43:05 +03:00
|
|
|
fn get_screen_size(&mut self) -> anyhow::Result<ScreenSize> {
|
2020-06-03 17:58:20 +03:00
|
|
|
Ok(self.render_tx.screen_size)
|
2019-11-09 06:55:12 +03:00
|
|
|
}
|
|
|
|
|
2019-12-15 08:43:05 +03:00
|
|
|
fn set_screen_size(&mut self, _size: ScreenSize) -> anyhow::Result<()> {
|
2020-06-27 02:49:07 +03:00
|
|
|
bail!("TermWizTerminalPane cannot set screen size");
|
2019-11-09 06:55:12 +03:00
|
|
|
}
|
|
|
|
|
2019-12-15 08:43:05 +03:00
|
|
|
fn render(&mut self, changes: &[Change]) -> anyhow::Result<()> {
|
2020-06-03 17:58:20 +03:00
|
|
|
self.renderer.render_to(changes, &mut self.render_tx)?;
|
2019-11-09 06:55:12 +03:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-12-15 08:43:05 +03:00
|
|
|
fn flush(&mut self) -> anyhow::Result<()> {
|
2020-06-03 17:58:20 +03:00
|
|
|
self.render_tx.render_tx.flush()?;
|
2019-11-09 06:55:12 +03:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-12-15 08:43:05 +03:00
|
|
|
fn poll_input(&mut self, wait: Option<Duration>) -> anyhow::Result<Option<InputEvent>> {
|
2019-11-09 06:55:12 +03:00
|
|
|
self.do_input_poll(wait).map(|i| {
|
|
|
|
if let Some(InputEvent::Resized { cols, rows }) = i.as_ref() {
|
2020-06-03 17:58:20 +03:00
|
|
|
self.render_tx.screen_size.cols = *cols;
|
|
|
|
self.render_tx.screen_size.rows = *rows;
|
2019-11-09 06:55:12 +03:00
|
|
|
}
|
|
|
|
i
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn waker(&self) -> TerminalWaker {
|
|
|
|
// TODO: TerminalWaker assumes that we're a SystemTerminal but that
|
|
|
|
// isn't the case here.
|
|
|
|
panic!("TermWizTerminal::waker called!?");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-27 02:49:07 +03:00
|
|
|
pub fn allocate(width: usize, height: usize) -> (TermWizTerminal, Rc<dyn Pane>) {
|
2020-06-03 17:58:20 +03:00
|
|
|
let render_pipe = Pipe::new().expect("Pipe creation not to fail");
|
|
|
|
|
2020-01-18 12:37:08 +03:00
|
|
|
let (input_tx, input_rx) = channel();
|
|
|
|
|
2020-06-03 17:58:20 +03:00
|
|
|
let renderer = new_wezterm_terminfo_renderer();
|
|
|
|
|
2020-01-18 12:37:08 +03:00
|
|
|
let tw_term = TermWizTerminal {
|
2020-06-03 17:58:20 +03:00
|
|
|
render_tx: TermWizTerminalRenderTty {
|
2020-06-06 04:03:19 +03:00
|
|
|
render_tx: BufWriter::new(render_pipe.write),
|
2020-06-03 17:58:20 +03:00
|
|
|
screen_size: ScreenSize {
|
|
|
|
cols: width,
|
|
|
|
rows: height,
|
|
|
|
xpixel: 0,
|
|
|
|
ypixel: 0,
|
|
|
|
},
|
2020-01-18 12:37:08 +03:00
|
|
|
},
|
2020-06-03 17:58:20 +03:00
|
|
|
input_rx,
|
|
|
|
renderer,
|
2020-01-18 12:37:08 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
let domain_id = 0;
|
2020-06-27 02:49:07 +03:00
|
|
|
let pane = TermWizTerminalPane::new(domain_id, width, height, input_tx, render_pipe.read);
|
2020-06-03 17:58:20 +03:00
|
|
|
|
|
|
|
// Add the tab to the mux so that the output is processed
|
2020-06-27 02:49:07 +03:00
|
|
|
let pane: Rc<dyn Pane> = Rc::new(pane);
|
|
|
|
|
2020-06-03 17:58:20 +03:00
|
|
|
let mux = Mux::get().unwrap();
|
2020-06-27 02:49:07 +03:00
|
|
|
mux.add_pane(&pane).expect("to be able to add pane to mux");
|
2020-06-03 17:58:20 +03:00
|
|
|
|
2020-06-27 02:49:07 +03:00
|
|
|
(tw_term, pane)
|
2020-01-18 12:37:08 +03:00
|
|
|
}
|
|
|
|
|
2020-06-03 17:58:20 +03:00
|
|
|
fn new_wezterm_terminfo_renderer() -> TerminfoRenderer {
|
|
|
|
let data = include_bytes!("../termwiz/data/xterm-256color");
|
|
|
|
let db = terminfo::Database::from_buffer(&data[..]).unwrap();
|
|
|
|
|
|
|
|
TerminfoRenderer::new(
|
|
|
|
Capabilities::new_with_hints(
|
|
|
|
ProbeHints::new_from_env()
|
|
|
|
.term(Some("xterm-256color".into()))
|
|
|
|
.terminfo_db(Some(db))
|
|
|
|
.color_level(Some(ColorLevel::TrueColor))
|
|
|
|
.colorterm(None)
|
|
|
|
.colorterm_bce(None)
|
|
|
|
.term_program(Some("WezTerm".into()))
|
|
|
|
.term_program_version(Some(crate::wezterm_version().into())),
|
|
|
|
)
|
|
|
|
.expect("cannot fail to make internal Capabilities"),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-11-09 06:55:12 +03:00
|
|
|
/// This function spawns a thread and constructs a GUI window with an
|
|
|
|
/// associated termwiz Terminal object to execute the provided function.
|
|
|
|
/// The function is expected to run in a loop to manage input and output
|
|
|
|
/// from the terminal window.
|
|
|
|
/// When it completes its loop it will fulfil a promise and yield
|
|
|
|
/// the return value from the function.
|
2020-01-16 20:15:07 +03:00
|
|
|
pub async fn run<
|
|
|
|
T: Send + 'static,
|
2020-01-26 00:00:16 +03:00
|
|
|
F: Send + 'static + FnOnce(TermWizTerminal) -> anyhow::Result<T>,
|
2020-01-16 20:15:07 +03:00
|
|
|
>(
|
2019-11-09 06:55:12 +03:00
|
|
|
width: usize,
|
|
|
|
height: usize,
|
|
|
|
f: F,
|
2020-01-16 20:15:07 +03:00
|
|
|
) -> anyhow::Result<T> {
|
2020-06-03 17:58:20 +03:00
|
|
|
let render_pipe = Pipe::new().expect("Pipe creation not to fail");
|
|
|
|
let render_rx = render_pipe.read;
|
2019-11-09 06:55:12 +03:00
|
|
|
let (input_tx, input_rx) = channel();
|
|
|
|
|
2020-06-03 17:58:20 +03:00
|
|
|
let renderer = new_wezterm_terminfo_renderer();
|
|
|
|
|
2019-11-09 06:55:12 +03:00
|
|
|
let tw_term = TermWizTerminal {
|
2020-06-03 17:58:20 +03:00
|
|
|
render_tx: TermWizTerminalRenderTty {
|
2020-06-06 04:03:19 +03:00
|
|
|
render_tx: BufWriter::new(render_pipe.write),
|
2020-06-03 17:58:20 +03:00
|
|
|
screen_size: ScreenSize {
|
|
|
|
cols: width,
|
|
|
|
rows: height,
|
|
|
|
xpixel: 0,
|
|
|
|
ypixel: 0,
|
|
|
|
},
|
2019-11-09 06:55:12 +03:00
|
|
|
},
|
2020-06-03 17:58:20 +03:00
|
|
|
input_rx,
|
|
|
|
renderer,
|
2019-11-09 06:55:12 +03:00
|
|
|
};
|
|
|
|
|
2020-01-16 20:15:07 +03:00
|
|
|
async fn register_tab(
|
|
|
|
input_tx: Sender<InputEvent>,
|
2020-06-03 17:58:20 +03:00
|
|
|
render_rx: FileDescriptor,
|
2020-01-16 20:15:07 +03:00
|
|
|
width: usize,
|
|
|
|
height: usize,
|
2020-01-24 19:39:34 +03:00
|
|
|
) -> anyhow::Result<WindowId> {
|
2019-11-09 06:55:12 +03:00
|
|
|
let mux = Mux::get().unwrap();
|
|
|
|
|
|
|
|
// TODO: make a singleton
|
|
|
|
let domain: Arc<dyn Domain> = Arc::new(TermWizTerminalDomain::new());
|
|
|
|
mux.add_domain(&domain);
|
|
|
|
|
|
|
|
let window_id = mux.new_empty_window();
|
|
|
|
|
2020-06-27 02:49:07 +03:00
|
|
|
let pane = TermWizTerminalPane::new(domain.domain_id(), width, height, input_tx, render_rx);
|
|
|
|
let pane: Rc<dyn Pane> = Rc::new(pane);
|
|
|
|
|
|
|
|
let tab = Rc::new(Tab::new());
|
|
|
|
tab.assign_pane(&pane);
|
2019-11-09 06:55:12 +03:00
|
|
|
|
|
|
|
mux.add_tab(&tab)?;
|
|
|
|
mux.add_tab_to_window(&tab, window_id)?;
|
|
|
|
|
2019-12-10 00:35:38 +03:00
|
|
|
let fontconfig = Rc::new(FontConfiguration::new());
|
2019-11-09 06:55:12 +03:00
|
|
|
|
|
|
|
let gui = front_end().unwrap();
|
2019-11-24 19:55:55 +03:00
|
|
|
gui.spawn_new_window(&fontconfig, &tab, window_id)?;
|
2019-11-09 06:55:12 +03:00
|
|
|
|
2020-01-24 19:39:34 +03:00
|
|
|
Ok(window_id)
|
2020-01-16 20:15:07 +03:00
|
|
|
}
|
2019-11-09 06:55:12 +03:00
|
|
|
|
2020-01-24 19:39:34 +03:00
|
|
|
let window_id: WindowId = promise::spawn::spawn_into_main_thread(async move {
|
2020-06-03 17:58:20 +03:00
|
|
|
register_tab(input_tx, render_rx, width, height).await
|
2020-01-24 19:39:34 +03:00
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap_or_else(|| bail!("task panicked or was cancelled"))?;
|
|
|
|
|
|
|
|
let result = promise::spawn::spawn_into_new_thread(move || f(tw_term))
|
|
|
|
.await
|
|
|
|
.unwrap_or_else(|| bail!("task panicked or was cancelled"));
|
|
|
|
|
|
|
|
// Since we're typically called with an outstanding Activity token active,
|
|
|
|
// the dead status of the tab will be ignored until after the activity
|
|
|
|
// resolves. In the case of SSH where (currently!) several prompts may
|
|
|
|
// be shown in succession, we don't want to leave lingering dead windows
|
|
|
|
// on the screen so let's ask the mux to kill off our window now.
|
2020-01-16 20:15:07 +03:00
|
|
|
promise::spawn::spawn_into_main_thread(async move {
|
2020-01-24 19:39:34 +03:00
|
|
|
let mux = Mux::get().unwrap();
|
|
|
|
mux.kill_window(window_id);
|
2019-11-09 06:55:12 +03:00
|
|
|
});
|
|
|
|
|
2020-01-24 19:39:34 +03:00
|
|
|
result
|
2019-11-09 06:55:12 +03:00
|
|
|
}
|
2019-11-09 08:11:22 +03:00
|
|
|
|
2020-01-26 00:00:16 +03:00
|
|
|
#[allow(unused)]
|
2019-11-09 08:11:22 +03:00
|
|
|
pub fn message_box_ok(message: &str) {
|
|
|
|
let title = "wezterm";
|
|
|
|
let message = message.to_string();
|
|
|
|
|
2020-06-03 17:58:20 +03:00
|
|
|
promise::spawn::block_on(run(60, 10, move |mut term| {
|
|
|
|
term.render(&[
|
|
|
|
Change::Title(title.to_string()),
|
|
|
|
Change::Text(message.to_string()),
|
|
|
|
])
|
|
|
|
.map_err(Error::msg)?;
|
|
|
|
|
|
|
|
let mut editor = LineEditor::new(&mut term);
|
|
|
|
editor.set_prompt("press enter to continue.");
|
|
|
|
|
|
|
|
let mut host = NopLineEditorHost::default();
|
|
|
|
editor.read_line(&mut host).ok();
|
|
|
|
Ok(())
|
|
|
|
}))
|
2019-11-09 08:11:22 +03:00
|
|
|
.ok();
|
|
|
|
}
|