应该将哪些字节写入 pty 以生成 KEY_HOME?

Which bytes should be written to a pty to generate KEY_HOME?

我正在尝试在 pty 中测试 ncurses 应用程序。应将哪些字节发送到 pty 主机以触发 KEY_RESIZE、KEY_HOME、KEY_END 等键码?

下面发布了一些代码,我真心希望没有人会浪费时间查看这些代码,但为了完整起见还是将其包括在内。此代码的行为如下:

$ perl -e "print pack('c', 8)" | ./wrappty ./show-key
KEY_BACKSPACE

也就是说,如果我将 0x08 写入 pty,ncurses 应用程序会将其视为退格键。应该写入什么字节序列来触发其他键码?我会冒险猜测特定的字节序列取决于终端,所以我想知道是否有 ptys 的标准,或者是否有合理的方法来确定正确的字节序列。

wrappty.c:

#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <unistd.h>
#ifdef __linux__
# include <pty.h>
# include <utmp.h>
#else
# include <util.h>
#endif

static void
show_data(const char *t, ssize_t s)
{
        for( ; s > 0; t++, s-- ) {
                const char *fmt = isprint(*t) ? "%c" : "0x%02x";
                fprintf(stderr, fmt, *(unsigned char *)t);
        }
        fflush(stderr);
}

static ssize_t
send_msg(int fd, int b)
{
        char buf[1024];
        if( b != EOF ) {
                write(fd, &b, 1);
        }
        ssize_t s;
        while( (s = read(fd, buf, sizeof buf)) == sizeof buf ) {
                show_data(buf, s);
        }
        show_data(buf, s);
        return s;
}

static void
wait_for(int fd, const char *expect, size_t siz)
{
        int rc = 0;
        char buf[1024];
        char *a = buf;
        assert(siz < sizeof buf);
        char *end = buf + siz;

        while( a < end ) {
                ssize_t s = read(fd, a, end - a);
                char *e = a + s;
                rc += 1;
                while( a < e && *a++ == *expect++ ) {
                        ;
                }
                if( s < 1 || a < e ) {
                        fprintf(stderr, "Ivalid data\nReceived: ");
                        show_data(buf, e - buf);
                        fprintf(stderr, "\nExpected: ");
                        show_data(expect, siz);
                        fputc('\n', stderr);
                        exit(1);
                }
        }
}

void noop(int sig, siginfo_t *i, void *v) { (void)sig; (void)i; (void)v; }

int
main(int argc, char **argv)
{
        int primary, secondary, c;

        struct winsize ws = { .ws_row = 24, .ws_col = 80 };

        (void) argc;
        if( openpty(&primary, &secondary, NULL, NULL, &ws) ) {
                err(EXIT_FAILURE, "openpty");
        }
        switch( fork() ) {
        case -1:
                err(1, "fork");
                break;
        case 0:
                if( close(primary) ) {
                        err(EXIT_FAILURE, "close");
                }
                if( login_tty(secondary) ) {
                        err(EXIT_FAILURE, "login_tty");
                }
                execvp(argv[1], argv + 1);
                err(EXIT_FAILURE, "execvp %s", argv[1]);
        }
        /* Parent */
        if( close(secondary) ) {
                err(EXIT_FAILURE, "close");
        }

        /* Initialization sequence from ncurses on macos */
        char *expected = "\x1b(B\x1b)0\x1b[?1049h\x1b[1;24r\x1b[m\x0f\x1b[4l"
                "\x1b[?1h\x1b=\x1b[H\x1b[J";
        struct sigaction act;
        memset(&act, 0, sizeof act);
        act.sa_sigaction = noop;
        if( sigaction( SIGALRM, &act, NULL ) ) {
                perror("sigaction");
                return EXIT_FAILURE;
        }
        struct timeval tp = {.tv_sec = 0, .tv_usec = 500000 };
        struct itimerval t = { .it_interval = tp, .it_value = tp };
        setitimer(ITIMER_REAL, &t, NULL);
        wait_for(primary, expected, strlen(expected));
        while( (c = getchar()) != EOF ) {
                send_msg(primary, c);
        }

        send_msg(primary, EOF);
        fputc('\n', stderr);
        return 0;
}

显示-key.c:

#define _POSIX_C_SOURCE 200809L                                                    
#define _XOPEN_SOURCE 600                                                          
#define _XOPEN_SOURCE_EXTENDED                                                     
#define _DARWIN_C_SOURCE                                                           
                                                                                   
