Ein “echtes” interaktives Terminalprogramm wie vim, htop, … in C/C++ ohne ncurses schreiben

Lesezeit: 8 Minuten

Benutzeravatar von leemes
leemes

Nein, ich möchte nicht verwenden ncurses, weil ich lernen möchte, wie das Terminal funktioniert und Spaß daran habe, es selbst zu programmieren. 🙂 Es muss nicht portabel sein, es muss nur auf Linux Xterm-basierten Terminal-Emulatoren funktionieren.

Was ich tun möchte, ist eine interaktive Terminalanwendung wie htop und vim zu programmieren. Damit meine ich nicht die Ausgabe von Zeichen, die wie Kästchen aussehen oder Farben setzen, das ist trivial; auch um den Inhalt an die Fenstergröße anzupassen. Was ich brauche ist

  1. wie kommt man Mausinteraktionen wie das Klicken auf ein Zeichen und das Scrollen des Mausrads (wenn sich die Maus an einem bestimmten Zeichen befindet), um das Scrollen zu implementieren [EDIT: in a terminal emulator of course]und

  2. wie vollständig speichern und wiederherstellen die Ausgabe des übergeordneten Prozesses und trenne meinen Druck von seiner Ausgabe, so dass nach dem Verlassen meiner Anwendung nichts als der Befehl, den ich in die Shell eingegeben habe, dort sein sollte, wie wenn htop ausgeführt und wieder beendet wird: Von dieser Anwendung ist nichts mehr sichtbar.

Ich möchte wirklich keine Ncurses verwenden. Aber wenn Sie wissen, welcher Teil von ncurses für diese Aufgaben zuständig ist, können Sie mir natürlich gerne sagen, wo ich ihn im Quellcode finden kann, damit ich ihn studieren kann.

  • “Sie können mir gerne sagen, wo im Quellcode ich es finden kann”

    – leemes

    12. Dezember 2011 um 15:14 Uhr

  • Ich habe das gesagt, weil ich sicher bin, dass ich den Ncurses-Quellcode nicht so gut kenne wie jemand, der ihn den ganzen Tag benutzt. 🙂

    – leemes

    12. Dezember 2011 um 15:15 Uhr

  • Die meisten Leute verwenden seine API, ändern aber nicht seine Implementierung, daher ist die Wahrscheinlichkeit gering, dass Sie solche Leute treffen. Lesen Sie einfach die Quellen, zum Beispiel habe ich die Datei “lib_mvcur.c” in einer Minute gefunden (einschließlich des Herunterladens der Quellen), die “Die Routinen zum Bewegen des physischen Cursors und zum Scrollen” enthält. Überprüfen Sie die Dateikommentare, die Dokumentation sieht gut aus

    – Andriy Tylychko

    12. Dezember 2011 um 15:22 Uhr

  • Die meisten Terminals emulieren mindestens a vt220, sodass Sie mit der Implementierung des Steuerelements dafür beginnen könnten. (Obwohl nur wenige Programme verrückt genug sind, um das zu tun, einschließlich vim, und sie eher ncurses oder zumindest termcap verwenden)

    – Nr

    12. Dezember 2011 um 15:26 Uhr

  • Erstens müssen Sie wissen, wie Sie das Terminal in den Raw-Modus versetzen, zweitens benötigen Sie mindestens termcap (libtermcap) oder rollen Sie Ihre eigene Eingabe- und Abstraktionsschicht. Ohne die Maus würde ich schätzen, dass dies ein paar Monate Arbeit für jemanden mit C- und Unix-Erfahrung erfordern würde. Das tgetc mit Timeout auf ESC ist für Parser unangenehm. Sicherlich nichts für schwache Nerven…

    – Wildpässer

    12. Dezember 2011 um 15:30 Uhr


rodrigos Benutzeravatar
rodrigo

Um das Terminal zu manipulieren, müssen Sie verwenden Kontrollsequenzen. Leider hängen diese Codes von dem jeweiligen Terminal ab, das Sie verwenden. Deshalb terminfo (bisher termcap) überhaupt existiert.

Sie sagen nicht, ob Sie terminfo verwenden möchten oder nicht. So:

  • Wenn Sie terminfo verwenden, erhalten Sie die richtige Steuersequenz für jede Aktion, die Ihr Terminal unterstützt.
  • Wenn Sie Termininfo nicht verwenden … nun, müssen Sie jede Aktion in jedem Terminaltyp, den Sie unterstützen möchten, manuell codieren.

