Rename terminal-query-state data structure

While at it, extract a function for initialization.  This looks pretty ugly
but it will get better with the next commit.
This commit is contained in:
Johannes Altmanninger
2025-04-25 22:24:06 +02:00
parent 788eddd0e8
commit 9b44a59b80
6 changed files with 110 additions and 111 deletions

View File

@@ -7,7 +7,7 @@
//! //!
//! Type "exit" or "quit" to terminate the program. //! Type "exit" or "quit" to terminate the program.
use std::{ops::ControlFlow, os::unix::prelude::OsStrExt, sync::atomic::Ordering}; use std::{cell::RefCell, ops::ControlFlow, os::unix::prelude::OsStrExt, sync::atomic::Ordering};
use libc::{STDIN_FILENO, TCSANOW, VEOF, VINTR}; use libc::{STDIN_FILENO, TCSANOW, VEOF, VINTR};
@@ -19,20 +19,16 @@
env::env_init, env::env_init,
input_common::{ input_common::{
terminal_protocol_hacks, terminal_protocols_enable_ifn, CharEvent, ImplicitEvent, terminal_protocol_hacks, terminal_protocols_enable_ifn, CharEvent, ImplicitEvent,
InputEventQueue, InputEventQueuer, KeyEvent, InputEventQueue, InputEventQueuer, KeyEvent, Queried, TerminalQuery,
}, },
key::{char_to_symbol, Key, Modifiers}, key::{char_to_symbol, Key, Modifiers},
nix::isatty, nix::isatty,
panic::panic_handler, panic::panic_handler,
print_help::print_help, print_help::print_help,
proc::set_interactive_session, proc::set_interactive_session,
reader::{check_exit_loop_maybe_warning, reader_init}, reader::{check_exit_loop_maybe_warning, initial_query, reader_init},
signal::signal_set_handlers, signal::signal_set_handlers,
terminal::{ terminal::{Capability, KITTY_KEYBOARD_SUPPORTED},
Capability, Output,
TerminalCommand::{QueryKittyKeyboardProgressiveEnhancements, QueryPrimaryDeviceAttribute},
KITTY_KEYBOARD_SUPPORTED,
},
threads, threads,
topic_monitor::topic_monitor_init, topic_monitor::topic_monitor_init,
wchar::prelude::*, wchar::prelude::*,
@@ -155,11 +151,9 @@ fn setup_and_process_keys(
// in fish-proper this is done once a command is run. // in fish-proper this is done once a command is run.
unsafe { libc::tcsetattr(0, TCSANOW, &*shell_modes()) }; unsafe { libc::tcsetattr(0, TCSANOW, &*shell_modes()) };
terminal_protocol_hacks(); terminal_protocol_hacks();
let blocking_query: RefCell<Option<TerminalQuery>> =
streams RefCell::new(Some(TerminalQuery::PrimaryDeviceAttribute(Queried::NotYet)));
.out initial_query(&blocking_query, streams.out, None);
.write_command(QueryKittyKeyboardProgressiveEnhancements);
streams.out.write_command(QueryPrimaryDeviceAttribute);
if continuous_mode { if continuous_mode {
streams.err.append(L!("\n")); streams.err.append(L!("\n"));

View File

@@ -7,8 +7,8 @@
use crate::future::IsSomeAnd; use crate::future::IsSomeAnd;
use crate::global_safety::RelaxedAtomicBool; use crate::global_safety::RelaxedAtomicBool;
use crate::input_common::{ use crate::input_common::{
BlockingWait, CharEvent, CharInputStyle, CursorPositionWait, ImplicitEvent, InputData, CharEvent, CharInputStyle, CursorPositionQuery, ImplicitEvent, InputData, InputEventQueuer,
InputEventQueuer, ReadlineCmd, R_END_INPUT_FUNCTIONS, ReadlineCmd, TerminalQuery, R_END_INPUT_FUNCTIONS,
}; };
use crate::key::ViewportPosition; use crate::key::ViewportPosition;
use crate::key::{self, canonicalize_raw_escapes, ctrl, Key, Modifiers}; use crate::key::{self, canonicalize_raw_escapes, ctrl, Key, Modifiers};
@@ -451,15 +451,15 @@ fn paste_commit(&mut self) {
))); )));
} }
fn blocking_wait(&self) -> RefMut<'_, Option<BlockingWait>> { fn blocking_query(&self) -> RefMut<'_, Option<TerminalQuery>> {
Reader::blocking_wait(self) Reader::blocking_query(self)
} }
fn on_mouse_left_click(&mut self, position: ViewportPosition) { fn on_mouse_left_click(&mut self, position: ViewportPosition) {
FLOG!(reader, "Mouse left click", position); FLOG!(reader, "Mouse left click", position);
self.request_cursor_position( self.request_cursor_position(
&mut Outputter::stdoutput().borrow_mut(), &mut Outputter::stdoutput().borrow_mut(),
CursorPositionWait::MouseLeft(position), CursorPositionQuery::MouseLeft(position),
); );
} }
} }

