From 1a73a2feaa9bd5b8ebcc8e449f333abf402aa802 Mon Sep 17 00:00:00 2001 From: g4c Date: Wed, 15 Sep 2021 11:14:39 +0800 Subject: [PATCH] write into tmux --- mux/src/tmux.rs | 38 +++++++++++++++++++++++--------------- mux/src/tmux_commands.rs | 28 +++++++++++++++++++++++++++- mux/src/tmux_pty.rs | 23 +++++++++++++++++++---- 3 files changed, 69 insertions(+), 20 deletions(-) diff --git a/mux/src/tmux.rs b/mux/src/tmux.rs index c38c70761..564bd74d9 100644 --- a/mux/src/tmux.rs +++ b/mux/src/tmux.rs @@ -46,12 +46,13 @@ pub(crate) struct TmuxTab { pub panes: HashSet, // tmux panes within tmux window } +pub(crate) type TmuxCmdQueue = VecDeque>; pub(crate) struct TmuxDomainState { pub pane_id: PaneId, pub domain_id: DomainId, // parser: RefCell, state: RefCell, - pub cmd_queue: RefCell>>, + pub cmd_queue: Arc>, pub gui_window: RefCell>, pub gui_tabs: RefCell>, pub remote_panes: RefCell>, @@ -73,7 +74,8 @@ impl TmuxDomainState { *self.state.borrow_mut() = State::Idle; } State::WaitingForResponse => { - let cmd = self.cmd_queue.borrow_mut().pop_front().unwrap(); + let mut cmd_queue = self.cmd_queue.as_ref().lock().unwrap(); + let cmd = cmd_queue.pop_front().unwrap(); let domain_id = self.domain_id; *self.state.borrow_mut() = State::Idle; let resp = response.clone(); @@ -120,17 +122,9 @@ impl TmuxDomainState { } // send pending commands to tmux - if *self.state.borrow() == State::Idle && !self.cmd_queue.borrow().is_empty() { - let domain_id = self.domain_id; - promise::spawn::spawn(async move { - let mux = Mux::get().expect("to be called on main thread"); - if let Some(domain) = mux.get_domain(domain_id) { - if let Some(tmux_domain) = domain.downcast_ref::() { - tmux_domain.send_next_command(); - } - } - }) - .detach(); + let cmd_queue = self.cmd_queue.as_ref().lock().unwrap(); + if *self.state.borrow() == State::Idle && !cmd_queue.is_empty() { + TmuxDomainState::kick_off(self.domain_id); } } @@ -138,7 +132,8 @@ impl TmuxDomainState { if *self.state.borrow() != State::Idle { return; } - if let Some(first) = self.cmd_queue.borrow().front() { + let cmd_queue = self.cmd_queue.as_ref().lock().unwrap(); + if let Some(first) = cmd_queue.front() { let cmd = first.get_command(); log::error!("sending cmd {:?}", cmd); let mux = Mux::get().expect("to be called on main thread"); @@ -150,6 +145,18 @@ impl TmuxDomainState { } } + pub fn kick_off(domain_id: usize) { + promise::spawn::spawn_into_main_thread(async move { + let mux = Mux::get().expect("to be called on main thread"); + if let Some(domain) = mux.get_domain(domain_id) { + if let Some(tmux_domain) = domain.downcast_ref::() { + tmux_domain.send_next_command(); + } + } + }) + .detach(); + } + pub fn create_gui_window(&self) { if self.gui_window.borrow().is_none() { let mux = Mux::get().expect("should be call at main thread"); @@ -174,12 +181,13 @@ impl TmuxDomain { pane_id, // parser, state: RefCell::new(State::WaitForInitialGuard), - cmd_queue: RefCell::new(cmd_queue), + cmd_queue: Arc::new(Mutex::new(cmd_queue)), gui_window: RefCell::new(None), gui_tabs: RefCell::new(Vec::default()), remote_panes: RefCell::new(HashMap::default()), tmux_session: RefCell::new(None), }); + Self { inner } } diff --git a/mux/src/tmux_commands.rs b/mux/src/tmux_commands.rs index 0ef33a74a..ed4464ef2 100644 --- a/mux/src/tmux_commands.rs +++ b/mux/src/tmux_commands.rs @@ -9,11 +9,13 @@ use crate::Pane; use anyhow::anyhow; use portable_pty::{MasterPty, PtySize}; use std::collections::HashSet; +use std::fmt::Debug; +use std::fmt::Write; use std::rc::Rc; use std::sync::{Arc, Condvar, Mutex}; use tmux_cc::*; -pub(crate) trait TmuxCommand: Send { +pub(crate) trait TmuxCommand: Send + Debug { fn get_command(&self) -> String; fn process_result(&self, domain_id: DomainId, result: &Guarded) -> anyhow::Result<()>; } @@ -118,7 +120,9 @@ impl TmuxDomainState { } let pane_pty = TmuxPty { + domain_id: self.domain_id, rx: channel.1.clone(), + cmd_queue: self.cmd_queue.clone(), active_lock: active_lock.clone(), master_pane: ref_pane, }; @@ -170,6 +174,7 @@ impl TmuxDomainState { } } +#[derive(Debug)] pub(crate) struct ListAllPanes; impl TmuxCommand for ListAllPanes { fn get_command(&self) -> String { @@ -250,6 +255,7 @@ impl TmuxCommand for ListAllPanes { } } +#[derive(Debug)] pub(crate) struct CapturePane(TmuxPaneId); impl TmuxCommand for CapturePane { fn get_command(&self) -> String { @@ -276,3 +282,23 @@ impl TmuxCommand for CapturePane { Ok(()) } } + +#[derive(Debug)] +pub(crate) struct SendKeys { + pub keys: Vec, + pub pane: TmuxPaneId, +} +impl TmuxCommand for SendKeys { + fn get_command(&self) -> String { + let mut s = String::new(); + for &byte in self.keys.iter() { + write!(&mut s, "0x{:X}\r", byte).expect("unable to write key"); + } + format!("send-keys -t {} {}", self.pane, s) + // FIXME: An unexpected duplicated command will prompt next line, why? + } + + fn process_result(&self, _domain_id: DomainId, _result: &Guarded) -> anyhow::Result<()> { + Ok(()) + } +} diff --git a/mux/src/tmux_pty.rs b/mux/src/tmux_pty.rs index bec2d88d0..d9ee78048 100644 --- a/mux/src/tmux_pty.rs +++ b/mux/src/tmux_pty.rs @@ -1,3 +1,7 @@ +use crate::{ + tmux::{RefTmuxRemotePane, TmuxCmdQueue, TmuxDomainState}, + tmux_commands::SendKeys, +}; use flume; use portable_pty::{Child, ExitStatus, MasterPty}; use std::{ @@ -5,8 +9,6 @@ use std::{ sync::{Arc, Condvar, Mutex}, }; -use crate::tmux::RefTmuxRemotePane; - pub(crate) struct TmuxReader { rx: flume::Receiver, } @@ -27,15 +29,26 @@ impl Read for TmuxReader { // A local tmux pane(tab) based on a tmux pty #[derive(Debug, Clone)] pub(crate) struct TmuxPty { + pub domain_id: usize, pub master_pane: RefTmuxRemotePane, pub rx: flume::Receiver, + pub cmd_queue: Arc>, pub active_lock: Arc<(Mutex, Condvar)>, - // TODO: wx } impl Write for TmuxPty { fn write(&mut self, buf: &[u8]) -> std::io::Result { - // TODO: write to wx of pty + let pane_id = { + let pane_lock = self.master_pane.lock().unwrap(); + pane_lock.pane_id + }; + log::info!("pane:{}, content:{:?}", &pane_id, buf); + let mut cmd_queue = self.cmd_queue.lock().unwrap(); + cmd_queue.push_back(Box::new(SendKeys { + pane: pane_id, + keys: buf.to_vec(), + })); + TmuxDomainState::kick_off(self.domain_id); Ok(0) } @@ -91,8 +104,10 @@ impl MasterPty for TmuxPty { fn try_clone_writer(&self) -> Result, anyhow::Error> { Ok(Box::new(TmuxPty { + domain_id: self.domain_id, master_pane: self.master_pane.clone(), rx: self.rx.clone(), + cmd_queue: self.cmd_queue.clone(), active_lock: self.active_lock.clone(), })) }