diff --git a/term/src/terminalstate/mod.rs b/term/src/terminalstate/mod.rs index 55028802212..18b0c702930 100644 --- a/term/src/terminalstate/mod.rs +++ b/term/src/terminalstate/mod.rs @@ -293,7 +293,7 @@ pub struct TerminalState { mouse_tracking: bool, /// Button events enabled button_event_mouse: bool, - current_mouse_button: MouseButton, + current_mouse_buttons: Vec, last_mouse_move: Option, cursor_visible: bool, @@ -441,7 +441,7 @@ impl TerminalState { g1_charset: CharSet::DecLineDrawing, shift_out: false, newline_mode: false, - current_mouse_button: MouseButton::None, + current_mouse_buttons: vec![], tabs: TabStop::new(size.physical_cols, 8), title: "wezterm".to_string(), icon_title: None, @@ -597,7 +597,18 @@ impl TerminalState { /// Advise the terminal about a change in its focus state pub fn focus_changed(&mut self, focused: bool) { if !focused { - self.current_mouse_button = MouseButton::None; + // notify app of release of buttons + let buttons = self.current_mouse_buttons.clone(); + for b in buttons { + self.mouse_event(MouseEvent { + kind: MouseEventKind::Release, + button: b, + modifiers: KeyModifiers::NONE, + x: 0, + y: 0, + }) + .ok(); + } } if self.focus_tracking { write!(self.writer, "{}{}", CSI, if focused { "I" } else { "O" }).ok(); diff --git a/term/src/terminalstate/mouse.rs b/term/src/terminalstate/mouse.rs index a54266e0584..569d7059563 100644 --- a/term/src/terminalstate/mouse.rs +++ b/term/src/terminalstate/mouse.rs @@ -14,9 +14,13 @@ impl TerminalState { position.max(0).saturating_add(1 + 32).min(127) as u8 as char } - fn mouse_report_button_number(&self, event: &MouseEvent) -> i8 { + fn mouse_report_button_number(&self, event: &MouseEvent) -> (i8, MouseButton) { let button = match event.button { - MouseButton::None => self.current_mouse_button, + MouseButton::None => self + .current_mouse_buttons + .last() + .copied() + .unwrap_or(MouseButton::None), b => b, }; let mut code = match button { @@ -38,11 +42,11 @@ impl TerminalState { code += 16; } - code + (code, button) } fn mouse_wheel(&mut self, event: MouseEvent) -> anyhow::Result<()> { - let button = self.mouse_report_button_number(&event); + let (button, _button) = self.mouse_report_button_number(&event); if self.sgr_mouse && (self.mouse_tracking || self.button_event_mouse || self.any_event_mouse) @@ -81,13 +85,14 @@ impl TerminalState { } fn mouse_button_press(&mut self, event: MouseEvent) -> anyhow::Result<()> { - self.current_mouse_button = event.button; + let (button, event_button) = self.mouse_report_button_number(&event); + self.current_mouse_buttons.retain(|&b| b != event_button); + self.current_mouse_buttons.push(event_button); if !(self.mouse_tracking || self.button_event_mouse || self.any_event_mouse) { return Ok(()); } - let button = self.mouse_report_button_number(&event); if self.sgr_mouse { write!( self.writer, @@ -112,31 +117,30 @@ impl TerminalState { } fn mouse_button_release(&mut self, event: MouseEvent) -> anyhow::Result<()> { - if self.current_mouse_button != MouseButton::None - && (self.mouse_tracking || self.button_event_mouse || self.any_event_mouse) - { - if self.sgr_mouse { - let release_button = self.mouse_report_button_number(&event); - self.current_mouse_button = MouseButton::None; - write!( - self.writer, - "\x1b[<{};{};{}m", - release_button, - event.x + 1, - event.y + 1 - )?; - self.writer.flush()?; - } else { - let release_button = 3; - self.current_mouse_button = MouseButton::None; - write!( - self.writer, - "\x1b[M{}{}{}", - (32 + release_button) as u8 as char, - Self::legacy_mouse_coord(event.x as i64), - Self::legacy_mouse_coord(event.y), - )?; - self.writer.flush()?; + let (release_button, button) = self.mouse_report_button_number(&event); + if !self.current_mouse_buttons.is_empty() { + self.current_mouse_buttons.retain(|&b| b != button); + if self.mouse_tracking || self.button_event_mouse || self.any_event_mouse { + if self.sgr_mouse { + write!( + self.writer, + "\x1b[<{};{};{}m", + release_button, + event.x + 1, + event.y + 1 + )?; + self.writer.flush()?; + } else { + let release_button = 3; + write!( + self.writer, + "\x1b[M{}{}{}", + (32 + release_button) as u8 as char, + Self::legacy_mouse_coord(event.x as i64), + Self::legacy_mouse_coord(event.y), + )?; + self.writer.flush()?; + } } } @@ -144,7 +148,7 @@ impl TerminalState { } fn mouse_move(&mut self, event: MouseEvent) -> anyhow::Result<()> { - let reportable = self.any_event_mouse || self.current_mouse_button != MouseButton::None; + let reportable = self.any_event_mouse || !self.current_mouse_buttons.is_empty(); // Note: self.mouse_tracking on its own is for clicks, not drags! if reportable && (self.button_event_mouse || self.any_event_mouse) { match self.last_mouse_move.as_ref() { @@ -155,7 +159,8 @@ impl TerminalState { } self.last_mouse_move.replace(event.clone()); - let button = 32 + self.mouse_report_button_number(&event); + let (button, _button) = self.mouse_report_button_number(&event); + let button = 32 + button; if self.sgr_mouse { write!( diff --git a/term/src/terminalstate/performer.rs b/term/src/terminalstate/performer.rs index 6980f432d35..6f2039f48be 100644 --- a/term/src/terminalstate/performer.rs +++ b/term/src/terminalstate/performer.rs @@ -1,4 +1,3 @@ -use crate::input::MouseButton; use crate::terminal::Alert; use crate::terminalstate::{default_color_map, CharSet, TabStop}; use crate::{ClipboardSelection, Position, TerminalState, VisibleRowIndex}; @@ -483,7 +482,7 @@ impl<'a> Performer<'a> { self.sixel_scrolls_right = false; self.any_event_mouse = false; self.button_event_mouse = false; - self.current_mouse_button = MouseButton::None; + self.current_mouse_buttons.clear(); self.cursor_visible = true; self.g0_charset = CharSet::Ascii; self.g1_charset = CharSet::DecLineDrawing; diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index 0473af38ad1..c28764bb1e6 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -203,7 +203,7 @@ pub struct TermWindow { window_background: Option>, - current_mouse_button: Option, + current_mouse_buttons: Vec, /// Keeps track of double and triple clicks last_mouse_click: Option, @@ -280,7 +280,7 @@ impl TermWindow { if self.focused.is_none() { self.last_mouse_click = None; - self.current_mouse_button = None; + self.current_mouse_buttons.clear(); } // Reset the cursor blink phase @@ -477,7 +477,7 @@ impl TermWindow { last_scroll_info: RenderableDimensions::default(), tab_state: RefCell::new(HashMap::new()), pane_state: RefCell::new(HashMap::new()), - current_mouse_button: None, + current_mouse_buttons: vec![], last_mouse_click: None, current_highlight: None, shape_cache: RefCell::new(LruCache::new( diff --git a/wezterm-gui/src/termwindow/mouseevent.rs b/wezterm-gui/src/termwindow/mouseevent.rs index d486dc1e708..c82324daf7e 100644 --- a/wezterm-gui/src/termwindow/mouseevent.rs +++ b/wezterm-gui/src/termwindow/mouseevent.rs @@ -78,7 +78,7 @@ impl super::TermWindow { match event.kind { WMEK::Release(ref press) => { - self.current_mouse_button = None; + self.current_mouse_buttons.retain(|p| p != press); if press == &MousePress::Left && self.scroll_drag_start.take().is_some() { // Completed a scrollbar drag return; @@ -102,7 +102,8 @@ impl super::TermWindow { Some(click) => click.add(button), }; self.last_mouse_click = Some(click); - self.current_mouse_button = Some(press.clone()); + self.current_mouse_buttons.retain(|p| p != press); + self.current_mouse_buttons.push(*press); } WMEK::VertWheel(amount) if !pane.is_mouse_grabbed() && !pane.is_alt_screen_active() => { @@ -475,12 +476,12 @@ impl super::TermWindow { } } WMEK::Move => { - if self.current_mouse_button.is_some() { + if !self.current_mouse_buttons.is_empty() { if let Some(LastMouseClick { streak, button, .. }) = self.last_mouse_click.as_ref() { if Some(*button) - == self.current_mouse_button.as_ref().map(mouse_press_to_tmb) + == self.current_mouse_buttons.last().map(mouse_press_to_tmb) { Some(MouseEventTrigger::Drag { streak: *streak, diff --git a/wezterm-input-types/src/lib.rs b/wezterm-input-types/src/lib.rs index 1242d54f901..3bd4e9c1d8e 100644 --- a/wezterm-input-types/src/lib.rs +++ b/wezterm-input-types/src/lib.rs @@ -145,7 +145,7 @@ bitflags! { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum MousePress { Left, Right,