wip
This commit is contained in:
31
src/main/java/dev/jlibghostty/Ghostty.java
Normal file
31
src/main/java/dev/jlibghostty/Ghostty.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package dev.jlibghostty;
|
||||
|
||||
import dev.jlibghostty.internal.GhosttyLibrary;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public final class Ghostty {
|
||||
private Ghostty() {
|
||||
}
|
||||
|
||||
public static Terminal open(TerminalOptions options) {
|
||||
return Terminal.open(options);
|
||||
}
|
||||
|
||||
public static boolean pasteIsSafe(String text) {
|
||||
return pasteIsSafe(text.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public static boolean pasteIsSafe(byte[] data) {
|
||||
return GhosttyLibrary.loadDefault().pasteIsSafe(data);
|
||||
}
|
||||
|
||||
public static String encodePaste(String text, boolean bracketed) {
|
||||
byte[] encoded = encodePaste(text.getBytes(StandardCharsets.UTF_8), bracketed);
|
||||
return new String(encoded, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static byte[] encodePaste(byte[] data, boolean bracketed) {
|
||||
return GhosttyLibrary.loadDefault().pasteEncode(data, bracketed);
|
||||
}
|
||||
}
|
||||
14
src/main/java/dev/jlibghostty/GhosttyException.java
Normal file
14
src/main/java/dev/jlibghostty/GhosttyException.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package dev.jlibghostty;
|
||||
|
||||
public final class GhosttyException extends RuntimeException {
|
||||
private final int resultCode;
|
||||
|
||||
public GhosttyException(String message, int resultCode) {
|
||||
super(message + " failed with libghostty-vt result " + resultCode);
|
||||
this.resultCode = resultCode;
|
||||
}
|
||||
|
||||
public int resultCode() {
|
||||
return resultCode;
|
||||
}
|
||||
}
|
||||
68
src/main/java/dev/jlibghostty/Terminal.java
Normal file
68
src/main/java/dev/jlibghostty/Terminal.java
Normal file
@@ -0,0 +1,68 @@
|
||||
package dev.jlibghostty;
|
||||
|
||||
import dev.jlibghostty.internal.GhosttyLibrary;
|
||||
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public final class Terminal implements AutoCloseable {
|
||||
private final GhosttyLibrary library;
|
||||
private final MemorySegment handle;
|
||||
private final AtomicBoolean closed = new AtomicBoolean();
|
||||
|
||||
private Terminal(GhosttyLibrary library, MemorySegment handle) {
|
||||
this.library = library;
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public static Terminal open(TerminalOptions options) {
|
||||
GhosttyLibrary library = GhosttyLibrary.loadDefault();
|
||||
return new Terminal(library, library.terminalNew(options));
|
||||
}
|
||||
|
||||
public void write(String vtData) {
|
||||
write(vtData.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public void write(byte[] vtData) {
|
||||
ensureOpen();
|
||||
library.terminalWrite(handle, vtData);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
ensureOpen();
|
||||
library.terminalReset(handle);
|
||||
}
|
||||
|
||||
public void resize(int columns, int rows, int cellWidthPx, int cellHeightPx) {
|
||||
ensureOpen();
|
||||
library.terminalResize(handle, columns, rows, cellWidthPx, cellHeightPx);
|
||||
}
|
||||
|
||||
public TerminalSnapshot snapshot() {
|
||||
ensureOpen();
|
||||
return new TerminalSnapshot(
|
||||
library.terminalGetU16(handle, GhosttyLibrary.TERMINAL_DATA_COLS),
|
||||
library.terminalGetU16(handle, GhosttyLibrary.TERMINAL_DATA_ROWS),
|
||||
library.terminalGetU16(handle, GhosttyLibrary.TERMINAL_DATA_CURSOR_X),
|
||||
library.terminalGetU16(handle, GhosttyLibrary.TERMINAL_DATA_CURSOR_Y),
|
||||
library.terminalGetBoolean(handle, GhosttyLibrary.TERMINAL_DATA_CURSOR_VISIBLE),
|
||||
library.terminalGetString(handle, GhosttyLibrary.TERMINAL_DATA_TITLE),
|
||||
library.terminalGetString(handle, GhosttyLibrary.TERMINAL_DATA_PWD)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (closed.compareAndSet(false, true)) {
|
||||
library.terminalFree(handle);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureOpen() {
|
||||
if (closed.get()) {
|
||||
throw new IllegalStateException("Terminal is closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/main/java/dev/jlibghostty/TerminalOptions.java
Normal file
19
src/main/java/dev/jlibghostty/TerminalOptions.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package dev.jlibghostty;
|
||||
|
||||
public record TerminalOptions(int columns, int rows, long maxScrollback) {
|
||||
public TerminalOptions {
|
||||
if (columns < 1 || columns > 65535) {
|
||||
throw new IllegalArgumentException("columns must be between 1 and 65535");
|
||||
}
|
||||
if (rows < 1 || rows > 65535) {
|
||||
throw new IllegalArgumentException("rows must be between 1 and 65535");
|
||||
}
|
||||
if (maxScrollback < 0) {
|
||||
throw new IllegalArgumentException("maxScrollback must be non-negative");
|
||||
}
|
||||
}
|
||||
|
||||
public static TerminalOptions of(int columns, int rows) {
|
||||
return new TerminalOptions(columns, rows, 10_000);
|
||||
}
|
||||
}
|
||||
12
src/main/java/dev/jlibghostty/TerminalSnapshot.java
Normal file
12
src/main/java/dev/jlibghostty/TerminalSnapshot.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package dev.jlibghostty;
|
||||
|
||||
public record TerminalSnapshot(
|
||||
int columns,
|
||||
int rows,
|
||||
int cursorX,
|
||||
int cursorY,
|
||||
boolean cursorVisible,
|
||||
String title,
|
||||
String workingDirectory
|
||||
) {
|
||||
}
|
||||
297
src/main/java/dev/jlibghostty/internal/GhosttyLibrary.java
Normal file
297
src/main/java/dev/jlibghostty/internal/GhosttyLibrary.java
Normal file
@@ -0,0 +1,297 @@
|
||||
package dev.jlibghostty.internal;
|
||||
|
||||
import dev.jlibghostty.GhosttyException;
|
||||
import dev.jlibghostty.TerminalOptions;
|
||||
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.Linker;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SymbolLookup;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static java.lang.foreign.ValueLayout.ADDRESS;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_BOOLEAN;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_BYTE;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_INT;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_SHORT;
|
||||
|
||||
public final class GhosttyLibrary {
|
||||
public static final int TERMINAL_DATA_COLS = 1;
|
||||
public static final int TERMINAL_DATA_ROWS = 2;
|
||||
public static final int TERMINAL_DATA_CURSOR_X = 3;
|
||||
public static final int TERMINAL_DATA_CURSOR_Y = 4;
|
||||
public static final int TERMINAL_DATA_CURSOR_VISIBLE = 7;
|
||||
public static final int TERMINAL_DATA_TITLE = 12;
|
||||
public static final int TERMINAL_DATA_PWD = 13;
|
||||
|
||||
private static final int GHOSTTY_SUCCESS = 0;
|
||||
private static final int GHOSTTY_OUT_OF_SPACE = -3;
|
||||
private static final int GHOSTTY_NO_VALUE = -4;
|
||||
|
||||
private static final Linker LINKER = Linker.nativeLinker();
|
||||
private static final ValueLayout.OfLong C_SIZE_T = sizeTLayout();
|
||||
|
||||
private static final GroupLayout TERMINAL_OPTIONS = MemoryLayout.structLayout(
|
||||
JAVA_SHORT.withName("cols"),
|
||||
JAVA_SHORT.withName("rows"),
|
||||
MemoryLayout.paddingLayout(4),
|
||||
C_SIZE_T.withName("max_scrollback")
|
||||
);
|
||||
|
||||
private static final GroupLayout GHOSTTY_STRING = MemoryLayout.structLayout(
|
||||
ADDRESS.withName("ptr"),
|
||||
C_SIZE_T.withName("len")
|
||||
);
|
||||
|
||||
private final MethodHandle terminalNew;
|
||||
private final MethodHandle terminalFree;
|
||||
private final MethodHandle terminalReset;
|
||||
private final MethodHandle terminalResize;
|
||||
private final MethodHandle terminalVtWrite;
|
||||
private final MethodHandle terminalGet;
|
||||
private final MethodHandle pasteIsSafe;
|
||||
private final MethodHandle pasteEncode;
|
||||
|
||||
private GhosttyLibrary(Path libraryPath) {
|
||||
try {
|
||||
SymbolLookup symbols = SymbolLookup.libraryLookup(libraryPath, Arena.global());
|
||||
|
||||
terminalNew = downcall(symbols, "ghostty_terminal_new",
|
||||
FunctionDescriptor.of(JAVA_INT, ADDRESS, ADDRESS, TERMINAL_OPTIONS));
|
||||
terminalFree = downcall(symbols, "ghostty_terminal_free",
|
||||
FunctionDescriptor.ofVoid(ADDRESS));
|
||||
terminalReset = downcall(symbols, "ghostty_terminal_reset",
|
||||
FunctionDescriptor.ofVoid(ADDRESS));
|
||||
terminalResize = downcall(symbols, "ghostty_terminal_resize",
|
||||
FunctionDescriptor.of(JAVA_INT, ADDRESS, JAVA_SHORT, JAVA_SHORT, JAVA_INT, JAVA_INT));
|
||||
terminalVtWrite = downcall(symbols, "ghostty_terminal_vt_write",
|
||||
FunctionDescriptor.ofVoid(ADDRESS, ADDRESS, C_SIZE_T));
|
||||
terminalGet = downcall(symbols, "ghostty_terminal_get",
|
||||
FunctionDescriptor.of(JAVA_INT, ADDRESS, JAVA_INT, ADDRESS));
|
||||
pasteIsSafe = downcall(symbols, "ghostty_paste_is_safe",
|
||||
FunctionDescriptor.of(JAVA_BOOLEAN, ADDRESS, C_SIZE_T));
|
||||
pasteEncode = downcall(symbols, "ghostty_paste_encode",
|
||||
FunctionDescriptor.of(JAVA_INT, ADDRESS, C_SIZE_T, JAVA_BOOLEAN, ADDRESS, C_SIZE_T, ADDRESS));
|
||||
} catch (IllegalCallerException e) {
|
||||
throw new IllegalStateException(
|
||||
"FFM native access is disabled. Run with --enable-native-access=dev.jlibghostty "
|
||||
+ "when using the module path, or --enable-native-access=ALL-UNNAMED on the classpath.",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static GhosttyLibrary loadDefault() {
|
||||
return Holder.INSTANCE;
|
||||
}
|
||||
|
||||
public MemorySegment terminalNew(TerminalOptions options) {
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
MemorySegment out = arena.allocate(ADDRESS);
|
||||
MemorySegment nativeOptions = arena.allocate(TERMINAL_OPTIONS);
|
||||
|
||||
nativeOptions.set(JAVA_SHORT, 0, (short) options.columns());
|
||||
nativeOptions.set(JAVA_SHORT, 2, (short) options.rows());
|
||||
nativeOptions.set(C_SIZE_T, 8, options.maxScrollback());
|
||||
|
||||
int result = (int) terminalNew.invoke(MemorySegment.NULL, out, nativeOptions);
|
||||
checkResult("ghostty_terminal_new", result);
|
||||
|
||||
MemorySegment terminal = out.get(ADDRESS, 0);
|
||||
if (terminal.address() == 0) {
|
||||
throw new IllegalStateException("ghostty_terminal_new returned a null terminal handle");
|
||||
}
|
||||
return terminal;
|
||||
} catch (Throwable t) {
|
||||
return rethrow(t);
|
||||
}
|
||||
}
|
||||
|
||||
public void terminalFree(MemorySegment terminal) {
|
||||
try {
|
||||
terminalFree.invoke(terminal);
|
||||
} catch (Throwable t) {
|
||||
rethrow(t);
|
||||
}
|
||||
}
|
||||
|
||||
public void terminalReset(MemorySegment terminal) {
|
||||
try {
|
||||
terminalReset.invoke(terminal);
|
||||
} catch (Throwable t) {
|
||||
rethrow(t);
|
||||
}
|
||||
}
|
||||
|
||||
public void terminalResize(MemorySegment terminal, int columns, int rows, int cellWidthPx, int cellHeightPx) {
|
||||
try {
|
||||
int result = (int) terminalResize.invoke(
|
||||
terminal,
|
||||
(short) columns,
|
||||
(short) rows,
|
||||
cellWidthPx,
|
||||
cellHeightPx
|
||||
);
|
||||
checkResult("ghostty_terminal_resize", result);
|
||||
} catch (Throwable t) {
|
||||
rethrow(t);
|
||||
}
|
||||
}
|
||||
|
||||
public void terminalWrite(MemorySegment terminal, byte[] data) {
|
||||
if (data.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
MemorySegment nativeData = arena.allocate(data.length, 1);
|
||||
MemorySegment.copy(data, 0, nativeData, JAVA_BYTE, 0, data.length);
|
||||
terminalVtWrite.invoke(terminal, nativeData, (long) data.length);
|
||||
} catch (Throwable t) {
|
||||
rethrow(t);
|
||||
}
|
||||
}
|
||||
|
||||
public int terminalGetU16(MemorySegment terminal, int key) {
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
MemorySegment out = arena.allocate(JAVA_SHORT);
|
||||
int result = (int) terminalGet.invoke(terminal, key, out);
|
||||
checkResult("ghostty_terminal_get", result);
|
||||
return Short.toUnsignedInt(out.get(JAVA_SHORT, 0));
|
||||
} catch (Throwable t) {
|
||||
return rethrow(t);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean terminalGetBoolean(MemorySegment terminal, int key) {
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
MemorySegment out = arena.allocate(JAVA_BOOLEAN);
|
||||
int result = (int) terminalGet.invoke(terminal, key, out);
|
||||
checkResult("ghostty_terminal_get", result);
|
||||
return out.get(JAVA_BOOLEAN, 0);
|
||||
} catch (Throwable t) {
|
||||
return rethrow(t);
|
||||
}
|
||||
}
|
||||
|
||||
public String terminalGetString(MemorySegment terminal, int key) {
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
MemorySegment out = arena.allocate(GHOSTTY_STRING);
|
||||
int result = (int) terminalGet.invoke(terminal, key, out);
|
||||
if (result == GHOSTTY_NO_VALUE) {
|
||||
return "";
|
||||
}
|
||||
checkResult("ghostty_terminal_get", result);
|
||||
|
||||
MemorySegment ptr = out.get(ADDRESS, 0);
|
||||
long len = out.get(C_SIZE_T, 8);
|
||||
if (ptr.address() == 0 || len == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
byte[] bytes = ptr.reinterpret(len).toArray(JAVA_BYTE);
|
||||
return new String(bytes, StandardCharsets.UTF_8);
|
||||
} catch (Throwable t) {
|
||||
return rethrow(t);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean pasteIsSafe(byte[] data) {
|
||||
if (data.length == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
MemorySegment nativeData = arena.allocate(data.length, 1);
|
||||
MemorySegment.copy(data, 0, nativeData, JAVA_BYTE, 0, data.length);
|
||||
return (boolean) pasteIsSafe.invoke(nativeData, (long) data.length);
|
||||
} catch (Throwable t) {
|
||||
return rethrow(t);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] pasteEncode(byte[] data, boolean bracketed) {
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
MemorySegment nativeData = MemorySegment.NULL;
|
||||
if (data.length > 0) {
|
||||
nativeData = arena.allocate(data.length, 1);
|
||||
MemorySegment.copy(data, 0, nativeData, JAVA_BYTE, 0, data.length);
|
||||
}
|
||||
|
||||
MemorySegment outWritten = arena.allocate(C_SIZE_T);
|
||||
int result = (int) pasteEncode.invoke(
|
||||
nativeData,
|
||||
(long) data.length,
|
||||
bracketed,
|
||||
MemorySegment.NULL,
|
||||
0L,
|
||||
outWritten
|
||||
);
|
||||
if (result != GHOSTTY_SUCCESS && result != GHOSTTY_OUT_OF_SPACE) {
|
||||
checkResult("ghostty_paste_encode", result);
|
||||
}
|
||||
|
||||
long required = outWritten.get(C_SIZE_T, 0);
|
||||
if (required == 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
MemorySegment out = arena.allocate(required, 1);
|
||||
result = (int) pasteEncode.invoke(
|
||||
nativeData,
|
||||
(long) data.length,
|
||||
bracketed,
|
||||
out,
|
||||
required,
|
||||
outWritten
|
||||
);
|
||||
checkResult("ghostty_paste_encode", result);
|
||||
|
||||
long written = outWritten.get(C_SIZE_T, 0);
|
||||
return out.asSlice(0, written).toArray(JAVA_BYTE);
|
||||
} catch (Throwable t) {
|
||||
return rethrow(t);
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodHandle downcall(SymbolLookup symbols, String name, FunctionDescriptor descriptor) {
|
||||
MemorySegment symbol = symbols.find(name)
|
||||
.orElseThrow(() -> new UnsatisfiedLinkError("Missing libghostty-vt symbol: " + name));
|
||||
return LINKER.downcallHandle(symbol, descriptor);
|
||||
}
|
||||
|
||||
private static void checkResult(String operation, int result) {
|
||||
if (result != GHOSTTY_SUCCESS) {
|
||||
throw new GhosttyException(operation, result);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T rethrow(Throwable t) {
|
||||
if (t instanceof RuntimeException runtimeException) {
|
||||
throw runtimeException;
|
||||
}
|
||||
if (t instanceof Error error) {
|
||||
throw error;
|
||||
}
|
||||
throw new IllegalStateException("Unexpected libghostty-vt FFM failure", t);
|
||||
}
|
||||
|
||||
private static ValueLayout.OfLong sizeTLayout() {
|
||||
ValueLayout layout = (ValueLayout) LINKER.canonicalLayouts().get("size_t");
|
||||
if (layout.byteSize() != Long.BYTES) {
|
||||
throw new UnsupportedOperationException("jlibghostty currently supports 64-bit platforms only");
|
||||
}
|
||||
return (ValueLayout.OfLong) layout;
|
||||
}
|
||||
|
||||
private static final class Holder {
|
||||
private static final GhosttyLibrary INSTANCE = new GhosttyLibrary(NativeLibraryLoader.resolve());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package dev.jlibghostty.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class NativeLibraryLoader {
|
||||
private static final String PROPERTY = "jlibghostty.library.path";
|
||||
private static final String ENVIRONMENT = "JLIBGHOSTTY_LIBRARY";
|
||||
|
||||
private NativeLibraryLoader() {
|
||||
}
|
||||
|
||||
public static Path resolve() {
|
||||
String propertyOverride = System.getProperty(PROPERTY);
|
||||
if (propertyOverride != null && !propertyOverride.isBlank()) {
|
||||
return Path.of(propertyOverride).toAbsolutePath();
|
||||
}
|
||||
|
||||
String environmentOverride = System.getenv(ENVIRONMENT);
|
||||
if (environmentOverride != null && !environmentOverride.isBlank()) {
|
||||
return Path.of(environmentOverride).toAbsolutePath();
|
||||
}
|
||||
|
||||
String resource = "/dev/jlibghostty/native/" + platform() + "/" + System.mapLibraryName("ghostty-vt");
|
||||
URL url = NativeLibraryLoader.class.getResource(resource);
|
||||
if (url == null) {
|
||||
throw new UnsatisfiedLinkError(
|
||||
"Bundled libghostty-vt not found at " + resource
|
||||
+ ". Set -D" + PROPERTY + "=/path/to/" + System.mapLibraryName("ghostty-vt")
|
||||
+ " or " + ENVIRONMENT + " to load an external library."
|
||||
);
|
||||
}
|
||||
|
||||
return extract(resource);
|
||||
}
|
||||
|
||||
private static Path extract(String resource) {
|
||||
try (InputStream in = NativeLibraryLoader.class.getResourceAsStream(resource)) {
|
||||
if (in == null) {
|
||||
throw new UnsatisfiedLinkError("Bundled libghostty-vt resource disappeared: " + resource);
|
||||
}
|
||||
|
||||
Path dir = Files.createTempDirectory("jlibghostty-");
|
||||
Path library = dir.resolve(System.mapLibraryName("ghostty-vt"));
|
||||
Files.copy(in, library, StandardCopyOption.REPLACE_EXISTING);
|
||||
library.toFile().setReadable(true);
|
||||
library.toFile().setExecutable(true);
|
||||
library.toFile().deleteOnExit();
|
||||
dir.toFile().deleteOnExit();
|
||||
return library;
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Could not extract bundled libghostty-vt", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String platform() {
|
||||
return normalizeOs() + "-" + normalizeArch();
|
||||
}
|
||||
|
||||
private static String normalizeOs() {
|
||||
String os = System.getProperty("os.name").toLowerCase(Locale.ROOT);
|
||||
if (os.contains("linux")) {
|
||||
return "linux";
|
||||
}
|
||||
if (os.contains("mac") || os.contains("darwin")) {
|
||||
return "macos";
|
||||
}
|
||||
throw new UnsupportedOperationException("Unsupported operating system for libghostty-vt: " + os);
|
||||
}
|
||||
|
||||
private static String normalizeArch() {
|
||||
String arch = System.getProperty("os.arch").toLowerCase(Locale.ROOT);
|
||||
return switch (arch) {
|
||||
case "amd64", "x86_64" -> "x86_64";
|
||||
case "aarch64", "arm64" -> "aarch64";
|
||||
default -> throw new UnsupportedOperationException("Unsupported architecture for libghostty-vt: " + arch);
|
||||
};
|
||||
}
|
||||
}
|
||||
3
src/main/java/module-info.java
Normal file
3
src/main/java/module-info.java
Normal file
@@ -0,0 +1,3 @@
|
||||
module dev.jlibghostty {
|
||||
exports dev.jlibghostty;
|
||||
}
|
||||
Reference in New Issue
Block a user