From 68121d50b52fb56038871c97c97e7a12ffe987c2 Mon Sep 17 00:00:00 2001 From: Gregor Lohaus Date: Fri, 29 May 2026 20:32:09 +0200 Subject: [PATCH] styling stuff --- src/main/java/dev/jlibghostty/RenderCell.java | 5 +- .../jlibghostty/internal/GhosttyLibrary.java | 49 ++++++++++++++++++- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/main/java/dev/jlibghostty/RenderCell.java b/src/main/java/dev/jlibghostty/RenderCell.java index c65c2c7..757a62f 100644 --- a/src/main/java/dev/jlibghostty/RenderCell.java +++ b/src/main/java/dev/jlibghostty/RenderCell.java @@ -8,7 +8,8 @@ public record RenderCell( Optional foreground, Optional background, Optional kittyPlaceholder, - boolean selected + boolean selected, + boolean inverse ) { public RenderCell( int column, @@ -17,7 +18,7 @@ public record RenderCell( Optional background, boolean selected ) { - this(column, codepoints, foreground, background, Optional.empty(), selected); + this(column, codepoints, foreground, background, Optional.empty(), selected, false); } public RenderCell { diff --git a/src/main/java/dev/jlibghostty/internal/GhosttyLibrary.java b/src/main/java/dev/jlibghostty/internal/GhosttyLibrary.java index 7363f04..6cc17ad 100644 --- a/src/main/java/dev/jlibghostty/internal/GhosttyLibrary.java +++ b/src/main/java/dev/jlibghostty/internal/GhosttyLibrary.java @@ -284,6 +284,35 @@ public final class GhosttyLibrary { MemoryLayout.paddingLayout(4) ); + // GhosttyStyleColor: { GhosttyStyleColorTag tag; union value; } (see ghostty/vt/style.h). + private static final GroupLayout GHOSTTY_STYLE_COLOR = MemoryLayout.structLayout( + C_INT.withName("tag"), + MemoryLayout.paddingLayout(4), + C_LONG_LONG.withName("value") + ); + + // GhosttyStyle sized struct. We only read the `inverse` flag, but lay out the whole + // struct so the offset and total size match the C ABI exactly. + private static final GroupLayout GHOSTTY_STYLE = MemoryLayout.structLayout( + C_SIZE_T.withName("size"), + GHOSTTY_STYLE_COLOR.withName("fg_color"), + GHOSTTY_STYLE_COLOR.withName("bg_color"), + GHOSTTY_STYLE_COLOR.withName("underline_color"), + C_BOOL.withName("bold"), + C_BOOL.withName("italic"), + C_BOOL.withName("faint"), + C_BOOL.withName("blink"), + C_BOOL.withName("inverse"), + C_BOOL.withName("invisible"), + C_BOOL.withName("strikethrough"), + C_BOOL.withName("overline"), + C_INT.withName("underline"), + MemoryLayout.paddingLayout(4) + ); + + private static final long STYLE_INVERSE_OFFSET = + GHOSTTY_STYLE.byteOffset(java.lang.foreign.MemoryLayout.PathElement.groupElement("inverse")); + private static final GroupLayout FORMATTER_SCREEN_EXTRA = MemoryLayout.structLayout( C_SIZE_T.withName("size"), C_BOOL.withName("cursor"), @@ -1002,7 +1031,8 @@ public final class GhosttyLibrary { renderStateRowCellColor(cells, RENDER_STATE_ROW_CELLS_DATA_FG_COLOR), renderStateRowCellColor(cells, RENDER_STATE_ROW_CELLS_DATA_BG_COLOR), renderStateRowCellKittyPlaceholder(cells, codepoints), - renderStateRowCellsGetBoolean(cells, RENDER_STATE_ROW_CELLS_DATA_SELECTED) + renderStateRowCellsGetBoolean(cells, RENDER_STATE_ROW_CELLS_DATA_SELECTED), + renderStateRowCellInverse(cells) )); column++; } @@ -1239,6 +1269,23 @@ public final class GhosttyLibrary { } } + // Reads the cell's reverse/inverse flag from its GhosttyStyle. The resolved fg/bg + // colors do NOT account for inverse, so a renderer must read this and swap fg/bg. + private boolean renderStateRowCellInverse(MemorySegment cells) { + try (Arena arena = Arena.ofConfined()) { + MemorySegment out = arena.allocate(GHOSTTY_STYLE); + out.set(C_SIZE_T, 0, GHOSTTY_STYLE.byteSize()); + int result = (int) renderStateRowCellsGet.invoke(cells, RENDER_STATE_ROW_CELLS_DATA_STYLE, out); + if (result == GHOSTTY_INVALID_VALUE) { + return false; + } + checkResult("ghostty_render_state_row_cells_get", result); + return out.get(C_BOOL, STYLE_INVERSE_OFFSET); + } catch (Throwable t) { + return rethrow(t); + } + } + private Optional renderStateRowCellKittyPlaceholder(MemorySegment cells, int[] codepoints) { if (codepoints.length == 0 || codepoints[0] != KittyPlaceholder.CODEPOINT) { return Optional.empty();