Da Sie dies zu Lernzwecken wünschen, werde ich im zweiten näher darauf eingehen.

Sie können den von Ihnen verwendeten Terminaltyp anhand der Umgebungsvariablen erkennen $TERM. Unter Linux sind die üblichsten xterm für Terminalemulatoren (XTerm, gnome-terminal, konsole) und linux für virtuelle Terminals (solche, wenn X nicht läuft).

Sie können die Steuersequenzen einfach mit Befehl entdecken tput. Aber tput Wenn Sie sie auf der Konsole ausdrucken, werden sie sofort angewendet. Wenn Sie sie also wirklich sehen möchten, verwenden Sie:

$ TERM=xterm tput löschen |  hd 00000000 1b 5b 48 1b 5b 32 4a |.[H.[2J|

$ TERM=linux tput clear | hd
00000000  1b 5b 48 1b 5b 4a                                 |.[H.[J|

That is, to clear the screen in a xterm you have to output ESC [ H ESC [ 2J in an xterm but ESC [ H ESC [ J in a linux terminal.

About the particular commands you ask about, you should read carefully man 5 terminfo. There is a lot of information there.

  • There are many ways to skin a cat. And there are many ways to clear the screen. Particularly, IIRC, ESC[2J clears the whole screen while ESC[J clears from the cursor to the end-of-screen. But since ESC[H moves the cursor to the HOME, those should be equivalent. Not surprisingly xterm and linux terminals tend to be quite similar.

    – rodrigo

    Dec 12, 2011 at 16:27

  • @leemes: That is because the terminal you are using seems to support both.

    – Martin York

    Dec 12, 2011 at 16:28

  • Only really obscure stuff actually varies heavily between real-world terminals, which all roughly conform to the ANSI/ECMA standard. If you just treat both ^H and ^? as backspace key and otherwise follow the standard, curses/termcap/terminfo is pretty much obsolete…

    – R.. GitHub STOP HELPING ICE

    Dec 12, 2011 at 18:56

  • @R.. I would say the opposite, that only the most used stuff is common . For example, pressing F2 (tput kf2) will generate ESC OQ in xterm but ESC[[B in linux. However pressing right-arrow (tput cuf1) will do ESC [C on both.

    – rodrigo

    Dec 12, 2011 at 19:28

santileortiz's user avatar
santileortiz

Although this is question a bit old, I thought I should share a short example of how to do this without using ncurses, it’s not difficult but I’m sure it won’t be as portable.

This code sets stdin in raw mode, switches to an alternate buffer screen (which saves the state of the terminal before launching it), enables mouse tracking and prints the button and the coordinates when the user clicks somewhere. After quitting with Ctrl+C the program reverts the terminal configuration.

#include <stdio.h>
#include <unistd.h>
#include <termios.h>

int main (void)
{
    unsigned char buff [6];  unsigned int x, y, btn;  struct termios original, roh;  // Ursprüngliche serielle Kommunikationskonfiguration für stdin speichern tcgetattr (STDIN_FILENO, &original);  // stdin in den Raw-Modus versetzen, damit die Tasten direkt durchkommen, // ohne dass die Eingabetaste gedrückt werden muss.  cfmakeraw (&raw);  tcsetattr (STDIN_FILENO, TCSANOW, &raw);  // Wechseln Sie zum Schreiben des alternativen Pufferbildschirms (STDOUT_FILENO, "\e[?47h", 6);

    // Enable mouse tracking
    write (STDOUT_FILENO, "\e[?9h", 5);
    while (1) {
        read (STDIN_FILENO, &buff, 1);
        if (buff[0] == 3) { // User pressd Ctr+C break;  } Sonst wenn (buff[0] == '\x1B') { // Wir gehen davon aus, dass alle empfangenen Escape-Sequenzen // gelesene Mauskoordinaten sind (STDIN_FILENO, &buff, 5);  btn = buff[2] - 32;  x = buff[3] - 32;  y = buff[4] - 32;  printf ("button:%u\n\rx:%u\n\ry:%u\n\n\r", btn, x, y);  } } // Setzen Sie das Terminal in seinen ursprünglichen Zustand zurück schreiben (STDOUT_FILENO, "\e[?9l", 5);
    write (STDOUT_FILENO, "\e[?47l", 6);
    tcsetattr (STDIN_FILENO, TCSANOW, &original);
    return 0;
}

Note: This will not work properly for terminals that have more than 255 columns.

The best references for escape sequences I’ve found are this and this one.

  • For anyone wondering why buff[0] == 3 bedeutet, dass Strg+C gedrückt wurde, weil in ASCII 0x03 das ETX-Symbol ist, das für “Textende” steht.

    – santileortiz

    13. Dezember 2016 um 6:53 Uhr

Benutzeravatar von James Kanze
James Kanze

Ich bin ein bisschen verwirrt. Sie sprechen von einer „Terminalanwendung“ wie vim; Terminalanwendungen erhalten keine Mausereignisse und reagieren nicht auf die Maus.

Wenn Sie über echte Terminalanwendungen sprechen, die in einem ausgeführt werden
xterm, ist es wichtig zu beachten, dass viele der Portabilitätsprobleme das Terminal und nicht das Betriebssystem betreffen. Das Terminal wird durch Senden verschiedener Escape-Sequenzen gesteuert. Welche was tun, hängt vom Endgerät ab; Die ANSI-Escape-Codes sind jedoch inzwischen ziemlich weit verbreitet, siehe http://en.wikipedia.org/wiki/ANSI_escape_code. Diese werden im Allgemeinen unter verstanden xtermzum Beispiel.

Möglicherweise müssen Sie am Anfang und am Ende eine zusätzliche Sequenz ausgeben, um in den „Vollbild“-Modus zu gelangen und ihn zu verlassen; dies ist notwendig für xterm.

Schließlich müssen Sie auf der Eingabe-/Ausgabeebene etwas Besonderes tun, um sicherzustellen, dass Ihr Ausgabetreiber keine Zeichen hinzufügt (z. B. ein einfaches LF in ein CRLF umwandelt) und sicherzustellen, dass die Eingabe nicht widerhallt und transparent ist , und kehrt sofort zurück. Unter Linux geschieht dies mit ioctl. (Vergessen Sie auch hier nicht, es wiederherzustellen, wenn Sie fertig sind.)

  • Es gibt GPM für Mausereignisse in Terminalanwendungen, oder?

    – Anton

    12. Dezember 2011 um 15:28 Uhr

  • Der Anfang dieser Antwort ist völlig falsch. Es gibt ein Protokoll, um dem Terminal mitzuteilen, dass Sie Mausklickereignisse wünschen, und ein Protokoll, damit sie vom Terminal an die Anwendung gesendet werden, alles als Escape-Sequenzen. Die Linux-Konsole unterstützt dies nicht und verwendet stattdessen den abscheulichen GPM-Ansatz, aber xterm und andere unterstützen es richtig.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    12. Dezember 2011 um 15:31 Uhr

  • Mit Mausereignissen meine ich innerhalb von Terminalemulatoren. Ich deaktiviere bereits das Echo der Benutzereingabe und des Eingabepuffers, daher verwende ich stdin als eine Art “Schlüsselereignis” -Stream und nicht als Textzeilen. Wo finde ich, welche zusätzlichen Sequenzen das sind, die ich für den “Vollbildmodus” an den Anfang / das Ende setzen muss? Gibt es keinen ANSI-Escape-Code?

    – leemes

    12. Dezember 2011 um 15:36 Uhr

  • @R..: Wo finde ich dieses Protokoll? Das ist genau das, wonach ich suche. Natürlich brauche ich im echten Linux-Terminal keine Maussteuerung, da es so etwas wie in vim und htop nicht gibt (deshalb vergleiche ich mit vim und htop)

    – leemes

    12. Dezember 2011 um 15:40 Uhr

  • @leemes Es ist eine ANSI-Flucht, die Sie senden müssen. Ich bin mir nicht sicher, was, aber es sollte in der sein termcaps Datei. (Der springende Punkt von ncurses/termcaps besteht darin, dass die Steuersequenzen aus einer Konfigurationsdatei gelesen werden und je nach Terminaltyp variieren.)

    – James Kanze

    12. Dezember 2011 um 17:29 Uhr

1411730cookie-checkEin “echtes” interaktives Terminalprogramm wie vim, htop, … in C/C++ ohne ncurses schreiben

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

Privacy policy