diff options
author | Kirill Bulatov <mail4score@gmail.com> | 2024-03-09 13:32:32 +0200 |
---|---|---|
committer | Christian Duerr <contact@christianduerr.com> | 2024-03-19 02:20:53 +0100 |
commit | 0a859ae3732d0f378474f80107186e46b61148fc (patch) | |
tree | 9cd482ea1aafc053bc4bfe656462a5303d3e4f93 | |
parent | 99e52bab77c2ff1412088a15b9aa2111a193d244 (diff) | |
download | alacritty-0a859ae3732d0f378474f80107186e46b61148fc.tar.gz alacritty-0a859ae3732d0f378474f80107186e46b61148fc.zip |
Send exit code events on child process exit
Fixes #7753.
-rw-r--r-- | alacritty/src/event.rs | 2 | ||||
-rw-r--r-- | alacritty_terminal/src/event.rs | 4 | ||||
-rw-r--r-- | alacritty_terminal/src/event_loop.rs | 7 | ||||
-rw-r--r-- | alacritty_terminal/src/tty/mod.rs | 4 | ||||
-rw-r--r-- | alacritty_terminal/src/tty/unix.rs | 2 | ||||
-rw-r--r-- | alacritty_terminal/src/tty/windows/child.rs | 23 | ||||
-rw-r--r-- | alacritty_terminal/src/tty/windows/mod.rs | 2 |
7 files changed, 31 insertions, 13 deletions
diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs index e1ee20f8..4fa397ad 100644 --- a/alacritty/src/event.rs +++ b/alacritty/src/event.rs @@ -1356,7 +1356,7 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> { TerminalEvent::PtyWrite(text) => self.ctx.write_to_pty(text.into_bytes()), TerminalEvent::MouseCursorDirty => self.reset_mouse_cursor(), TerminalEvent::CursorBlinkingChange => self.ctx.update_cursor_blinking(), - TerminalEvent::Exit | TerminalEvent::Wakeup => (), + TerminalEvent::Exit | TerminalEvent::ChildExit(_) | TerminalEvent::Wakeup => (), }, #[cfg(unix)] EventType::IpcConfig(_) => (), diff --git a/alacritty_terminal/src/event.rs b/alacritty_terminal/src/event.rs index caa1e9d6..e17d41ea 100644 --- a/alacritty_terminal/src/event.rs +++ b/alacritty_terminal/src/event.rs @@ -52,6 +52,9 @@ pub enum Event { /// Shutdown request. Exit, + + /// Child process exited with an error code. + ChildExit(i32), } impl Debug for Event { @@ -69,6 +72,7 @@ impl Debug for Event { Event::Wakeup => write!(f, "Wakeup"), Event::Bell => write!(f, "Bell"), Event::Exit => write!(f, "Exit"), + Event::ChildExit(code) => write!(f, "ChildExit({code})"), } } } diff --git a/alacritty_terminal/src/event_loop.rs b/alacritty_terminal/src/event_loop.rs index 42025b00..929c642d 100644 --- a/alacritty_terminal/src/event_loop.rs +++ b/alacritty_terminal/src/event_loop.rs @@ -259,7 +259,11 @@ where for event in events.iter() { match event.key { tty::PTY_CHILD_EVENT_TOKEN => { - if let Some(tty::ChildEvent::Exited) = self.pty.next_child_event() { + if let Some(tty::ChildEvent::Exited(code)) = self.pty.next_child_event() + { + if let Some(code) = code { + self.event_proxy.send_event(Event::ChildExit(code)); + } if self.hold { // With hold enabled, make sure the PTY is drained. let _ = self.pty_read(&mut state, &mut buf, pipe.as_mut()); @@ -267,7 +271,6 @@ where // Without hold, shutdown the terminal. self.terminal.lock().exit(); } - self.event_proxy.send_event(Event::Wakeup); break 'event_loop; } diff --git a/alacritty_terminal/src/tty/mod.rs b/alacritty_terminal/src/tty/mod.rs index d1bb023c..35d227b8 100644 --- a/alacritty_terminal/src/tty/mod.rs +++ b/alacritty_terminal/src/tty/mod.rs @@ -67,8 +67,8 @@ pub trait EventedReadWrite { /// Events concerning TTY child processes. #[derive(Debug, PartialEq, Eq)] pub enum ChildEvent { - /// Indicates the child has exited. - Exited, + /// Indicates the child has exited, with an error code if available. + Exited(Option<i32>), } /// A pseudoterminal (or PTY). diff --git a/alacritty_terminal/src/tty/unix.rs b/alacritty_terminal/src/tty/unix.rs index 2c06d54f..1034c5a9 100644 --- a/alacritty_terminal/src/tty/unix.rs +++ b/alacritty_terminal/src/tty/unix.rs @@ -381,7 +381,7 @@ impl EventedPty for Pty { None }, Ok(None) => None, - Ok(_) => Some(ChildEvent::Exited), + Ok(exit_status) => Some(ChildEvent::Exited(exit_status.and_then(|s| s.code()))), } } } diff --git a/alacritty_terminal/src/tty/windows/child.rs b/alacritty_terminal/src/tty/windows/child.rs index 7de6563f..dc6b992c 100644 --- a/alacritty_terminal/src/tty/windows/child.rs +++ b/alacritty_terminal/src/tty/windows/child.rs @@ -7,10 +7,10 @@ use std::sync::{mpsc, Arc, Mutex}; use polling::os::iocp::{CompletionPacket, PollerIocpExt}; use polling::{Event, Poller}; -use windows_sys::Win32::Foundation::{BOOLEAN, HANDLE}; +use windows_sys::Win32::Foundation::{BOOLEAN, FALSE, HANDLE}; use windows_sys::Win32::System::Threading::{ - GetProcessId, RegisterWaitForSingleObject, UnregisterWait, INFINITE, WT_EXECUTEINWAITTHREAD, - WT_EXECUTEONLYONCE, + GetExitCodeProcess, GetProcessId, RegisterWaitForSingleObject, UnregisterWait, INFINITE, + WT_EXECUTEINWAITTHREAD, WT_EXECUTEONLYONCE, }; use crate::tty::ChildEvent; @@ -23,6 +23,7 @@ struct Interest { struct ChildExitSender { sender: mpsc::Sender<ChildEvent>, interest: Arc<Mutex<Option<Interest>>>, + child_handle: AtomicPtr<c_void>, } /// WinAPI callback to run when child process exits. @@ -32,7 +33,13 @@ extern "system" fn child_exit_callback(ctx: *mut c_void, timed_out: BOOLEAN) { } let event_tx: Box<_> = unsafe { Box::from_raw(ctx as *mut ChildExitSender) }; - let _ = event_tx.sender.send(ChildEvent::Exited); + + let mut exit_code = 0_u32; + let child_handle = event_tx.child_handle.load(Ordering::Relaxed) as HANDLE; + let status = unsafe { GetExitCodeProcess(child_handle, &mut exit_code) }; + let exit_code = if status == FALSE { None } else { Some(exit_code as i32) }; + event_tx.sender.send(ChildEvent::Exited(exit_code)).ok(); + let interest = event_tx.interest.lock().unwrap(); if let Some(interest) = interest.as_ref() { interest.poller.post(CompletionPacket::new(interest.event)).ok(); @@ -53,7 +60,11 @@ impl ChildExitWatcher { let mut wait_handle: HANDLE = 0; let interest = Arc::new(Mutex::new(None)); - let sender_ref = Box::new(ChildExitSender { sender: event_tx, interest: interest.clone() }); + let sender_ref = Box::new(ChildExitSender { + sender: event_tx, + interest: interest.clone(), + child_handle: AtomicPtr::from(child_handle as *mut c_void), + }); let success = unsafe { RegisterWaitForSingleObject( @@ -145,6 +156,6 @@ mod tests { poller.wait(&mut events, Some(WAIT_TIMEOUT)).unwrap(); assert_eq!(events.iter().next().unwrap().key, PTY_CHILD_EVENT_TOKEN); // Verify that at least one `ChildEvent::Exited` was received. - assert_eq!(child_exit_watcher.event_rx().try_recv(), Ok(ChildEvent::Exited)); + assert_eq!(child_exit_watcher.event_rx().try_recv(), Ok(ChildEvent::Exited(Some(1)))); } } diff --git a/alacritty_terminal/src/tty/windows/mod.rs b/alacritty_terminal/src/tty/windows/mod.rs index e42e2b6c..6af162c8 100644 --- a/alacritty_terminal/src/tty/windows/mod.rs +++ b/alacritty_terminal/src/tty/windows/mod.rs @@ -115,7 +115,7 @@ impl EventedPty for Pty { match self.child_watcher.event_rx().try_recv() { Ok(ev) => Some(ev), Err(TryRecvError::Empty) => None, - Err(TryRecvError::Disconnected) => Some(ChildEvent::Exited), + Err(TryRecvError::Disconnected) => Some(ChildEvent::Exited(None)), } } } |