term_session_server/
session.rs1use portable_pty::{CommandBuilder, PtySize};
2use term_session_muxio_service_definitions::ChannelName;
3use term_wm_pty_engine::{Pty, PtyResult, PtyStatus};
4
5pub struct Session {
6 pub id: u64,
7 pub pty: Pty,
8 pub title: Option<String>,
9 pub exited: bool,
10 pub exit_code: Option<i32>,
11 pub cols: u16,
12 pub rows: u16,
13}
14
15fn default_shell_command() -> CommandBuilder {
16 #[cfg(not(windows))]
17 let shell = std::env::var("SHELL").unwrap_or_else(|_| "bash".to_string());
18 #[cfg(windows)]
19 let shell = std::env::var("COMSPEC").unwrap_or_else(|_| "cmd.exe".to_string());
20 let mut cmd = CommandBuilder::new(shell);
21 if let Ok(cwd) = std::env::current_dir() {
22 cmd.cwd(cwd);
23 }
24 cmd
25}
26
27impl Session {
28 pub fn spawn(
29 id: u64,
30 cmd: Option<Vec<String>>,
31 cols: u16,
32 rows: u16,
33 channel: Option<&ChannelName>,
34 ) -> PtyResult<Self> {
35 let size = PtySize {
36 rows,
37 cols,
38 pixel_width: 0,
39 pixel_height: 0,
40 };
41 let pty = if let Some(cmd_parts) = &cmd {
42 let mut builder = CommandBuilder::new(&cmd_parts[0]);
43 for arg in &cmd_parts[1..] {
44 builder.arg(arg);
45 }
46 if let Some(ch) = channel {
47 builder.env("TERM_WM_CHANNEL", ch.to_string());
48 }
49 if let Ok(cwd) = std::env::current_dir() {
50 builder.cwd(cwd);
51 }
52 Pty::spawn(builder, size)?
53 } else {
54 let mut builder = default_shell_command();
55 if let Some(ch) = channel {
56 builder.env("TERM_WM_CHANNEL", ch.to_string());
57 }
58 Pty::spawn(builder, size)?
59 };
60 Ok(Self {
61 id,
62 pty,
63 title: None,
64 exited: false,
65 exit_code: None,
66 cols,
67 rows,
68 })
69 }
70
71 pub fn read_output(&mut self) -> Vec<u8> {
72 self.pty.screen();
74 if let Some(title) = self.pty.take_pending_title() {
76 self.title = Some(title);
77 }
78 self.pty.drain_pending()
79 }
80
81 pub fn sync_screen(&mut self) {
86 self.pty.screen();
87 if let Some(title) = self.pty.take_pending_title() {
88 self.title = Some(title);
89 }
90 }
91
92 pub fn check_exited(&mut self) -> bool {
93 if !self.exited && self.pty.has_exited() {
94 self.exited = true;
95 self.exit_code = self.pty.exit_status().map(|s| s.exit_code() as i32);
96 true
97 } else {
98 false
99 }
100 }
101
102 pub fn take_exit_code(&mut self) -> Option<i32> {
103 self.exit_code.take()
104 }
105
106 pub fn generate_snapshot(&mut self) -> Vec<u8> {
107 self.pty.generate_snapshot()
108 }
109
110 pub fn set_status_callback(&mut self, cb: Option<Box<dyn Fn(PtyStatus) + Send + Sync>>) {
111 self.pty.set_status_callback(cb);
112 }
113}