#include <ctype.h>                                                                 
#include <curses.h>                                                                
#include <err.h>                                                                   
#include <fcntl.h>                                                                 
#include <limits.h>                                                                
#include <stdlib.h>                                                                
#include <string.h>                                                                
#include <signal.h>                                                                
#include <unistd.h>                                                                
                                                                                   
int xopen(const char *path, int flags);                                            
void                                                                               
handle(int sig, siginfo_t *i, void *v)                                             
{                                                                                  
        (void)i;                                                                   
        (void)v;                                                                   
        char *n = NULL;                                                            
        switch(sig) {                                                              
        case SIGHUP:  n =  "SIGHUP\n"; break;                                      
        case SIGTERM:  n = "SIGTERM\n"; break;                                     
        case SIGINT:  n =  "SIGINT\n"; break;                                      
        }                                                                          
        if(n)                                                                      
                write(2, n, strlen(n));                                            
        return;                                                                    
}                                                                                  

int                                                                                
main(int argc, char **argv)                                                        
{                                                                                  
        int r;                                                                     
        wint_t w = 0;                                                              
        if( argc > 1 ) {                                                           
                int fd = xopen(argv[1], O_WRONLY);                                 
                dup2(fd, STDERR_FILENO);                                           
        }                                                                          
        unsetenv("COLUMNS");                                                       
        unsetenv("LINES");                                                         
        struct sigaction act;                                                      
        memset(&act, 0, sizeof act);                                               
        act.sa_sigaction = handle;                                                 
        if( sigaction( SIGTERM, &act, NULL ) ) { perror("sigaction"); exit(1); }   
        if( sigaction( SIGINT, &act, NULL ) ) { perror("sigaction"); exit(1); }    
        if( sigaction( SIGHUP, &act, NULL ) ) { perror("sigaction"); exit(1); }    
        if( initscr() == NULL ) {                                                  
                err(1, "initscr");                                                 
        }                                                                          
        noecho();                                                                  
        keypad(stdscr, true);                                                      
        while( (r = get_wch(&w)) != ERR ) {                                        
                char *d = NULL;                                                    
                if( r == KEY_CODE_YES ) switch(w) {                                
                case KEY_RESIZE: d = "KEY_RESIZE"; break;                          
                case KEY_HOME: d = "KEY_HOME"; break;                              
                case KEY_END: d = "KEY_END"; break;                                
                case KEY_PPAGE: d = "KEY_PPAGE"; break;                            
                case KEY_NPAGE: d = "KEY_NPAGE"; break;                            
                case KEY_BACKSPACE: d = "KEY_BACKSPACE"; break;                    
                case KEY_DC: d = "KEY_DC"; break;                                  
                case KEY_IC: d = "KEY_IC"; break;                                  
                case KEY_BTAB: d = "KEY_BTAB"; break;                              
                case KEY_ENTER: d = "KEY_ENTER"; break;                            
                case KEY_UP: d = "KEY_UP"; break;                                  
                case KEY_DOWN: d = "KEY_DOWN"; break;                              
                case KEY_RIGHT: d = "KEY_RIGHT"; break;                            
                case KEY_LEFT: d = "KEY_LEFT"; break;                              
                }                                                                  
                if( d != NULL ) {                                                  
                        /*printw("(%s)", d);*/                                     
                        fprintf(stderr, "%s", d);                                  
                } else {                                                           
                        /*printw("%lc", w);*/                                      
                        fprintf(stderr, "%lc", w);                                 
                }                                                                  
                /*doupdate();*/                                                    
                fflush(stderr);                                                    
        } 
        endwin();                                                                  
        fprintf(stderr, "ERR\n");                                                  
        return 0;                                                                  
}                                                                                  
                                                                                   
int                                                                                
xopen(const char *path, int flags)                                                 
{                                                                                  
        int f = open(path, flags);                                                 
        if( f == -1 ) {                                                            
                perror(path);                                                      
                exit(EXIT_FAILURE);                                                
        }                                                                          
        return f;                                                                  
} 

使用 tigetstr for fetching strings that correspond to the keys. Most are easily related to the curses names (which are listed in the getch 手册页):

foo = tigetstr("khome"); // KEY_HOME

最好有一个table,但由于命名约定直观,似乎没有人觉得有必要...