Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 86 additions & 14 deletions examples/PrintCaps.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

public class PrintCaps {
public static void main(String[] args) {
boolean force = args.length > 0 && "--force".equals(args[0]);

TermCaps detected = TermCaps.detect();
System.out.println("Terminal capabilities (detected from environment):");
printCaps(detected);
Expand All @@ -22,25 +24,95 @@ public static void main(String[] args) {
terminal.enableRawMode();
TermCaps probed = TermProber.probe(terminal, () -> terminal.read(500));
printCaps(probed);

System.out.println();
testStyles(probed, force);
} catch (IOException e) {
System.out.println(" (query failed: " + e.getMessage() + ")");
}
}

private static void printCaps(TermCaps caps) {
System.out.println(" colors : " + caps.colors());
System.out.println(" altScreen : " + caps.altScreen());
System.out.println(" mouse : " + caps.mouse());
System.out.println(" bracketedPaste : " + caps.bracketedPaste());
System.out.println(" focusTracking : " + caps.focusTracking());
System.out.println(" hyperlinks : " + caps.hyperlinks());
System.out.println(" settableTitle : " + caps.settableTitle());
System.out.println(" unicode : " + caps.unicode());
System.out.println(" italic : " + caps.italic());
System.out.println(" strikethrough : " + caps.strikethrough());
System.out.println(" overline : " + caps.overline());
System.out.println(" sixel : " + caps.sixel());
System.out.println(" kittyGraphics : " + caps.kittyGraphics());
System.out.println(" iterm2Images : " + caps.iterm2Images());
System.out.println(" colors : " + caps.colors());
System.out.println(" altScreen : " + caps.altScreen());
System.out.println(" mouse : " + caps.mouse());
System.out.println(" bracketedPaste : " + caps.bracketedPaste());
System.out.println(" focusTracking : " + caps.focusTracking());
System.out.println(" synchronizedOutput : " + caps.synchronizedOutput());
System.out.println(" hyperlinks : " + caps.hyperlinks());
System.out.println(" settableTitle : " + caps.settableTitle());
System.out.println(" unicode : " + caps.unicode());
System.out.println(" italic : " + caps.italic());
System.out.println(" strikethrough : " + caps.strikethrough());
System.out.println(" overline : " + caps.overline());
System.out.println(" undercurl : " + caps.undercurl());
System.out.println(" extendedUnderline : " + caps.extendedUnderline());
System.out.println(" sixel : " + caps.sixel());
System.out.println(" kittyGraphics : " + caps.kittyGraphics());
System.out.println(" iterm2Images : " + caps.iterm2Images());
System.out.println(" kittyKeyboard : " + caps.kittyKeyboard());
}

private static void testStyles(TermCaps caps, boolean force) {
System.out.println(" Testing text styles:");

// Italic
if (force || caps.italic()) {
System.out.println(" \u001b[3mItalic text\u001b[23m" + (force ? "" : " (supported)"));
} else {
System.out.println(" Italic text (not supported)");
}

// Strikethrough
if (force || caps.strikethrough()) {
System.out.println(" \u001b[9mStrikethrough text\u001b[29m" + (force ? "" : " (supported)"));
} else {
System.out.println(" Strikethrough text (not supported)");
}

// Overline
if (force || caps.overline()) {
System.out.println(" \u001b[53mOverline text\u001b[55m" + (force ? "" : " (supported)"));
} else {
System.out.println(" Overline text (not supported)");
}

// Undercurl
if (force || caps.undercurl()) {
System.out.println(" \u001b[4:3mUndercurl text\u001b[4:0m" + (force ? "" : " (supported)"));
} else {
System.out.println(" Undercurl text (not supported)");
}

// Extended underline styles
if (force || caps.extendedUnderline()) {
System.out.println(" \u001b[4:1mSingle underline\u001b[4:0m" + (force ? "" : " (supported)"));
System.out.println(" \u001b[4:2mDouble underline\u001b[4:0m" + (force ? "" : " (supported)"));
System.out.println(" \u001b[4:4mDotted underline\u001b[4:0m" + (force ? "" : " (supported)"));
System.out.println(" \u001b[4:5mDashed underline\u001b[4:0m" + (force ? "" : " (supported)"));
System.out.println(" \u001b[4:1m\u001b[58:2::255:0:0mRed underline (RGB)\u001b[59m\u001b[4:0m" + (force ? "" : " (supported)"));
System.out.println(" \u001b[4:1m\u001b[58:5:46mGreen underline (color 46)\u001b[59m\u001b[4:0m" + (force ? "" : " (supported)"));
} else {
System.out.println(" Extended underline styles (not supported)");
}

// Unicode
if (force || caps.unicode()) {
System.out.println(" Unicode: ✓ ★ ♥ ☺ → ⇒ ≈ ≠ ∞" + (force ? "" : " (supported)"));
} else {
System.out.println(" Unicode (not supported)");
}

// Hyperlinks
if (force || caps.hyperlinks()) {
System.out.println(" \u001b]8;;https://github.com/codejive/java-miniterm\u001b\\Hyperlink example\u001b]8;;\u001b\\" + (force ? "" : " (supported)"));
} else {
System.out.println(" Hyperlink example (not supported)");
}

System.out.println();
if (!force) {
System.out.println(" (Use --force to test all styles regardless of detected capabilities)");
}
}
}
172 changes: 172 additions & 0 deletions examples/PrintKittyKB.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS org.codejive.miniterm:miniterm${miniterm.ffm:}:${miniterm.version:0.1.7}
//DEPS org.codejive.miniterm:ansiparser:${miniterm.version:0.1.7}
//DEPS org.codejive.miniterm:termcap:${miniterm.version:0.1.7}

