Я нашел Cywin-специфическое решение для этого ... и также может быть (?) Единственным способом перехватить, изолировать и идентифицировать ввод символов с клавиатуры.
Получение правильного ввода Unicode с использованием JLine и Cygwin
Как указано здесь в моем собственном ответе на вопрос, который я задал год назад, Cygwin (в любом случае в моей настройке) требуется какая-то дополнительная буферизация и кодирование, как для ввода, так и для вывода на консоль, если это необходимо для обработки Юникод правильно.
Чтобы применить это И для одновременного применения JLine, я делаю это после перехода terminal.enterRawMode()
:
BufferedReader br = new BufferedReader( new InputStreamReader( terminal.input(), 'UTF-8' ))
NB terminal.input()
возвращает экземпляр org.jline.utils.NonBlockingInputStream
.
ввод "ẃ" (AltGr + W на клавиатуре UK Extd) затем используется в одной команде br.read()
, и полученное значение int
равно 7811, правильное значение кодовой точки. Ура: символ Unicode отсутствует в BMP (базовая многоязычная плоскость) был правильно использован.
Обработка байтов управляющих символов клавиатуры:
Но я также хочу перехватывать, изолировать и правильно идентифицировать байты, соответствующие различным управляющим символам. TAB является однобайтовым (9), BACKSPACE является однобайтовым (127), с ним легко иметь дело, но стрелка вверх поставляется в виде 3 отдельно читаемых байтов , то есть трех отдельных * Команды 1029 * разблокированы, даже с использованием вышеуказанного BufferedReader
. Некоторые контрольные последовательности содержат 7 таких байтов, например, Ctrl-Shift-F5 - 27 (escape), за которыми следуют 6 других отдельно прочитанных байтов, int
значения: 91, 49, 53, 59, 54, 126. Я еще не нашел, где такие последовательности могут быть задокументированы: если кто-нибудь знает, пожалуйста, добавьте комментарий.
Затем необходимо изолировать эти «сгруппированные байты»: то есть у вас есть поток байтов: откуда вы знаете, что эти 3 (или 7 ...) должны быть интерпретированы совместно ?
Это возможно, если использовать тот факт, что, когда несколько байтов доставляются для одного такого управляющего символа, они доставляются с интервалом менее одной миллисекунды между каждым. Не удивительно, что, возможно. Этот скрипт Groovy, кажется, работает для моих целей:
import org.apache.commons.lang3.StringUtils
@Grab(group='org.jline', module='jline', version='3.7.0')
@Grab(group='org.apache.commons', module='commons-lang3', version='3.7')
def terminal = org.jline.terminal.TerminalBuilder.builder().jna( true ).system( true ).build()
terminal.enterRawMode()
// BufferedReader needed for correct Unicode input using Cygwin
BufferedReader br = new BufferedReader( new InputStreamReader(terminal.input(), 'UTF-8' ))
// PrintStream needed for correct Unicode output using Cygwin
outPS = new PrintStream(System.out, true, 'UTF-8' )
userResponse = ''
int readInt
boolean continueLoop = true
while( continueLoop ) {
readInt = br.read()
while( readInt == 27 ) {
println "escape"
long startNano = System.nanoTime()
long nanoDiff = 0
// figure of 500000 nanoseconds arrived at by experimentation: see below
while( nanoDiff < 500000 ) {
readInt = br.read()
long timeNow = System.nanoTime()
nanoDiff = timeNow - startNano
println "z readInt $readInt char ${(char)readInt} nanoDiff $nanoDiff"
startNano = timeNow
}
}
switch( readInt ) {
case [10, 13]:
println ''
continueLoop = false
break
case 9:
println '...TAB'
continueLoop = false
break
case 127:
// backspace
if( ! userResponse.empty ) {
print '\b \b'
// chop off last character
userResponse = StringUtils.chop( userResponse )
}
break
default:
char unicodeChar = (char)readInt
outPS.print( unicodeChar )
userResponse += unicodeChar
}
}
outPS.print( "userResponse |$userResponse|")
br.close()
terminal.close()
Приведенный выше код позволяет мне успешно «изолировать» отдельные многобайтовые символы управления клавиатурой:
3 точки в строке println "...TAB"
печатаются в одной и той же строке сразу после нажатия пользователем клавиши TAB (которая с указанным выше кодом не печатается в строке ввода). Это открывает двери для таких вещей, как «автозаполнение» строк, как в некоторых командах BASH ...
Достаточно ли быстр этот параметр 500000 наносекунд (0,5 мс)? Может быть!
Самые быстрые машинистки могут печатать со скоростью 220 слов в минуту. Предполагая, что среднее количество символов на слово составляет 8 (что кажется высоким), получается 29 символов в секунду или примерно 34 мс на символ. В теории все должно быть в порядке. Но «мошенническое» нажатие двух клавиш одновременно может означать, что они нажаты менее чем за 0,5 мс друг от друга ... однако для приведенного выше кода это имеет значение, только если обе из них являются escape-последовательностями . Кажется, работает нормально. Согласно моим экспериментам, оно не может быть намного меньше 500000 нс, потому что это может занять до 70000 - 80000 нс между каждым байтом в многобайтовой последовательности (хотя обычно это занимает меньше времени) ... и все виды прерываний или смешно происходящее может, конечно, помешать доставке этих байтов. На самом деле установка 1000000 (1 мс), кажется, работает нормально.
Обратите внимание, что теперь у нас, похоже, есть проблема с приведенным выше кодом, если мы хотим перехватить и обработать escape-последовательности: вышеуказанный код блокирует br.read()
внутри цикла nanoDiff
while
в конце escape-последовательности , Это нормально, потому что мы можем отслеживать последовательность байтов, которую мы получаем, когда происходит цикл while
(до его блокировки).