aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKirill Bulatov <mail4score@gmail.com>2024-03-09 13:32:32 +0200
committerChristian Duerr <contact@christianduerr.com>2024-03-19 02:20:53 +0100
commit0a859ae3732d0f378474f80107186e46b61148fc (patch)
tree9cd482ea1aafc053bc4bfe656462a5303d3e4f93
parent99e52bab77c2ff1412088a15b9aa2111a193d244 (diff)
downloadalacritty-0a859ae3732d0f378474f80107186e46b61148fc.tar.gz
alacritty-0a859ae3732d0f378474f80107186e46b61148fc.zip
Send exit code events on child process exit
Fixes #7753.
-rw-r--r--alacritty/src/event.rs2
-rw-r--r--alacritty_terminal/src/event.rs4
-rw-r--r--alacritty_terminal/src/event_loop.rs7
-rw-r--r--alacritty_terminal/src/tty/mod.rs4
-rw-r--r--alacritty_terminal/src/tty/unix.rs2
-rw-r--r--alacritty_terminal/src/tty/windows/child.rs23
-rw-r--r--alacritty_terminal/src/tty/windows/mod.rs2
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)),
}
}
}