通过JNI从Java中使用ncurses,在按键之前,Windows不会触发getch

ewm0tg9j  于 2024-01-05  发布在  Java
关注(0)|答案(1)|浏览(206)

我的目标是用JVM语言创建基于文本的应用程序,
现在我正尝试在JNI中使用ncurses。
当我直接从C使用ncurses时,终端将触发getch()并返回410(KEY_RESIZE)。
但是我通过JNI使用ncurses,getch()不会做任何事情,直到按下任何其他键。

  • 这是JVM的局限性吗?
  • 这种情况有什么变通办法吗?
  • 还是我不应该首先通过JNI使用ncurses?

以下是完整的可复制代码:

  1. public class Main {
  2. public static native void init();
  3. public static native int getch();
  4. public static native void printw(String string);
  5. public static native void endwin();
  6. public static void main(String[] args) {
  7. System.loadLibrary("jcurses");
  8. init();
  9. for (int key; (key = getch()) != 'q';)
  10. printw("%d\n".formatted(key));
  11. endwin();
  12. }
  13. }
  1. #include <ncurses.h>
  2. #include "Main.h"
  3. JNIEXPORT void JNICALL Java_Main_init(JNIEnv *env, jclass clazz) {
  4. initscr();
  5. keypad(stdscr, TRUE);
  6. noecho();
  7. }
  8. JNIEXPORT jint JNICALL Java_Main_getch(JNIEnv *env, jclass clazz) {
  9. return (long) getch();
  10. }
  11. JNIEXPORT void JNICALL Java_Main_printw(JNIEnv *env, jclass clazz, jstring javaString) {
  12. const char *nativeString = (*env)->GetStringUTFChars(env, javaString, 0);
  13. printw("%s", nativeString);
  14. (*env)->ReleaseStringUTFChars(env, javaString, nativeString);
  15. }
  16. JNIEXPORT void JNICALL Java_Main_endwin(JNIEnv *env, jclass clazz) {
  17. endwin();
  18. }
  1. all:
  2. javac -h . Main.java
  3. gcc -I"$$JAVA_HOME/include" -I"$$JAVA_HOME/include/linux" jcurses.c -shared -o libjcurses.so -lncurses
  4. run:
  5. java -Djava.library.path=. Main

为了比较,这里是做同样事情的纯C代码。
调整窗口大小将立即显示410,但不能用JNI做同样的事情。

  1. #include <ncurses.h>
  2. int main() {
  3. initscr();
  4. keypad(stdscr, TRUE);
  5. noecho();
  6. for (int key; (key = getch()) != 'q';)
  7. printw("%d\n", key);
  8. endwin();
  9. return 0;
  10. }


在以下设备上测试:

  • x86 ubuntu22.04,ssh来自Windows 7 git bash
  • arm64 ubuntu22.04,原生gnome终端
taor4pac

taor4pac1#

我发现问题在于JVM“消耗”了SIGWINCH信号,
因此nCurses不能检测到窗口大小的变化。
因此,一种可能的解决方案是使用sun.misc.Signal类来捕获信号并进行渲染,
但这是一个绝对坏主意,因为它已过时。

  1. // WARNING! this package is deprecated!
  2. import sun.misc.Signal;
  3. Signal.handle(new Signal("WINCH"), signal -> {
  4. // re-render our contents
  5. });

字符串
(This答案不应被接受)

相关问题