package examples;

import java.io.IOException;
import org.codejive.miniterm.Terminal;
import org.codejive.miniterm.ansiparser.AnsiReader;
import org.codejive.miniterm.termcap.TermCaps;
import org.codejive.miniterm.termcap.TermProber;

public class PrintKittyKB {

// ANSI helpers
private static final String CSI = "\033[";

public static void main(String[] args) {
try (Terminal terminal = Terminal.create()) {
terminal.enableRawMode();

// Detect terminal capabilities
TermCaps caps = TermProber.probe(terminal, () -> terminal.read(500));

System.out.println("Terminal capabilities:");
System.out.println(" Kitty keyboard support: " + caps.kittyKeyboard());
System.out.println();

if (!caps.kittyKeyboard()) {
System.out.println("Warning: Terminal does not report Kitty keyboard support.");
System.out.println("Attempting to enable anyway (some terminals support it without reporting).");
System.out.println();
}

// Query terminal for Kitty keyboard protocol support
System.out.println("Querying terminal for Kitty keyboard protocol...");
TermCaps.queryKittyKeyboard(terminal);

// Try to read the response
AnsiReader reader = new AnsiReader(() -> terminal.read(500));
String response = reader.read();
if (response != null && TermCaps.isKittyKeyboardResponse(response)) {
System.out.println("Terminal responded: " + escape(response));
System.out.println("Kitty keyboard protocol is supported!");
} else {
System.out.println("No response received (timeout or not supported).");
}
System.out.println();

// Enable Kitty keyboard protocol with flags:
// 1 = Disambiguate escape codes
// 2 = Report event types (press, release, repeat)
// 4 = Report alternate keys
// Combined: 7 = Full enhancement
int flags = 7;
System.out.println("Enabling Kitty keyboard protocol with flags: " + flags);
System.out.println(" 1 = Disambiguate escape codes");
System.out.println(" 2 = Report event types (press, release, repeat)");
System.out.println(" 4 = Report alternate keys");
System.out.println();

TermCaps.pushKittyKeyboard(terminal, flags);

System.out.println();
System.out.println("Press keys to see enhanced keyboard events.");
System.out.println("Try: letters (plain text), Ctrl+Shift+A, Alt+Shift+B, arrow keys.");
System.out.println("(Press Ctrl+C to exit)");
System.out.println();
System.out.println(String.format("%-40s | %-30s | %s", "Raw", "Hex", "Type"));
System.out.println(String.format("%-40s-+-%-30s-+-%s",
"-".repeat(40), "-".repeat(30), "-".repeat(30)));

try {
String token;
while ((token = reader.read()) != null) {
if (token.isEmpty()) continue;

// Check for Ctrl+C (raw byte 3 or Kitty protocol CSI 99;5u)
if (!token.startsWith("\033") && token.length() == 1 && token.charAt(0) == 3) {
break;
}
// Check for Kitty-encoded Ctrl+C: CSI 99;5u (ESC[99;5u)
if ((CSI + "99;5u").equals(token)) {
break;
}

// Print the event details on a single line
String type = analyzeToken(token);
System.out.println(String.format("%-40s | %-30s | %s",
escape(token), toHex(token), type));
}
} finally {
// Restore original keyboard protocol
TermCaps.popKittyKeyboard(terminal);
}
} catch (IOException e) {
e.printStackTrace();
}
}



private static String analyzeToken(String token) {
if (token == null || token.isEmpty()) {
return "Empty";
}

// Check for Kitty keyboard protocol sequences
// Format: CSI <unicode-key-code> : <alternate-key-codes> ; <modifiers> : <event-type> ; <text> u
if (token.startsWith(CSI) && token.endsWith("u")) {
return "Kitty keyboard protocol sequence";
}

// Check for standard CSI sequences
if (token.startsWith(CSI)) {
return "CSI sequence (standard)";
}

// Check for escape sequences
if (token.startsWith("\033")) {
return "Escape sequence";
}

// Check for control characters
if (token.length() == 1) {
char c = token.charAt(0);
if (c < 32) {
return "Control character (Ctrl+" + (char) (c + 64) + ")";
}
if (c == 127) {
return "DEL character";
}
return "Printable character: '" + c + "'";
}

// Multi-byte sequence
return "Multi-byte sequence";
}

private static String escape(String s) {
if (s == null) return "null";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '\033') {
sb.append("ESC");
} else if (c == '\n') {
sb.append("\\n");
} else if (c == '\r') {
sb.append("\\r");
} else if (c == '\t') {
sb.append("\\t");
} else if (c < 32 || c == 127) {
sb.append("\\x").append(String.format("%02x", (int) c));
} else {
sb.append(c);
}
}
return sb.toString();
}

