stacking
This commit is contained in:
@@ -45,7 +45,10 @@ public record AppConfig(
|
||||
"navigate_down", binding(document, "keybindings.navigate_down", defaults.keybindings.get("navigate_down")),
|
||||
"navigate_up", binding(document, "keybindings.navigate_up", defaults.keybindings.get("navigate_up")),
|
||||
"navigate_right", binding(document, "keybindings.navigate_right", defaults.keybindings.get("navigate_right")),
|
||||
"toggle_floating", binding(document, "keybindings.toggle_floating", defaults.keybindings.get("toggle_floating"))
|
||||
"toggle_floating", binding(document, "keybindings.toggle_floating", defaults.keybindings.get("toggle_floating")),
|
||||
"new_floating", binding(document, "keybindings.new_floating", defaults.keybindings.get("new_floating")),
|
||||
"next_floating", binding(document, "keybindings.next_floating", defaults.keybindings.get("next_floating")),
|
||||
"close_pane", binding(document, "keybindings.close_pane", defaults.keybindings.get("close_pane"))
|
||||
)
|
||||
);
|
||||
} catch (TomlException ex) {
|
||||
@@ -59,7 +62,7 @@ public record AppConfig(
|
||||
100,
|
||||
30,
|
||||
defaultShell(),
|
||||
"Symbols Nerd Font Mono",
|
||||
"JetBrainsMono Nerd Font",
|
||||
15.0,
|
||||
1200.0,
|
||||
760.0,
|
||||
@@ -69,7 +72,10 @@ public record AppConfig(
|
||||
"navigate_down", KeyBinding.parse("ALT+J"),
|
||||
"navigate_up", KeyBinding.parse("ALT+K"),
|
||||
"navigate_right", KeyBinding.parse("ALT+L"),
|
||||
"toggle_floating", KeyBinding.parse("ALT+F")
|
||||
"toggle_floating", KeyBinding.parse("ALT+F"),
|
||||
"new_floating", KeyBinding.parse("ALT+SHIFT+F"),
|
||||
"next_floating", KeyBinding.parse("ALT+F12"),
|
||||
"close_pane", KeyBinding.parse("ALT+X")
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -83,8 +89,7 @@ public record AppConfig(
|
||||
}
|
||||
|
||||
private static String defaultShell() {
|
||||
String shell = System.getenv("SHELL");
|
||||
return shell == null || shell.isBlank() ? "/bin/sh" : shell;
|
||||
return "/bin/bash";
|
||||
}
|
||||
|
||||
private static KeyBinding binding(TomlTable table, String key, KeyBinding fallback) {
|
||||
|
||||
@@ -18,7 +18,7 @@ public record KeyBinding(boolean alt, boolean control, boolean shift, KeyCode co
|
||||
case "ALT", "META" -> alt = true;
|
||||
case "CTRL", "CONTROL" -> control = true;
|
||||
case "SHIFT" -> shift = true;
|
||||
default -> code = KeyCode.getKeyCode(token);
|
||||
default -> code = keyCode(token);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,4 +34,19 @@ public record KeyBinding(boolean alt, boolean control, boolean shift, KeyCode co
|
||||
&& event.isShiftDown() == shift
|
||||
&& event.getCode() == code;
|
||||
}
|
||||
|
||||
private static KeyCode keyCode(String token) {
|
||||
KeyCode alias = switch (token) {
|
||||
case "GRAVE", "BACKTICK", "BACK_QUOTE", "`" -> KeyCode.BACK_QUOTE;
|
||||
default -> null;
|
||||
};
|
||||
if (alias != null) {
|
||||
return alias;
|
||||
}
|
||||
try {
|
||||
return KeyCode.valueOf(token);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
return KeyCode.getKeyCode(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,20 @@ final class KeyEncoder {
|
||||
}
|
||||
|
||||
static String encode(KeyEvent event) {
|
||||
if (event.isControlDown() && !event.isAltDown() && !event.isMetaDown()) {
|
||||
String control = controlSequence(event);
|
||||
if (control != null) {
|
||||
return control;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.isAltDown() && !event.isControlDown() && !event.isMetaDown()) {
|
||||
String alt = altSequence(event);
|
||||
if (alt != null) {
|
||||
return alt;
|
||||
}
|
||||
}
|
||||
|
||||
KeyCode code = event.getCode();
|
||||
return switch (code) {
|
||||
case ENTER -> "\r";
|
||||
@@ -23,7 +37,43 @@ final class KeyEncoder {
|
||||
case DELETE -> "\u001b[3~";
|
||||
case PAGE_UP -> "\u001b[5~";
|
||||
case PAGE_DOWN -> "\u001b[6~";
|
||||
case F1 -> "\u001bOP";
|
||||
case F2 -> "\u001bOQ";
|
||||
case F3 -> "\u001bOR";
|
||||
case F4 -> "\u001bOS";
|
||||
case F5 -> "\u001b[15~";
|
||||
case F6 -> "\u001b[17~";
|
||||
case F7 -> "\u001b[18~";
|
||||
case F8 -> "\u001b[19~";
|
||||
case F9 -> "\u001b[20~";
|
||||
case F10 -> "\u001b[21~";
|
||||
case F11 -> "\u001b[23~";
|
||||
case F12 -> "\u001b[24~";
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private static String controlSequence(KeyEvent event) {
|
||||
KeyCode code = event.getCode();
|
||||
if (code.isLetterKey()) {
|
||||
return String.valueOf((char) (Character.toUpperCase(code.getName().charAt(0)) - '@'));
|
||||
}
|
||||
return switch (code) {
|
||||
case SPACE -> "\u0000";
|
||||
case OPEN_BRACKET -> "\u001b";
|
||||
case BACK_SLASH -> "\u001c";
|
||||
case CLOSE_BRACKET -> "\u001d";
|
||||
case DIGIT6 -> "\u001e";
|
||||
case MINUS -> "\u001f";
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private static String altSequence(KeyEvent event) {
|
||||
KeyCode code = event.getCode();
|
||||
if (code.isLetterKey() || code.isDigitKey()) {
|
||||
return "\u001b" + code.getName().toLowerCase();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
package com.gregor.jprototerm;
|
||||
|
||||
import javafx.scene.canvas.GraphicsContext;
|
||||
import javafx.scene.image.Image;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public final class KittyGraphicsRegistry {
|
||||
private final boolean enabled;
|
||||
private final StringBuilder stream = new StringBuilder();
|
||||
private final Map<Integer, StringBuilder> chunks = new HashMap<>();
|
||||
private final List<Placement> placements = new ArrayList<>();
|
||||
|
||||
public KittyGraphicsRegistry(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public synchronized void accept(String text) {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
stream.append(text);
|
||||
parseBufferedCommands();
|
||||
}
|
||||
|
||||
public synchronized void draw(GraphicsContext gc, double originX, double originY, double cellWidth, double lineHeight) {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Placement placement : placements) {
|
||||
double x = originX + placement.column * cellWidth;
|
||||
double y = originY + placement.row * lineHeight;
|
||||
double width = placement.columns <= 0 ? placement.image.getWidth() : placement.columns * cellWidth;
|
||||
double height = placement.rows <= 0 ? placement.image.getHeight() : placement.rows * lineHeight;
|
||||
gc.drawImage(placement.image, x, y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void clear() {
|
||||
chunks.clear();
|
||||
placements.clear();
|
||||
stream.setLength(0);
|
||||
}
|
||||
|
||||
private void parseBufferedCommands() {
|
||||
int start;
|
||||
while ((start = stream.indexOf("\u001b_G")) >= 0) {
|
||||
int end = commandEnd(start + 3);
|
||||
if (end < 0) {
|
||||
if (start > 0) {
|
||||
stream.delete(0, start);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
String command = stream.substring(start + 3, end);
|
||||
handleCommand(command);
|
||||
stream.delete(0, end + terminatorLength(end));
|
||||
}
|
||||
|
||||
if (stream.length() > 16384) {
|
||||
stream.delete(0, stream.length() - 4096);
|
||||
}
|
||||
}
|
||||
|
||||
private int commandEnd(int from) {
|
||||
int bell = stream.indexOf("\u0007", from);
|
||||
int st = stream.indexOf("\u001b\\", from);
|
||||
if (bell < 0) {
|
||||
return st;
|
||||
}
|
||||
if (st < 0) {
|
||||
return bell;
|
||||
}
|
||||
return Math.min(bell, st);
|
||||
}
|
||||
|
||||
private int terminatorLength(int end) {
|
||||
return stream.charAt(end) == '\u0007' ? 1 : 2;
|
||||
}
|
||||
|
||||
private void handleCommand(String command) {
|
||||
int separator = command.indexOf(';');
|
||||
if (separator < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, String> control = parseControl(command.substring(0, separator));
|
||||
String payload = command.substring(separator + 1).replace("\n", "").replace("\r", "");
|
||||
|
||||
int id = intControl(control, "i", 1);
|
||||
boolean more = intControl(control, "m", 0) == 1;
|
||||
chunks.computeIfAbsent(id, ignored -> new StringBuilder()).append(payload);
|
||||
if (more) {
|
||||
return;
|
||||
}
|
||||
|
||||
String data = chunks.remove(id).toString();
|
||||
try {
|
||||
byte[] bytes = Base64.getDecoder().decode(data);
|
||||
Image image = new Image(new ByteArrayInputStream(bytes));
|
||||
if (!image.isError()) {
|
||||
placements.add(new Placement(
|
||||
image,
|
||||
intControl(control, "x", 0),
|
||||
intControl(control, "y", 0),
|
||||
intControl(control, "c", 0),
|
||||
intControl(control, "r", 0)
|
||||
));
|
||||
}
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
chunks.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, String> parseControl(String text) {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
for (String part : text.split(",")) {
|
||||
int equals = part.indexOf('=');
|
||||
if (equals > 0) {
|
||||
result.put(part.substring(0, equals), part.substring(equals + 1));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int intControl(Map<String, String> control, String key, int fallback) {
|
||||
try {
|
||||
return Integer.parseInt(control.getOrDefault(key, String.valueOf(fallback)));
|
||||
} catch (NumberFormatException ex) {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
private record Placement(Image image, int column, int row, int columns, int rows) {
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ public final class Main extends Application {
|
||||
StackPane root = new StackPane(terminalView.canvas());
|
||||
terminalView.canvas().widthProperty().bind(root.widthProperty());
|
||||
terminalView.canvas().heightProperty().bind(root.heightProperty());
|
||||
terminalView.canvas().setOnMousePressed(event -> terminalView.canvas().requestFocus());
|
||||
|
||||
Scene scene = new Scene(root, config.windowWidth(), config.windowHeight());
|
||||
scene.addEventFilter(KeyEvent.KEY_PRESSED, event -> handlePressed(config, event));
|
||||
@@ -38,6 +39,7 @@ public final class Main extends Application {
|
||||
workspace.close();
|
||||
});
|
||||
stage.show();
|
||||
terminalView.canvas().requestFocus();
|
||||
}
|
||||
|
||||
private void handlePressed(AppConfig config, KeyEvent event) {
|
||||
@@ -56,6 +58,15 @@ public final class Main extends Application {
|
||||
} else if (config.keybindings().get("toggle_floating").matches(event)) {
|
||||
workspace.toggleFloating();
|
||||
event.consume();
|
||||
} else if (config.keybindings().get("new_floating").matches(event)) {
|
||||
workspace.createFloatingPane();
|
||||
event.consume();
|
||||
} else if (config.keybindings().get("next_floating").matches(event)) {
|
||||
workspace.nextFloatingPane();
|
||||
event.consume();
|
||||
} else if (config.keybindings().get("close_pane").matches(event)) {
|
||||
workspace.closeActivePane();
|
||||
event.consume();
|
||||
} else {
|
||||
String encoded = KeyEncoder.encode(event);
|
||||
if (encoded != null) {
|
||||
@@ -78,8 +89,7 @@ public final class Main extends Application {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.setProperty("prism.order", "es2,sw");
|
||||
System.setProperty("prism.verbose", "true");
|
||||
System.setProperty("prism.order", System.getProperty("prism.order", "es2,sw"));
|
||||
launch(Main.class, args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
package com.gregor.jprototerm;
|
||||
|
||||
import com.pty4j.PtyProcess;
|
||||
import com.pty4j.PtyProcessBuilder;
|
||||
import com.pty4j.WinSize;
|
||||
import javafx.application.Platform;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public final class ShellSession implements AutoCloseable {
|
||||
private final Process process;
|
||||
private final PtyProcess process;
|
||||
private final OutputStream stdin;
|
||||
private final ExecutorService reader;
|
||||
private volatile boolean closed;
|
||||
|
||||
private ShellSession(Process process, TerminalPane pane, KittyGraphicsRegistry graphicsRegistry) {
|
||||
private ShellSession(PtyProcess process, TerminalPane pane) {
|
||||
this.process = process;
|
||||
this.stdin = process.getOutputStream();
|
||||
this.reader = Executors.newSingleThreadExecutor(runnable -> {
|
||||
@@ -22,27 +27,35 @@ public final class ShellSession implements AutoCloseable {
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
});
|
||||
reader.submit(() -> readOutput(pane, graphicsRegistry));
|
||||
reader.submit(() -> readOutput(pane));
|
||||
}
|
||||
|
||||
public static ShellSession start(String shell, TerminalPane pane, KittyGraphicsRegistry graphicsRegistry) {
|
||||
public static ShellSession start(String shell, TerminalPane pane, int columns, int rows) {
|
||||
try {
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(
|
||||
"script",
|
||||
"-qfec",
|
||||
shell + " -i",
|
||||
"/dev/null"
|
||||
).redirectErrorStream(true);
|
||||
processBuilder.environment().put("TERM", "xterm-kitty");
|
||||
processBuilder.environment().put("COLORTERM", "truecolor");
|
||||
Process process = processBuilder.start();
|
||||
return new ShellSession(process, pane, graphicsRegistry);
|
||||
Map<String, String> environment = new HashMap<>(System.getenv());
|
||||
environment.put("TERM", "xterm-kitty");
|
||||
environment.put("COLORTERM", "truecolor");
|
||||
|
||||
PtyProcess process = new PtyProcessBuilder(new String[] {shell, "-i"})
|
||||
.setEnvironment(environment)
|
||||
.setInitialColumns(columns)
|
||||
.setInitialRows(rows)
|
||||
.setDirectory(System.getProperty("user.home"))
|
||||
.start();
|
||||
return new ShellSession(process, pane);
|
||||
} catch (IOException ex) {
|
||||
pane.write("failed to start shell: " + ex.getMessage() + "\r\n");
|
||||
throw new IllegalStateException("Could not start shell " + shell, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void resize(int columns, int rows) {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
process.setWinSize(new WinSize(columns, rows));
|
||||
}
|
||||
|
||||
public void send(String text) {
|
||||
if (closed) {
|
||||
return;
|
||||
@@ -55,17 +68,17 @@ public final class ShellSession implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
private void readOutput(TerminalPane pane, KittyGraphicsRegistry graphicsRegistry) {
|
||||
private void readOutput(TerminalPane pane) {
|
||||
byte[] buffer = new byte[8192];
|
||||
try {
|
||||
int read;
|
||||
while ((read = process.getInputStream().read(buffer)) != -1) {
|
||||
String text = new String(buffer, 0, read, StandardCharsets.UTF_8);
|
||||
if (!closed) {
|
||||
graphicsRegistry.accept(text);
|
||||
byte[] bytes = new byte[read];
|
||||
System.arraycopy(buffer, 0, bytes, 0, read);
|
||||
Platform.runLater(() -> {
|
||||
if (!closed) {
|
||||
pane.write(text);
|
||||
pane.write(bytes);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,15 +1,37 @@
|
||||
package com.gregor.jprototerm;
|
||||
|
||||
import dev.jlibghostty.KittyImageCompression;
|
||||
import dev.jlibghostty.KittyImageFormat;
|
||||
import dev.jlibghostty.KittyImageSnapshot;
|
||||
import dev.jlibghostty.KittyPlacement;
|
||||
import dev.jlibghostty.KittyRenderInfo;
|
||||
import dev.jlibghostty.RenderCell;
|
||||
import dev.jlibghostty.RenderColor;
|
||||
import dev.jlibghostty.RenderCursorStyle;
|
||||
import dev.jlibghostty.RenderRow;
|
||||
import dev.jlibghostty.RenderStateSnapshot;
|
||||
import javafx.scene.canvas.Canvas;
|
||||
import javafx.scene.canvas.GraphicsContext;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.PixelFormat;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.text.Font;
|
||||
import javafx.scene.text.FontSmoothingType;
|
||||
import javafx.scene.text.Text;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class TerminalCanvasView {
|
||||
private static final Color DEFAULT_FOREGROUND = Color.rgb(225, 229, 235);
|
||||
private static final Color SELECTED_BACKGROUND = Color.rgb(52, 92, 140);
|
||||
|
||||
private final Canvas canvas = new Canvas();
|
||||
private final TerminalWorkspace workspace;
|
||||
private final AppConfig config;
|
||||
private final Map<Long, Image> kittyImageCache = new HashMap<>();
|
||||
|
||||
public TerminalCanvasView(TerminalWorkspace workspace, AppConfig config) {
|
||||
this.workspace = workspace;
|
||||
@@ -29,7 +51,7 @@ public final class TerminalCanvasView {
|
||||
GraphicsContext gc = canvas.getGraphicsContext2D();
|
||||
gc.setFill(Color.rgb(16, 16, 18));
|
||||
gc.fillRect(0, 0, width, height);
|
||||
gc.setFontSmoothingType(FontSmoothingType.GRAY);
|
||||
gc.setFontSmoothingType(FontSmoothingType.LCD);
|
||||
|
||||
for (TerminalPane pane : workspace.panes()) {
|
||||
drawPane(gc, pane);
|
||||
@@ -55,20 +77,172 @@ public final class TerminalCanvasView {
|
||||
|
||||
Font font = Font.font(config.fontFamily(), config.fontSize());
|
||||
gc.setFont(font);
|
||||
gc.setFill(Color.rgb(225, 229, 235));
|
||||
|
||||
double lineHeight = Math.ceil(config.fontSize() * 1.35);
|
||||
FontMetrics metrics = measureFontMetrics(font);
|
||||
int columns = Math.max(1, (int) ((pane.width() - 24.0) / metrics.cellWidth));
|
||||
int rows = Math.max(1, (int) ((pane.height() - 24.0) / metrics.lineHeight));
|
||||
pane.resize(columns, rows, (int) Math.round(metrics.cellWidth), (int) Math.round(metrics.lineHeight));
|
||||
|
||||
double left = pane.x() + 12.0;
|
||||
double baseline = pane.y() + 18.0;
|
||||
int maxLines = Math.max(1, (int) ((pane.height() - 24.0) / lineHeight));
|
||||
double top = pane.y() + 12.0;
|
||||
double baseline = top + metrics.baselineOffset;
|
||||
|
||||
String[] lines = pane.snapshotText().split("\\R", -1);
|
||||
int start = Math.max(0, lines.length - maxLines);
|
||||
for (int i = start; i < lines.length; i++) {
|
||||
gc.fillText(lines[i], left, baseline + ((i - start) * lineHeight));
|
||||
RenderStateSnapshot snapshot = pane.renderSnapshot();
|
||||
if (snapshot != null) {
|
||||
for (RenderRow row : snapshot.renderRows()) {
|
||||
drawRow(gc, row, left, top, baseline, metrics.cellWidth, metrics.lineHeight);
|
||||
}
|
||||
}
|
||||
|
||||
pane.graphicsRegistry().draw(gc, pane.x() + 12.0, pane.y() + 12.0, config.fontSize() * 0.62, lineHeight);
|
||||
if (snapshot != null) {
|
||||
drawCursor(gc, snapshot, left, top, metrics.cellWidth, metrics.lineHeight);
|
||||
}
|
||||
|
||||
if (config.kittyGraphics()) {
|
||||
drawKittyGraphics(gc, pane, left, top, metrics.cellWidth, metrics.lineHeight);
|
||||
}
|
||||
gc.restore();
|
||||
}
|
||||
|
||||
private static FontMetrics measureFontMetrics(Font font) {
|
||||
Text text = new Text("Mg");
|
||||
text.setFont(font);
|
||||
double textHeight = text.getLayoutBounds().getHeight();
|
||||
double lineHeight = Math.max(1.0, Math.ceil(textHeight * 1.2));
|
||||
double baselineOffset = -text.getLayoutBounds().getMinY() + ((lineHeight - textHeight) / 2.0);
|
||||
|
||||
Text cell = new Text("M");
|
||||
cell.setFont(font);
|
||||
double cellWidth = Math.max(1.0, Math.ceil(cell.getLayoutBounds().getWidth()));
|
||||
return new FontMetrics(cellWidth, lineHeight, baselineOffset);
|
||||
}
|
||||
|
||||
private static void drawRow(
|
||||
GraphicsContext gc,
|
||||
RenderRow row,
|
||||
double left,
|
||||
double top,
|
||||
double baseline,
|
||||
double cellWidth,
|
||||
double lineHeight
|
||||
) {
|
||||
for (RenderCell cell : row.cells()) {
|
||||
double x = left + (cell.column() * cellWidth);
|
||||
double cellTop = top + (row.row() * lineHeight);
|
||||
cell.background().ifPresent(background -> {
|
||||
gc.setFill(toFxColor(background));
|
||||
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);
|
||||
Color foreground = cell.foreground().map(TerminalCanvasView::toFxColor).orElse(DEFAULT_FOREGROUND);
|
||||
gc.setFill(foreground);
|
||||
gc.fillText(cell.text(), x, y);
|
||||
}
|
||||
}
|
||||
|
||||
private static Color toFxColor(RenderColor color) {
|
||||
return Color.rgb(color.red(), color.green(), color.blue());
|
||||
}
|
||||
|
||||
private static void drawCursor(GraphicsContext gc, RenderStateSnapshot snapshot, double left, double top, double cellWidth, double lineHeight) {
|
||||
if (!snapshot.cursorVisible() || !snapshot.cursorViewportHasValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
double x = left + (snapshot.cursorViewportX() * cellWidth);
|
||||
double y = top + (snapshot.cursorViewportY() * lineHeight);
|
||||
gc.setStroke(Color.rgb(225, 229, 235));
|
||||
gc.setFill(Color.rgb(225, 229, 235, 0.28));
|
||||
gc.setLineWidth(1.5);
|
||||
|
||||
RenderCursorStyle style = snapshot.cursorStyle();
|
||||
if (style == RenderCursorStyle.BAR) {
|
||||
gc.strokeLine(x + 0.5, y + 2.0, x + 0.5, y + lineHeight - 2.0);
|
||||
} else if (style == RenderCursorStyle.UNDERLINE) {
|
||||
gc.strokeLine(x + 1.0, y + lineHeight - 2.0, x + cellWidth - 1.0, y + lineHeight - 2.0);
|
||||
} else if (style == RenderCursorStyle.BLOCK) {
|
||||
gc.fillRect(x + 0.5, y + 1.0, Math.max(1.0, cellWidth - 1.0), Math.max(1.0, lineHeight - 2.0));
|
||||
} else {
|
||||
gc.strokeRect(x + 0.5, y + 1.0, Math.max(1.0, cellWidth - 1.0), Math.max(1.0, lineHeight - 2.0));
|
||||
}
|
||||
}
|
||||
|
||||
private void drawKittyGraphics(GraphicsContext gc, TerminalPane pane, double originX, double originY, double cellWidth, double lineHeight) {
|
||||
pane.kittyGraphics().ifPresent(graphics -> {
|
||||
for (KittyPlacement placement : graphics.placements()) {
|
||||
Image image = imageFor(placement);
|
||||
if (image == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
KittyRenderInfo renderInfo = placement.renderInfo().orElse(null);
|
||||
double x = originX;
|
||||
double y = originY;
|
||||
double width = image.getWidth();
|
||||
double height = image.getHeight();
|
||||
|
||||
if (renderInfo != null) {
|
||||
x += renderInfo.viewportColumn() * cellWidth;
|
||||
y += renderInfo.viewportRow() * lineHeight;
|
||||
width = renderInfo.gridColumns() > 0 ? renderInfo.gridColumns() * cellWidth : renderInfo.pixelWidth();
|
||||
height = renderInfo.gridRows() > 0 ? renderInfo.gridRows() * lineHeight : renderInfo.pixelHeight();
|
||||
} else {
|
||||
width = placement.columns() > 0 ? placement.columns() * cellWidth : width;
|
||||
height = placement.rows() > 0 ? placement.rows() * lineHeight : height;
|
||||
}
|
||||
|
||||
gc.drawImage(image, x + placement.xOffset(), y + placement.yOffset(), width, height);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Image imageFor(KittyPlacement placement) {
|
||||
return placement.image()
|
||||
.map(snapshot -> kittyImageCache.computeIfAbsent(snapshot.id(), ignored -> decodeImage(snapshot)))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private Image decodeImage(KittyImageSnapshot snapshot) {
|
||||
if (snapshot.compression() != KittyImageCompression.NONE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (snapshot.format() == KittyImageFormat.PNG) {
|
||||
return new Image(new ByteArrayInputStream(snapshot.data()));
|
||||
}
|
||||
|
||||
int width = Math.toIntExact(snapshot.width());
|
||||
int height = Math.toIntExact(snapshot.height());
|
||||
WritableImage image = new WritableImage(width, height);
|
||||
byte[] data = snapshot.data();
|
||||
|
||||
if (snapshot.format() == KittyImageFormat.RGBA) {
|
||||
image.getPixelWriter().setPixels(0, 0, width, height, PixelFormat.getByteBgraInstance(), rgbaToBgra(data), 0, width * 4);
|
||||
} else if (snapshot.format() == KittyImageFormat.RGB) {
|
||||
image.getPixelWriter().setPixels(0, 0, width, height, PixelFormat.getByteRgbInstance(), data, 0, width * 3);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
private static byte[] rgbaToBgra(byte[] rgba) {
|
||||
byte[] bgra = new byte[rgba.length];
|
||||
for (int i = 0; i + 3 < rgba.length; i += 4) {
|
||||
bgra[i] = rgba[i + 2];
|
||||
bgra[i + 1] = rgba[i + 1];
|
||||
bgra[i + 2] = rgba[i];
|
||||
bgra[i + 3] = rgba[i + 3];
|
||||
}
|
||||
return bgra;
|
||||
}
|
||||
|
||||
private record FontMetrics(double cellWidth, double lineHeight, double baselineOffset) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,38 @@
|
||||
package com.gregor.jprototerm;
|
||||
|
||||
import dev.jlibghostty.Ghostty;
|
||||
import dev.jlibghostty.KittyGraphics;
|
||||
import dev.jlibghostty.RenderStateSnapshot;
|
||||
import dev.jlibghostty.Terminal;
|
||||
import dev.jlibghostty.TerminalOptions;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public final class TerminalPane implements AutoCloseable {
|
||||
private final Terminal terminal;
|
||||
private final KittyGraphicsRegistry graphicsRegistry;
|
||||
private final AtomicReference<String> snapshotText = new AtomicReference<>("");
|
||||
private final AtomicReference<RenderStateSnapshot> renderSnapshot = new AtomicReference<>();
|
||||
private ShellSession session;
|
||||
private boolean floating;
|
||||
private boolean visible = true;
|
||||
private double x;
|
||||
private double y;
|
||||
private double width;
|
||||
private double height;
|
||||
private int columns;
|
||||
private int rows;
|
||||
private int pixelWidth;
|
||||
private int pixelHeight;
|
||||
|
||||
private TerminalPane(Terminal terminal, KittyGraphicsRegistry graphicsRegistry) {
|
||||
private TerminalPane(Terminal terminal, int columns, int rows) {
|
||||
this.terminal = terminal;
|
||||
this.graphicsRegistry = graphicsRegistry;
|
||||
this.columns = columns;
|
||||
this.rows = rows;
|
||||
}
|
||||
|
||||
public static TerminalPane create(int columns, int rows, boolean kittyGraphics) {
|
||||
public static TerminalPane create(int columns, int rows) {
|
||||
Terminal terminal = Ghostty.open(TerminalOptions.of(columns, rows));
|
||||
TerminalPane pane = new TerminalPane(terminal, new KittyGraphicsRegistry(kittyGraphics));
|
||||
TerminalPane pane = new TerminalPane(terminal, columns, rows);
|
||||
pane.refresh();
|
||||
return pane;
|
||||
}
|
||||
@@ -36,6 +44,13 @@ public final class TerminalPane implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
public void write(byte[] bytes) {
|
||||
synchronized (terminal) {
|
||||
terminal.write(bytes);
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public void attach(ShellSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
@@ -46,12 +61,14 @@ public final class TerminalPane implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
public String snapshotText() {
|
||||
return snapshotText.get();
|
||||
public RenderStateSnapshot renderSnapshot() {
|
||||
return renderSnapshot.get();
|
||||
}
|
||||
|
||||
public KittyGraphicsRegistry graphicsRegistry() {
|
||||
return graphicsRegistry;
|
||||
public Optional<KittyGraphics> kittyGraphics() {
|
||||
synchronized (terminal) {
|
||||
return terminal.kittyGraphics();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean floating() {
|
||||
@@ -62,6 +79,14 @@ public final class TerminalPane implements AutoCloseable {
|
||||
this.floating = floating;
|
||||
}
|
||||
|
||||
public boolean visible() {
|
||||
return visible;
|
||||
}
|
||||
|
||||
public void setVisible(boolean visible) {
|
||||
this.visible = visible;
|
||||
}
|
||||
|
||||
public double x() {
|
||||
return x;
|
||||
}
|
||||
@@ -85,8 +110,29 @@ public final class TerminalPane implements AutoCloseable {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public void resize(int columns, int rows, int pixelWidth, int pixelHeight) {
|
||||
if (columns <= 0 || rows <= 0 || pixelWidth <= 0 || pixelHeight <= 0) {
|
||||
return;
|
||||
}
|
||||
if (this.columns == columns && this.rows == rows && this.pixelWidth == pixelWidth && this.pixelHeight == pixelHeight) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (terminal) {
|
||||
terminal.resize(columns, rows, pixelWidth, pixelHeight);
|
||||
if (session != null) {
|
||||
session.resize(columns, rows);
|
||||
}
|
||||
this.columns = columns;
|
||||
this.rows = rows;
|
||||
this.pixelWidth = pixelWidth;
|
||||
this.pixelHeight = pixelHeight;
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
snapshotText.set(String.valueOf(terminal.snapshot()));
|
||||
renderSnapshot.set(terminal.renderSnapshot());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -8,6 +8,7 @@ public final class TerminalWorkspace implements AutoCloseable {
|
||||
private final AppConfig config;
|
||||
private final List<TerminalPane> panes = new ArrayList<>();
|
||||
private int activeIndex;
|
||||
private int hiddenFloatingFocusIndex = -1;
|
||||
|
||||
public TerminalWorkspace(AppConfig config) {
|
||||
this.config = config;
|
||||
@@ -19,7 +20,18 @@ public final class TerminalWorkspace implements AutoCloseable {
|
||||
}
|
||||
|
||||
public List<TerminalPane> panes() {
|
||||
return List.copyOf(panes);
|
||||
List<TerminalPane> visible = panes.stream().filter(TerminalPane::visible).toList();
|
||||
TerminalPane active = activePane();
|
||||
if (!active.visible() || !active.floating()) {
|
||||
return visible;
|
||||
}
|
||||
|
||||
List<TerminalPane> ordered = new ArrayList<>(visible.size());
|
||||
visible.stream()
|
||||
.filter(pane -> pane != active)
|
||||
.forEach(ordered::add);
|
||||
ordered.add(active);
|
||||
return List.copyOf(ordered);
|
||||
}
|
||||
|
||||
public boolean isActive(TerminalPane pane) {
|
||||
@@ -27,20 +39,29 @@ public final class TerminalWorkspace implements AutoCloseable {
|
||||
}
|
||||
|
||||
public void layout(double width, double height) {
|
||||
List<TerminalPane> tiled = panes.stream().filter(pane -> !pane.floating()).toList();
|
||||
List<TerminalPane> tiled = panes.stream()
|
||||
.filter(TerminalPane::visible)
|
||||
.filter(pane -> !pane.floating())
|
||||
.toList();
|
||||
int tileCount = Math.max(1, tiled.size());
|
||||
double tileWidth = width / tileCount;
|
||||
for (int i = 0; i < tiled.size(); i++) {
|
||||
tiled.get(i).bounds(i * tileWidth, 0, tileWidth, height);
|
||||
}
|
||||
|
||||
for (TerminalPane pane : panes) {
|
||||
if (pane.floating()) {
|
||||
List<TerminalPane> floating = panes.stream()
|
||||
.filter(TerminalPane::visible)
|
||||
.filter(TerminalPane::floating)
|
||||
.toList();
|
||||
for (int i = 0; i < floating.size(); i++) {
|
||||
TerminalPane pane = floating.get(i);
|
||||
if (pane.visible() && pane.floating()) {
|
||||
double floatingWidth = Math.max(420, width * 0.58);
|
||||
double floatingHeight = Math.max(260, height * 0.58);
|
||||
double offset = i * 28.0;
|
||||
pane.bounds(
|
||||
(width - floatingWidth) / 2.0,
|
||||
(height - floatingHeight) / 2.0,
|
||||
Math.min(width - floatingWidth - 12.0, ((width - floatingWidth) / 2.0) + offset),
|
||||
Math.min(height - floatingHeight - 12.0, ((height - floatingHeight) / 2.0) + offset),
|
||||
floatingWidth,
|
||||
floatingHeight
|
||||
);
|
||||
@@ -50,7 +71,12 @@ public final class TerminalWorkspace implements AutoCloseable {
|
||||
|
||||
public void navigate(Direction direction) {
|
||||
TerminalPane current = activePane();
|
||||
if (current.floating() && navigateFloatingStack(direction)) {
|
||||
return;
|
||||
}
|
||||
|
||||
panes.stream()
|
||||
.filter(TerminalPane::visible)
|
||||
.filter(pane -> pane != current)
|
||||
.filter(pane -> directionFilter(direction, current, pane))
|
||||
.min(Comparator.comparingDouble(pane -> distance(current, pane)))
|
||||
@@ -58,23 +84,160 @@ public final class TerminalWorkspace implements AutoCloseable {
|
||||
}
|
||||
|
||||
public void toggleFloating() {
|
||||
TerminalPane active = activePane();
|
||||
if (active.floating()) {
|
||||
panes.remove(activeIndex);
|
||||
active.close();
|
||||
activeIndex = Math.max(0, activeIndex - 1);
|
||||
List<TerminalPane> floating = panes.stream()
|
||||
.filter(TerminalPane::floating)
|
||||
.toList();
|
||||
if (floating.isEmpty()) {
|
||||
createFloatingPane();
|
||||
return;
|
||||
}
|
||||
|
||||
boolean anyVisible = floating.stream().anyMatch(TerminalPane::visible);
|
||||
if (anyVisible) {
|
||||
TerminalPane active = activePane();
|
||||
hiddenFloatingFocusIndex = active.floating() ? activeIndex : firstVisibleFloatingIndex();
|
||||
floating.forEach(pane -> pane.setVisible(false));
|
||||
activeIndex = firstVisibleNonFloatingIndex();
|
||||
} else {
|
||||
floating.forEach(pane -> pane.setVisible(true));
|
||||
activeIndex = visibleIndexOrFallback(hiddenFloatingFocusIndex, panes.indexOf(floating.get(floating.size() - 1)));
|
||||
hiddenFloatingFocusIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public void createFloatingPane() {
|
||||
TerminalPane pane = openPane(true);
|
||||
panes.add(pane);
|
||||
activeIndex = panes.size() - 1;
|
||||
}
|
||||
|
||||
public void nextFloatingPane() {
|
||||
TerminalPane next = nextFloatingAfter(activeIndex);
|
||||
next.setVisible(true);
|
||||
activeIndex = panes.indexOf(next);
|
||||
}
|
||||
|
||||
public void closeActivePane() {
|
||||
TerminalPane active = activePane();
|
||||
if (!active.floating() || panes.stream().filter(pane -> !pane.floating()).count() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int removed = activeIndex;
|
||||
int previous = previousVisibleIndex(removed);
|
||||
panes.remove(removed);
|
||||
active.close();
|
||||
activeIndex = adjustIndexAfterRemoval(previous, removed);
|
||||
hiddenFloatingFocusIndex = adjustHiddenFocusAfterRemoval(hiddenFloatingFocusIndex, removed);
|
||||
}
|
||||
|
||||
private TerminalPane nextFloatingAfter(int index) {
|
||||
for (int i = index + 1; i < panes.size(); i++) {
|
||||
TerminalPane pane = panes.get(i);
|
||||
if (pane.floating()) {
|
||||
return pane;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i <= index && i < panes.size(); i++) {
|
||||
TerminalPane pane = panes.get(i);
|
||||
if (pane.floating()) {
|
||||
return pane;
|
||||
}
|
||||
}
|
||||
return createAndReturnFloatingPane();
|
||||
}
|
||||
|
||||
private TerminalPane createAndReturnFloatingPane() {
|
||||
TerminalPane pane = openPane(true);
|
||||
panes.add(pane);
|
||||
return pane;
|
||||
}
|
||||
|
||||
private boolean navigateFloatingStack(Direction direction) {
|
||||
List<TerminalPane> floating = panes.stream()
|
||||
.filter(TerminalPane::visible)
|
||||
.filter(TerminalPane::floating)
|
||||
.toList();
|
||||
if (floating.size() < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int current = floating.indexOf(activePane());
|
||||
if (current < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int next = switch (direction) {
|
||||
case LEFT, UP -> current - 1;
|
||||
case DOWN, RIGHT -> current + 1;
|
||||
};
|
||||
if (next < 0 || next >= floating.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
activeIndex = panes.indexOf(floating.get(next));
|
||||
return true;
|
||||
}
|
||||
|
||||
private int firstVisibleFloatingIndex() {
|
||||
for (int i = 0; i < panes.size(); i++) {
|
||||
TerminalPane pane = panes.get(i);
|
||||
if (pane.visible() && pane.floating()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int firstVisibleNonFloatingIndex() {
|
||||
for (int i = 0; i < panes.size(); i++) {
|
||||
TerminalPane pane = panes.get(i);
|
||||
if (pane.visible() && !pane.floating()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int previousVisibleIndex(int index) {
|
||||
for (int i = index - 1; i >= 0; i--) {
|
||||
if (panes.get(i).visible()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
for (int i = index + 1; i < panes.size(); i++) {
|
||||
if (panes.get(i).visible()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return firstVisibleNonFloatingIndex();
|
||||
}
|
||||
|
||||
private int visibleIndexOrFallback(int index, int fallback) {
|
||||
if (index >= 0 && index < panes.size() && panes.get(index).visible()) {
|
||||
return index;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
private static int adjustIndexAfterRemoval(int index, int removedIndex) {
|
||||
if (index < 0) {
|
||||
return 0;
|
||||
}
|
||||
return index > removedIndex ? index - 1 : index;
|
||||
}
|
||||
|
||||
private static int adjustHiddenFocusAfterRemoval(int index, int removedIndex) {
|
||||
if (index < 0 || index == removedIndex) {
|
||||
return -1;
|
||||
}
|
||||
return index > removedIndex ? index - 1 : index;
|
||||
}
|
||||
|
||||
private TerminalPane openPane(boolean floating) {
|
||||
TerminalPane pane = TerminalPane.create(config.columns(), config.rows(), config.kittyGraphics());
|
||||
TerminalPane pane = TerminalPane.create(config.columns(), config.rows());
|
||||
pane.setFloating(floating);
|
||||
pane.attach(ShellSession.start(config.shell(), pane, pane.graphicsRegistry()));
|
||||
pane.attach(ShellSession.start(config.shell(), pane, config.columns(), config.rows()));
|
||||
return pane;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "java.lang.Object",
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
},
|
||||
{
|
||||
"name": "java.lang.String",
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
},
|
||||
{
|
||||
"name": "java.lang.Runnable",
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
},
|
||||
{
|
||||
"name": "java.lang.Thread",
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
},
|
||||
{
|
||||
"name": "java.nio.Buffer",
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
},
|
||||
{
|
||||
"name": "java.nio.ByteBuffer",
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
},
|
||||
{
|
||||
"name": "java.nio.DirectByteBuffer",
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
},
|
||||
{
|
||||
"name": "java.nio.IntBuffer",
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
},
|
||||
{
|
||||
"name": "java.nio.FloatBuffer",
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
},
|
||||
{
|
||||
"name": "java.nio.LongBuffer",
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
},
|
||||
{
|
||||
"name": "java.nio.ShortBuffer",
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
},
|
||||
{
|
||||
"name": "com.sun.glass.ui.gtk.GtkApplication",
|
||||
"allDeclaredConstructors": true,
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
},
|
||||
{
|
||||
"name": "com.sun.glass.ui.gtk.GtkWindow",
|
||||
"allDeclaredConstructors": true,
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
},
|
||||
{
|
||||
"name": "com.sun.glass.ui.gtk.GtkView",
|
||||
"allDeclaredConstructors": true,
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
},
|
||||
{
|
||||
"name": "com.sun.glass.ui.Application",
|
||||
"allDeclaredConstructors": true,
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
},
|
||||
{
|
||||
"name": "com.sun.glass.ui.Window",
|
||||
"allDeclaredConstructors": true,
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
},
|
||||
{
|
||||
"name": "com.sun.glass.ui.View",
|
||||
"allDeclaredConstructors": true,
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
},
|
||||
{
|
||||
"name": "com.sun.glass.ui.Screen",
|
||||
"allDeclaredConstructors": true,
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
},
|
||||
{
|
||||
"name": "com.sun.glass.ui.Pixels",
|
||||
"allDeclaredConstructors": true,
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
},
|
||||
{
|
||||
"name": "com.sun.glass.ui.Clipboard",
|
||||
"allDeclaredConstructors": true,
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
},
|
||||
{
|
||||
"name": "com.sun.glass.ui.Cursor",
|
||||
"allDeclaredConstructors": true,
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
},
|
||||
{
|
||||
"name": "com.sun.glass.ui.CommonDialogs",
|
||||
"allDeclaredConstructors": true,
|
||||
"allPublicMethods": true,
|
||||
"allDeclaredMethods": true
|
||||
}
|
||||
]
|
||||
@@ -1,43 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "com.gregor.jprototerm.Main",
|
||||
"allDeclaredConstructors": true,
|
||||
"allPublicConstructors": true
|
||||
},
|
||||
{
|
||||
"name": "com.sun.javafx.tk.quantum.QuantumToolkit",
|
||||
"allDeclaredConstructors": true,
|
||||
"allPublicConstructors": true
|
||||
},
|
||||
{
|
||||
"name": "com.sun.glass.ui.gtk.GtkPlatformFactory",
|
||||
"allDeclaredConstructors": true,
|
||||
"allPublicConstructors": true
|
||||
},
|
||||
{
|
||||
"name": "com.sun.glass.ui.gtk.GtkApplication",
|
||||
"allDeclaredConstructors": true,
|
||||
"allPublicConstructors": true
|
||||
},
|
||||
{
|
||||
"name": "com.sun.prism.es2.ES2Pipeline",
|
||||
"allDeclaredConstructors": true,
|
||||
"allPublicConstructors": true,
|
||||
"allDeclaredMethods": true,
|
||||
"allPublicMethods": true
|
||||
},
|
||||
{
|
||||
"name": "com.sun.prism.es2.X11GLFactory",
|
||||
"allDeclaredConstructors": true,
|
||||
"allPublicConstructors": true,
|
||||
"allDeclaredMethods": true,
|
||||
"allPublicMethods": true
|
||||
},
|
||||
{
|
||||
"name": "com.sun.prism.sw.SWPipeline",
|
||||
"allDeclaredConstructors": true,
|
||||
"allPublicConstructors": true,
|
||||
"allDeclaredMethods": true,
|
||||
"allPublicMethods": true
|
||||
}
|
||||
]
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"resources": [
|
||||
{ "pattern": ".*\\.css$" },
|
||||
{ "pattern": ".*\\.toml$" }
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user