diff options
Diffstat (limited to 'src/app/input')
| -rw-r--r-- | src/app/input/mod.rs | 13 | ||||
| -rw-r--r-- | src/app/input/terminal.rs | 175 |
2 files changed, 156 insertions, 32 deletions
diff --git a/src/app/input/mod.rs b/src/app/input/mod.rs index 5eee4250..738b7963 100644 --- a/src/app/input/mod.rs +++ b/src/app/input/mod.rs @@ -71,21 +71,23 @@ use super::App; // --------------------------------------------------------------------------- impl App { - pub(super) async fn handle_key(&mut self, key: TerminalKey) { + pub(super) async fn handle_key( + &mut self, + key: TerminalKey, + ) -> Option<super::TerminalInputTarget> { if self.state.popup_pane.is_some() { - self.handle_terminal_key(key).await; - return; + return self.handle_terminal_key(key).await; } let key_event = key.as_key_event(); if modal_paste_target_active(&self.state) && is_modal_paste_shortcut(&key_event) { if let Some(text) = crate::platform::read_clipboard_text() { self.paste_into_active_text_input(&text); } - return; + return None; } match self.state.mode { - Mode::Terminal => self.handle_terminal_key(key).await, + Mode::Terminal => return self.handle_terminal_key(key).await, Mode::Prefix => self.handle_prefix_key(key), Mode::Navigate => self.handle_navigate_key(key), Mode::Copy => self.handle_copy_mode_key(key), @@ -114,6 +116,7 @@ impl App { Mode::Terminal => unreachable!(), }, } + None } pub(super) async fn handle_paste(&mut self, text: String) { diff --git a/src/app/input/terminal.rs b/src/app/input/terminal.rs index 51a3899c..61c6bbb3 100644 --- a/src/app/input/terminal.rs +++ b/src/app/input/terminal.rs @@ -3,20 +3,24 @@ use crossterm::event::KeyCode; use tracing::{debug, warn}; use crate::{ - app::{App, Mode}, + app::{App, Mode, TerminalInputTarget}, input::TerminalKey, }; struct PreparedPaneInput { ws_idx: usize, pane_id: crate::layout::PaneId, + target: TerminalInputTarget, bytes: Bytes, } enum PreparedPopupInput { NotOpen, Consumed, - Bytes(Bytes), + Bytes { + target: TerminalInputTarget, + bytes: Bytes, + }, } fn is_modifier_only_key(code: &KeyCode) -> bool { @@ -24,26 +28,27 @@ fn is_modifier_only_key(code: &KeyCode) -> bool { } impl App { - pub(crate) fn handle_terminal_key_headless(&mut self, key: TerminalKey) { + pub(crate) fn handle_terminal_key_headless( + &mut self, + key: TerminalKey, + ) -> Option<TerminalInputTarget> { match self.prepare_popup_key_forward(key) { PreparedPopupInput::NotOpen => {} - PreparedPopupInput::Consumed => return, - PreparedPopupInput::Bytes(bytes) => { + PreparedPopupInput::Consumed => return None, + PreparedPopupInput::Bytes { target, bytes } => { let Some(runtime) = self.popup_runtime() else { self.close_popup_pane(); - return; + return None; }; - let _ = runtime.try_send_bytes(bytes); - return; + return runtime.try_send_bytes(bytes).is_ok().then_some(target); } } - let Some(input) = self.prepare_terminal_key_forward(key) else { - return; - }; - if let Some(runtime) = self.lookup_runtime_sender(input.ws_idx, input.pane_id) { - let _ = runtime.try_send_bytes(input.bytes); - } + let input = self.prepare_terminal_key_forward(key)?; + let sent = self + .lookup_runtime_sender(input.ws_idx, input.pane_id) + .is_some_and(|runtime| runtime.try_send_bytes(input.bytes).is_ok()); + sent.then_some(input.target) } fn prepare_terminal_key_forward(&mut self, key: TerminalKey) -> Option<PreparedPaneInput> { @@ -116,6 +121,7 @@ impl App { let ws_idx = self.state.active?; let ws = self.state.workspaces.get(ws_idx)?; let pane_id = ws.focused_pane_id()?; + let terminal_id = ws.terminal_id(pane_id)?.clone(); let rt = self.state .runtime_for_pane_in_workspace(&self.terminal_runtimes, ws_idx, pane_id)?; @@ -207,6 +213,7 @@ impl App { Some(PreparedPaneInput { ws_idx, pane_id, + target: TerminalInputTarget { terminal_id }, bytes: Bytes::from(bytes), }) } @@ -215,7 +222,15 @@ impl App { if self.state.popup_pane.is_none() { return PreparedPopupInput::NotOpen; } - let Some(rt) = self.popup_runtime() else { + let Some(terminal_id) = self + .state + .popup_pane + .as_ref() + .map(|popup| popup.terminal_id.clone()) + else { + return PreparedPopupInput::NotOpen; + }; + let Some(rt) = self.terminal_runtimes.get(&terminal_id) else { self.close_popup_pane(); return PreparedPopupInput::Consumed; }; @@ -225,30 +240,136 @@ impl App { if bytes.is_empty() { PreparedPopupInput::Consumed } else { - PreparedPopupInput::Bytes(Bytes::from(bytes)) + PreparedPopupInput::Bytes { + target: TerminalInputTarget { terminal_id }, + bytes: Bytes::from(bytes), + } + } + } + + pub(crate) fn host_keyboard_report_all_requested(&self) -> bool { + let runtime = if self.state.popup_pane.is_some() { + self.popup_runtime() + } else if self.state.mode == Mode::Terminal { + self.state.active.and_then(|ws_idx| { + self.state + .focused_runtime_in_workspace(&self.terminal_runtimes, ws_idx) + }) + } else { + None + }; + + runtime.is_some_and(|runtime| runtime.keyboard_protocol().reports_all_keys()) + } + + fn terminal_input_runtime( + &self, + target: &TerminalInputTarget, + ) -> Option<&crate::terminal::TerminalRuntime> { + if let Some(runtime) = self.terminal_runtimes.get(&target.terminal_id) { + return Some(runtime); + } + #[cfg(test)] + for (ws_idx, workspace) in self.state.workspaces.iter().enumerate() { + for tab in &workspace.tabs { + for (&pane_id, pane) in &tab.panes { + if pane.attached_terminal_id == target.terminal_id { + return self.state.runtime_for_pane_in_workspace( + &self.terminal_runtimes, + ws_idx, + pane_id, + ); + } + } + } } + None + } + + pub(crate) fn forward_terminal_key_to_target_headless( + &self, + target: &TerminalInputTarget, + key: TerminalKey, + ) -> bool { + let Some(runtime) = self.terminal_input_runtime(target) else { + return false; + }; + let bytes = runtime.encode_terminal_key(key); + bytes.is_empty() || runtime.try_send_bytes(Bytes::from(bytes)).is_ok() } - pub(super) async fn handle_terminal_key(&mut self, key: TerminalKey) { + pub(crate) async fn forward_terminal_key_to_target( + &self, + target: &TerminalInputTarget, + key: TerminalKey, + ) -> bool { + let Some(runtime) = self.terminal_input_runtime(target) else { + return false; + }; + let bytes = runtime.encode_terminal_key(key); + bytes.is_empty() || runtime.send_bytes(Bytes::from(bytes)).await.is_ok() + } + + fn take_pressed_keys_for_source( + &mut self, + source_id: crate::app::InputSourceId, + ) -> Vec<crate::app::PressedTerminalKey> { + let pressed = self + .pressed_terminal_keys + .iter() + .filter(|((id, _), _)| *id == source_id) + .map(|(_, pressed)| pressed.clone()) + .collect(); + self.pressed_terminal_keys + .retain(|(id, _), _| *id != source_id); + self.suppressed_repeat_keys + .retain(|(id, _)| *id != source_id); + pressed + } + + pub(crate) fn release_input_source_headless(&mut self, source_id: crate::app::InputSourceId) { + for pressed in self.take_pressed_keys_for_source(source_id) { + let release = pressed + .key + .with_kind(crossterm::event::KeyEventKind::Release); + let _ = self.forward_terminal_key_to_target_headless(&pressed.target, release); + } + } + + pub(crate) async fn release_input_source(&mut self, source_id: crate::app::InputSourceId) { + for pressed in self.take_pressed_keys_for_source(source_id) { + let release = pressed + .key + .with_kind(crossterm::event::KeyEventKind::Release); + let _ = self + .forward_terminal_key_to_target(&pressed.target, release) + .await; + } + } + + pub(super) async fn handle_terminal_key( + &mut self, + key: TerminalKey, + ) -> Option<TerminalInputTarget> { match self.prepare_popup_key_forward(key) { PreparedPopupInput::NotOpen => {} - PreparedPopupInput::Consumed => return, - PreparedPopupInput::Bytes(bytes) => { + PreparedPopupInput::Consumed => return None, + PreparedPopupInput::Bytes { target, bytes } => { let Some(runtime) = self.popup_runtime() else { self.close_popup_pane(); - return; + return None; }; - let _ = runtime.send_bytes(bytes).await; - return; + return runtime.send_bytes(bytes).await.is_ok().then_some(target); } } - let Some(input) = self.prepare_terminal_key_forward(key) else { - return; + let input = self.prepare_terminal_key_forward(key)?; + let sent = if let Some(runtime) = self.lookup_runtime_sender(input.ws_idx, input.pane_id) { + runtime.send_bytes(input.bytes).await.is_ok() + } else { + false }; - if let Some(runtime) = self.lookup_runtime_sender(input.ws_idx, input.pane_id) { - let _ = runtime.send_bytes(input.bytes).await; - } + sent.then_some(input.target) } } |