private static String toHex(String s) {
if (s == null) return "null";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
if (i > 0) sb.append(" ");
sb.append(String.format("%02x", (int) s.charAt(i)));
}
return sb.toString();
}
}
98 changes: 98 additions & 0 deletions examples/PrintPaste.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS org.codejive.miniterm:miniterm${miniterm.ffm:}:${miniterm.version:0.1.7}
//DEPS org.codejive.miniterm:ansiparser:${miniterm.version:0.1.7}

package examples;

import java.io.IOException;
import org.codejive.miniterm.Terminal;
import org.codejive.miniterm.ansiparser.AnsiParser;

public class PrintPaste {
private static final String ENABLE_BRACKETED_PASTE = "\u001b[?2004h";
private static final String DISABLE_BRACKETED_PASTE = "\u001b[?2004l";
private static final String PASTE_START = "\u001b[200~";
private static final String PASTE_END = "\u001b[201~";

public static void main(String[] args) {
try (Terminal terminal = Terminal.create()) {
terminal.enableRawMode();
terminal.write(ENABLE_BRACKETED_PASTE);
System.out.println("Bracketed paste mode enabled.");
System.out.println("Try pasting text (Ctrl+V or right-click paste).");
System.out.println("Press Ctrl+C to exit.\n");

boolean inPaste = false;
StringBuilder pasteBuffer = new StringBuilder();

while (true) {
String token = AnsiParser.parse(() -> terminal.read(1000));
if (token == null) {
break; // EOF
} else if (token.isEmpty()) {
continue; // timeout
} else if (token.equals("\u0003")) { // Ctrl+C
break;
} else if (token.equals(PASTE_START)) {
inPaste = true;
pasteBuffer.setLength(0);
System.out.println("[PASTE START]");
} else if (token.equals(PASTE_END)) {
inPaste = false;
System.out.print("\r\n[PASTE END]\r\n");
System.out.print("Pasted content (" + pasteBuffer.length() + " chars):\r\n");
System.out.print("---\r\n");
// Print with proper line endings for raw mode
String content = pasteBuffer.toString();
for (int i = 0; i < content.length(); i++) {
char ch = content.charAt(i);
if (ch == '\n') {
System.out.print("\r\n");
} else if (ch == '\r') {
// Skip standalone CR, will be handled with LF
if (i + 1 < content.length() && content.charAt(i + 1) != '\n') {
System.out.print("\r\n");
}
} else {
System.out.print(ch);
}
}
System.out.print("\r\n---\r\n\r\n");
} else if (inPaste) {
pasteBuffer.append(token);
} else {
// Regular typed input
if (token.length() == 1) {
char ch = token.charAt(0);
if (ch >= 32 && ch < 127) {
System.out.println("Typed: '" + ch + "' (code: " + (int) ch + ")");
} else {
System.out.println("Typed: code " + (int) ch);
}
} else {
System.out.println("Sequence: " + escapeString(token));
}
}
}

terminal.write(DISABLE_BRACKETED_PASTE);
} catch (IOException e) {
e.printStackTrace();
}
}

private static String escapeString(String s) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if (ch == '\u001b') {
sb.append("\\e");
} else if (ch >= 32 && ch < 127) {
sb.append(ch);
} else {
sb.append(String.format("\\x%02x", (int) ch));
}
}
return sb.toString();
}
}
Loading
Loading