これは長年私を悩ませてきました。私は最新の端末エミュレータとのようなほとんどのCLIプログラムを使用していますが、less
代替vim
画面(つまり開始時に入力し、終了時に保持します)、一部の人々はこれをしたくありません。悪意のあるプログラムにはtop
、screen
dialog
。
これらのプログラムは起動するとウィンドウの内容を消去します(つまり最後のN行。ここで、Nは端末ウィンドウの高さです。終了すると、一部(top
)は最後の状態を表示し続け、他の一部(screen
)は画面を再び消去します。すべての場合に、ロールバックバッファの最後のN行を上書きしました。
xcfce4-terminal
私は時間が経つにつれて、およびurxvt
(screen
代替画面が正しくアクティブになっている間)を含むさまざまなコンピュータとさまざまな端末エミュレータでこれを確認しました:altscreen on
。だから私はこれが私の端末の問題だとは思わない。むしろ、これがこのプログラムの組み込み動作であると信じています(少なくともArchlinuxに配布されるため、パッチされませんでした)。
だから私の質問は次のようになります
- このプログラムはなぜこのように動作しますか?ここには妥当な理由があるようですが?
この問題をどのように解決できますか?現在、私は以下のような愚かなラッパースクリプトを使用していますが、よりきれいな方法がありますか?
# 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;
}