brewery
notashelf /
15a4a97033d61c56a0c0031fe24cae6266e1417c

beer

public

A terminal worth pouring time into

Star0tarball
clone
ssh://git@git.notashelf.dev:33/notashelf/beer.git

Commit 15a4a97033d6

tarball

NotAShelf <raf@notashelf.dev> · 2026-06-26 07:21 UTC

unverified2 files changed+56-3
config: add `[mouse]` table with scroll multiplier and alternate-scroll

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I58d4f2cb0010c167c7c317bf10dea99b6a6a6964
diff --git a/src/config.rs b/src/config.rsindex 83a8004..a56e260 100644--- a/src/config.rs+++ b/src/config.rs@@ -16,6 +16,7 @@ pub struct Config {     pub cursor: Cursor,     pub scrollback: Scrollback,     pub bell: Bell,+    pub mouse: Mouse,     /// Chord → action, e.g. `"Ctrl+Shift+C" = "copy"`. Merged over the defaults;     /// a value of `"none"` unbinds.     pub key_bindings: std::collections::HashMap<String, String>,@@ -41,6 +42,27 @@ pub struct Bell {     pub visual: bool, } +/// `[mouse]`: pointer and wheel behaviour.+#[derive(Debug, Clone, Deserialize)]+#[serde(default, rename_all = "kebab-case")]+pub struct Mouse {+    /// Multiplier applied to the lines scrolled per wheel notch.+    pub scroll_multiplier: f64,+    /// On the alternate screen, translate the wheel into arrow-key presses so+    /// full-screen apps that did not request mouse reporting (less, man, …)+    /// still scroll.+    pub alternate_scroll: bool,+}++impl Default for Mouse {+    fn default() -> Self {+        Self {+            scroll_multiplier: 1.0,+            alternate_scroll: true,+        }+    }+}+ /// `[colors]`: foreground/background, the 16 base palette entries, and accents. /// Each value is an X11 colour spec (`#rrggbb` or `rgb:rr/gg/bb`); unset entries /// keep the built-in default.diff --git a/src/wayland.rs b/src/wayland.rsindex b5ef3a4..77263b7 100644--- a/src/wayland.rs+++ b/src/wayland.rs@@ -641,6 +641,24 @@ impl App {         }     } +    /// Send `count` cursor-up/down keys to the shell for alternate-scroll,+    /// honouring the application cursor-key mode (DECCKM).+    fn alternate_scroll(&mut self, up: bool, count: isize) {+        let app_cursor = self+            .session+            .as_ref()+            .is_some_and(|s| s.term.grid().app_cursor());+        let seq: &[u8] = match (up, app_cursor) {+            (true, false) => b"\x1b[A",+            (true, true) => b"\x1bOA",+            (false, false) => b"\x1b[B",+            (false, true) => b"\x1bOB",+        };+        for _ in 0..count {+            self.write_to_pty(seq);+        }+    }+     /// Scroll the viewport one page back (`up`) or toward the live screen.     fn scroll_page(&mut self, up: bool) {         if let Some(session) = self.session.as_mut() {@@ -1955,19 +1973,32 @@ impl PointerHandler for App {                     if raw == 0.0 {                         continue;                     }-                    let lines = (raw.abs() * scale).ceil().max(1.0) as isize;+                    let mult = self.config.mouse.scroll_multiplier.max(0.0);+                    let lines = (raw.abs() * scale * mult).ceil().max(1.0) as isize;+                    let up = raw < 0.0;                     // Reporting apps get wheel buttons (64 up / 65 down) as                     // presses, one per line, capped so a flick cannot flood.                     if self.mouse_reporting() {-                        let code = if raw < 0.0 { 64 } else { 65 };+                        let code = if up { 64 } else { 65 };                         for _ in 0..lines.clamp(1, 8) {                             self.try_report_button(code, true);                         }                         continue;                     }+                    // On the alternate screen there is no scrollback to move, so+                    // (when enabled) translate the wheel into cursor-key presses+                    // for apps that did not request mouse reporting.+                    let alt = self+                        .session+                        .as_ref()+                        .is_some_and(|s| s.term.grid().alt_active());+                    if alt && self.config.mouse.alternate_scroll {+                        self.alternate_scroll(up, lines.clamp(1, 8));+                        continue;+                    }                     // Positive axis = scroll down (toward live); the viewport                     // scrolls the opposite way (negative offset delta).-                    let delta = if raw < 0.0 { lines } else { -lines };+                    let delta = if up { lines } else { -lines };                     if let Some(session) = self.session.as_mut() {                         session.term.scroll_view(delta);                         self.needs_draw = true;