在 curses 模式之外使用 getch 的可移植性如何?
How portable is the use of getch outside curses mode?
我正在开发一个终端程序来识别各个按键,包括键盘按键,但如果可能,我不想在 curses/program 模式下进行。我想我可能只是利用 curses 并使用 tcgetattr()
和 tcsetattr()
在 curses 模式之外做我想做的事情,而不是使用 terminfo 和某种映射或树结构来重新发明轮子以进行快速键盘键匹配仍在使用 curses I/O 函数为我翻译键盘按键。令我惊讶的是,这有效 (Linux, ncurses 6.1.20180127):
/**
* Most error checking elided for brevity.
*/
#include <stdio.h> // printf
#include <string.h> // memcpy
#include <curses.h>
#include <termios.h> // tcgetattr, tcsetattr
int main(void)
{
struct termios curr, new_shell_mode;
int c, fd;
SCREEN *sp;
FILE *ttyf;
/*
* Initialize desired abilities in curses.
* This unfortunately clears the screen, so
* a refresh() is required, followed by
* endwin().
*/
ttyf = fopen("/dev/tty", "r+b");
fd = fileno(ttyf);
sp = newterm(NULL, ttyf, ttyf);
raw();
noecho();
nonl();
keypad(stdscr, TRUE);
refresh();
// Save the current curses mode TTY attributes for later use.
tcgetattr(fd, &curr);
endwin();
/*
* Set the shell/non-curses mode TTY attributes to
* match those of program/curses mode (3 attempts).
*/
memcpy(&new_shell_mode, &curr, sizeof curr);
for (c = 0; c < 3; c++) {
tcsetattr(fd, TCSADRAIN, &new_shell_mode);
tcgetattr(fd, &curr);
if (0 == memcmp(&new_shell_mode, &curr, sizeof curr))
break;
}
// If new shell mode could fully be set, get a key press.
if (c != 3)
c = getch();
reset_shell_mode();
delscreen(sp);
fclose(ttyf);
printf("%02X\n", c);
return 0;
}
但是,考虑到我已经退出 curses 模式,实际上 safe/portable 是否仍按所示方式使用 getch()
?
或者我是否需要采用更困难的方式使用 setupterm()
来加载 terminfo 数据库并循环遍历 strnames
数组,为每个数组调用 tigetstr()
,然后设置我的手动拥有 termios 标志并自己处理读取按键?
XSI Curses 规范似乎没有禁止这样做,前提是 stdscr
仍然有效,这似乎是在程序退出或调用 delwin()
之前,我可以继续使用它,并且因为 stdscr
连接到我的 ttyf
文件,它是终端,我可以用它来获得按键,而无需自己处理所有事情。
您使用 newterm
初始化了 curses,但 无关紧要 你调用的 endwin
:如果 curses 执行 refresh
作为副词,curses 将恢复全屏模式-调用 getch
.
的效果
这不仅仅是 ncurses,还有任何 curses 实现(除了 1980 年代早已过时的 BSD 版本)。 X/Open Curses 笔记
If the current or specified window is not a pad, and it has been moved or modified since the last refresh operation, then it will be refreshed before another character is read.
在您的示例中,什么都没有 "moved or modified"。但是 getch
检查。 (endwin
/termios 的东西可能没有任何好处,因为 newterm
没有明确说明-屏幕直到第一个 refresh
).
我正在开发一个终端程序来识别各个按键,包括键盘按键,但如果可能,我不想在 curses/program 模式下进行。我想我可能只是利用 curses 并使用 tcgetattr()
和 tcsetattr()
在 curses 模式之外做我想做的事情,而不是使用 terminfo 和某种映射或树结构来重新发明轮子以进行快速键盘键匹配仍在使用 curses I/O 函数为我翻译键盘按键。令我惊讶的是,这有效 (Linux, ncurses 6.1.20180127):
/**
* Most error checking elided for brevity.
*/
#include <stdio.h> // printf
#include <string.h> // memcpy
#include <curses.h>
#include <termios.h> // tcgetattr, tcsetattr
int main(void)
{
struct termios curr, new_shell_mode;
int c, fd;
SCREEN *sp;
FILE *ttyf;
/*
* Initialize desired abilities in curses.
* This unfortunately clears the screen, so
* a refresh() is required, followed by
* endwin().
*/
ttyf = fopen("/dev/tty", "r+b");
fd = fileno(ttyf);
sp = newterm(NULL, ttyf, ttyf);
raw();
noecho();
nonl();
keypad(stdscr, TRUE);
refresh();
// Save the current curses mode TTY attributes for later use.
tcgetattr(fd, &curr);
endwin();
/*
* Set the shell/non-curses mode TTY attributes to
* match those of program/curses mode (3 attempts).
*/
memcpy(&new_shell_mode, &curr, sizeof curr);
for (c = 0; c < 3; c++) {
tcsetattr(fd, TCSADRAIN, &new_shell_mode);
tcgetattr(fd, &curr);
if (0 == memcmp(&new_shell_mode, &curr, sizeof curr))
break;
}
// If new shell mode could fully be set, get a key press.
if (c != 3)
c = getch();
reset_shell_mode();
delscreen(sp);
fclose(ttyf);
printf("%02X\n", c);
return 0;
}
但是,考虑到我已经退出 curses 模式,实际上 safe/portable 是否仍按所示方式使用 getch()
?
或者我是否需要采用更困难的方式使用 setupterm()
来加载 terminfo 数据库并循环遍历 strnames
数组,为每个数组调用 tigetstr()
,然后设置我的手动拥有 termios 标志并自己处理读取按键?
XSI Curses 规范似乎没有禁止这样做,前提是 stdscr
仍然有效,这似乎是在程序退出或调用 delwin()
之前,我可以继续使用它,并且因为 stdscr
连接到我的 ttyf
文件,它是终端,我可以用它来获得按键,而无需自己处理所有事情。
您使用 newterm
初始化了 curses,但 无关紧要 你调用的 endwin
:如果 curses 执行 refresh
作为副词,curses 将恢复全屏模式-调用 getch
.
这不仅仅是 ncurses,还有任何 curses 实现(除了 1980 年代早已过时的 BSD 版本)。 X/Open Curses 笔记
If the current or specified window is not a pad, and it has been moved or modified since the last refresh operation, then it will be refreshed before another character is read.
在您的示例中,什么都没有 "moved or modified"。但是 getch
检查。 (endwin
/termios 的东西可能没有任何好处,因为 newterm
没有明确说明-屏幕直到第一个 refresh
).