From 774241a5dee67eca099b482a570c735ce326022e Mon Sep 17 00:00:00 2001 From: Autumn Lamonte Date: Thu, 29 Aug 2024 15:55:21 -0500 Subject: [PATCH] 'cargo xtask build' working using refactored functions. --- zellij-client/src/input_handler.rs | 90 ++++++++++++++++++++++++++-- zellij-client/src/os_input_output.rs | 6 +- zellij-server/src/panes/grid.rs | 35 ++++++----- zellij-server/src/tab/mod.rs | 14 ++++- zellij-utils/src/input/mod.rs | 5 +- zellij-utils/src/input/mouse.rs | 85 -------------------------- 6 files changed, 123 insertions(+), 112 deletions(-) diff --git a/zellij-client/src/input_handler.rs b/zellij-client/src/input_handler.rs index 5ead1d0bc1..7e9c87fc24 100644 --- a/zellij-client/src/input_handler.rs +++ b/zellij-client/src/input_handler.rs @@ -15,7 +15,8 @@ use zellij_utils::{ options::Options, }, ipc::{ClientToServerMsg, ExitReason}, - termwiz::input::InputEvent, + position::Position, + termwiz::input::{InputEvent, Modifiers, MouseButtons, MouseEvent as TermwizMouseEvent}, }; /// Handles the dispatching of [`Action`]s according to the current @@ -34,6 +35,88 @@ struct InputHandler { mouse_mode_active: bool, } +fn termwiz_mouse_convert(original_event: &mut MouseEvent, event: &TermwizMouseEvent) { + let button_bits = &event.mouse_buttons; + original_event.left = button_bits.contains(MouseButtons::LEFT); + original_event.right = button_bits.contains(MouseButtons::RIGHT); + original_event.middle = button_bits.contains(MouseButtons::MIDDLE); + original_event.wheel_up = button_bits.contains(MouseButtons::VERT_WHEEL) + && button_bits.contains(MouseButtons::WHEEL_POSITIVE); + original_event.wheel_down = button_bits.contains(MouseButtons::VERT_WHEEL) + && !button_bits.contains(MouseButtons::WHEEL_POSITIVE); + + let mods = &event.modifiers; + original_event.shift = mods.contains(Modifiers::SHIFT); + original_event.alt = mods.contains(Modifiers::ALT); + original_event.ctrl = mods.contains(Modifiers::CTRL); +} + +fn from_termwiz(old_event: &mut MouseEvent, event: TermwizMouseEvent) -> MouseEvent { + // We use the state of old_event vs new_event to determine if this + // event is a Press, Release, or Motion. This is an unfortunate + // side effect of the pre-SGR-encoded X10 mouse protocol design in + // which release events don't carry information about WHICH + // button(s) were released, so we have to maintain a wee bit of + // state in between events. + // + // Note that only Left, Right, and Middle are saved in between + // calls. WheelUp/WheelDown typically do not generate Release + // events. + let mut new_event = MouseEvent::new(); + termwiz_mouse_convert(&mut new_event, &event); + new_event.position = Position::new(event.y.saturating_sub(1) as i32, event.x.saturating_sub(1)); + + if (new_event.left && !old_event.left) + || (new_event.right && !old_event.right) + || (new_event.middle && !old_event.middle) + || new_event.wheel_up + || new_event.wheel_down + { + // This is a mouse Press event. + new_event.event_type = MouseEventType::Press; + + // Hang onto the button state. + *old_event = new_event; + } else if event.mouse_buttons.is_empty() + && !old_event.left + && !old_event.right + && !old_event.middle + { + // This is a mouse Motion event (no buttons are down). + new_event.event_type = MouseEventType::Motion; + + // Hang onto the button state. + *old_event = new_event; + } else if event.mouse_buttons.is_empty() + && (old_event.left || old_event.right || old_event.middle) + { + // This is a mouse Release event. Note that we set + // old_event.{button} to false (to release), but set ONLY the + // new_event that were released to true before sending the + // event up. + if old_event.left { + old_event.left = false; + new_event.left = true; + } + if old_event.right { + old_event.right = false; + new_event.right = true; + } + if old_event.middle { + old_event.middle = false; + new_event.middle = true; + } + new_event.event_type = MouseEventType::Release; + } else { + // Dragging with some button down. Return it as a Motion + // event, and hang on to the button state. + new_event.event_type = MouseEventType::Motion; + *old_event = new_event; + } + + new_event +} + impl InputHandler { /// Returns a new [`InputHandler`] with the attributes specified as arguments. fn new( @@ -86,10 +169,7 @@ impl InputHandler { self.handle_key(&key, raw_bytes, false); }, InputEvent::Mouse(mouse_event) => { - let mouse_event = zellij_utils::input::mouse::MouseEvent::from_termwiz( - &mut self.mouse_old_event, - mouse_event, - ); + let mouse_event = from_termwiz(&mut self.mouse_old_event, mouse_event); self.handle_mouse_event(&mouse_event); }, InputEvent::Paste(pasted_text) => { diff --git a/zellij-client/src/os_input_output.rs b/zellij-client/src/os_input_output.rs index 47dcafb091..41d03b0f72 100644 --- a/zellij-client/src/os_input_output.rs +++ b/zellij-client/src/os_input_output.rs @@ -22,8 +22,10 @@ use zellij_utils::{ const SIGWINCH_CB_THROTTLE_DURATION: time::Duration = time::Duration::from_millis(50); -const ENABLE_MOUSE_SUPPORT: &str = "\u{1b}[?1000h\u{1b}[?1002h\u{1b}[?1015h\u{1b}[?1006h"; -const DISABLE_MOUSE_SUPPORT: &str = "\u{1b}[?1006l\u{1b}[?1015l\u{1b}[?1002l\u{1b}[?1000l"; +const ENABLE_MOUSE_SUPPORT: &str = + "\u{1b}[?1000h\u{1b}[?1002h\u{1b}[?1003h\u{1b}[?1015h\u{1b}[?1006h"; +const DISABLE_MOUSE_SUPPORT: &str = + "\u{1b}[?1006l\u{1b}[?1015l\u{1b}[?1003l\u{1b}[?1002l\u{1b}[?1000l"; fn into_raw_mode(pid: RawFd) { let mut tio = termios::tcgetattr(pid).expect("could not get terminal attribute"); diff --git a/zellij-server/src/panes/grid.rs b/zellij-server/src/panes/grid.rs index 72bf43ddb2..b43428488e 100644 --- a/zellij-server/src/panes/grid.rs +++ b/zellij-server/src/panes/grid.rs @@ -386,6 +386,7 @@ pub enum MouseTracking { Off, Normal, ButtonEventTracking, + AnyEventTracking, } impl Default for MouseTracking { @@ -1980,30 +1981,36 @@ impl Grid { value } pub fn mouse_event_signal(&self, event: &MouseEvent) -> Option { - match &self.mouse_mode { - MouseMode::NoEncoding | MouseMode::Utf8 => { + let emit = match (&self.mouse_tracking, event.event_type) { + (MouseTracking::Off, _) => false, + (MouseTracking::AnyEventTracking, _) => true, + (_, MouseEventType::Press | MouseEventType::Release) => true, + (MouseTracking::ButtonEventTracking, MouseEventType::Motion) => { + event.left | event.right | event.middle | event.wheel_up | event.wheel_down + }, + (_, _) => false, + }; + + match (emit, &self.mouse_mode) { + (true, MouseMode::NoEncoding | MouseMode::Utf8) => { let mut msg: Vec = vec![27, b'[', b'M', self.mouse_buttons_value_x10(event)]; msg.append(&mut utf8_mouse_coordinates( - // AZL: Why is event.position not staying 0-based - // on both axes? - event.position.column(), - event.position.line() - 1, + event.position.column() + 1, + event.position.line() + 1, )); Some(String::from_utf8_lossy(&msg).into()) }, - MouseMode::Sgr => Some(format!( + (true, MouseMode::Sgr) => Some(format!( "\u{1b}[<{:?};{:?};{:?}{}", self.mouse_buttons_value_sgr(event), - // AZL: Why is event.position not staying 0-based on - // both axes? - event.position.column(), - event.position.line() - 1, + event.position.column() + 1, + event.position.line() + 1, match event.event_type { MouseEventType::Press => 'M', _ => 'm', } )), - _ => None, + (_, _) => None, } } pub fn mouse_left_click_signal(&self, position: &Position, is_held: bool) -> Option { @@ -2675,7 +2682,7 @@ impl Perform for Grid { self.mouse_tracking = MouseTracking::Off; }, 1003 => { - // TBD: any-even mouse tracking + self.mouse_tracking = MouseTracking::Off; }, 1004 => { self.focus_event_tracking = false; @@ -2778,7 +2785,7 @@ impl Perform for Grid { self.mouse_tracking = MouseTracking::ButtonEventTracking; }, 1003 => { - // TBD: any-even mouse tracking + self.mouse_tracking = MouseTracking::AnyEventTracking; }, 1004 => { self.focus_event_tracking = true; diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs index 58d509d652..0784c25c0b 100644 --- a/zellij-server/src/tab/mod.rs +++ b/zellij-server/src/tab/mod.rs @@ -3023,9 +3023,19 @@ impl Tab { || format!("failed to handle mouse event {event:?} for client {client_id}"); let active_pane = self.get_active_pane_or_floating_pane_mut(client_id); if let Some(active_pane) = active_pane { - if let Some(mouse_event) = active_pane.mouse_event(&event) { - self.write_to_active_terminal(&None, mouse_event.into_bytes(), false, client_id) + let relative_position = active_pane.relative_position(&event.position); + let mut pass_event = *event; + pass_event.position = relative_position; + if let Some(mouse_event) = active_pane.mouse_event(&pass_event) { + if !active_pane.position_is_on_frame(&event.position) { + self.write_to_active_terminal( + &None, + mouse_event.into_bytes(), + false, + client_id, + ) .with_context(err_context)?; + } } } Ok(()) diff --git a/zellij-utils/src/input/mod.rs b/zellij-utils/src/input/mod.rs index 296b531380..aaf466d6ac 100644 --- a/zellij-utils/src/input/mod.rs +++ b/zellij-utils/src/input/mod.rs @@ -3,15 +3,12 @@ pub mod command; pub mod config; pub mod keybinds; pub mod layout; +pub mod mouse; pub mod options; pub mod permission; pub mod plugins; pub mod theme; -// Can't use this in wasm due to dependency on the `termwiz` crate. -#[cfg(not(target_family = "wasm"))] -pub mod mouse; - #[cfg(not(target_family = "wasm"))] pub use not_wasm::*; diff --git a/zellij-utils/src/input/mouse.rs b/zellij-utils/src/input/mouse.rs index eb4e9f02f6..30060fdcf9 100644 --- a/zellij-utils/src/input/mouse.rs +++ b/zellij-utils/src/input/mouse.rs @@ -59,89 +59,4 @@ impl MouseEvent { event } - - fn termwiz_mouse_convert(&mut self, event: &termwiz::input::MouseEvent) { - let button_bits = &event.mouse_buttons; - self.left = button_bits.contains(termwiz::input::MouseButtons::LEFT); - self.right = button_bits.contains(termwiz::input::MouseButtons::RIGHT); - self.middle = button_bits.contains(termwiz::input::MouseButtons::MIDDLE); - self.wheel_up = button_bits.contains(termwiz::input::MouseButtons::VERT_WHEEL) - && button_bits.contains(termwiz::input::MouseButtons::WHEEL_POSITIVE); - self.wheel_down = button_bits.contains(termwiz::input::MouseButtons::VERT_WHEEL) - && !button_bits.contains(termwiz::input::MouseButtons::WHEEL_POSITIVE); - - let mods = &event.modifiers; - self.shift = mods.contains(termwiz::input::Modifiers::SHIFT); - self.alt = mods.contains(termwiz::input::Modifiers::ALT); - self.ctrl = mods.contains(termwiz::input::Modifiers::CTRL); - } - - pub fn from_termwiz(old_event: &mut MouseEvent, event: termwiz::input::MouseEvent) -> Self { - // We use the state of old_event vs new_event to determine if - // this event is a Press, Release, or Motion. This is an - // unfortunate side effect of the pre-SGR-encoded X10 mouse - // protocol design in which release events don't carry - // information about WHICH button(s) were released, so we have - // to maintain a wee bit of state in between events. - // - // Note that only Left, Right, and Middle are saved in between - // calls. WheelUp/WheelDown typically do not generate Release - // events. - let mut new_event = MouseEvent::new(); - new_event.termwiz_mouse_convert(&event); - new_event.position = - Position::new(event.y.saturating_sub(1) as i32, event.x.saturating_sub(1)); - - if (new_event.left && !old_event.left) - || (new_event.right && !old_event.right) - || (new_event.middle && !old_event.middle) - || new_event.wheel_up - || new_event.wheel_down - { - // This is a mouse Press event. - new_event.event_type = MouseEventType::Press; - - // Hang onto the button state. - *old_event = new_event; - } else if event - .mouse_buttons - .contains(termwiz::input::MouseButtons::NONE) - && !old_event.left - && !old_event.right - && !old_event.middle - { - // This is a mouse Motion event (no buttons are down). - new_event.event_type = MouseEventType::Motion; - - // Hang onto the button state. - *old_event = new_event; - } else if event - .mouse_buttons - .contains(termwiz::input::MouseButtons::NONE) - && (old_event.left || old_event.right || old_event.middle) - { - // This is a mouse Release event. Note that we set - // old_event.{button} to false (to release), but set ONLY - // the new_event that were released to true before sending - // the event up. - if old_event.left { - old_event.left = false; - new_event.left = true; - } - if old_event.right { - old_event.right = false; - new_event.right = true; - } - if old_event.middle { - old_event.middle = false; - new_event.middle = true; - } - new_event.event_type = MouseEventType::Release; - } else { - // Unrecognized mouse state. Return it as a blank Motion event. - new_event.event_type = MouseEventType::Motion; - } - - new_event - } }