From fe6e528a4919b56184312151c35ca5c1d8b1bc5b Mon Sep 17 00:00:00 2001 From: Emamul Andalib Date: Tue, 24 Feb 2026 11:27:22 +0100 Subject: [PATCH] terminal: Fix mouse scroll report count for negative scroll lines (#49931) Follow-up to #45600. ## Summary Fix mouse scroll reports sending only one event when scrolling down in terminal apps with mouse mode (tmux, neovim, etc.), regardless of how many lines were scrolled. ## The Problem After #45600, trackpad scrolling speed was fixed. But when scrolling **down** (negative `scroll_lines`), the terminal was still sending only **one** scroll report per gesture, no matter how many lines the user scrolled. Scrolling up worked correctly. ## Root Cause In `scroll_report()` we had: https://github.com/zed-industries/zed/blob/a8043dcff8f28a0443d7ec238e7f020689ebe1ff/crates/terminal/src/mappings/mouse.rs#L96 `scroll_lines` can be negative (scroll down) or positive (scroll up). For negative values: | scroll_lines | max(scroll_lines, 1) | Reports sent | Verdict | |--------------|---------------------|--------------|------| | 3 (up) | 3 | 3 |Right | -3 (down) | 1 | 1 |WRONG| So we always sent exactly 1 report when scrolling down, losing the scroll magnitude. Use `scroll_lines.unsigned_abs()` instead of `max(scroll_lines, 1)`. This matches how `alt_scroll()` in the same file already handles `scroll_lines`. Now both directions send the correct number of reports. https://github.com/zed-industries/zed/blob/a8043dcff8f28a0443d7ec238e7f020689ebe1ff/crates/terminal/src/mappings/mouse.rs#L102 ## Testing - Added unit tests: `scroll_report_repeats_for_negative_scroll_lines` and `scroll_report_repeats_for_positive_scroll_lines` - Manually tested scrolling in tmux and neovim with mouse mode --- Release Notes: - Fixed mouse scroll in terminal apps (tmux, neovim, etc.) only sending one scroll event when scrolling down, regardless of scroll amount --- crates/terminal/src/mappings/mouse.rs | 46 +++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/crates/terminal/src/mappings/mouse.rs b/crates/terminal/src/mappings/mouse.rs index 8c3eed8b54972806bdb71c5d4671cfe2a4705ce4..ffd60a83aab24bd7272d90d3e12d48fcdf65bf17 100644 --- a/crates/terminal/src/mappings/mouse.rs +++ b/crates/terminal/src/mappings/mouse.rs @@ -1,4 +1,4 @@ -use std::cmp::{self, max, min}; +use std::cmp::{self, min}; use std::iter::repeat; use alacritty_terminal::grid::Dimensions; @@ -93,12 +93,54 @@ pub fn scroll_report( e.modifiers, MouseFormat::from_mode(mode), ) - .map(|report| repeat(report).take(max(scroll_lines, 1) as usize)) + .map(|report| repeat(report).take(scroll_lines.unsigned_abs() as usize)) } else { None } } +#[cfg(test)] +mod tests { + use super::*; + use gpui::{ScrollDelta, TouchPhase, point}; + + #[test] + fn scroll_report_repeats_for_negative_scroll_lines() { + let grid_point = AlacPoint::new(GridLine(0), GridCol(0)); + + let scroll_event = ScrollWheelEvent { + delta: ScrollDelta::Lines(point(0., -1.)), + touch_phase: TouchPhase::Moved, + ..Default::default() + }; + + let mode = TermMode::MOUSE_MODE; + let reports: Vec> = scroll_report(grid_point, -3, &scroll_event, mode) + .expect("mouse mode should produce a scroll report") + .collect(); + + assert_eq!(reports.len(), 3); + } + + #[test] + fn scroll_report_repeats_for_positive_scroll_lines() { + let grid_point = AlacPoint::new(GridLine(0), GridCol(0)); + + let scroll_event = ScrollWheelEvent { + delta: ScrollDelta::Lines(point(0., 1.)), + touch_phase: TouchPhase::Moved, + ..Default::default() + }; + + let mode = TermMode::MOUSE_MODE; + let reports: Vec> = scroll_report(grid_point, 3, &scroll_event, mode) + .expect("mouse mode should produce a scroll report") + .collect(); + + assert_eq!(reports.len(), 3); + } +} + pub fn alt_scroll(scroll_lines: i32) -> Vec { let cmd = if scroll_lines > 0 { b'A' } else { b'B' };