I am trying to debug my call to termios function tcgetattr(). It returns -1 as the result and I searched internet to find out more details about what the call fails. I found that I can make a call to explain_tcgetattr() to get more details about the error. In the code below I added the method signature to my interface so that the proxy can implement it, but I get the exception I have added below.
My code:
package org.example;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Structure;
import java.io.IOException;
import java.util.Arrays;
public class App {
private static LibC.Termios originalAttributes;
private static int rows = 10;
private static int columns = 10;
public static void main(String[] args) throws IOException {
// System.out.println("Hello World");
/*System.out.println("\033[4;44;31mHello World\033[0mHello");
System.out.println("\033[2J");
System.out.println("\033[5H");*/
enableRawMode();
initEditor();
while (true){
refreshScreen();
int key = readKey();
handleKey(key);
}
}
private static void initEditor() {
LibC.Winsize windowSize = getWindowSize();
columns = windowSize.ws_col;
rows = windowSize.ws_row;
}
private static void refreshScreen() {
StringBuilder builder = new StringBuilder();
builder.append("\033[2J");
builder.append("\033[H");
for (int i = 0; i < rows - 1; i++) {
builder.append("~\r\n");
}
String statusMessage = "Marco Code's Editor - v0.0.1";
builder.append("\033[7m")
.append(statusMessage)
.append(" ".repeat(Math.max(0, columns - statusMessage.length())))
.append("\033[0m");
builder.append("\033[H");
System.out.print(builder);
}
private static int readKey() throws IOException {
return System.in.read();
}
private static void handleKey(int key) {
if (key == 'q') {
System.out.print("\033[2J");
System.out.print("\033[H");
LibC.INSTANCE.tcsetattr(LibC.SYSTEM_OUT_FD, LibC.TCSAFLUSH, originalAttributes);
System.exit(0);
}
}
private static void enableRawMode() {
LibC.Termios termios = new LibC.Termios();
int rc = LibC.INSTANCE.tcgetattr(LibC.SYSTEM_OUT_FD, termios);
if (rc != 0) {
System.err.println("There was a problem calling tcgetattr: "+rc);
System.out.println(LibC.INSTANCE.explain_tcgetattr(LibC.SYSTEM_OUT_FD, termios));
System.exit(rc);
}
originalAttributes = LibC.Termios.of(termios);
termios.c_lflag &= ~(LibC.ECHO | LibC.ICANON | LibC.IEXTEN | LibC.ISIG);
termios.c_iflag &= ~(LibC.IXON | LibC.ICRNL);
termios.c_oflag &= ~(LibC.OPOST);
/* termios.c_cc[LibC.VMIN] = 0;
termios.c_cc[LibC.VTIME] = 1;*/
LibC.INSTANCE.tcsetattr(LibC.SYSTEM_OUT_FD, LibC.TCSAFLUSH, termios);
}
private static LibC.Winsize getWindowSize() {
final LibC.Winsize winsize = new LibC.Winsize();
final int rc = LibC.INSTANCE.ioctl(LibC.SYSTEM_OUT_FD, LibC.TIOCGWINSZ, winsize);
if (rc != 0) {
System.err.println("ioctl failed with return code[={}]" + rc);
System.exit(1);
}
return winsize;
}
}
interface LibC extends Library {
int SYSTEM_OUT_FD = 0;
int ISIG = 1, ICANON = 2, ECHO = 10, TCSAFLUSH = 2,
IXON = 2000, ICRNL = 400, IEXTEN = 100000, OPOST = 1, VMIN = 6, VTIME = 5, TIOCGWINSZ = 0x5413;
// we're loading the C standard library for POSIX systems
LibC INSTANCE = Native.load("c", LibC.class);
@Structure.FieldOrder(value = {"ws_row", "ws_col", "ws_xpixel", "ws_ypixel"})
class Winsize extends Structure {
public short ws_row, ws_col, ws_xpixel, ws_ypixel;
}
@Structure.FieldOrder(value = {"c_iflag", "c_oflag", "c_cflag", "c_lflag", "c_cc"})
class Termios extends Structure {
public int c_iflag, c_oflag, c_cflag, c_lflag;
public byte[] c_cc = new byte[19];
public Termios() {
}
public static Termios of(Termios t) {
Termios copy = new Termios();
copy.c_iflag = t.c_iflag;
copy.c_oflag = t.c_oflag;
copy.c_cflag = t.c_cflag;
copy.c_lflag = t.c_lflag;
copy.c_cc = t.c_cc.clone();
return copy;
}
@Override
public String toString() {
return "Termios{" +
"c_iflag=" + c_iflag +
", c_oflag=" + c_oflag +
", c_cflag=" + c_cflag +
", c_lflag=" + c_lflag +
", c_cc=" + Arrays.toString(c_cc) +
'}';
}
}
int tcgetattr(int fd, Termios termios);
int tcsetattr(int fd, int optional_actions,
Termios termios);
int ioctl(int fd, int opt, Winsize winsize);
//THIS GIVES LINKAGE ERROR
int explain_tcgetattr(int fildes, Termios termios);
}
Exception:
There was a problem calling tcgetattr: -1
Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up function 'explain_tcgetattr': /snap/openjdk/1465/jdk/bin/java: undefined symbol: explain_tcgetattr
at com.sun.jna.Function.<init>(Function.java:252)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:620)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:596)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:582)
at com.sun.jna.Library$Handler.invoke(Library.java:248)
at org.example.$Proxy0.explain_tcgetattr(Unknown Source)
at org.example.App.enableRawMode(App.java:81)
at org.example.App.main(App.java:22)
Below is the description from man page:
Also I am not sure if String is the Java equivalent of const char * in C. Please advise.

The
tcgetattr()man page references the POSIX spec which defines the possible failures, both of which are associated with the file descriptor:So your file descriptor may be invalid, or may not be a terminal (it could be a pipe, for example). As you are executing in Java, it's likely that the JVM has taken over the file descriptor and it is not acting as you might expect it to.
You've defined your output file descriptor as 0, which is actually stdin; stdout is typically 1:
It's possible neither of these will actually work. You're executing in Java which really obfuscates the native
intvalue of the standard input/output file descriptors. This presentation claims the following code will fetch it for you:As far as your debugging errors, the comments have indicated you need another library for the explain function (which is a fancy wrapper around fetching
errnowhich you can do with just the C library to get one of the two file-descriptor-related errors).Also check your structure mapping. You've included the "at least the following members" list from the man page, but your header file may contain more elements or a different array size. See this termios.h file for example which has one byte less than yours and
c_ccarray offset by a byte.