应该将哪些字节写入 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;
}
我正在尝试在 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;
}