web-dev-qa-db-ja.com

ncursesなしでC / C ++でvim、htopなどの「実際の」対話型端末プログラムを書く

いいえ、私はncursesを使用したくありません。ターミナルの仕組みを学び、自分でプログラミングを楽しみたいからです。 :)それはポータブルである必要はありません、それはLinux xtermベースのターミナルエミュレータでのみ動作する必要があります。

私がやりたいのは、htopやvimのようなインタラクティブなターミナルアプリケーションをプログラミングすることです。私が言いたいのは、ボックスや設定色のように見える文字の出力ではなく、これは取るに足らないことです。また、コンテンツをウィンドウサイズに合わせます。私が必要なのは

  1. 取得方法マウスインタラクション文字をクリックしてマウスホイールをスクロールする(マウスが特定の文字にある場合)ようにスクロールを実装する[編集:もちろんターミナルエミュレーターで =]、および

  2. 完全にどのように保存して復元親プロセスの出力と出力を私の出力から分離するため、アプリケーションを終了した後、シェルに入力したコマンドだけがそこにあるはずです(htopを実行するときなど)もう一度終了すると、このアプリケーションからは何も見えなくなります。

私はncursesを使いたくありません。しかしもちろん、ncursesのどの部分がこれらのタスクの原因であるかがわかっている場合は、ソースコードのどこにあるかを教えてもらえるので、検討します。

49
leemes

私は少し混乱しています。 vimのような「ターミナルアプリケーション」について話します。端末アプリケーションはマウスイベントを取得せず、マウスに応答しません。

xtermで実行される実際の端末アプリケーションについて話している場合、重要なのは、移植性の問題の多くがOSではなく端末に関係していることです。端末は、さまざまなエスケープシーケンスを送信することによって制御されます。どれが何をするかは端末によって異なります。現在、ANSIエスケープコードはかなり広く普及していますが、 http://en.wikipedia.org/wiki/ANSI_escape_code を参照してください。これらは一般的に、たとえばxtermによって理解されます。

「フルスクリーン」モードを開始および終了するには、最初と最後に追加のシーケンスを出力する必要がある場合があります。これはxtermに必要です。

最後に、入力/出力レベルで特別なことを行って、出力ドライバーが文字を追加しないようにする必要があります(たとえば、単純なLFをCRLFに変換する)。その入力はエコーされず、透過的で、すぐに返されます。Linuxでは、これはioctlを使用して行われます(この場合も、終了時に元に戻すことを忘れないでください)。

3
James Kanze

端末を操作するには、制御シーケンスを使用する必要があります。残念ながら、これらのコードは、使用している特定の端末によって異なります。そもそもterminfo(以前はtermcap)が存在する理由です。

Terminfoを使用するかどうかは言いません。そう:

  • Terminfoを使用する場合は、ターミナルがサポートする各アクションの正しい制御シーケンスを提供します。
  • Terminfoを使用しない場合は、サポートしたいすべての端末タイプのすべてのアクションを手動でコーディングする必要があります。

学習目的でこれが必要なので、2番目に詳しく説明します。

使用している端末タイプは、環境変数$TERMから確認できます。 Linuxで最も一般的なのは、ターミナルエミュレータ(XTerm、gnome-terminal、konsole)の場合はxterm、仮想ターミナル(Xが実行されていない場合)の場合はlinuxです。

コマンドtputを使用すると、制御シーケンスを簡単に見つけることができます。ただし、tputはそれらをコンソールに出力するため、すぐに適用されるため、本当に表示したい場合は、次を使用します。

$ 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|

つまり、xtermで画面をクリアするには、xtermではESC [ H ESC [ 2Jを出力する必要がありますが、LinuxターミナルではESC [ H ESC [ Jを出力する必要があります。

質問する特定のコマンドについては、man 5 terminfoを注意深く読んでください。そこにはたくさんの情報があります。

18
rodrigo

これは少し古い質問ですが、ncursesを使用せずにこれを行う方法の簡単な例を共有する必要があると思いましたが、難しくはありませんが、移植性はそれほど高くありません。

このコードは、stdinをrawモードに設定し、代替バッファー画面(起動前に端末の状態を保存する)に切り替え、マウスの追跡を有効にし、ユーザーがどこかをクリックしたときにボタンと座標を印刷します。で終了した後 Ctrl+C プログラムは端末設定を元に戻します。

#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;
}

注:これは、255列を超える端末では正しく機能しません。

私が見つけたエスケープシーケンスの最適なリファレンスは this および this oneです。

7
santileortiz