我的目标是使用 JVM 语言创建基于文本的应用程序,
现在我正在尝试将 ncurses 与 JNI 一起使用。
当我直接从 C 使用 ncurses 时,调整终端大小将触发
getch()
并返回 410 (KEY_RESIZE)。getch()
在按下任何其他键之前不会执行任何操作。
这是完整的可重现代码:
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
为了比较,这里是做同样事情的纯 C 代码。
调整窗口大小将立即显示 410,但无法使用 JNI 执行相同操作。
#include <ncurses.h>
int main() {
initscr();
keypad(stdscr, TRUE);
noecho();
for (int key; (key = getch()) != 'q';)
printw("%d\n", key);
endwin();
return 0;
}
在以下设备上测试:
我发现问题是 JVM“消耗”了 SIGWINCH 信号,
使得ncurses无法检测到窗口的变化。
因此,一个可能的解决方案是使用
sun.misc.Signal
来捕获信号并进行渲染,// WARNING! this package is deprecated!
import sun.misc.Signal;
Signal.handle(new Signal("WINCH"), signal -> {
// re-render our contents
});
(此答案不应被接受)