View File

@@ -755,7 +755,7 @@ pub fn function_set_status(&mut self, status: bool) {
} }
#[derive(Eq, PartialEq)] #[derive(Eq, PartialEq)]
pub enum CursorPositionWait { pub enum CursorPositionQuery {
MouseLeft(ViewportPosition), MouseLeft(ViewportPosition),
ScrollbackPush, ScrollbackPush,
} }
@@ -767,9 +767,9 @@ pub enum Queried {
} }
#[derive(Eq, PartialEq)] #[derive(Eq, PartialEq)]
pub enum BlockingWait { pub enum TerminalQuery {
Startup(Queried), PrimaryDeviceAttribute(Queried),
CursorPosition(CursorPositionWait), CursorPositionReport(CursorPositionQuery),
} }
/// A trait which knows how to produce a stream of input events. /// A trait which knows how to produce a stream of input events.
@@ -777,7 +777,7 @@ pub enum BlockingWait {
pub trait InputEventQueuer { pub trait InputEventQueuer {
/// Return the next event in the queue, or none if the queue is empty. /// Return the next event in the queue, or none if the queue is empty.
fn try_pop(&mut self) -> Option<CharEvent> { fn try_pop(&mut self) -> Option<CharEvent> {
if self.is_blocked() { if self.is_blocked_querying() {
match self.get_input_data().queue.front()? { match self.get_input_data().queue.front()? {
CharEvent::Key(_) | CharEvent::Readline(_) | CharEvent::Command(_) => { CharEvent::Key(_) | CharEvent::Readline(_) | CharEvent::Command(_) => {
return None; // No code execution while blocked. return None; // No code execution while blocked.
@@ -909,7 +909,7 @@ fn try_readch(&mut self, blocking: bool) -> Option<CharEvent> {
Some(seq.chars().skip(1).map(CharEvent::from_char)), Some(seq.chars().skip(1).map(CharEvent::from_char)),
) )
}; };
if self.is_blocked() { if self.is_blocked_querying() {
FLOG!( FLOG!(
reader, reader,
"Still blocked on response from terminal, deferring key event", "Still blocked on response from terminal, deferring key event",
@@ -928,7 +928,7 @@ fn try_readch(&mut self, blocking: bool) -> Option<CharEvent> {
reader, reader,
"Received interrupt key, giving up waiting for response from terminal" "Received interrupt key, giving up waiting for response from terminal"
); );
let ok = unblock_input(self.blocking_wait()); let ok = stop_query(self.blocking_query());
assert!(ok); assert!(ok);
} }
continue; continue;
@@ -1131,15 +1131,15 @@ fn parse_csi(&mut self, buffer: &mut Vec<u8>) -> Option<KeyEvent> {
if code != 0 || c != b'M' || modifiers.is_some() { if code != 0 || c != b'M' || modifiers.is_some() {
return None; return None;
} }
let wait_guard = self.blocking_wait(); let query = self.blocking_query();
let Some(wait) = &*wait_guard else { let Some(query) = &*query else {
drop(wait_guard); drop(query);
self.on_mouse_left_click(position); self.on_mouse_left_click(position);
return None; return None;
}; };
match wait { match query {
BlockingWait::Startup(_) => {} TerminalQuery::PrimaryDeviceAttribute(_) => {}
BlockingWait::CursorPosition(_) => { TerminalQuery::CursorPositionReport(_) => {
// TODO: re-queue it I guess. // TODO: re-queue it I guess.
FLOG!( FLOG!(
reader, reader,
@@ -1180,22 +1180,23 @@ fn parse_csi(&mut self, buffer: &mut Vec<u8>) -> Option<KeyEvent> {
return invalid_sequence(buffer); return invalid_sequence(buffer);
}; };
FLOG!(reader, "Received cursor position report y:", y, "x:", x); FLOG!(reader, "Received cursor position report y:", y, "x:", x);
let wait_guard = self.blocking_wait(); let query = self.blocking_query();
let Some(BlockingWait::CursorPosition(wait)) = &*wait_guard else { use TerminalQuery::CursorPositionReport;
let Some(CursorPositionReport(cursor_pos_query)) = &*query else {
return None; return None;
}; };
let continuation = match wait { let continuation = match cursor_pos_query {
CursorPositionWait::MouseLeft(click_position) => { CursorPositionQuery::MouseLeft(click_position) => {
ImplicitEvent::MouseLeftClickContinuation( ImplicitEvent::MouseLeftClickContinuation(
ViewportPosition { x, y }, ViewportPosition { x, y },
*click_position, *click_position,
) )
} }
CursorPositionWait::ScrollbackPush => { CursorPositionQuery::ScrollbackPush => {
ImplicitEvent::ScrollbackPushContinuation(y) ImplicitEvent::ScrollbackPushContinuation(y)
} }
}; };
drop(wait_guard); drop(query);
self.push_front(CharEvent::Implicit(continuation)); self.push_front(CharEvent::Implicit(continuation));
return None; return None;
} }
@@ -1628,9 +1629,9 @@ fn drop_leading_readline_events(&mut self) {
} }
} }
fn blocking_wait(&self) -> RefMut<'_, Option<BlockingWait>>; fn blocking_query(&self) -> RefMut<'_, Option<TerminalQuery>>;
fn is_blocked(&self) -> bool { fn is_blocked_querying(&self) -> bool {
self.blocking_wait().is_some() self.blocking_query().is_some()
} }
fn on_mouse_left_click(&mut self, _position: ViewportPosition) {} fn on_mouse_left_click(&mut self, _position: ViewportPosition) {}
@@ -1646,7 +1647,7 @@ fn enqueue_interrupt_key(&mut self) {
let vintr = shell_modes().c_cc[libc::VINTR]; let vintr = shell_modes().c_cc[libc::VINTR];
if vintr != 0 { if vintr != 0 {
let interrupt_evt = CharEvent::from_key(KeyEvent::from_single_byte(vintr)); let interrupt_evt = CharEvent::from_key(KeyEvent::from_single_byte(vintr));
if unblock_input(self.blocking_wait()) { if stop_query(self.blocking_query()) {
FLOG!( FLOG!(
reader, reader,
"Received interrupt, giving up on waiting for terminal response" "Received interrupt, giving up on waiting for terminal response"
@@ -1750,12 +1751,8 @@ pub(crate) fn decode_input_byte(
invalid(out_seq, || FLOG!(reader, "Illegal codepoint")) invalid(out_seq, || FLOG!(reader, "Illegal codepoint"))
} }
pub(crate) fn unblock_input(mut wait_guard: RefMut<'_, Option<BlockingWait>>) -> bool { pub(crate) fn stop_query(mut query: RefMut<'_, Option<TerminalQuery>>) -> bool {
if wait_guard.is_none() { query.take().is_some()
return false;
}
*wait_guard = None;
true
} }
fn invalid_sequence(buffer: &[u8]) -> Option<KeyEvent> { fn invalid_sequence(buffer: &[u8]) -> Option<KeyEvent> {
@@ -1784,14 +1781,14 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
/// A simple, concrete implementation of InputEventQueuer. /// A simple, concrete implementation of InputEventQueuer.
pub struct InputEventQueue { pub struct InputEventQueue {
data: InputData, data: InputData,
blocking_wait: RefCell<Option<BlockingWait>>, blocking_query: RefCell<Option<TerminalQuery>>,
} }
impl InputEventQueue { impl InputEventQueue {
pub fn new(in_fd: RawFd) -> Self { pub fn new(in_fd: RawFd) -> Self {
Self { Self {
data: InputData::new(in_fd), data: InputData::new(in_fd),
blocking_wait: RefCell::new(None), blocking_query: RefCell::new(None),
} }
} }
} }
@@ -1810,8 +1807,8 @@ fn select_interrupted(&mut self) {
self.enqueue_interrupt_key(); self.enqueue_interrupt_key();
} }
} }
fn blocking_wait(&self) -> RefMut<'_, Option<BlockingWait>> { fn blocking_query(&self) -> RefMut<'_, Option<TerminalQuery>> {
self.blocking_wait.borrow_mut() self.blocking_query.borrow_mut()
} }
} }

View File

@@ -14,7 +14,7 @@
}; };
use crate::fds::{open_dir, BEST_O_SEARCH}; use crate::fds::{open_dir, BEST_O_SEARCH};
use crate::global_safety::RelaxedAtomicBool; use crate::global_safety::RelaxedAtomicBool;
use crate::input_common::{terminal_protocols_disable_ifn, BlockingWait, Queried}; use crate::input_common::{terminal_protocols_disable_ifn, Queried, TerminalQuery};
use crate::io::IoChain; use crate::io::IoChain;
use crate::job_group::MaybeJobId; use crate::job_group::MaybeJobId;
use crate::operation_context::{OperationContext, EXPANSION_LIMIT_DEFAULT}; use crate::operation_context::{OperationContext, EXPANSION_LIMIT_DEFAULT};
@@ -440,7 +440,7 @@ pub struct Parser {
/// Global event blocks. /// Global event blocks.
pub global_event_blocks: AtomicU64, pub global_event_blocks: AtomicU64,
pub blocking_wait: RefCell<Option<BlockingWait>>, pub blocking_query: RefCell<Option<TerminalQuery>>,
} }
impl Parser { impl Parser {
@@ -458,7 +458,9 @@ pub fn new(variables: Rc<EnvStack>, cancel_behavior: CancelBehavior) -> Parser {
cancel_behavior, cancel_behavior,
profile_items: RefCell::default(), profile_items: RefCell::default(),
global_event_blocks: AtomicU64::new(0), global_event_blocks: AtomicU64::new(0),
blocking_wait: RefCell::new(Some(BlockingWait::Startup(Queried::NotYet))), blocking_query: RefCell::new(Some(TerminalQuery::PrimaryDeviceAttribute(
Queried::NotYet,
))),
}; };
match open_dir(CStr::from_bytes_with_nul(b".\0").unwrap(), BEST_O_SEARCH) { match open_dir(CStr::from_bytes_with_nul(b".\0").unwrap(), BEST_O_SEARCH) {

View File

@@ -23,6 +23,7 @@
#[cfg(not(target_has_atomic = "64"))] #[cfg(not(target_has_atomic = "64"))]
use portable_atomic::AtomicU64; use portable_atomic::AtomicU64;
use std::borrow::Cow; use std::borrow::Cow;
use std::cell::RefCell;
use std::cell::RefMut; use std::cell::RefMut;
use std::cell::UnsafeCell; use std::cell::UnsafeCell;
use std::cmp; use std::cmp;
@@ -79,12 +80,12 @@
SearchType, SearchType,
}; };
use crate::input::init_input; use crate::input::init_input;
use crate::input_common::stop_query;
use crate::input_common::terminal_protocols_disable_ifn; use crate::input_common::terminal_protocols_disable_ifn;
use crate::input_common::unblock_input; use crate::input_common::CursorPositionQuery;
use crate::input_common::BlockingWait;
use crate::input_common::CursorPositionWait;
use crate::input_common::ImplicitEvent; use crate::input_common::ImplicitEvent;
use crate::input_common::Queried; use crate::input_common::Queried;
use crate::input_common::TerminalQuery;
use crate::input_common::IN_DVTM; use crate::input_common::IN_DVTM;
use crate::input_common::IN_MIDNIGHT_COMMANDER; use crate::input_common::IN_MIDNIGHT_COMMANDER;
use crate::input_common::{ use crate::input_common::{
@@ -236,6 +237,31 @@ fn redirect_tty_after_sighup() {
} }
} }
pub(crate) fn initial_query(
blocking_query: &RefCell<Option<TerminalQuery>>,
out: &mut impl Output,
vars: Option<&dyn Environment>,
) {
if *blocking_query.borrow() != Some(TerminalQuery::PrimaryDeviceAttribute(Queried::NotYet)) {
return;
}
*blocking_query.borrow_mut() = {
let query = if is_dumb() || IN_MIDNIGHT_COMMANDER.load() || IN_DVTM.load() {
None
} else {
// Query for kitty keyboard protocol support.
out.write_command(QueryKittyKeyboardProgressiveEnhancements);
out.write_command(QueryXtversion);
if let Some(vars) = vars {
query_capabilities_via_dcs(out.by_ref(), vars);
}
out.write_command(QueryPrimaryDeviceAttribute);
Some(TerminalQuery::PrimaryDeviceAttribute(Queried::Yes))
};
query
};
}
/// The stack of current interactive reading contexts. /// The stack of current interactive reading contexts.
fn reader_data_stack() -> &'static mut Vec<Pin<Box<ReaderData>>> { fn reader_data_stack() -> &'static mut Vec<Pin<Box<ReaderData>>> {
struct ReaderDataStack(UnsafeCell<Vec<Pin<Box<ReaderData>>>>); struct ReaderDataStack(UnsafeCell<Vec<Pin<Box<ReaderData>>>>);
@@ -1498,16 +1524,16 @@ pub fn combine_command_and_autosuggestion(
} }
impl<'a> Reader<'a> { impl<'a> Reader<'a> {
pub(crate) fn blocking_wait(&self) -> RefMut<'_, Option<BlockingWait>> { pub(crate) fn blocking_query(&self) -> RefMut<'_, Option<TerminalQuery>> {
self.parser.blocking_wait.borrow_mut() self.parser.blocking_query.borrow_mut()
} }
pub fn request_cursor_position(&mut self, out: &mut Outputter, wait: CursorPositionWait) { pub fn request_cursor_position(&mut self, out: &mut Outputter, q: CursorPositionQuery) {
let mut wait_guard = self.blocking_wait(); let mut query = self.blocking_query();
assert!(wait_guard.is_none()); assert!(query.is_none());
*wait_guard = Some(BlockingWait::CursorPosition(wait)); *query = Some(TerminalQuery::CursorPositionReport(q));
out.write_command(QueryCursorPosition); out.write_command(QueryCursorPosition);
drop(wait_guard); drop(query);
self.save_screen_state(); self.save_screen_state();
} }
@@ -2179,21 +2205,11 @@ fn readline(&mut self, nchars: Option<NonZeroUsize>) -> Option<WString> {
} }
} }
if *self.blocking_wait() == Some(BlockingWait::Startup(Queried::NotYet)) { initial_query(
if is_dumb() || IN_MIDNIGHT_COMMANDER.load() || IN_DVTM.load() { &self.parser.blocking_query,
*self.blocking_wait() = None; &mut BufferedOutputter::new(Outputter::stdoutput()),
} else { Some(self.parser.vars()),
*self.blocking_wait() = Some(BlockingWait::Startup(Queried::Yes)); );
let mut out = Outputter::stdoutput().borrow_mut();
out.begin_buffering();
// Query for kitty keyboard protocol support.
out.write_command(QueryKittyKeyboardProgressiveEnhancements);
out.write_command(QueryXtversion);
query_capabilities_via_dcs(out.by_ref(), self.parser.vars());
out.write_command(QueryPrimaryDeviceAttribute);
out.end_buffering();
}
}
// HACK: Don't abandon line for the first prompt, because // HACK: Don't abandon line for the first prompt, because
// if we're started with the terminal it might not have settled, // if we're started with the terminal it might not have settled,
@@ -2511,35 +2527,25 @@ fn handle_char_event(&mut self, injected_event: Option<CharEvent>) -> ControlFlo
self.save_screen_state(); self.save_screen_state();
} }
ImplicitEvent::PrimaryDeviceAttribute => { ImplicitEvent::PrimaryDeviceAttribute => {
let wait_guard = self.blocking_wait(); let query = self.blocking_query();
let Some(wait) = &*wait_guard else { if !matches!(*query, Some(TerminalQuery::PrimaryDeviceAttribute(_))) {
// Rogue reply. // Rogue reply.
return ControlFlow::Continue(()); return ControlFlow::Continue(());
};
let BlockingWait::Startup(stage) = wait else {
// Rogue reply.
return ControlFlow::Continue(());
};
match stage {
Queried::NotYet => panic!(),
Queried::Yes => {
if KITTY_KEYBOARD_SUPPORTED.load(Ordering::Relaxed)
== Capability::Unknown as _
{
KITTY_KEYBOARD_SUPPORTED
.store(Capability::NotSupported as _, Ordering::Release);
}
}
} }
unblock_input(wait_guard); if KITTY_KEYBOARD_SUPPORTED.load(Ordering::Relaxed) == Capability::Unknown as _
{
KITTY_KEYBOARD_SUPPORTED
.store(Capability::NotSupported as _, Ordering::Release);
}
stop_query(query);
} }
ImplicitEvent::MouseLeftClickContinuation(cursor, click_position) => { ImplicitEvent::MouseLeftClickContinuation(cursor, click_position) => {
self.mouse_left_click(cursor, click_position); self.mouse_left_click(cursor, click_position);
unblock_input(self.blocking_wait()); stop_query(self.blocking_query());
} }
ImplicitEvent::ScrollbackPushContinuation(cursor_y) => { ImplicitEvent::ScrollbackPushContinuation(cursor_y) => {
self.screen.push_to_scrollback(cursor_y); self.screen.push_to_scrollback(cursor_y);
unblock_input(self.blocking_wait()); stop_query(self.blocking_query());
} }
}, },
} }
@@ -3798,18 +3804,18 @@ fn handle_readline_command(&mut self, c: ReadlineCmd) {
if !SCROLL_FORWARD_SUPPORTED.load() { if !SCROLL_FORWARD_SUPPORTED.load() {
return; return;
} }
let wait_guard = self.blocking_wait(); let query = self.blocking_query();
let Some(wait) = &*wait_guard else { let Some(query) = &*query else {
drop(wait_guard); drop(query);
self.request_cursor_position( self.request_cursor_position(
&mut Outputter::stdoutput().borrow_mut(), &mut Outputter::stdoutput().borrow_mut(),
CursorPositionWait::ScrollbackPush, CursorPositionQuery::ScrollbackPush,
); );
return; return;
}; };
match wait { match query {
BlockingWait::Startup(_) => panic!(), TerminalQuery::PrimaryDeviceAttribute(_) => panic!(),
BlockingWait::CursorPosition(_) => { TerminalQuery::CursorPositionReport(_) => {
// TODO: re-queue it I guess. // TODO: re-queue it I guess.
FLOG!( FLOG!(
reader, reader,

View File

@@ -1,6 +1,6 @@
use crate::env::EnvStack; use crate::env::EnvStack;
use crate::input::{EventQueuePeeker, InputMappingSet, KeyNameStyle, DEFAULT_BIND_MODE}; use crate::input::{EventQueuePeeker, InputMappingSet, KeyNameStyle, DEFAULT_BIND_MODE};
use crate::input_common::{BlockingWait, CharEvent, InputData, InputEventQueuer, KeyEvent}; use crate::input_common::{CharEvent, InputData, InputEventQueuer, KeyEvent, TerminalQuery};
use crate::key::Key; use crate::key::Key;
use crate::wchar::prelude::*; use crate::wchar::prelude::*;
use std::cell::{RefCell, RefMut}; use std::cell::{RefCell, RefMut};
@@ -8,7 +8,7 @@
struct TestInputEventQueuer { struct TestInputEventQueuer {
input_data: InputData, input_data: InputData,
blocking_wait: RefCell<Option<BlockingWait>>, blocking_query: RefCell<Option<TerminalQuery>>,
} }
impl InputEventQueuer for TestInputEventQueuer { impl InputEventQueuer for TestInputEventQueuer {
@@ -18,8 +18,8 @@ fn get_input_data(&self) -> &InputData {
fn get_input_data_mut(&mut self) -> &mut InputData { fn get_input_data_mut(&mut self) -> &mut InputData {
&mut self.input_data &mut self.input_data
} }
fn blocking_wait(&self) -> RefMut<'_, Option<BlockingWait>> { fn blocking_query(&self) -> RefMut<'_, Option<TerminalQuery>> {
self.blocking_wait.borrow_mut() self.blocking_query.borrow_mut()
} }
} }
@@ -28,7 +28,7 @@ fn test_input() {
let vars = Rc::new(EnvStack::new()); let vars = Rc::new(EnvStack::new());
let mut input = TestInputEventQueuer { let mut input = TestInputEventQueuer {
input_data: InputData::new(i32::MAX), // value doesn't matter since we don't read from it input_data: InputData::new(i32::MAX), // value doesn't matter since we don't read from it
blocking_wait: RefCell::new(None), blocking_query: RefCell::new(None),
}; };
// Ensure sequences are order independent. Here we add two bindings where the first is a prefix // Ensure sequences are order independent. Here we add two bindings where the first is a prefix
// of the second, and then emit the second key list. The second binding should be invoked, not // of the second, and then emit the second key list. The second binding should be invoked, not