자주 사용하는 프로그램을 대체 화면으로 사용

자주 사용하는 프로그램을 대체 화면으로 사용

이것은 수년 동안 나를 괴롭혀왔습니다. 저는 최신 터미널 에뮬레이터와 및 와 같은 대부분의 CLI 프로그램을 사용하지만 less대체 vim화면(시작할 때 입력하고 종료할 때 유지), 어떤 사람들은 이것을 원하지 않습니다. 악성 프로그램에는 top, screendialog.

이러한 프로그램은 시작되면 창의 내용을 지웁니다(마지막 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;
}

관련 정보