From d3bf209e259132205247f92a921d2b2501a13570 Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Thu, 10 Sep 2026 10:41:50 +0200 Subject: [PATCH 1/2] feat: added pixel support to mouse example Also updated docs where relevant --- examples/PrintMouse.java | 31 ++++++++++++++----- mousetrack/README.md | 7 +++++ .../miniterm/mousetrack/MouseEvent.java | 18 ++++++++--- .../miniterm/mousetrack/MouseTracking.java | 10 ++++++ 4 files changed, 55 insertions(+), 11 deletions(-) diff --git a/examples/PrintMouse.java b/examples/PrintMouse.java index 4b447cd..af7b3c3 100755 --- a/examples/PrintMouse.java +++ b/examples/PrintMouse.java @@ -18,14 +18,18 @@ public class PrintMouse { private static final String CURSOR_UP = CSI + "A"; private static final String ERASE_EOL = CSI + "K"; + private static final int VIEW_LINES = 6; + public static void main(String[] args) { try (Terminal terminal = Terminal.create()) { terminal.enableRawMode(); MouseTracking.enable(terminal, MouseTracking.Protocol.ANY_MOTION); MouseTracking.enableEncoding(terminal, MouseTracking.Encoding.SGR); + boolean pixelMode = false; + MouseEvent lastEv = null; try { - // Reserve 5 lines and print the initial (empty) view - printView(terminal, null); + // Reserve the view lines and print the initial (empty) view + printView(terminal, null, pixelMode); AnsiReader reader = new AnsiReader(() -> terminal.read(-1)); String token; @@ -33,13 +37,24 @@ public static void main(String[] args) { if (token.isEmpty()) continue; if (!token.startsWith("\033") && token.charAt(0) == 3) break; // Ctrl+C if (MouseTracking.isMouseEvent(token)) { - MouseEvent ev = MouseTracking.parse(token); - // Move cursor back up 5 lines to overwrite the previous view - terminal.write(CURSOR_UP.repeat(5)); - printView(terminal, ev); + lastEv = MouseTracking.parse(token); + } else if (!token.startsWith("\033") && (token.equals("t") || token.equals("T"))) { + pixelMode = !pixelMode; + if (pixelMode) { + MouseTracking.enableEncoding(terminal, MouseTracking.Encoding.SGR_PIXELS); + } else { + MouseTracking.disableEncoding(terminal, MouseTracking.Encoding.SGR_PIXELS); + MouseTracking.enableEncoding(terminal, MouseTracking.Encoding.SGR); + } + } else { + continue; } + // Move cursor back up to overwrite the previous view + terminal.write(CURSOR_UP.repeat(VIEW_LINES)); + printView(terminal, lastEv, pixelMode); } } finally { + MouseTracking.disableEncoding(terminal, MouseTracking.Encoding.SGR_PIXELS); MouseTracking.disableEncoding(terminal, MouseTracking.Encoding.SGR); MouseTracking.disable(terminal, MouseTracking.Protocol.ANY_MOTION); } @@ -48,16 +63,18 @@ public static void main(String[] args) { } } - private static void printView(Terminal terminal, MouseEvent ev) throws IOException { + private static void printView(Terminal terminal, MouseEvent ev, boolean pixelMode) throws IOException { String type = ev == null ? "-" : ev.type().name(); String button = ev == null ? "-" : ev.button().name(); String position = ev == null ? "-" : ev.x() + ", " + ev.y(); String mods = ev == null ? "-" : modifiers(ev); + String unit = pixelMode ? "PIXEL" : "CELL"; writeLine(terminal, "Type: " + type); writeLine(terminal, "Button: " + button); writeLine(terminal, "Position: " + position); writeLine(terminal, "Modifiers: " + mods); + writeLine(terminal, "Coordinates: " + unit + " (press T to toggle cell/pixel coordinates)"); writeLine(terminal, "(move the mouse, click or scroll — Ctrl+C to exit)"); } diff --git a/mousetrack/README.md b/mousetrack/README.md index 96ccc04..6f40ab5 100644 --- a/mousetrack/README.md +++ b/mousetrack/README.md @@ -128,6 +128,13 @@ try { Not all terminals support mode 1016; check the terminal's documentation before relying on it. +Some terminals reset `SGR` as a side effect of disabling `SGR_PIXELS`. If you toggle `SGR_PIXELS` off at runtime, re-enable `SGR` right after, e.g.: + +```java +MouseTracking.disableEncoding(terminal, MouseTracking.Encoding.SGR_PIXELS); +MouseTracking.enableEncoding(terminal, MouseTracking.Encoding.SGR); +``` + ## Adding the dependency ### JBang diff --git a/mousetrack/src/main/java/org/codejive/miniterm/mousetrack/MouseEvent.java b/mousetrack/src/main/java/org/codejive/miniterm/mousetrack/MouseEvent.java index 07f2da0..34fec9d 100644 --- a/mousetrack/src/main/java/org/codejive/miniterm/mousetrack/MouseEvent.java +++ b/mousetrack/src/main/java/org/codejive/miniterm/mousetrack/MouseEvent.java @@ -80,18 +80,28 @@ public Button button() { } /** - * Returns the 1-based column at which the event occurred. + * Returns the horizontal coordinate at which the event occurred. * - * @return column, ≥ 1 + *

By default this is the 1-based column of the character cell. If {@link + * MouseTracking.Encoding#SGR_PIXELS} was enabled when the event was reported, this is instead a + * 0-based pixel offset from the left edge of the terminal window. This class cannot tell the + * two apart — callers must track which encoding is active. + * + * @return column or pixel offset, depending on the active encoding */ public int x() { return x; } /** - * Returns the 1-based row at which the event occurred. + * Returns the vertical coordinate at which the event occurred. + * + *

By default this is the 1-based row of the character cell. If {@link + * MouseTracking.Encoding#SGR_PIXELS} was enabled when the event was reported, this is instead a + * 0-based pixel offset from the top edge of the terminal window. This class cannot tell the two + * apart — callers must track which encoding is active. * - * @return row, ≥ 1 + * @return row or pixel offset, depending on the active encoding */ public int y() { return y; diff --git a/mousetrack/src/main/java/org/codejive/miniterm/mousetrack/MouseTracking.java b/mousetrack/src/main/java/org/codejive/miniterm/mousetrack/MouseTracking.java index 139dcee..ac97c38 100644 --- a/mousetrack/src/main/java/org/codejive/miniterm/mousetrack/MouseTracking.java +++ b/mousetrack/src/main/java/org/codejive/miniterm/mousetrack/MouseTracking.java @@ -53,6 +53,16 @@ * *

{@link Encoding#SGR_PIXELS} (DEC mode 1016) extends {@link Encoding#SGR} to report pixel-level * coordinates instead of cell-based coordinates. Enable it together with {@link Encoding#SGR}. + * + *

The wire format is unchanged by {@link Encoding#SGR_PIXELS} — only the meaning of the {@code + * Px}/{@code Py} fields changes, from 1-based character-cell column/row to pixel offsets from the + * terminal's top-left corner. {@link #parse(String)} decodes both the same way; callers must track + * which encoding is active to know how to interpret {@link MouseEvent#x()} and {@link + * MouseEvent#y()}. + * + *

Some terminals reset {@link Encoding#SGR} as a side effect of disabling {@link + * Encoding#SGR_PIXELS}. If you toggle {@link Encoding#SGR_PIXELS} off at runtime, re-enable {@link + * Encoding#SGR} right after to be safe. */ public final class MouseTracking { From b1b1b6a103431adddea0e1772922527d9b5eef05 Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Thu, 10 Sep 2026 11:25:59 +0200 Subject: [PATCH 2/2] fix: `AnsiParser` now knows how to handle legacy X10 mouse events --- .../miniterm/ansiparser/AnsiParser.java | 17 ++++++++++++ .../miniterm/ansiparser/AnsiParserTest.java | 26 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/ansiparser/src/main/java/org/codejive/miniterm/ansiparser/AnsiParser.java b/ansiparser/src/main/java/org/codejive/miniterm/ansiparser/AnsiParser.java index 810899a..73bfb96 100644 --- a/ansiparser/src/main/java/org/codejive/miniterm/ansiparser/AnsiParser.java +++ b/ansiparser/src/main/java/org/codejive/miniterm/ansiparser/AnsiParser.java @@ -14,6 +14,7 @@ * *