diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs index d0670951..f3a181be 100644 --- a/src/tests/e2e/cases.rs +++ b/src/tests/e2e/cases.rs @@ -1733,7 +1733,7 @@ pub fn focus_tab_with_layout() { let mut step_is_complete = false; if remote_terminal.status_bar_appears() && remote_terminal.tip_appears() - && remote_terminal.snapshot_contains("Tab #3") + && remote_terminal.snapshot_contains("Tab #9") && remote_terminal.cursor_position_is(63, 2) { step_is_complete = true; diff --git a/zellij-client/src/input_handler.rs b/zellij-client/src/input_handler.rs index 5ae838c4..58d060bf 100644 --- a/zellij-client/src/input_handler.rs +++ b/zellij-client/src/input_handler.rs @@ -95,29 +95,9 @@ impl InputHandler { } } } - Ok(( - InputInstruction::PastedText(( - send_bracketed_paste_start, - raw_bytes, - send_bracketed_paste_end, - )), - _error_context, - )) => { + Ok((InputInstruction::PastedText(raw_bytes), _error_context)) => { if self.mode == InputMode::Normal || self.mode == InputMode::Locked { - if send_bracketed_paste_start { - let bracketed_paste_start = vec![27, 91, 50, 48, 48, 126]; // \u{1b}[200~ - let paste_start_action = Action::Write(bracketed_paste_start); - self.dispatch_action(paste_start_action); - } - - let pasted_text_action = Action::Write(raw_bytes); - self.dispatch_action(pasted_text_action); - - if send_bracketed_paste_end { - let bracketed_paste_end = vec![27, 91, 50, 48, 49, 126]; // \u{1b}[201~ - let paste_end_action = Action::Write(bracketed_paste_end); - self.dispatch_action(paste_end_action); - } + self.dispatch_action(Action::Write(raw_bytes)); } } Ok((InputInstruction::SwitchToMode(input_mode), _error_context)) => { diff --git a/zellij-client/src/lib.rs b/zellij-client/src/lib.rs index 0e4f0e20..739b6e1d 100644 --- a/zellij-client/src/lib.rs +++ b/zellij-client/src/lib.rs @@ -107,7 +107,7 @@ impl ClientInfo { pub(crate) enum InputInstruction { KeyEvent(termion::event::Event, Vec), SwitchToMode(InputMode), - PastedText((bool, Vec, bool)), // (send_brackted_paste_start, pasted_text, send_bracketed_paste_end) + PastedText(Vec), } pub fn start_client( diff --git a/zellij-client/src/os_input_output.rs b/zellij-client/src/os_input_output.rs index 9b8e8430..5d5fa5fe 100644 --- a/zellij-client/src/os_input_output.rs +++ b/zellij-client/src/os_input_output.rs @@ -82,6 +82,7 @@ pub trait ClientOsApi: Send + Sync { fn unset_raw_mode(&self, fd: RawFd); /// Returns the writer that allows writing to standard output. fn get_stdout_writer(&self) -> Box; + fn get_stdin_reader(&self) -> Box; /// Returns the raw contents of standard input. fn read_from_stdin(&self) -> Vec; /// Returns a [`Box`] pointer to this [`ClientOsApi`] struct. @@ -128,6 +129,11 @@ impl ClientOsApi for ClientOsInputOutput { let stdout = ::std::io::stdout(); Box::new(stdout) } + fn get_stdin_reader(&self) -> Box { + let stdin = ::std::io::stdin(); + Box::new(stdin) + } + fn send_to_server(&self, msg: ClientToServerMsg) { self.send_instructions_to_server .lock() diff --git a/zellij-client/src/stdin_handler.rs b/zellij-client/src/stdin_handler.rs index 67149b4d..3abb497a 100644 --- a/zellij-client/src/stdin_handler.rs +++ b/zellij-client/src/stdin_handler.rs @@ -29,143 +29,81 @@ fn keys_to_adjust() -> HashMap, Vec> { keys_to_adjust } -fn bracketed_paste_end_position(stdin_buffer: &[u8]) -> Option { - let bracketed_paste_end = vec![27, 91, 50, 48, 49, 126]; // \u{1b}[201~ - let mut bp_position = 0; - let mut position = None; - for (i, byte) in stdin_buffer.iter().enumerate() { - if Some(byte) == bracketed_paste_end.get(bp_position) { - position = Some(i); - bp_position += 1; - if bp_position == bracketed_paste_end.len() { - break; - } - } else { - bp_position = 0; - position = None; - } - } - if bp_position == bracketed_paste_end.len() { - position - } else { - None - } -} - pub(crate) fn stdin_loop( os_input: Box, send_input_instructions: SenderWithContext, ) { let mut pasting = false; - let bracketed_paste_start = vec![27, 91, 50, 48, 48, 126]; // \u{1b}[200~ + let mut pasted_text = vec![]; + let bracketed_paste_start = termion::event::Event::Unsupported(vec![27, 91, 50, 48, 48, 126]); // \u{1b}[200~ + let bracketed_paste_end = termion::event::Event::Unsupported(vec![27, 91, 50, 48, 49, 126]); // \u{1b}[201~ let csi_mouse_sgr_start = vec![27, 91, 60]; let adjusted_keys = keys_to_adjust(); - loop { - let mut stdin_buffer = os_input.read_from_stdin(); - if pasting - || (stdin_buffer.len() > bracketed_paste_start.len() - && stdin_buffer - .iter() - .take(bracketed_paste_start.len()) - .eq(&bracketed_paste_start)) - { - match bracketed_paste_end_position(&stdin_buffer) { - Some(paste_end_position) => { - let starts_with_bracketed_paste_start = stdin_buffer - .iter() - .take(bracketed_paste_start.len()) - .eq(&bracketed_paste_start); + for key_result in os_input.get_stdin_reader().events_and_raw() { + let (key_event, mut raw_bytes) = key_result.unwrap(); - let ends_with_bracketed_paste_end = true; - - let mut pasted_input: Vec = - stdin_buffer.drain(..=paste_end_position).collect(); - if starts_with_bracketed_paste_start { - drop(pasted_input.drain(..6)); // bracketed paste start - } - drop(pasted_input.drain(pasted_input.len() - 6..)); // bracketed paste end - - send_input_instructions - .send(InputInstruction::PastedText(( - starts_with_bracketed_paste_start, - pasted_input, - ends_with_bracketed_paste_end, - ))) - .unwrap(); - pasting = false; - } - None => { - let starts_with_bracketed_paste_start = stdin_buffer - .iter() - .take(bracketed_paste_start.len()) - .eq(&bracketed_paste_start); - if starts_with_bracketed_paste_start { - drop(stdin_buffer.drain(..6)); // bracketed paste start - } - - send_input_instructions - .send(InputInstruction::PastedText(( - starts_with_bracketed_paste_start, - stdin_buffer, - false, - ))) - .unwrap(); - pasting = true; - continue; - } - } - } - if stdin_buffer.is_empty() { + if key_event == bracketed_paste_start { + pasting = true; + pasted_text.append(&mut raw_bytes); + continue; + } else if pasting && key_event == bracketed_paste_end { + pasting = false; + let mut pasted_text: Vec = pasted_text.drain(..).collect(); + pasted_text.append(&mut raw_bytes); + send_input_instructions + .send(InputInstruction::PastedText(pasted_text)) + .unwrap(); + continue; + } else if pasting { + pasted_text.append(&mut raw_bytes); continue; } - for key_result in stdin_buffer.events_and_raw() { - let (key_event, raw_bytes) = key_result.unwrap(); - let raw_bytes = adjusted_keys.get(&raw_bytes).cloned().unwrap_or(raw_bytes); - if let termion::event::Event::Mouse(me) = key_event { - let mouse_event = zellij_utils::input::mouse::MouseEvent::from(me); - if let MouseEvent::Hold(_) = mouse_event { - // as long as the user is holding the mouse down (no other stdin, eg. - // MouseRelease) we need to keep sending this instruction to the app, - // because the app itself doesn't have an event loop in the proper - // place - let mut poller = os_input.stdin_poller(); + + let raw_bytes = adjusted_keys.get(&raw_bytes).cloned().unwrap_or(raw_bytes); + if let termion::event::Event::Mouse(me) = key_event { + let mouse_event = zellij_utils::input::mouse::MouseEvent::from(me); + if let MouseEvent::Hold(_) = mouse_event { + // as long as the user is holding the mouse down (no other stdin, eg. + // MouseRelease) we need to keep sending this instruction to the app, + // because the app itself doesn't have an event loop in the proper + // place + let mut poller = os_input.stdin_poller(); + send_input_instructions + .send(InputInstruction::KeyEvent( + key_event.clone(), + raw_bytes.clone(), + )) + .unwrap(); + loop { + let ready = poller.ready(); + if ready { + break; + } send_input_instructions .send(InputInstruction::KeyEvent( key_event.clone(), raw_bytes.clone(), )) .unwrap(); - loop { - let ready = poller.ready(); - if ready { - break; - } - send_input_instructions - .send(InputInstruction::KeyEvent( - key_event.clone(), - raw_bytes.clone(), - )) - .unwrap(); - } - continue; } + continue; } - - // FIXME: termion does not properly parse some csi sgr mouse sequences - // like ctrl + click. - // As a workaround, to avoid writing these sequences to tty stdin, - // we discard them. - if let termion::event::Event::Unsupported(_) = key_event { - if raw_bytes.len() > csi_mouse_sgr_start.len() - && raw_bytes[0..csi_mouse_sgr_start.len()] == csi_mouse_sgr_start - { - continue; - } - } - - send_input_instructions - .send(InputInstruction::KeyEvent(key_event, raw_bytes)) - .unwrap(); } + + // FIXME: termion does not properly parse some csi sgr mouse sequences + // like ctrl + click. + // As a workaround, to avoid writing these sequences to tty stdin, + // we discard them. + if let termion::event::Event::Unsupported(_) = key_event { + if raw_bytes.len() > csi_mouse_sgr_start.len() + && raw_bytes[0..csi_mouse_sgr_start.len()] == csi_mouse_sgr_start + { + continue; + } + } + + send_input_instructions + .send(InputInstruction::KeyEvent(key_event, raw_bytes)) + .unwrap(); } } diff --git a/zellij-client/src/unit/input_handler_tests.rs b/zellij-client/src/unit/input_handler_tests.rs index 881c7f63..017ec8ab 100644 --- a/zellij-client/src/unit/input_handler_tests.rs +++ b/zellij-client/src/unit/input_handler_tests.rs @@ -106,6 +106,9 @@ impl ClientOsApi for FakeClientOsApi { fn get_stdout_writer(&self) -> Box { unimplemented!() } + fn get_stdin_reader(&self) -> Box { + unimplemented!() + } fn read_from_stdin(&self) -> Vec { unimplemented!() }