Skip to main content

term_session_client/
remote_pane.rs

1use std::cell::Cell;
2use std::io;
3use std::sync::{Arc, Mutex};
4
5use muxio_rpc_service::error::RpcServiceError;
6use muxio_tokio_rpc_ipc_client::RpcIpcClient;
7use portable_pty::{ExitStatus, PtySize};
8use term_session_muxio_service_definitions::{CloseSession, ResizePty};
9use term_wm_pty_engine::{Pane, PtyResult};
10use tokio::runtime::Handle;
11use tokio::sync::mpsc;
12use tokio::sync::mpsc::error::TryRecvError;
13
14type InputWriter = Box<dyn FnMut(&[u8]) -> io::Result<()> + Send>;
15
16pub struct RemotePane {
17    pub id: u64,
18    client: std::sync::Arc<RpcIpcClient>,
19    rt: Handle,
20    parser: Arc<Mutex<vt100::Parser>>,
21    exited: Cell<bool>,
22    push_rx: mpsc::UnboundedReceiver<Vec<u8>>,
23    input_writer: InputWriter,
24}
25
26impl RemotePane {
27    pub fn new(
28        id: u64,
29        client: std::sync::Arc<RpcIpcClient>,
30        rt: Handle,
31        cols: u16,
32        rows: u16,
33        push_rx: mpsc::UnboundedReceiver<Vec<u8>>,
34        input_writer: InputWriter,
35    ) -> Self {
36        Self {
37            id,
38            client,
39            rt,
40            parser: Arc::new(Mutex::new(vt100::Parser::new(rows, cols, 0))),
41            exited: Cell::new(false),
42            push_rx,
43            input_writer,
44        }
45    }
46
47    pub fn drain_pushes(&mut self) {
48        loop {
49            match self.push_rx.try_recv() {
50                Ok(data) => {
51                    let mut parser = self.parser.lock().unwrap();
52                    parser.process(&data);
53                }
54                Err(TryRecvError::Disconnected) => {
55                    self.exited.set(true);
56                    break;
57                }
58                Err(TryRecvError::Empty) => break,
59            }
60        }
61    }
62
63    fn rpc_to_pty<E: std::fmt::Display>(e: E) -> Box<dyn std::error::Error + Send + Sync> {
64        Box::new(io::Error::other(format!("{e}")))
65    }
66}
67
68impl Pane for RemotePane {
69    fn exit_status(&self) -> Option<ExitStatus> {
70        None
71    }
72
73    fn resize(&mut self, size: PtySize) -> PtyResult<()> {
74        let result: Result<(), RpcServiceError> = self.rt.block_on(async {
75            use muxio_tokio_rpc_ipc_client::RpcCallPrebuffered;
76            ResizePty::call(&*self.client, (self.id, size.cols, size.rows)).await
77        });
78        {
79            let mut parser = self.parser.lock().unwrap();
80            parser.screen_mut().set_size(size.rows, size.cols);
81        }
82        result.map_err(Self::rpc_to_pty)
83    }
84
85    fn has_exited(&mut self) -> bool {
86        self.exited.get()
87    }
88
89    fn alternate_screen(&mut self) -> bool {
90        let parser = self.parser.lock().unwrap();
91        parser.screen().alternate_screen()
92    }
93
94    fn scrollback(&mut self) -> usize {
95        0
96    }
97
98    fn set_scrollback(&mut self, _rows: usize) {}
99
100    fn scrollback_len(&self) -> usize {
101        0
102    }
103
104    fn write_bytes(&mut self, input: &[u8]) -> io::Result<()> {
105        (self.input_writer)(input)
106    }
107
108    fn shared_parser(&mut self) -> Arc<Mutex<vt100::Parser>> {
109        self.parser.clone()
110    }
111
112    fn max_scrollback(&mut self) -> usize {
113        0
114    }
115
116    fn take_exit_status(&mut self) -> Option<ExitStatus> {
117        None
118    }
119
120    fn bytes_received(&self) -> usize {
121        0
122    }
123
124    fn last_bytes_text(&self) -> String {
125        String::new()
126    }
127
128    fn kill_child(&mut self) -> PtyResult<()> {
129        self.rt
130            .block_on(async {
131                use muxio_tokio_rpc_ipc_client::RpcCallPrebuffered;
132                CloseSession::call(&*self.client, self.id).await
133            })
134            .map_err(Self::rpc_to_pty)?;
135        self.exited.set(true);
136        Ok(())
137    }
138
139    fn take_pending_title(&mut self) -> Option<String> {
140        None
141    }
142}