From 6d78be7aa28a103d9952743bb83e22dc11e812bb Mon Sep 17 00:00:00 2001 From: Andy Dixon <7983876+andydixon@users.noreply.github.com> Date: Tue, 20 Oct 2026 13:27:39 +0100 Subject: [PATCH] Freeze issue experimental fix due to mouse --- rust/app/src/app.rs | 22 ++++++++++++++++++++++ rust/app/src/terminal.rs | 27 ++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/rust/app/src/app.rs b/rust/app/src/app.rs index d79164b..ad8c120 100644 --- a/rust/app/src/app.rs +++ b/rust/app/src/app.rs @@ -992,9 +992,31 @@ impl eframe::App for App { self.screenshot.is_none() } + fn raw_input_hook(&mut self, _ctx: &egui::Context, raw: &mut egui::RawInput) { + if raw.events.iter().any(|e| matches!(e, egui::Event::WindowFocused(false))) { + // winit can miss mouse-up events when macOS hides or minimizes the window. + // Release egui's pointer buttons too, so every widget remains interactive on return. + let pos = raw.screen_rect.map_or(egui::pos2(-1000.0, -1000.0), |r| r.max + egui::vec2(100.0, 100.0)); + for button in [egui::PointerButton::Primary, egui::PointerButton::Secondary, egui::PointerButton::Middle] { + raw.events.push(egui::Event::PointerButton { pos, button, pressed: false, modifiers: egui::Modifiers::default() }); + } + } + } + fn ui(&mut self, ui: &mut Ui, _frame: &mut eframe::Frame) { let ctx = ui.ctx().clone(); let t = Tokens::get(&ctx); + // If the window is hidden or minimized during a drag, macOS may not deliver the + // matching mouse-up. Clear terminal-side pointer capture on every pane, including + // panes that are currently hidden behind another tab. + let lost_focus = ctx.input(|i| i.events.iter().any(|e| matches!(e, egui::Event::WindowFocused(false)))); + if lost_focus { + for pane in self.s.panes.values_mut() { + if let crate::views::View::Terminal(term) = &mut pane.view { + term.cancel_pointer_interaction(); + } + } + } self.s.now = ctx.input(|i| i.time); self.drain_events(); self.handle_requests(); diff --git a/rust/app/src/terminal.rs b/rust/app/src/terminal.rs index ef44ee4..05b95db 100644 --- a/rust/app/src/terminal.rs +++ b/rust/app/src/terminal.rs @@ -396,6 +396,13 @@ impl TermView { self.focus_requested = true; } + /// A window can lose its mouse-up event when it is hidden or minimized mid-drag. + /// Drop the transient pointer state while keeping any text already selected. + pub fn cancel_pointer_interaction(&mut self) { + self.drag = None; + self.mouse_down = None; + } + pub fn selection_text(&self) -> Option { self.term.selection_to_string().filter(|s| !s.is_empty()) } @@ -780,13 +787,31 @@ impl TermView { self.term.scroll_display(Scroll::Delta(n)); } } - Event::PointerButton { pos, button, pressed, modifiers } if resp.rect.contains(*pos) => { + Event::PointerButton { pos, button, pressed, modifiers } => { let b = match button { PointerButton::Primary => 0, PointerButton::Middle => 1, PointerButton::Secondary => 2, _ => continue, }; + // A mouse-up outside this widget still ends a drag that began here. + // Without this, the next pointer movement keeps extending the selection. + let was_mouse_down = self.mouse_down == Some(b); + if !*pressed { + if b == 0 { + self.drag = None; + } + if was_mouse_down { + self.mouse_down = None; + } + } + if !resp.rect.contains(*pos) { + if !*pressed && was_mouse_down { + let (c, r) = to_cell(*pos); + out.input.extend(mouse_report(b, c, r, false, mode)); + } + continue; + } if reporting && b != 2 || reporting && mode.contains(TermMode::MOUSE_MODE) && b == 2 && !*pressed { let (c, r) = to_cell(*pos); out.input.extend(mouse_report(b, c, r, *pressed, mode));