通过 JNI 使用 Java 中的 ncurses,调整窗口大小在按下按键之前不会触发 getch

问题描述 投票:0回答:1

我的目标是使用 JVM 语言创建基于文本的应用程序,
现在我正在尝试将 ncurses 与 JNI 一起使用。

当我直接从 C 使用 ncurses 时,调整终端大小将触发

getch()
并返回 410 (KEY_RESIZE)。
但我通过 JNI 使用 ncurses,
getch()
在按下任何其他键之前不会执行任何操作。

  • 是JVM的限制吗?
  • 这种情况有什么解决办法吗?
  • 或者我不应该首先通过 JNI 使用 ncurses 吗?

这是完整的可重现代码:

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;
}

在以下设备上测试:

  • x86 ubuntu22.04,来自 Windows 7 的 ssh git bash
  • arm64 ubuntu22.04,本机 gnome 终端
java java-native-interface ncurses
1个回答
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
});

(此答案不应被接受)

© www.soinside.com 2019 - 2024. All rights reserved.