max / shop
- Co-Authored-By
- Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 file changed,
+32 insertions,
-7 deletions
| @@ -67,12 +67,27 @@ | |||
| 67 | 67 | ||
| 68 | 68 | let font_data: Vec<u8> = FONT_BYTES.to_vec(); | |
| 69 | 69 | ||
| 70 | - | let shell = std::env::var("SHELL").unwrap_or_else(|_| "/bin/bash".into()); | |
| 70 | + | // `--exec CMD` runs `sh -c CMD` instead of the interactive shell — useful | |
| 71 | + | // for benchmarks and one-shot invocations. Shop exits when the child | |
| 72 | + | // exits (PTY EOF triggers the read=0 branch). | |
| 73 | + | let args: Vec<String> = std::env::args().collect(); | |
| 74 | + | let exec_cmd = args | |
| 75 | + | .iter() | |
| 76 | + | .position(|a| a == "--exec") | |
| 77 | + | .and_then(|i| args.get(i + 1).cloned()); | |
| 78 | + | let (spawn_cmd, spawn_args): (String, Vec<String>) = match &exec_cmd { | |
| 79 | + | Some(cmd) => ("/bin/sh".into(), vec!["-c".into(), cmd.clone()]), | |
| 80 | + | None => ( | |
| 81 | + | std::env::var("SHELL").unwrap_or_else(|_| "/bin/bash".into()), | |
| 82 | + | Vec::new(), | |
| 83 | + | ), | |
| 84 | + | }; | |
| 85 | + | let spawn_args_refs: Vec<&str> = spawn_args.iter().map(String::as_str).collect(); | |
| 71 | 86 | let cols_initial = grid_cols(INITIAL.0); | |
| 72 | 87 | let rows_initial = grid_rows(INITIAL.1); | |
| 73 | 88 | let pty = Pty::spawn( | |
| 74 | - | &shell, | |
| 75 | - | &[], | |
| 89 | + | &spawn_cmd, | |
| 90 | + | &spawn_args_refs, | |
| 76 | 91 | PtySize { | |
| 77 | 92 | cols: cols_initial, | |
| 78 | 93 | rows: rows_initial, | |
| @@ -81,7 +96,7 @@ | |||
| 81 | 96 | }, | |
| 82 | 97 | "xterm-256color", | |
| 83 | 98 | )?; | |
| 84 | - | info!(shell = %shell, "spawned shell"); | |
| 99 | + | info!(spawn = %spawn_cmd, "spawned child"); | |
| 85 | 100 | ||
| 86 | 101 | let conn = Connection::connect_to_env()?; | |
| 87 | 102 | let spec = WindowSpec { | |
| @@ -230,9 +245,19 @@ | |||
| 230 | 245 | app.cursor_phase = !app.cursor_phase; | |
| 231 | 246 | app.pending_redraw = true; | |
| 232 | 247 | } | |
| 233 | - | Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => break, | |
| 234 | - | Err(e) => { | |
| 235 | - | warn!("pty read: {e}"); | |
| 248 | + | Err(e) | |
| 249 | + | if matches!( | |
| 250 | + | e.kind(), | |
| 251 | + | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::Interrupted | |
| 252 | + | ) => | |
| 253 | + | { | |
| 254 | + | break; | |
| 255 | + | } | |
| 256 | + | Err(_) => { | |
| 257 | + | // Linux returns EIO on the PTY master once the slave | |
| 258 | + | // has been closed by the child exiting. Treat any | |
| 259 | + | // non-transient read error as EOF and exit. | |
| 260 | + | app.chrome.exit = true; | |
| 236 | 261 | break; | |
| 237 | 262 | } | |
| 238 | 263 | } |