但是,现在我遇到了一个问题,当信号到达时,我的程序在 getch 中挂起。ncurses 文档说明处理过的信号永远不会中断 getch。这意味着 tui 的大小直到按下输入键才会更新。
有没有可移植的方法来打断 getch?我目前的方法是在信号处理程序中使用 ungetch 来插入一个虚拟按键,但我不确定这是否被允许。实际上,我找不到任何关于 curses 函数是否可以在信号处理程序中使用的文档。有没有想法如何正确处理这个问题?
问候
但是,现在我遇到了一个问题,当信号到达时,我的程序在 getch 中挂起。ncurses 文档说明处理过的信号永远不会中断 getch。这意味着 tui 的大小直到按下输入键才会更新。
有没有可移植的方法来打断 getch?我目前的方法是在信号处理程序中使用 ungetch 来插入一个虚拟按键,但我不确定这是否被允许。实际上,我找不到任何关于 curses 函数是否可以在信号处理程序中使用的文档。有没有想法如何正确处理这个问题?
问候
唯一的指导原则是尽可能少地在中断例程中执行操作。如果您正在执行比设置标志更多的操作,那么您应该考虑重新思考解决方案。
curses
系统有一种处理这个问题的方法,但需要开发人员做一些工作。
您可以使用适当的延迟设置半延迟模式,以便getch()
在此期间没有按键可用时返回ERR
。这有效地使您退出了getch()
调用,因此您可以进行所需的任何其他curses操纵。
因此,这是我的建议。首先,将SIGWINCH处理程序更改为仅设置一个名为resized
的标志,您的“主”程序可以检测到它。
其次,为您的应用程序提供一种特殊形式的getch()
,类似于(伪代码,显然):
def getch_10th():
set half delay mode for (for example) 1/10th second
do:
if resized:
do whatever it takes to resize window
set ch to result of real getch() (with timeout, of course)
while timed out
return ch
#include <curses.h>
#include <signal.h>
// Flag and signal handler.
static volatile int resized = 1;
static void handle_resize (int sig) {
resized = 1;
}
// Get a character, handling resize events.
int getch10th (void) {
int ch;
do {
if (resized) {
resized = 0;
endwin();
refresh();
mvprintw (1, 0, "Size = %dx%d. \n", COLS, LINES);
refresh();
}
halfdelay (1);
ch = getch();
} while (ch == ERR || ch == KEY_RESIZE);
return ch;
}
main
函数来测试它:// Simplified main capturing keystrokes.
int main (void) {
WINDOW * w = initscr();
noecho();
signal (SIGWINCH, handle_resize);
for (;;) {
int ch = getch10th();
mvprintw(0, 0, "Got character 0x%02x. \n\n", ch);
}
endwin();
return 0;
}
getch10th()
函数中还存在KEY_RESIZE
。这是因为一些实现实际上会排队一个特殊的键来处理这种情况(在引发SIGWINCH
后强制getch()
返回)。resized
变量不应该按照原始帖子中所建议的是类型为sig_atomic_t
吗?而且它也不应该声明为volatile
吗? - Roland Illigselect()
上等待,只有当输入就绪时才调用 getch()
? select()
显然会可靠地在接收到信号时中断。 - Crowmanvolatile
和 sig_atomic_t
的习惯,即使没有其他原因。@Crowman 我能想到的一个原因是与 select(2)
的可移植性问题有关,而将其与 curses
混合使用似乎更加棘手。现在虽然那些熟练掌握 select()
的人会知道要检查哪些事情,但还是有很多微妙的问题和需要考虑的事情——我猜你已经意识到了。 - Pryftan然而,OP从未展示工作代码来演示这种情况。
除了OpenBSD,在那里该功能被禁用(直到2011年),getch应该在SIGWINCH
上返回KEY_RESIZE
。禁用它的原因是对sig_atomic_t
的担忧,这在2007中得到了解决(例如,请参见OpenBSD的ncurses没有USE_SIGWINCH)。
KEY_RESIZE
的通常原因是建立了一个 SIGWINCH
处理程序(这会阻止 ncurses 中的处理程序运行)。这就是ncurses - resizing glitch中的实际问题(没有一个建议的答案解决了这个问题)。resizeterm
手册页面总结了这一点。如果您阅读了这篇文章,您就会意识到。
KEY_RESIZE
,则实现支持与ncurses更/少兼容的SIGWINCH
处理程序,顺便说一句,从ncurses的curs_getch(3x)手册页中引用的评论并不是指阻止SIGWINCH
,而是库中的一种解决方法,以防止它在信号中断读取时向应用程序返回错误。如果您阅读该手册页面的整个可移植性部分,这一点应该是显而易见的。
当然,Emacs仅使用ncurses的termcap接口,而不是curses应用程序。因此,关于它如何处理SIGWINCH
的评论是无关紧要的。
根据FreeBSD文档getch,中断getch取决于您使用的系统:
关心可移植性的程序员应为以下两种情况做好准备:(a) 信号接收不会中断getch; (b) 信号接收会中断getch并导致其返回errno设置为EINTR的ERR。在ncurses实现下,被处理的信号永远不会中断getch。
我认为您应该考虑使用线程,一个线程负责使用getch()并将数据传递给主线程进行处理。当发送SIGWINCH信号时,您将能够终止使用getch()的线程,并在需要时重新启动它。但是,终止使用getch()的线程可能是无用的,因为主线程不会被getch()阻塞。
您可以在此页面中了解如何获得非阻塞输入,而无需使用ncurses。但是,您可以使用read(STDIN_FILENO, &c, sizeof(char))
来读取输入,而不是在示例中使用fgetc()
,因为如果失败,read将返回一个值<= 0。read(2)
在出现错误时返回-1
(并适当设置errno
)。实际上,0
并不是一个错误。您说“失败”,但我不会认为读取零字节是一种失败。虽然这可能只是语义问题。 - Pryftan虽然已经有一段时间了,但我相信你可以从处理程序中使用longjmp。不确定它在ncurses中的工作方式。
http://www.csl.mtu.edu/cs4411.ck/www/NOTES/non-local-goto/sig-1.html
编辑:我有一个新的答案******************************
我认为非阻塞读取是你的救星,以下代码对我来说完美地工作。此外,在我的实现中,当发生调整大小事件时,我会得到整数值“410”,但此版本未使用该调整大小标志。试试这个?
我将超时设置为10毫秒,当没有任何操作时,它将每秒返回ERR而不是字符100次,并立即捕获调整大小。每秒100次什么都不是...在我的低端机器上,8分钟的时间甚至不会在cput“time”命令(0.000用户和sys使用)中注册。
#include <curses.h>
#include <signal.h>
int resized = 0;
void handle_resize(int sig)
{
resized = 1;
}
int main(int argc, char * argv[])
{
WINDOW * w;
w = initscr();
timeout(10); // getch returns ERR if no data within X ms
cbreak();
noecho();
signal(SIGWINCH, handle_resize);
int c, i;
while (1) {
c = getch();
if(resized) {
resized = 0;
endwin();
refresh();
clear();
mvprintw(0, 0, "COLS = %d, LINES = %d", COLS, LINES);
for (i = 0; i < COLS; i++)
mvaddch(1, i, '*');
refresh();
}
if (c != ERR) {
mvprintw(2, 0, "Got character %c (%d)\n", c, c);
}
}//while 1
endwin();
return 0;
}
~Z
(410)。其次:如果可以以可移植的方式向stdin
注入字符,那么是否也可以在它们返回后使用非阻塞的getch
来检索键,并在select
/poll
上选择stdin
?但我仍然缺少关于当直接操作stdin
时curses的反应的规范。 - user1678062
endwin
和refresh
,这似乎是一种有效的方法,但我在文档中找不到任何相关内容,而且我不喜欢使用未定义的行为(特别是在C语言中)。 @fearless_fool:就我理解代码而言,emacs并没有使用(n)curses来检索字符或处理调整大小事件,而是自己完成了大部分工作。但也许我可以找到另一个应用程序……但我仍然可能不知道该应用程序使用的解决方案是已定义的行为还是仅仅是凑巧可行的。 - user1678062man 7 signal
。 - Jitecurs_getch(3X)
也没有说明是否可以在信号处理程序中调用ungetch
。 - user1678062