Ecrire un" vrai " programme de terminal interactif comme vim, htop, ... en C/C++ sans ncurses

Non, je ne veux pas utiliser ncurses, parce que je veux apprendre comment les le terminal fonctionne et avoir du plaisir programmation sur mon propre. :) Il n'a pas doit être portable, il doit fonctionner uniquement sur les émulateurs de terminaux linux xterm.

ce que je veux faire, c'est programmer une application de terminal interactive comme htop et vim are. Ce que je veux dire n'est pas la sortie de caractères qui ressemblent à des boîtes ou des couleurs de réglage, c'est trivial; aussi pour faire le contenu adapté à la taille de la fenêtre. Ce dont j'ai besoin est

  1. comment obtenir interactions avec la souris comme cliquer sur un caractère et faire défiler la roue de la souris (quand la souris est à un caractère spécifique) pour mettre en place le défilement [EDIT: dans un émulateur de terminal de cours] et

  2. complètement sauvegarder et restaurer la sortie du processus parent et séparer mon impression de sa sortie, Donc après avoir quitté mon demande rien, mais la commande je suis entré dans la coque devrait être là, comme lors de l'exécution de htop et l'abandon de nouveau: rien n'est visible à partir de cette application plus.

je ne veux vraiment pas utiliser ncurses. Mais bien sûr, si vous savez quelle partie de ncurses est responsable de ces tâches, vous êtes les bienvenus pour me dire où dans le code source je peux le trouver, je vais l'étudier.

42
demandé sur leemes 2011-12-12 19:05:14

3 réponses

je suis un peu confus. Vous parlez d'une "application terminale"", comme vim; les applications de terminaux n'ont pas d'événements de souris, et ne répondre à la souris.

si vous parlez de véritables applications de terminal, qui fonctionnent dans un xterm, l'important de penser à noter est que beaucoup de la portabilité les problèmes concernent le terminal, et non L'OS. Le terminal est contrôlé en envoyant différentes séquences d'évasion. Ceux qui font ce qui dépend du terminal; les codes de fuite ANSI sont maintenant assez répandue, cependant, voir http://en.wikipedia.org/wiki/ANSI_escape_code. Ceux-ci sont généralement compris par xterm, par exemple.

vous devrez peut-être produire une séquence supplémentaire au début et à la fin pour entrer et quitter le mode "plein écran"; Ceci est nécessaire pour xterm.

enfin, vous devrez faire quelque chose de spécial au niveau des entrées/sorties pour vous assurer que votre pilote de sortie n'ajoute pas de caractères (par exemple, convertir un simple LF en CRLF), et s'assurer que les entrées ne résonnent pas, qu'elles sont transparentes et qu'elles reviennent immédiatement. Sous Linux, ceci est fait en utilisant ioctl. (Encore une fois, n'oubliez pas de le Restaurer lorsque vous aurez terminé.)

4
répondu James Kanze 2011-12-12 15:22:19

pour manipuler le terminal vous devez utiliser séquences de contrôle. Malheureusement, ces codes dépendent du terminal que vous utilisez. C'est pourquoi terminfo (précédemment termcap) existe en premier lieu.

Vous ne dites pas si vous souhaitez utiliser terminfo ou pas. Donc:

  • si vous utilisez terminfo, il vous donnera la séquence de contrôle correcte pour chaque action prise en charge par votre terminal.
  • si vous ne voulez pas utiliser terminfo... Eh bien, vous devez coder manuellement chaque action dans chaque type de terminal que vous voulez prendre en charge.

comme vous le voulez à des fins d'apprentissage, je vais développer dans la seconde.

Vous pouvez découvrir le type de terminal que vous utilisez à partir de la variable d'environnement $TERM. Sous linux les plus habituelles sont xterm pour les émulateurs de terminaux (XTerm, gnome-terminal, konsole), et linux pour les terminaux virtuels (lorsque X n'est pas en cours d'exécution).

Vous pouvez découvrez facilement les séquences de contrôle avec la commande tput. Mais comme tput impressions sur la console, ils s'appliquent immédiatement, donc si vous voulez vraiment les voir, utilisez:

$ TERM=xterm tput clear | 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|

Qui est, pour effacer l'écran dans un xterm vous avez de sortie ESC [ H ESC [ 2J dans un xterm, mais ESC [ H ESC [ J dans un terminal linux.

au sujet des commandes particulières que vous demandez, vous devriez lire attentivement man 5 terminfo. Il y a beaucoup de renseignements.

16
répondu rodrigo 2017-12-07 00:34:03

bien que ce soit une question un peu vieille, j'ai pensé que je devrais partager un exemple court de la façon de le faire sans utiliser ncurses, il n'est pas difficile, mais je suis sûr qu'il ne sera pas aussi portable.

ce code positionne stdin en mode brut, passe à un écran tampon alternatif (qui sauve l'état du terminal avant de le lancer), permet de suivre la souris et d'imprimer le bouton et les coordonnées lorsque l'utilisateur clique quelque part. Après avoir cessé de fumer avec Ctrl+ C la le programme inverse la configuration du terminal.

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

int main (void)
{
    unsigned char buff [6];
    unsigned int x, y, btn;
    struct termios original, raw;

    // Save original serial communication configuration for stdin
    tcgetattr( STDIN_FILENO, &original);

    // Put stdin in raw mode so keys get through directly without
    // requiring pressing enter.
    cfmakeraw (&raw);
    tcsetattr (STDIN_FILENO, TCSANOW, &raw);

    // Switch to the alternate buffer screen
    write (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;
        } else if (buff[0] == '\x1B') {
            // We assume all escape sequences received 
            // are mouse coordinates
            read (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);
        }
    }

    // Revert the terminal back to its original state
    write (STDOUT_FILENO, "\e[?9l", 5);
    write (STDOUT_FILENO, "\e[?47l", 6);
    tcsetattr (STDIN_FILENO, TCSANOW, &original);
    return 0;
}

Note: cela ne fonctionnera pas correctement pour les terminaux qui ont plus de 255 colonnes.

Les meilleures références pour les séquences d'échappement que j'ai trouvé sont et .

7
répondu santileortiz 2016-02-21 09:01:42