small improvements
This commit is contained in:
@@ -384,7 +384,10 @@ public final class Compositor {
|
|||||||
double ey = localY(event.getY(), pane, target);
|
double ey = localY(event.getY(), pane, target);
|
||||||
KeyModifiers modifiers = modifiers(event);
|
KeyModifiers modifiers = modifiers(event);
|
||||||
for (int i = 0; i < rows; i++) {
|
for (int i = 0; i < rows; i++) {
|
||||||
sent |= send(pane, target, MouseInput.press(wheelButton, ex, ey, modifiers), mouseButtonPressed, event);
|
if (!send(pane, target, MouseInput.press(wheelButton, ex, ey, modifiers), mouseButtonPressed, event)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sent = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!sent) {
|
if (!sent) {
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ final class GhosttyTerminalRenderer extends TerminalRenderer {
|
|||||||
// The default cell background (used for cells with no explicit bg, and as the foreground
|
// The default cell background (used for cells with no explicit bg, and as the foreground
|
||||||
// for reverse-video cells whose background is the terminal default).
|
// for reverse-video cells whose background is the terminal default).
|
||||||
private static final Color PANE_BACKGROUND = Color.rgb(9, 10, 12);
|
private static final Color PANE_BACKGROUND = Color.rgb(9, 10, 12);
|
||||||
|
private static final Color ACTIVE_BORDER = Color.rgb(87, 166, 255);
|
||||||
|
private static final Color INACTIVE_BORDER = Color.rgb(52, 57, 65);
|
||||||
|
private static final Color CURSOR_FILL = Color.rgb(225, 229, 235, 0.28);
|
||||||
|
|
||||||
// A full-screen redraw asks for one Color per cell; most cells share a handful of colors,
|
// A full-screen redraw asks for one Color per cell; most cells share a handful of colors,
|
||||||
// so cache them by packed RGB instead of allocating a Color each time. Bounded so a
|
// so cache them by packed RGB instead of allocating a Color each time. Bounded so a
|
||||||
@@ -48,6 +51,7 @@ final class GhosttyTerminalRenderer extends TerminalRenderer {
|
|||||||
private final TerminalMetrics metrics;
|
private final TerminalMetrics metrics;
|
||||||
// Decoded kitty images for this renderer's pane (kitty graphics state is per-terminal).
|
// Decoded kitty images for this renderer's pane (kitty graphics state is per-terminal).
|
||||||
private final Map<KittyImageKey, Image> kittyImageCache = new HashMap<>();
|
private final Map<KittyImageKey, Image> kittyImageCache = new HashMap<>();
|
||||||
|
private final StringBuilder textRun = new StringBuilder(256);
|
||||||
|
|
||||||
GhosttyTerminalRenderer(TerminalMetrics metrics) {
|
GhosttyTerminalRenderer(TerminalMetrics metrics) {
|
||||||
this.metrics = metrics;
|
this.metrics = metrics;
|
||||||
@@ -163,10 +167,17 @@ final class GhosttyTerminalRenderer extends TerminalRenderer {
|
|||||||
|
|
||||||
double contentBottom = top + snapshot.rows() * lineHeight;
|
double contentBottom = top + snapshot.rows() * lineHeight;
|
||||||
int lastRow = snapshot.rows() - 1;
|
int lastRow = snapshot.rows() - 1;
|
||||||
|
List<RenderRow> rows = snapshot.renderRows();
|
||||||
|
boolean allRowsDirty = allRowsDirty(snapshot, rows);
|
||||||
|
if (allRowsDirty) {
|
||||||
|
gc.setFill(PANE_BACKGROUND);
|
||||||
|
gc.fillRect(px, py, pw, ph);
|
||||||
|
}
|
||||||
|
|
||||||
boolean cursorRowDirty = false;
|
boolean cursorRowDirty = false;
|
||||||
double bandMin = Double.POSITIVE_INFINITY;
|
double bandMin = Double.POSITIVE_INFINITY;
|
||||||
double bandMax = Double.NEGATIVE_INFINITY;
|
double bandMax = Double.NEGATIVE_INFINITY;
|
||||||
for (RenderRow row : snapshot.renderRows()) {
|
for (RenderRow row : rows) {
|
||||||
if (!row.dirty()) {
|
if (!row.dirty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -174,8 +185,10 @@ final class GhosttyTerminalRenderer extends TerminalRenderer {
|
|||||||
// would leave sub-pixel seams between rows.
|
// would leave sub-pixel seams between rows.
|
||||||
double y0 = Math.floor(top + (row.row() * lineHeight));
|
double y0 = Math.floor(top + (row.row() * lineHeight));
|
||||||
double y1 = Math.ceil(top + ((row.row() + 1) * lineHeight));
|
double y1 = Math.ceil(top + ((row.row() + 1) * lineHeight));
|
||||||
|
if (!allRowsDirty) {
|
||||||
gc.setFill(PANE_BACKGROUND);
|
gc.setFill(PANE_BACKGROUND);
|
||||||
gc.fillRect(px, y0, pw, y1 - y0);
|
gc.fillRect(px, y0, pw, y1 - y0);
|
||||||
|
}
|
||||||
paintSidePadding(gc, row, px, pw, left, cellWidth, y0, y1 - y0);
|
paintSidePadding(gc, row, px, pw, left, cellWidth, y0, y1 - y0);
|
||||||
drawRow(gc, row, left, top, baseline, cellWidth, lineHeight);
|
drawRow(gc, row, left, top, baseline, cellWidth, lineHeight);
|
||||||
bandMin = Math.min(bandMin, y0);
|
bandMin = Math.min(bandMin, y0);
|
||||||
@@ -213,8 +226,21 @@ final class GhosttyTerminalRenderer extends TerminalRenderer {
|
|||||||
gc.restore();
|
gc.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean allRowsDirty(RenderStateSnapshot snapshot, List<RenderRow> rows) {
|
||||||
|
if (rows.size() != snapshot.rows()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < rows.size(); i++) {
|
||||||
|
RenderRow row = rows.get(i);
|
||||||
|
if (!row.dirty() || row.row() != i) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private void drawBorder(GraphicsContext gc, double x, double y, double width, double height, boolean active) {
|
private void drawBorder(GraphicsContext gc, double x, double y, double width, double height, boolean active) {
|
||||||
gc.setStroke(active ? Color.rgb(87, 166, 255) : Color.rgb(52, 57, 65));
|
gc.setStroke(active ? ACTIVE_BORDER : INACTIVE_BORDER);
|
||||||
gc.setLineWidth(active ? 2.0 : 1.0);
|
gc.setLineWidth(active ? 2.0 : 1.0);
|
||||||
gc.strokeRect(x + 0.5, y + 0.5, width - 1.0, height - 1.0);
|
gc.strokeRect(x + 0.5, y + 0.5, width - 1.0, height - 1.0);
|
||||||
}
|
}
|
||||||
@@ -266,7 +292,7 @@ final class GhosttyTerminalRenderer extends TerminalRenderer {
|
|||||||
gc.fillRect(paneX, contentBottom, paneWidth, paneY + paneHeight - contentBottom);
|
gc.fillRect(paneX, contentBottom, paneWidth, paneY + paneHeight - contentBottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void drawRow(
|
private void drawRow(
|
||||||
GraphicsContext gc,
|
GraphicsContext gc,
|
||||||
RenderRow row,
|
RenderRow row,
|
||||||
double left,
|
double left,
|
||||||
@@ -275,45 +301,143 @@ final class GhosttyTerminalRenderer extends TerminalRenderer {
|
|||||||
double cellWidth,
|
double cellWidth,
|
||||||
double lineHeight
|
double lineHeight
|
||||||
) {
|
) {
|
||||||
|
drawRowBackgrounds(gc, row, left, top, cellWidth, lineHeight);
|
||||||
|
drawRowText(gc, row, left, baseline, cellWidth, lineHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void drawRowBackgrounds(
|
||||||
|
GraphicsContext gc,
|
||||||
|
RenderRow row,
|
||||||
|
double left,
|
||||||
|
double top,
|
||||||
|
double cellWidth,
|
||||||
|
double lineHeight
|
||||||
|
) {
|
||||||
|
Color runBackground = null;
|
||||||
|
int runStartColumn = 0;
|
||||||
|
int previousColumn = -1;
|
||||||
for (RenderCell cell : row.cells()) {
|
for (RenderCell cell : row.cells()) {
|
||||||
if (cell.kittyPlaceholder().isPresent()) {
|
if (cell.kittyPlaceholder().isPresent()) {
|
||||||
|
flushBackgroundRun(gc, runBackground, left, top, cellWidth, lineHeight, row.row(), runStartColumn, previousColumn);
|
||||||
|
runBackground = null;
|
||||||
|
previousColumn = -1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
double x = left + (cell.column() * cellWidth);
|
Color bg = cell.selected() ? SELECTED_BACKGROUND : cellBackgroundOverride(cell);
|
||||||
double cellTop = top + (row.row() * lineHeight);
|
if (bg == null) {
|
||||||
|
flushBackgroundRun(gc, runBackground, left, top, cellWidth, lineHeight, row.row(), runStartColumn, previousColumn);
|
||||||
|
runBackground = null;
|
||||||
|
previousColumn = -1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Resolve fg/bg (null bg = terminal default, painted by the pane background).
|
if (runBackground == null || bg != runBackground || cell.column() != previousColumn + 1) {
|
||||||
// Avoid Optional.map's allocation on this hot path.
|
flushBackgroundRun(gc, runBackground, left, top, cellWidth, lineHeight, row.row(), runStartColumn, previousColumn);
|
||||||
|
runBackground = bg;
|
||||||
|
runStartColumn = cell.column();
|
||||||
|
}
|
||||||
|
previousColumn = cell.column();
|
||||||
|
}
|
||||||
|
flushBackgroundRun(gc, runBackground, left, top, cellWidth, lineHeight, row.row(), runStartColumn, previousColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void flushBackgroundRun(
|
||||||
|
GraphicsContext gc,
|
||||||
|
Color background,
|
||||||
|
double left,
|
||||||
|
double top,
|
||||||
|
double cellWidth,
|
||||||
|
double lineHeight,
|
||||||
|
int row,
|
||||||
|
int startColumn,
|
||||||
|
int endColumn
|
||||||
|
) {
|
||||||
|
if (background == null || endColumn < startColumn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gc.setFill(background);
|
||||||
|
gc.fillRect(
|
||||||
|
left + (startColumn * cellWidth),
|
||||||
|
top + (row * lineHeight),
|
||||||
|
(endColumn - startColumn + 1) * cellWidth,
|
||||||
|
lineHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawRowText(
|
||||||
|
GraphicsContext gc,
|
||||||
|
RenderRow row,
|
||||||
|
double left,
|
||||||
|
double baseline,
|
||||||
|
double cellWidth,
|
||||||
|
double lineHeight
|
||||||
|
) {
|
||||||
|
StringBuilder run = textRun;
|
||||||
|
run.setLength(0);
|
||||||
|
Color runForeground = null;
|
||||||
|
int runStartColumn = 0;
|
||||||
|
int previousColumn = -1;
|
||||||
|
|
||||||
|
for (RenderCell cell : row.cells()) {
|
||||||
|
if (cell.kittyPlaceholder().isPresent() || cell.codepoints().length == 0) {
|
||||||
|
flushTextRun(gc, run, runForeground, left, baseline, cellWidth, lineHeight, row.row(), runStartColumn);
|
||||||
|
runForeground = null;
|
||||||
|
previousColumn = -1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color fg = cellForegroundColor(cell);
|
||||||
|
if (run.length() == 0 || fg != runForeground || cell.column() != previousColumn + 1) {
|
||||||
|
flushTextRun(gc, run, runForeground, left, baseline, cellWidth, lineHeight, row.row(), runStartColumn);
|
||||||
|
runForeground = fg;
|
||||||
|
runStartColumn = cell.column();
|
||||||
|
}
|
||||||
|
run.append(cell.text());
|
||||||
|
previousColumn = cell.column();
|
||||||
|
}
|
||||||
|
flushTextRun(gc, run, runForeground, left, baseline, cellWidth, lineHeight, row.row(), runStartColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void flushTextRun(
|
||||||
|
GraphicsContext gc,
|
||||||
|
StringBuilder run,
|
||||||
|
Color foreground,
|
||||||
|
double left,
|
||||||
|
double baseline,
|
||||||
|
double cellWidth,
|
||||||
|
double lineHeight,
|
||||||
|
int row,
|
||||||
|
int startColumn
|
||||||
|
) {
|
||||||
|
if (run.length() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gc.setFill(foreground);
|
||||||
|
gc.fillText(run.toString(), left + (startColumn * cellWidth), baseline + (row * lineHeight));
|
||||||
|
run.setLength(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Background override for a cell: null means the pane default background already covers it.
|
||||||
|
private static Color cellBackgroundOverride(RenderCell cell) {
|
||||||
|
if (cell.inverse()) {
|
||||||
|
var fg = cell.foreground();
|
||||||
|
return fg.isPresent() ? toFxColor(fg.get()) : DEFAULT_FOREGROUND;
|
||||||
|
}
|
||||||
|
var bgOpt = cell.background();
|
||||||
|
Color bg = bgOpt.isPresent() ? toFxColor(bgOpt.get()) : null;
|
||||||
|
return bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Color cellForegroundColor(RenderCell cell) {
|
||||||
var fgOpt = cell.foreground();
|
var fgOpt = cell.foreground();
|
||||||
var bgOpt = cell.background();
|
var bgOpt = cell.background();
|
||||||
Color fg = fgOpt.isPresent() ? toFxColor(fgOpt.get()) : DEFAULT_FOREGROUND;
|
Color fg = fgOpt.isPresent() ? toFxColor(fgOpt.get()) : DEFAULT_FOREGROUND;
|
||||||
Color bg = bgOpt.isPresent() ? toFxColor(bgOpt.get()) : null;
|
Color bg = bgOpt.isPresent() ? toFxColor(bgOpt.get()) : null;
|
||||||
|
|
||||||
// Reverse video: ghostty does not bake inverse into the resolved colours, so we
|
|
||||||
// swap them here, falling back to the terminal defaults for whichever is unset.
|
|
||||||
if (cell.inverse()) {
|
if (cell.inverse()) {
|
||||||
Color swappedBg = fg;
|
return (bg != null) ? bg : PANE_BACKGROUND;
|
||||||
fg = (bg != null) ? bg : PANE_BACKGROUND;
|
|
||||||
bg = swappedBg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bg != null) {
|
|
||||||
gc.setFill(bg);
|
|
||||||
gc.fillRect(x, cellTop, cellWidth, lineHeight);
|
|
||||||
}
|
|
||||||
if (cell.selected()) {
|
|
||||||
gc.setFill(SELECTED_BACKGROUND);
|
|
||||||
gc.fillRect(x, cellTop, cellWidth, lineHeight);
|
|
||||||
}
|
|
||||||
if (cell.codepoints().length == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
double y = baseline + (row.row() * lineHeight);
|
|
||||||
gc.setFill(fg);
|
|
||||||
gc.fillText(cell.text(), x, y);
|
|
||||||
}
|
}
|
||||||
|
return fg;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Color toFxColor(RenderColor color) {
|
private static Color toFxColor(RenderColor color) {
|
||||||
@@ -337,8 +461,8 @@ final class GhosttyTerminalRenderer extends TerminalRenderer {
|
|||||||
|
|
||||||
double x = left + (snapshot.cursorViewportX() * cellWidth);
|
double x = left + (snapshot.cursorViewportX() * cellWidth);
|
||||||
double y = top + (snapshot.cursorViewportY() * lineHeight);
|
double y = top + (snapshot.cursorViewportY() * lineHeight);
|
||||||
gc.setStroke(Color.rgb(225, 229, 235));
|
gc.setStroke(DEFAULT_FOREGROUND);
|
||||||
gc.setFill(Color.rgb(225, 229, 235, 0.28));
|
gc.setFill(CURSOR_FILL);
|
||||||
gc.setLineWidth(1.5);
|
gc.setLineWidth(1.5);
|
||||||
|
|
||||||
RenderCursorStyle style = snapshot.cursorStyle();
|
RenderCursorStyle style = snapshot.cursorStyle();
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ public final class LinuxPty implements AutoCloseable {
|
|||||||
|
|
||||||
private final Arena arena = Arena.ofShared();
|
private final Arena arena = Arena.ofShared();
|
||||||
private final MemorySegment readBuffer = arena.allocate(65536);
|
private final MemorySegment readBuffer = arena.allocate(65536);
|
||||||
|
private final MemorySegment writeBuffer = arena.allocate(65536);
|
||||||
private final Object writeLock = new Object();
|
private final Object writeLock = new Object();
|
||||||
private final int masterFd;
|
private final int masterFd;
|
||||||
private final int pid;
|
private final int pid;
|
||||||
@@ -186,17 +187,20 @@ public final class LinuxPty implements AutoCloseable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
synchronized (writeLock) {
|
synchronized (writeLock) {
|
||||||
try (Arena a = Arena.ofConfined()) {
|
int offset = 0;
|
||||||
MemorySegment buf = a.allocate(data.length);
|
|
||||||
MemorySegment.copy(data, 0, buf, ValueLayout.JAVA_BYTE, 0, data.length);
|
|
||||||
long offset = 0;
|
|
||||||
while (offset < data.length) {
|
while (offset < data.length) {
|
||||||
long n = callLong(WRITE, masterFd, buf.asSlice(offset), data.length - offset);
|
int chunk = (int) Math.min(writeBuffer.byteSize(), data.length - offset);
|
||||||
if (n < 0) {
|
MemorySegment.copy(data, offset, writeBuffer, ValueLayout.JAVA_BYTE, 0, chunk);
|
||||||
|
|
||||||
|
long written = 0;
|
||||||
|
while (written < chunk) {
|
||||||
|
long n = callLong(WRITE, masterFd, writeBuffer.asSlice(written), chunk - written);
|
||||||
|
if (n <= 0) {
|
||||||
throw new IllegalStateException("write to pty failed");
|
throw new IllegalStateException("write to pty failed");
|
||||||
}
|
}
|
||||||
offset += n;
|
written += n;
|
||||||
}
|
}
|
||||||
|
offset += chunk;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import javafx.scene.shape.Shape;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,7 +34,7 @@ final class Tab implements AutoCloseable {
|
|||||||
private double lastTopInset;
|
private double lastTopInset;
|
||||||
// Bumped whenever one of this tab's panes changes content; the compositor reads the current
|
// Bumped whenever one of this tab's panes changes content; the compositor reads the current
|
||||||
// tab's value each frame as an O(1) "anything to repaint?" check.
|
// tab's value each frame as an O(1) "anything to repaint?" check.
|
||||||
private long contentVersion;
|
private final AtomicLong contentVersion = new AtomicLong();
|
||||||
|
|
||||||
Tab(AppConfig config, TerminalMetrics metrics) {
|
Tab(AppConfig config, TerminalMetrics metrics) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
@@ -54,7 +55,7 @@ final class Tab implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
long contentVersion() {
|
long contentVersion() {
|
||||||
return contentVersion;
|
return contentVersion.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -291,7 +292,7 @@ final class Tab implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void markContentChanged() {
|
private void markContentChanged() {
|
||||||
contentVersion++;
|
contentVersion.incrementAndGet();
|
||||||
}
|
}
|
||||||
|
|
||||||
private TerminalPane openPane(boolean asFloating) {
|
private TerminalPane openPane(boolean asFloating) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import javafx.scene.canvas.GraphicsContext;
|
|||||||
import javafx.scene.shape.Shape;
|
import javafx.scene.shape.Shape;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* One terminal: owns its ghostty {@link Terminal}, the {@link ShellSession}/pty driving it,
|
* One terminal: owns its ghostty {@link Terminal}, the {@link ShellSession}/pty driving it,
|
||||||
@@ -49,7 +50,7 @@ public final class TerminalPane implements AutoCloseable, RenderTarget {
|
|||||||
private int rows;
|
private int rows;
|
||||||
private int pixelWidth;
|
private int pixelWidth;
|
||||||
private int pixelHeight;
|
private int pixelHeight;
|
||||||
private long contentVersion;
|
private final AtomicLong contentVersion = new AtomicLong();
|
||||||
private long snapshotVersion = -1;
|
private long snapshotVersion = -1;
|
||||||
|
|
||||||
private TerminalPane(Terminal terminal, TerminalMetrics metrics, boolean kittyEnabled,
|
private TerminalPane(Terminal terminal, TerminalMetrics metrics, boolean kittyEnabled,
|
||||||
@@ -169,16 +170,17 @@ public final class TerminalPane implements AutoCloseable, RenderTarget {
|
|||||||
|
|
||||||
private RenderStateSnapshot takeSnapshot(boolean full) {
|
private RenderStateSnapshot takeSnapshot(boolean full) {
|
||||||
synchronized (terminal) {
|
synchronized (terminal) {
|
||||||
|
long version = contentVersion.get();
|
||||||
if (full) {
|
if (full) {
|
||||||
renderState.update(terminal);
|
renderState.update(terminal);
|
||||||
cachedSnapshot = renderState.snapshot();
|
cachedSnapshot = renderState.snapshot();
|
||||||
renderState.resetDirty();
|
renderState.resetDirty();
|
||||||
snapshotVersion = contentVersion;
|
snapshotVersion = version;
|
||||||
} else if (snapshotVersion != contentVersion) {
|
} else if (snapshotVersion != version) {
|
||||||
renderState.update(terminal);
|
renderState.update(terminal);
|
||||||
cachedSnapshot = renderState.snapshotIncremental();
|
cachedSnapshot = renderState.snapshotIncremental();
|
||||||
renderState.resetDirty();
|
renderState.resetDirty();
|
||||||
snapshotVersion = contentVersion;
|
snapshotVersion = version;
|
||||||
}
|
}
|
||||||
return cachedSnapshot;
|
return cachedSnapshot;
|
||||||
}
|
}
|
||||||
@@ -192,7 +194,7 @@ public final class TerminalPane implements AutoCloseable, RenderTarget {
|
|||||||
|
|
||||||
/** This pane's own content revision, bumped on every change (see {@link #refresh()}). */
|
/** This pane's own content revision, bumped on every change (see {@link #refresh()}). */
|
||||||
public long contentVersion() {
|
public long contentVersion() {
|
||||||
return contentVersion;
|
return contentVersion.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -276,7 +278,7 @@ public final class TerminalPane implements AutoCloseable, RenderTarget {
|
|||||||
// Mark this pane's content dirty (the snapshot is computed lazily in the paint path,
|
// Mark this pane's content dirty (the snapshot is computed lazily in the paint path,
|
||||||
// so a burst of writes collapses into one snapshot per frame) and tell the owning tab
|
// so a burst of writes collapses into one snapshot per frame) and tell the owning tab
|
||||||
// one of its panes changed.
|
// one of its panes changed.
|
||||||
contentVersion++;
|
contentVersion.incrementAndGet();
|
||||||
onContentChange.run();
|
onContentChange.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user