probably wrong fix

This commit is contained in:
Gregor Lohaus
2026-05-30 01:23:16 +02:00
parent 7dbbf89b27
commit ba884cd0a2
2 changed files with 12 additions and 87 deletions

View File

@@ -100,9 +100,6 @@ public final class TerminalCanvasView {
}
// GhosttyRenderStateDirty values (stable C ABI; see ghostty/vt/render.h).
private static final int DIRTY_PARTIAL = 1;
private static final int DIRTY_FULL = 2;
// Thin tab strip shown at the top when more than one tab is open.
private static final double TAB_BAR_HEIGHT = 22.0;
@@ -188,50 +185,20 @@ public final class TerminalCanvasView {
gc.restore();
}
// Repaint one pane whose content changed, then restore the (opaque) panes stacked above
// it wherever they overlap the repainted region, so the z-order stays correct.
// Repaint one pane whose content changed (a full, reliable repaint of the pane), then
// restore the (opaque) panes stacked above it where they overlap, keeping z-order.
private void repaintPaneContent(GraphicsContext gc, List<TerminalPane> panes, int index, Font font, FontMetrics metrics) {
TerminalPane pane = panes.get(index);
double px = Math.round(pane.x());
double py = Math.round(pane.y());
double pw = pane.width();
double ph = pane.height();
boolean kitty = config.kittyGraphics() && paneHasKittyGraphics(pane);
// A pane just resized (e.g. from a split) can't be trusted to report its dirty rows
// for the app's post-SIGWINCH redraw, so force a full snapshot once.
boolean forceFull = pane.consumeFullRender();
double regionY0;
double regionY1;
gc.save();
clipRect(gc, px, py, pw, ph);
if (kitty || forceFull) {
drawPaneContent(gc, pane, font, metrics, pane.renderSnapshotFull(), px, py, pw, ph, kitty);
regionY0 = py;
regionY1 = py + ph;
} else {
RenderStateSnapshot snapshot = pane.renderSnapshot();
int dirty = snapshot == null ? DIRTY_FULL : snapshot.dirty();
if (dirty == DIRTY_FULL) {
drawPaneContent(gc, pane, font, metrics, snapshot, px, py, pw, ph, false);
regionY0 = py;
regionY1 = py + ph;
} else if (dirty == DIRTY_PARTIAL) {
double[] band = drawDirtyRows(gc, pane, font, metrics, snapshot, px, py, pw, ph);
drawPaneContent(gc, pane, font, metrics, pane.renderSnapshot(), px, py, pw, ph,
config.kittyGraphics() && paneHasKittyGraphics(pane));
gc.restore();
if (band == null) {
return;
}
restoreStackedAbove(gc, panes, index, font, metrics, px, band[0], pw, band[1] - band[0]);
return;
} else {
gc.restore();
return; // dirty == FALSE: nothing visible changed.
}
}
gc.restore();
restoreStackedAbove(gc, panes, index, font, metrics, px, regionY0, pw, regionY1 - regionY0);
restoreStackedAbove(gc, panes, index, font, metrics, px, py, pw, ph);
}
// Redraw any panes above `index` in z-order that intersect the given screen rect, so a
@@ -251,7 +218,7 @@ public final class TerminalCanvasView {
}
gc.save();
clipRect(gc, ox0, oy0, ox1 - ox0, oy1 - oy0);
drawPaneContent(gc, above, font, metrics, above.renderSnapshotFull(), ax, ay, above.width(), above.height(),
drawPaneContent(gc, above, font, metrics, above.renderSnapshot(), ax, ay, above.width(), above.height(),
config.kittyGraphics() && paneHasKittyGraphics(above));
gc.restore();
}

View File

@@ -6,7 +6,6 @@ import dev.jlibghostty.MouseAction;
import dev.jlibghostty.MouseEncoder;
import dev.jlibghostty.MouseEncoderSize;
import dev.jlibghostty.MouseInput;
import dev.jlibghostty.RenderState;
import dev.jlibghostty.RenderStateSnapshot;
import dev.jlibghostty.ScrollViewport;
import dev.jlibghostty.Terminal;
@@ -27,9 +26,6 @@ public final class TerminalPane implements AutoCloseable {
private final Terminal terminal;
private final MouseEncoder mouseEncoder = new MouseEncoder();
// A persistent render state (reused across frames) is what makes ghostty's per-row
// dirty tracking meaningful: update() accumulates dirty since the last resetDirty().
private RenderState renderState = new RenderState();
private RenderStateSnapshot cachedSnapshot;
private ShellSession session;
private boolean floating;
@@ -47,7 +43,6 @@ public final class TerminalPane implements AutoCloseable {
private volatile long renderVersion;
private long snapshotVersion = -1;
private volatile boolean closed;
private boolean needsFullRender;
private TerminalPane(Terminal terminal, int columns, int rows) {
this.terminal = terminal;
@@ -135,34 +130,15 @@ public final class TerminalPane implements AutoCloseable {
}
/**
* Incremental snapshot: cells are marshalled only for rows that changed since the last
* frame (global dirty == PARTIAL), reused across calls for the same content version.
* Snapshotting is deferred here rather than done in refresh(), so a burst of writes
* between two frames collapses into a single snapshot.
* Full render snapshot of the current screen, memoised per content version (so a burst
* of writes between two frames yields one snapshot). Uses a throwaway render state per
* snapshot, which always returns the complete, correct screen — a persistent render
* state's per-row dirty tracking proved unreliable across resizes and screen clears.
*/
public RenderStateSnapshot renderSnapshot() {
return snapshot(false);
}
/**
* Full snapshot with every row's cells populated. Used where the whole pane is redrawn
* regardless of dirty state (the kitty-graphics path).
*/
public RenderStateSnapshot renderSnapshotFull() {
return snapshot(true);
}
private RenderStateSnapshot snapshot(boolean full) {
synchronized (terminal) {
if (full) {
renderState.update(terminal);
cachedSnapshot = renderState.snapshot();
renderState.resetDirty();
snapshotVersion = renderVersion;
} else if (snapshotVersion != renderVersion) {
renderState.update(terminal);
cachedSnapshot = renderState.snapshotIncremental();
renderState.resetDirty();
if (snapshotVersion != renderVersion) {
cachedSnapshot = terminal.renderSnapshot();
snapshotVersion = renderVersion;
}
return cachedSnapshot;
@@ -241,27 +217,10 @@ public final class TerminalPane implements AutoCloseable {
this.rows = rows;
this.pixelWidth = pixelWidth;
this.pixelHeight = pixelHeight;
// A persistent render state gets corrupted by a terminal resize: its next snapshot
// comes back blank (a throwaway render state, as ghostty's API was originally used,
// never had this). Recreate it so the next snapshot is a clean full render of the
// resized grid, even for an idle pane that won't redraw on its own.
renderState.close();
renderState = new RenderState();
snapshotVersion = -1;
// The app (e.g. a TUI) also redraws a moment later via SIGWINCH; force the next
// content repaint to use a full snapshot so we don't rely on dirty across the resize.
needsFullRender = true;
refresh();
}
}
/** Returns and clears the "force a full repaint" flag set by {@link #resize}. */
public boolean consumeFullRender() {
boolean pending = needsFullRender;
needsFullRender = false;
return pending;
}
private void refresh() {
// Only mark the pane dirty; the snapshot itself is computed lazily in
// renderSnapshot() so a burst of writes collapses into a single snapshot per frame.
@@ -281,7 +240,6 @@ public final class TerminalPane implements AutoCloseable {
session = null;
}
mouseEncoder.close();
renderState.close();
synchronized (terminal) {
terminal.close();
}