diff --git a/src/input.rs b/src/input.rs index 3e05b7f78..b8d230479 100644 --- a/src/input.rs +++ b/src/input.rs @@ -463,18 +463,10 @@ impl<'a> InputEventQueuer for Reader<'a> { } fn is_blocked(&self) -> bool { - self.blocking_wait.is_some() + self.blocking_wait().is_some() } - fn unblock_input(&mut self) -> bool { - if !self.is_blocked() { - return false; - } - self.blocking_wait = None; - true - } - - fn blocking_wait(&self) -> Option<&BlockingWait> { - self.blocking_wait.as_ref() + fn blocking_wait(&self) -> MutexGuard> { + self.data.blocking_wait() } fn on_mouse_left_click(&mut self, position: ViewportPosition) { diff --git a/src/input_common.rs b/src/input_common.rs index f98b02237..5d9a02e8d 100644 --- a/src/input_common.rs +++ b/src/input_common.rs @@ -25,6 +25,7 @@ use std::os::fd::RawFd; use std::os::unix::ffi::OsStrExt; use std::ptr; use std::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering}; +use std::sync::{Mutex, MutexGuard}; // The range of key codes for inputrc-style keyboard functions. pub const R_END_INPUT_FUNCTIONS: usize = (ReadlineCmd::ReverseRepeatJump as usize) + 1; @@ -801,7 +802,7 @@ pub trait InputEventQueuer { reader, "Received interrupt key, giving up waiting for response from terminal" ); - let ok = self.unblock_input(); + let ok = unblock_input(self.blocking_wait()); assert!(ok); } continue; @@ -1062,7 +1063,9 @@ pub trait InputEventQueuer { if code != 0 || c != b'M' || modifiers.is_some() { return None; } - let Some(wait) = self.blocking_wait() else { + let wait_guard = self.blocking_wait(); + let Some(wait) = &*wait_guard else { + drop(wait_guard); self.on_mouse_left_click(position); return None; }; @@ -1109,7 +1112,8 @@ pub trait InputEventQueuer { return invalid_sequence(buffer); }; FLOG!(reader, "Received cursor position report y:", y, "x:", x); - let Some(BlockingWait::CursorPosition(wait)) = self.blocking_wait() else { + let wait_guard = self.blocking_wait(); + let Some(BlockingWait::CursorPosition(wait)) = &*wait_guard else { CURSOR_POSITION_REPORTING_SUPPORTED.store(true); return None; }; @@ -1124,6 +1128,7 @@ pub trait InputEventQueuer { ImplicitEvent::ScrollbackPushContinuation(y) } }; + drop(wait_guard); self.push_front(CharEvent::Implicit(continuation)); return None; } @@ -1536,15 +1541,13 @@ pub trait InputEventQueuer { } } - fn blocking_wait(&self) -> Option<&BlockingWait> { - None + fn blocking_wait(&self) -> MutexGuard> { + static NO_WAIT: Mutex> = Mutex::new(None); + NO_WAIT.lock().unwrap() } fn is_blocked(&self) -> bool { false } - fn unblock_input(&mut self) -> bool { - false - } fn on_mouse_left_click(&mut self, _position: ViewportPosition) {} @@ -1559,7 +1562,7 @@ pub trait InputEventQueuer { let vintr = shell_modes().c_cc[libc::VINTR]; if vintr != 0 { let interrupt_evt = CharEvent::from_key(Key::from_single_byte(vintr)); - if self.unblock_input() { + if unblock_input(self.blocking_wait()) { FLOG!( reader, "Received interrupt, giving up on waiting for terminal response" @@ -1590,6 +1593,14 @@ pub trait InputEventQueuer { } } +pub(crate) fn unblock_input(mut wait_guard: MutexGuard>) -> bool { + if wait_guard.is_none() { + return false; + } + *wait_guard = None; + true +} + fn invalid_sequence(buffer: &[u8]) -> Option { FLOG!( reader, diff --git a/src/reader.rs b/src/reader.rs index 43a5dac97..786b04cfd 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -82,6 +82,7 @@ use crate::history::{ }; use crate::input::init_input; use crate::input_common::kitty_progressive_enhancements_query; +use crate::input_common::unblock_input; use crate::input_common::BlockingWait; use crate::input_common::Capability; use crate::input_common::CursorPositionWait; @@ -521,7 +522,7 @@ pub struct ReaderData { /// The representation of the current screen contents. screen: Screen, - pub blocking_wait: Option, + pub blocking_wait: Rc>>, /// Data associated with input events. /// This is made public so that InputEventQueuer can be implemented on us. @@ -1159,6 +1160,10 @@ fn reader_received_sighup() -> bool { impl ReaderData { fn new(history: Arc, conf: ReaderConfig) -> Pin> { + let blocking_wait = reader_current_data().map_or_else( + || Rc::new(Mutex::new(Some(BlockingWait::Startup(Queried::NotYet)))), + |parent| parent.blocking_wait.clone(), + ); let input_data = InputData::new(conf.inputfd); Pin::new(Box::new(Self { canary: Rc::new(()), @@ -1176,7 +1181,7 @@ impl ReaderData { last_flash: Default::default(), flash_autosuggestion: false, screen: Screen::new(), - blocking_wait: Some(BlockingWait::Startup(Queried::NotYet)), + blocking_wait, input_data, queued_repaint: false, history, @@ -1203,6 +1208,10 @@ impl ReaderData { })) } + pub(crate) fn blocking_wait(&self) -> MutexGuard> { + self.blocking_wait.lock().unwrap() + } + // We repaint our prompt if fstat reports the tty as having changed. // But don't react to tty changes that we initiated, because of commands or // on-variable events (e.g. for fish_bind_mode). See #3481. @@ -1410,11 +1419,12 @@ impl ReaderData { pub fn request_cursor_position( &mut self, out: &mut Outputter, - cursor_position_wait: Option, + wait: Option, ) { - if let Some(cursor_position_wait) = cursor_position_wait { - assert!(self.blocking_wait.is_none()); - self.blocking_wait = Some(BlockingWait::CursorPosition(cursor_position_wait)); + if let Some(cursor_position_wait) = wait { + let mut wait_guard = self.blocking_wait(); + assert!(wait_guard.is_none()); + *wait_guard = Some(BlockingWait::CursorPosition(cursor_position_wait)); } let _ = out.write_all(b"\x1b[6n"); self.save_screen_state(); @@ -2167,11 +2177,11 @@ impl<'a> Reader<'a> { } } - if zelf.blocking_wait == Some(BlockingWait::Startup(Queried::NotYet)) { + if *zelf.blocking_wait() == Some(BlockingWait::Startup(Queried::NotYet)) { if is_dumb() || IN_MIDNIGHT_COMMANDER.load() || IN_DVTM.load() { - zelf.blocking_wait = None; + *zelf.blocking_wait() = None; } else { - zelf.blocking_wait = Some(BlockingWait::Startup(Queried::Once)); + *zelf.blocking_wait() = Some(BlockingWait::Startup(Queried::Once)); let mut out = Outputter::stdoutput().borrow_mut(); out.begin_buffering(); // Query for kitty keyboard protocol support. @@ -2497,7 +2507,8 @@ impl<'a> Reader<'a> { self.save_screen_state(); } ImplicitEvent::PrimaryDeviceAttribute => { - let Some(wait) = &self.blocking_wait else { + let mut wait_guard = self.blocking_wait(); + let Some(wait) = &*wait_guard else { // Rogue reply. return ControlFlow::Continue(()); }; @@ -2520,22 +2531,23 @@ impl<'a> Reader<'a> { query_capabilities_via_dcs(out.by_ref()); let _ = out.write_all(QUERY_PRIMARY_DEVICE_ATTRIBUTE); out.end_buffering(); + *wait_guard = Some(BlockingWait::Startup(Queried::Twice)); + drop(wait_guard); self.save_screen_state(); - self.blocking_wait = Some(BlockingWait::Startup(Queried::Twice)); return ControlFlow::Continue(()); } } Queried::Twice => (), } - self.unblock_input(); + unblock_input(wait_guard); } ImplicitEvent::MouseLeftClickContinuation(cursor, click_position) => { self.mouse_left_click(cursor, click_position); - self.unblock_input(); + unblock_input(self.blocking_wait()); } ImplicitEvent::ScrollbackPushContinuation(cursor_y) => { self.screen.push_to_scrollback(cursor_y); - self.unblock_input(); + unblock_input(self.blocking_wait()); } }, } @@ -3786,7 +3798,9 @@ impl<'a> Reader<'a> { if !SCROLL_FORWARD_SUPPORTED.load() || !CURSOR_UP_SUPPORTED.load() { return; } - let Some(wait) = self.blocking_wait() else { + let wait_guard = self.blocking_wait(); + let Some(wait) = &*wait_guard else { + drop(wait_guard); self.request_cursor_position( &mut Outputter::stdoutput().borrow_mut(), Some(CursorPositionWait::ScrollbackPush), diff --git a/tests/checks/tmux-read.fish b/tests/checks/tmux-read.fish new file mode 100644 index 000000000..d37c286a5 --- /dev/null +++ b/tests/checks/tmux-read.fish @@ -0,0 +1,20 @@ +#RUN: %fish %s +#REQUIRES: command -v tmux +#REQUIRES: test -z "$CI" + +set -g isolated_tmux_fish_extra_args -C ' + function fish_greeting + set -l name (read) + echo hello $name + end +' +isolated-tmux-start + +isolated-tmux send-keys name Enter 'echo foo' Enter +tmux-sleep +isolated-tmux capture-pane -p +# CHECK: read> name +# CHECK: hello name +# CHECK: prompt 0> echo foo +# CHECK: foo +# CHECK: prompt 1>