よく使うプログラムを代替画面として使う

よく使うプログラムを代替画面として使う

これは長年私を悩ませてきました。私は最新の端末エミュレータとのようなほとんどのCLIプログラムを使用していますが、less代替vim画面(つまり開始時に入力し、終了時に保持します)、一部の人々はこれをしたくありません。悪意のあるプログラムにはtopscreendialog

これらのプログラムは起動するとウィンドウの内容を消去します(つまり最後のN行。ここで、Nは端末ウィンドウの高さです。終了すると、一部(top)は最後の状態を表示し続け、他の一部(screen)は画面を再び消去します。すべての場合に、ロールバックバッファの最後のN行を上書きしました。

xcfce4-terminal私は時間が経つにつれて、およびurxvtscreen代替画面が正しくアクティブになっている間)を含むさまざまなコンピュータとさまざまな端末エミュレータでこれを確認しました:altscreen on。だから私はこれが私の端末の問題だとは思わない。むしろ、これがこのプログラムの組み込み動作であると信じています(少なくともArchlinuxに配布されるため、パッチされませんでした)。

だから私の質問は次のようになります

  1. このプログラムはなぜこのように動作しますか?ここには妥当な理由があるようですが?
  2. この問題をどのように解決できますか?現在、私は以下のような愚かなラッパースクリプトを使用していますが、よりきれいな方法がありますか?

    # save this as: ~/bin/top
    
    if [ -t 1 ]; then  # only use alt screen if output is a terminal
        tput smcup  # toggle alt screen on
        /usr/bin/top "$@"
        tput rmcup  # toggle alt screen off
    else
        /usr/bin/top "$@"
    fi
    

ベストアンサー1

シェルスクリプトを使用して行を折り返すのではなく、プログラムが停止したときに通常の画面に戻すことができる短いCプログラムを作成できます。

#define _GNU_SOURCE 1

#include <stdbool.h>
#include <stdio.h>


#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

/* Quick hack; todo: use terminfo instead */
#include <stdlib.h>
static void enter_alt_screen(void)
{
    system("tput smcup");
}
static void leave_alt_screen(void)
{
    system("tput rmcup");
}

int main(int argc, char **argv)
{
    if (argc < 2) {
        fprintf(stderr, "Usage: %s command args...", argv[0]);
        return 1;
    }

    if (!isatty(fileno(stdout))) {
        /* not a terminal; act normally */
        execvp(argv[1], argv+1);
    }

    enter_alt_screen();
    const pid_t child = fork();
    switch (child) {
    case -1:
        leave_alt_screen();
        perror("fork");
        return 1;
    case 0:
        /* child */
        execvp(argv[1], argv+1);
        leave_alt_screen();
        perror("exec");
        return 1;
    }

    int status;
    while (waitpid(child, &status, WCONTINUED) == child) {
        if (WIFSTOPPED(status)) {
            leave_alt_screen();
        } else if (WIFCONTINUED(status)) {
            enter_alt_screen();
        } else if (WIFSIGNALED(status)) {
            leave_alt_screen();
            signal(WTERMSIG(status), signal(SIGTERM, SIG_DFL));
            raise(WTERMSIG(status));
        } else if (WIFEXITED(status)) {
            leave_alt_screen();
            return WEXITSTATUS(status);
        }
    }
    return 1;
}

おすすめ記事