Томас отвечает, откуда KEY_RESIZE
.Для меня это отличное введение в отладку кода C и ответ на второй вопрос о нажатии клавиши.
Краткий ответ дается как рабочий код (с потенциальной проблемой, возникающей из-за вызова функций ncurses в обработчиках сигналов,см. обновленный ответ Томаса, но здесь он не является основной причиной проблемы):
#include <ncurses.h>
#include <signal.h>
WINDOW *stdscr = NULL;
void handler(int num) {
endwin();
wrefresh(stdscr);
}
int main()
{
stdscr = initscr();
cbreak();
keypad(stdscr, 1);
wrefresh(stdscr);
struct sigaction old_action;
struct sigaction new_action;
new_action.sa_handler = handler;
new_action.sa_flags = 0; // !
sigemptyset(&new_action.sa_mask);
sigaction(SIGWINCH, &new_action, &old_action);
while (1) {
int ch = wgetch(stdscr);
if (ch == KEY_RESIZE) break;
}
endwin();
return 0;
}
Длинный ответ утомителен.
По сути, ncurses ожидает, что обработчик SIGWINCH
быть установленным без флага SA_RESTART
.
Библиотека ncurses вызывает fifo_push
, определенную в ncurses/base/lib_getch.c
, для чтения входного потока.И эта функция имеет блокировку read
, когда вы блокируете getch
.
При SIGWINCH
, этот вызов прерывается и возвращает -1
с errno
, установленным в EINTR
.
Библиотека ncurses будет обрабатывать это в _nc_wgetch
, который вызывает _nc_handle_sigwinch
, чтобы проверить, произошло ли SIGWINCH
.Если это так, то он вызывает от _nc_update_screensize
до ungetch
a KEY_RESIZE
.
Пока все хорошо.Но что, если мы использовали SA_RESTART
при установке обработчика SIGWINCH
?Системный вызов read
будет перезапущен при прерывании.Вот почему программа на C не закрывается сразу после изменения размера окна, а должна прочитать еще одну клавишу.
Более интересно то, что ncurses ожидает установки SA_RESTART
при установке обработчиков сигналов (в ncurses/tty/lib_tstp.c
):
Примечание. Этот код хрупок!Его проблема в том, что разные ОС по-разному обрабатывают перезапуск системных вызовов, прерываемых сигналами.Код ncurses нуждается в перезапуске сигнального вызова, иначе прерванные вызовы wgetch () вернут FAIL, возможно, заставляя приложение думать, что входной поток завершился и должен завершиться.В частности, вы знаете, что у вас есть эта проблема, если, когда вы приостанавливаете рысь, использующую ncurses, с помощью ^ Z и возобновляете его, он немедленно * умирает.
Но SIGWINCH
является исключением ...
#ifdef SA_RESTART
#ifdef SIGWINCH
if (sig != SIGWINCH)
#endif
new_act.sa_flags |= SA_RESTART;
#endif /* SA_RESTART */
Исходный код C не работает, потому что man signal
говорит:
По умолчанию в glibc 2 и более поздних функция-обертка signal () не вызывает системный вызов ядра,Вместо этого он вызывает sigaction (2) с использованием флагов, предоставляющих семантику BSD.
И семантика BSD:
sa.sa_flags = SA_RESTART;
Python?Python не беспокоится о SA_RESTART
:
PyOS_sighandler_t
PyOS_setsig(int sig, PyOS_sighandler_t handler)
{
#ifdef HAVE_SIGACTION
...
struct sigaction context, ocontext;
context.sa_handler = handler;
sigemptyset(&context.sa_mask);
context.sa_flags = 0;
if (sigaction(sig, &context, &ocontext) == -1)
return SIG_ERR;
return ocontext.sa_handler;
#else
...
#endif
}