aboutsummaryrefslogtreecommitdiff
path: root/src/app/input
diff options
context:
space:
mode:
Diffstat (limited to 'src/app/input')
-rw-r--r--src/app/input/mod.rs13
-rw-r--r--src/app/input/terminal.rs175
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)
}
}