Pfeiltasten mit stdin erkennen

Lesezeit: 5 Minuten

Jacks Benutzeravatar
Jack

ist es möglich, eine plattformübergreifende Möglichkeit zu haben, Rücktaste und Pfeiltasten in einem C- oder OCaml-Programm zu handhaben?

Eigentlich wäre eine OCaml-Lösung wünschenswert, aber viele Standard-Unix-Funktionen werden direkt in entsprechende API-Aufrufe eingebunden, sodass es kein Problem geben sollte, eine C-Lösung zu portieren.

Was ich erreichen werde, ist, die Pfeiltasten zu fangen, um ihr Verhalten innerhalb der Shell zu überschreiben (indem ich die letzte Zeile oder Operationen wie diese umschreibe). Ich denke, dass dieses Ding vor dem eigentlichen Programm liegt und nicht vom Code selbst gehandhabt wird, also weiß ich nicht, ob es möglich ist.

Das Programm ist entweder auf Linux, OS X und Windows (auf Cygwin) kompiliert, also würde ich es gerne für alle Plattformen machen.

  • Ab 2023 ist hier die Lösung

    – Scott

    20. Februar um 0:09 Uhr

Benutzeravatar von Niki Yoshiuchi
Niki Yoshiuchi

Ich habe kürzlich etwas ziemlich Ähnliches gemacht (obwohl mein Code nur Linux ist). Sie müssen stdin auf den nicht kanonischen Modus setzen, um Pfeiltastendrücke lesen zu können. Dies sollte unter OS X und Linux funktionieren und wird wahrscheinlich auch unter Cygwin funktionieren, obwohl ich es nicht mit Sicherheit sagen kann.

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 stdin TCSAFLUSH terminfo); (* reset stdin when you quit*)
tcsetattr stdin TCSAFLUSH newterminfo;

Wenn der kanonische Modus deaktiviert ist, müssen Sie nicht auf einen Zeilenumbruch warten, um von stdin zu lesen. c_vmin stellt die Mindestanzahl von Zeichen dar, die vor der Rückkehr gelesen werden müssen (Sie möchten wahrscheinlich in der Lage sein, jeweils ein einzelnes Zeichen zu lesen) und c_vtime ist die maximale Lesewartezeit (in Einheiten von 0,1 s).

Vielleicht möchten Sie auch festlegen c_echo auf false setzen, damit die gedrückten Pfeiltasten an das Terminal ausgegeben werden (aber dann müssen Sie alles andere manuell drucken.

Die meisten Terminals stellen das Drücken der Pfeiltasten mit dar ANSI-Escape-Sequenzen. Wenn du läufst cat ohne Argumente und starten Sie die Pfeiltasten zu drücken, können Sie die verwendeten Escape-Sequenzen sehen. Sie sind typisch

nach oben - "\033[A"
down - "\033[B"
left - "\033[D"
right - "\033[C"

Where ‘\033’ is the ascii value for esc

  • I almost managed to make this work, but how should I manage the manual echoing of the character? I ask this because the stdin is then forwarded to a parser which parses line by line and interprets it. I should place in the middle and print out every char? But then when the user presses the upkey it should replace the already printed out character to something different..

    – Jack

    Nov 12, 2010 at 14:09

  • Having reread your questions I think using readline/ledit is probably your best bet. But if you want to do things manually, then you’re going to have to manually control the stdin of your parser. Read from stdin, check for an up-char escape sequence, right to your parser’s stdin. I’m doing something similar here: github.com/aplusbi/reflow/blob/master/reflow.ml in the main function, where I’m reading stdin to a string and then writing it to a file_descr. You’d probably end up doing the same, but checking for escape sequences first.

    – Niki Yoshiuchi

    Nov 12, 2010 at 15:17

  • those arrow characters are actually from ECMA 48 standartd here man7.org/linux/man-pages/man4/console_codes.4.html you can see which control characters of ECMA 48 does Linux support: it would appear that to move cursor left 5 characters you can do \027[5D

    – aeroson

    Nov 17, 2016 at 9:39


  • @aeroson It appears not. The ESC key corresponds to ASCII code 27, which is 33 octal. Prefacing a number with 0 is often used to specify octal, so Niki’s answer is correct when specifying \033. \027 would be Octal 23 = 23 decimal = ASCII ETB (End of Transmission Block), which likely has no special meaning for ECMA 48.

    – TOOGAM

    Apr 11, 2019 at 9:45

  • More detailed anser: here

    – Scott

    Feb 20 at 0:10

Use ncurses to extract the sequences for the arrow key capabilities and then look for them when you read stdin. It’s probably easier to use something like libedit or readline instead, and let that deal with the keyhandling.

  • A couple further notes on the ncurses solution: on a single platform, it is cross-terminal, taking care of the hairy terminal differences for you. Also, there are versions available for Windows so you can have the editing features in Windows consoles as well.

    – Michael Ekstrand

    Nov 10, 2010 at 13:58

The standard way of supporting keyboard input beyond lines of printable characters is through the ncurses library, which has an Ocaml binding. Another common possibility is the readline library (most famously used by Bash).

If all you’re doing is reading input line by line, but want your users to have a decent line editor, there’s no need to include any support in your program. Instead, just tell your users to use a wrapper program such as rlwrap (which is based on readline) or ledit. These wrappers do provide line edition and history, the two features you list as your requirements. I would recommend that you build input processing into your program only if you want something fancier, such as program-specific completion when the user presses Tab.

Backspace is an ASCII character and will be placed in stdin like any other character. The character escape sequence '\b' is the backspace character.

For cursor keys, these are not associated with any control character, so do not generate data in the stdin stream. Low level access is necessarily platform specific, although there are cross-platform libraries that abstract away the platform differences. I believe ncurses is available for all the platforms you mentioned.

1444150cookie-checkPfeiltasten mit stdin erkennen

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy