Неблокирующий ввод в Java, реализованный через JNI

Я стремлюсь разработать командную оболочку с использованием Java. Одной важной функцией, которую я должен был немедленно реализовать, было «автоматическое завершение», чему способствует командная оболочка при нажатии клавиши «tab». Я полагал, что для этого мне нужно прочитать команду ввода посимвольно в неблокирующем режиме.

К сожалению, насколько я понял, ни один из Java API не поддерживает неблокирующий ввод. То есть Java API, которые предназначены для чтения ввода, ждут (блокируют) до тех пор, пока пользователь не нажмет клавишу «Ввод», что крайне нежелательно в моем случае. Кроме того, я решил не использовать сторонние библиотеки Java. (например, JLine).

Итак, мне пришлось прибегнуть к JNI. Родной файл .C выглядит следующим образом:

 JNIEXPORT jint JNICALL Java_autoComplete_IOUtils_read_1character(JNIEnv *env, jobject obj)
{

int ch = getch(); // getch() - non-blocking input and doesn't echo the characters
return (jint)ch;
}

И соответствующий метод Java, в котором вызывается вышеупомянутый нативный метод:

 public static String GetLine()
{

    int i = 0;
    do
    {
        char variable = (char) read_character(); // Native method is invoked here 

        System.out.println(variable); // Just printing it for my reference 

        cmdLine = new StringBuffer(cmdLine).insert(i, variable).toString(); // cmdLine is of type String
        i++;

    } while ((search(cmdLine.charAt(i - 1), interruptableCharacterArray)) == false);

    return (new String(cmdLine));

}

 //Checks if the entered character is any one of those keys in the interruptableCharacterArray . As of now , interruptableCharacterArray contains only '\t' (The tab key )


  private static boolean search(char charAt,char[] interruptableCharacterArray2)
{
    for (int i = 0; i < index; i++)
    {
        if (interruptableCharacterArray2[i] == charAt)
            return true;
    }
    return false;
}

Также я могу заверить вас, что нет никаких проблем с подключением родной .dll. И «InterruptableCharacterArray» на данный момент содержит только «\ t» (клавиша «tab»). Следовательно, значение индекса на данный момент равно 1.

Проблема:

1> Кажется, что элемент управления не блокируется:

char variable = (char) read_character();

То есть, похоже, что пользователь не может вводить данные через консоль Java в той строке, которая предназначена для этого. И такая же проблема даже в случае getche() (не блокирует, но эхо)

Вместо этого на каждой итерации для «переменной» берется какое-то значение мусора по умолчанию, которое выводится на консоль при выполнении следующего оператора:

System.out.println(variable);

Однако этот мой код работает как шарм, когда getch() заменяется его блокирующими аналогами, такими как getc(stdin),getchar() и т.д.

Я не могу понять, в чем именно заключается проблема с вызовом getch() через JNI.

2> Кроме того, я был бы признателен, если бы были предложены какие-либо другие альтернативные решения для достижения того же.

Заранее спасибо !

Редактировать: Командная оболочка должна быть реализована как в ОС Windows, так и в ОС Unix.


person bsv0099    schedule 27.02.2013    source источник
comment
То, что вам нужно, это не неблокирующий ввод-вывод, а необработанный ввод-вывод терминала. Я предлагаю вам взглянуть на такие вещи, как stty   -  person Ingo    schedule 27.02.2013
comment
@Ingo: Большое спасибо! Что насчет ОС Windows?   -  person bsv0099    schedule 27.02.2013
comment
Пожалуйста, обратите внимание на ссылки, размещенные Аароном. JLine2, очевидно, также обрабатывает терминалы Windows, так что вы можете получить идеи там. Кстати, чтобы стать хорошим гражданином переполнения стека, вы должны проголосовать за полезные ответы и принять лучшие. Я думаю, что пост Аарона заслуживает этого.   -  person Ingo    schedule 27.02.2013


Ответы (1)


Прежде чем вы сможете иметь неблокирующий ввод-вывод с терминала, вам нужно перевести его в «сырой» режим: терминалы обычно собирают одну строку ввода (таким образом, вы можете редактировать текущую строку с помощью клавиш курсора и т. д.) и просто отправить его на стандартный ввод приложения, когда оно «закончено» (= когда пользователь нажимает ввод).

В «сыром» режиме любое нажатие клавиши немедленно отправляется в приложение, и редактирование невозможно (если только приложение не реализует это само).

Чтобы включить это, см. руководство для stty(1). Чтобы изменить режим, вам понадобится функция C tcsetattr(3).

Я предлагаю заглянуть в исходный код для JLine2, даже если вы не хотите его использовать, особенно в UnixTerminal.java и TerminalLineSettings

person Aaron Digulla    schedule 27.02.2013
comment
Очень хорошо, но в одном вы ошиблись: в режиме варки вы не можете редактировать командную строку с помощью клавиш управления курсором, только с помощью клавиши DEL или Backspace. Это необработанный режим без эха, который позволяет программам осуществлять редактирование с помощью клавиш курсора. - person Ingo; 27.02.2013
comment
@Ingo: Да, ты прав для Unix. Оболочка реализует редактирование, переводя терминал в режим raw/noecho в Unix. На Amiga у терминала были собственные возможности редактирования. Для переноса туда unix-шеллов мы должны заменить этот код на stdin.readline() :-) - person Aaron Digulla; 27.02.2013
comment
@AaronDigulla - Большое спасибо! Я бы работал в этом направлении. Кроме того, не могли бы вы рассказать мне, как это сделать в ОС Windows? - person bsv0099; 27.02.2013