My goal is create text-based applications with JVM languages,
and now I am trying to use ncurses with JNI.
When I use ncurses directly from C, resizing the terminal will trigger getch() and return 410 (KEY_RESIZE).
But I use ncurses via JNI, getch() won't do anything until pressing any other keys.
- Is it a limitation of JVM?
- Any workaround for this situation?
- or shouldn't I use ncurses via JNI in the first place?
Here is the full reproducible code:
public class Main {
public static native void init();
public static native int getch();
public static native void printw(String string);
public static native void endwin();
public static void main(String[] args) {
System.loadLibrary("jcurses");
init();
for (int key; (key = getch()) != 'q';)
printw("%d\n".formatted(key));
endwin();
}
}
#include <ncurses.h>
#include "Main.h"
JNIEXPORT void JNICALL Java_Main_init(JNIEnv *env, jclass clazz) {
initscr();
keypad(stdscr, TRUE);
noecho();
}
JNIEXPORT jint JNICALL Java_Main_getch(JNIEnv *env, jclass clazz) {
return (long) getch();
}
JNIEXPORT void JNICALL Java_Main_printw(JNIEnv *env, jclass clazz, jstring javaString) {
const char *nativeString = (*env)->GetStringUTFChars(env, javaString, 0);
printw("%s", nativeString);
(*env)->ReleaseStringUTFChars(env, javaString, nativeString);
}
JNIEXPORT void JNICALL Java_Main_endwin(JNIEnv *env, jclass clazz) {
endwin();
}
all:
javac -h . Main.java
gcc -I"$$JAVA_HOME/include" -I"$$JAVA_HOME/include/linux" jcurses.c -shared -o libjcurses.so -lncurses
run:
java -Djava.library.path=. Main
For comparison, here is the pure C code to do the same thing.
Resizing the window will shows up 410 immediately but cannot do the same with JNI.
#include <ncurses.h>
int main() {
initscr();
keypad(stdscr, TRUE);
noecho();
for (int key; (key = getch()) != 'q';)
printw("%d\n", key);
endwin();
return 0;
}
Tested on devices below:
- x86 ubuntu22.04, ssh from Windows 7 git bash
- arm64 ubuntu22.04, native gnome terminal
I found that the issue is that the JVM "consumes" the SIGWINCH signal,
so that ncurses cannot detect the changes of the size of the window.
Therefore a possible solution is using the
sun.misc.Signalclass to catch the signal and do the rendering stuff,but this is an absolutely bad idea due to its deprecation.
(This answer should not be accepted)