From 0f939eeffc551bf4693e8b02820025870aa66d16 Mon Sep 17 00:00:00 2001 From: Seto Date: Thu, 30 Apr 2026 16:12:13 +0800 Subject: [PATCH 1/3] fix: improve VT buffer scrolling logic to respect scroll regions and add support for the HVP escape sequence --- .../app/plugin/core/terminal/vt/VtBuffer.java | 29 +++++++++++++++---- .../plugin/core/terminal/vt/VtHandler.java | 1 + 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/terminal/vt/VtBuffer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/terminal/vt/VtBuffer.java index d4e9d6a121..bb5713ba0e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/terminal/vt/VtBuffer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/terminal/vt/VtBuffer.java @@ -129,7 +129,14 @@ public class VtBuffer { if (c == 0) { return; } - checkVerticalScroll(); + // Only scroll if cursor is completely off-screen (beyond the display). + // Do NOT scroll when cursor is outside the scroll region but still within + // the display (e.g., on a fixed status bar line). Scroll region scrolling + // for line feeds is handled by moveCursorDown() via checkVerticalScroll(). + while (curY >= rows) { + scrollViewportDown(true); + curY = Math.max(0, curY - 1); + } // At this point, we have no choice but to wrap lines.get(curY).putChar(curX, c, curAttrs); } @@ -310,13 +317,25 @@ public class VtBuffer { */ public void moveCursorRight(int n, boolean wrap, boolean isCursorShowing) { if (wrap && curX + n >= cols) { - checkVerticalScroll(); + // When wrapping, only use scroll-region-aware scrolling if cursor is + // inside the scroll region. If outside (e.g., status bar), just clamp. + if (curY >= scrollStart && curY < scrollEnd) { + checkVerticalScroll(); + } curX = 0; - lines.get(curY).wrappedToNext = true; + if (curY < rows) { + lines.get(curY).wrappedToNext = true; + } curY++; bottomY = Math.max(bottomY, curY); - if (isCursorShowing) { - checkVerticalScroll(); + if (curY >= scrollStart && curY < scrollEnd) { + if (isCursorShowing) { + checkVerticalScroll(); + } + } + else { + // Outside scroll region: clamp to display bounds + curY = Math.min(curY, rows - 1); } } else { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/terminal/vt/VtHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/terminal/vt/VtHandler.java index cf39d2eecf..71742e0af0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/terminal/vt/VtHandler.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/terminal/vt/VtHandler.java @@ -876,6 +876,7 @@ public interface VtHandler { handleMoveCursorCol(n - 1); return; } + case 'f': // Horizontal and Vertical Position (same as CUP) case 'H': { // Cursor position OfInt bits = parseCsiInts(csiParam); int n = bits.hasNext() ? bits.nextInt() : 1; From 40f46a0841599541db0eb464b569487e33d47da5 Mon Sep 17 00:00:00 2001 From: Seto Date: Thu, 30 Apr 2026 16:35:31 +0800 Subject: [PATCH 2/3] fix: ensure vertical scrolling occurs during VT buffer wrap regardless of cursor visibility --- .../app/plugin/core/terminal/vt/VtBuffer.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/terminal/vt/VtBuffer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/terminal/vt/VtBuffer.java index bb5713ba0e..ab61bdfaec 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/terminal/vt/VtBuffer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/terminal/vt/VtBuffer.java @@ -317,9 +317,12 @@ public class VtBuffer { */ public void moveCursorRight(int n, boolean wrap, boolean isCursorShowing) { if (wrap && curX + n >= cols) { - // When wrapping, only use scroll-region-aware scrolling if cursor is - // inside the scroll region. If outside (e.g., status bar), just clamp. - if (curY >= scrollStart && curY < scrollEnd) { + // Decide based on the pre-wrap position whether we are in the scroll region. + // If the cursor was inside the scroll region before wrapping, the wrap may + // push it past the bottom margin and should trigger scroll-region scrolling. + // If outside (e.g., on a fixed status bar line), just clamp to the display. + boolean wasInScrollRegion = (curY >= scrollStart && curY < scrollEnd); + if (wasInScrollRegion) { checkVerticalScroll(); } curX = 0; @@ -328,10 +331,11 @@ public class VtBuffer { } curY++; bottomY = Math.max(bottomY, curY); - if (curY >= scrollStart && curY < scrollEnd) { - if (isCursorShowing) { - checkVerticalScroll(); - } + if (wasInScrollRegion) { + // Always scroll when wrapping within the scroll region, regardless + // of cursor visibility. This prevents deferred scrolls from leaking + // into subsequent putChar() calls. + checkVerticalScroll(); } else { // Outside scroll region: clamp to display bounds From 1b44ac3559b761e713d70b2675027accce2e82d7 Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Mon, 4 May 2026 19:21:43 +0000 Subject: [PATCH 3/3] GP-6784: Certify --- .../java/ghidra/app/plugin/core/terminal/vt/VtBuffer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/terminal/vt/VtBuffer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/terminal/vt/VtBuffer.java index ab61bdfaec..ded24e78b0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/terminal/vt/VtBuffer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/terminal/vt/VtBuffer.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.