term_session_client/
remote_pane.rs1use 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}