2010-11-09 4 views
10

Возможно ли иметь кросс-платформенный способ обработки клавиш backspace и стрелок в программе C или OCaml?Распознавание клавиш со стрелками с помощью stdin

На самом деле решение OCaml было бы оценено, но многие стандартные функции unix привязаны непосредственно к соответствующим вызовам API, поэтому при портировании решения C не должно быть проблем.

То, что я собираюсь достичь, - это уловить клавиши со стрелками, чтобы переопределить его поведение внутри оболочки (путем повторной обработки последней строки или операций, подобных этим). Я думаю, что эта вещь падает до фактической программы, и она не обрабатывается самим кодом, поэтому я не знаю, возможно ли это.

Программа составлена ​​либо на Linux, OS X и Windows, (на Cygwin), поэтому я хотел бы сделать это для всех платформ ..

ответ

8

Я недавно сделал что-то очень похожее (хотя мой код только для Linux). Вы должны установить stdin в неканонический режим, чтобы читать нажатия клавиш со стрелками. Это должно работать на OS X и Linux и, вероятно, работать на Cygwin, хотя я не могу сказать точно.

open Unix 
let terminfo = tcgetattr stdin in 
    let newterminfo = {terminfo with c_icanon = false; c_vmin = 0; c_vtime = 0} in 
    at_exit (fun _ -> tcsetattr tsdin TCSAFLUSH terminfo); (* reset stdin when you quit*) 
    tcsetattr stdin TCSAFLUSH newterminfo; 

когда канонический режим выключен, вам не нужно ждать новой строки, чтобы читать из стандартного ввода. c_vmin представляет минимальное количество символов для чтения перед возвратом (возможно, вы захотите прочитать один символ за раз), а c_vtime - максимальное время ожидания чтения (в единицах 0,1 с).

Вы также можете установить c_echo ложь так, чтобы стрелка нажатия клавиш выводятся на терминал (но тогда вам придется вручную печатать все остальное.

Большинство терминалов представляют стрелка нажатия клавиш с использованием ANSI escape sequences. Если запустить cat без аргументов и начинают дубасить клавиши со стрелками вы можете увидеть управляющие последовательности используются. Они, как правило,

up - "\027[A" 
down - "\027[B" 
left - "\027[D" 
right - "\027[C" 

Где «\ 027» это значение ASCII для esc

+0

Мне почти удалось сделать эту работу, но как мне управлять ручным эхом персонажа? Я спрашиваю об этом, потому что stdin затем перенаправляется в парсер, который анализирует строки за строкой и интерпретирует его. Я должен разместить посередине и распечатать каждый символ? Но тогда, когда пользователь нажимает клавишу upkey, он должен заменить уже напечатанный символ на что-то другое. – Jack

+0

Перечитав ваши вопросы, я думаю, что использование readline/ledit - это, вероятно, ваш лучший выбор. Но если вы хотите делать что-то вручную, вам придется вручную управлять stdin вашего синтаксического анализатора. Прочтите из stdin, проверьте, есть ли escape-последовательность, прямо на stdin вашего парсера. Я делаю что-то подобное здесь: https://github.com/aplusbi/reflow/blob/master/reflow.ml в функции 'main', где я читаю stdin для строки и затем записываю ее в файл_descr , Вы, вероятно, в конечном итоге делаете то же самое, но сначала проверяете escape-последовательности. –

+0

эти символы стрелок на самом деле из стандартного ECMA 48 http://man7.org/linux/man-pages/man4/console_codes.4.html вы можете видеть, какие управляющие символы ECMA 48 поддерживает Linux: казалось бы, что переместить курсор влево 5 символов, которые вы можете сделать \ 027 [5D – aeroson

5

Используйте Ncurses для извлечения последовательности для ключевых возможностей стрелок, а затем посмотреть для них, когда вы читаете stdin. Вероятно, проще использовать что-то вроде libedit или readline вместо этого, и пусть это касается обработки ключа.

+0

Еще одна заметка о решении ncurses: на одной платформе это кросс-терминал, заботясь о волосатых терминальных различиях для вас. Кроме того, для Windows доступны версии, поэтому вы можете иметь возможности редактирования в консолях Windows. –

2

Стандартный способ поддержки ввода клавиатуры за пределами строк печатаемых символов осуществляется через библиотеку ncurses, которая имеет Ocaml binding. Еще одной распространенной возможностью является библиотека readline (наиболее известная Баш).

Если все, что вы делаете, читает ввод по строкам, но вы хотите, чтобы ваши пользователи имели достойный редактор строк, нет необходимости включать какую-либо поддержку в вашу программу. Вместо этого попросите своих пользователей использовать программу-обертку, такую ​​как rlwrap (которая основана на readline) или ledit. Эти обертки предоставляют линейную редакцию и историю, две функции, которые вы перечисляете в качестве ваших требований. Я бы порекомендовал вам встраивать обработку ввода в вашу программу только в том случае, если вам нужно что-то более интересное, например, для завершения программы, когда пользователь нажимает Вкладка.

1

Backspace является символом ASCII и будет размещен в stdin, как и любой другой символ. Управляющая последовательность символов '\b' является символом обратного пробела.

Для клавиш курсора они не связаны ни с одним управляющим символом, поэтому не генерируют данные в потоке stdin. Доступ на низком уровне обязательно зависит от платформы, хотя существуют библиотеки с кросс-платформенной платформой, которые абстрагируют разницу в платформе. Я считаю, что ncurses доступен для всех платформ, о которых вы говорили.

+1

Я уверен, что любой терминал в каноническом режиме не помещает символ backspace в stdin. Текст буферизуется, поэтому, если вы набрали «abcd \ b \ n», вы получите «abc \ n» в своей программе. –

+0

@Niki: К сожалению, большую часть времени я провожу кодирование встроенных систем, подключенных к программному обеспечению эмулятора немого терминала. – Clifford

Смежные вопросы