Задать вопрос
@reus

Как отловить нажатие пользователем кнопки Esc в консоли win?

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

Реализация должно работать в консоли windows, но я не знаю как это реализовать.
Не подскажете куда можно посмотреть по данному вопросу?
ЗЫ GUI (swing/fx/etc) не катят
  • Вопрос задан
  • 644 просмотра
Подписаться 1 Оценить Комментировать
Решения вопроса 1
sergey-gornostaev
@sergey-gornostaev Куратор тега Java
Седой и строгий
Это невозможно без использования JNI. Если использование нативных вызовов для вас приемлимо, то первым делом нам нужен сам java-код. Поместим его в ExitOnEscape.java:
public class ExitOnEscape implements Runnable {
    private boolean doTheStuff = true; // Флаг активности рабочего потока

    public native boolean readConsole();

    public void run() { // Рабочий поток
        while (doTheStuff) { // Работать пока установлен флаг
            System.out.println("Doing something..."); // Основная логика программы
            try {
                Thread.sleep(500);
            }
            catch (InterruptedException exc) {}
        }
    }

    public void doSomething() {
        new Thread(this).start(); // Запускаем рабочий поток

        while(true) { // В главном потоке читаем консоль нативной функцией в бесконечном цикле
            if(readConsole()) { // Если нажат escape
                doTheStuff = false; // Сбрасываем флаг активности
                break; // И выходим из цикла
            }
        }
    }

    public static void main(String[] args) {
        System.loadLibrary("ExitOnEscape");
        ExitOnEscape exitOnEscape = new ExitOnEscape();
        exitOnEscape.doSomething();
    }
}

Как видно, в коде есть только две строки отличающиеся от того, с чем приходится сталкиваться ежедневно. Это объявление нативного метода
public native boolean readConsole();
и загрузка библиотеки
System.loadLibrary("ExitOnEscape");
Компилируем его:
javac ExitOnEscape.java
Теперь необходимо из полученного класса сгенерировать заголовочный файл для нашей будущей библиотеки. В этом нам поможет утилита javah входящая в состав JDK:
javah ExitOnEscape
В рабочем каталоге должен появиться файл ExitOnEscape.h со следующим содержимым:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class ExitOnEscape */

#ifndef _Included_ExitOnEscape
#define _Included_ExitOnEscape
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     ExitOnEscape
 * Method:    readConsole
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_ExitOnEscape_readConsole
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

В нём интересно только объявление функции, имеющее вид Java_<имя класса>_<имя метода>. Первый параметр функции JNIEnv - это указатель на таблицу функций механизма JNI, служащих для обеспечения взаимодействия между java- и c-кодом. Второй параметр имеет тип jobject и принимает экземпляр класса ExitOnEscape. Возвращает функция jboolean соответствующий типу boolean в java.
JNIEXPORT и JNICALL - это зависимые от компилятора макро-определения для экспортирования функций, особого внимания они не заслуживают.

А теперь самая интересная часть - реализация этой функции. Опишем её в файле ExitOnEscape.c:
#include <stdio.h>
#include <conio.h>
#include "ExitOnEscape.h"

JNIEXPORT jboolean JNICALL Java_ExitOnEscape_readConsole(JNIEnv* env, jobject obj) {
    char c = getch(); // Считываем введённый символ
    if (c == 27) // Если это escape
        return JNI_TRUE; // Возвращаем true
    else
        return JNI_FALSE; // Иначе false
}

Для компиляции в Windows я использовал MinGW-w64:
gcc -Wl,--add-stdcall-alias ^
-I"%JAVA_HOME%\include" ^
-I"%JAVA_HOME%\include\win32" ^
-shared -o ExitOnEscape.dll ExitOnEscape.c

В рабочем каталоге должен появиться файл ExitOnEscape.dll. После этого можно запускать java-программу.
java ExitOnEscape
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы