У меня проблема с ncurses, и я не смог найти решение в Интернете, поэтому я написал следующую небольшую программу, чтобы продемонстрировать проблему.
Вы можете скомпилировать его через:
sudo aptitude install ncurses-dev
g++ -lncurses -o resize resize.cpp
Он отображает целочисленный счетчик, увеличивающийся каждую секунду, разветвляясь на процесс таймера, который периодически отправляет один байт родительскому процессу через пару сокетов. Вы можете выйти из него, нажав CTRL+C.
Когда вы изменяете размер терминала, вы должны получить сообщение об ошибке «Прерванный системный вызов». Таким образом, вызов чтения прерывается SIGWINCH при изменении размера. Но как мне этого избежать? Или обычно системный вызов прерывается? Но как мне обработать прерванный системный вызов, чтобы продолжить увеличение счетчика, поскольку после прерывания дескриптор файла кажется мертвым.
Если вы используете неблокирующие сокеты, вместо этого вы получите «Ресурс временно недоступен».
Я использую стабильную версию Debian Wheezy, поэтому версия ncurses — 5.9-10, а версия libstdc++ — 4.7.2-5.
#include <ncurses.h>
#include <signal.h>
#include <netdb.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <string>
#include <iostream>
//Define a second.
timespec span = {1, 0};
//Handles both, SIGWINCH and SIGINT
void handle(int signal) {
switch (signal) {
case SIGWINCH:
//Reinitialize ncurses to get new size
endwin();
refresh();
printw("Catched SIGWINCH and handled it.\n");
refresh();
break;
case SIGINT:
//Catched CTRL+C and quit
endwin();
exit(0);
break;
}
}
//This registers above signal handler function
void set_handler_for(int signal) {
struct sigaction action;
action.sa_handler = handle;
action.sa_flags = 0;
if (-1 == sigemptyset(&action.sa_mask) or -1 == sigaction(signal, &action, NULL))
throw "Cannot set signal handler";
}
main() {
int fd[2];
//In this try block we fork into the timer process
try {
set_handler_for(SIGINT);
set_handler_for(SIGWINCH);
//Creating a socketpair to communicate between timer and parent process
if (-1 == socketpair(PF_LOCAL, SOCK_STREAM, 0, fd))
throw "Cannot create socketpair";
pid_t pid;
//Doing the fork
if (-1 == (pid = fork()))
throw "Cannot fork process";
if (!pid) {
//We are the timer, so closing the other end of the socketpair
close(fd[0]);
//We send one byte every second to the parent process
while (true) {
char byte;
ssize_t bytes = write(fd[1], &byte, sizeof byte);
if (0 >= bytes)
throw "Cannot write";
nanosleep(&span, 0);
}
//Here the timer process ends
exit(0);
}
//We are the parent process, so closing the other end of the socketpair
close(fd[1]);
}
catch (const char*& what) {
std::cerr << what << std::endl;
exit(1);
}
//Parent process - Initializing ncurses
initscr();
noecho();
curs_set(0);
nodelay(stdscr, TRUE);
//In this try block we read (blocking) the byte from the timer process every second
try {
int tick = 0;
while (true) {
char byte;
ssize_t bytes = read(fd[0], &byte, sizeof byte);
if (0 >= bytes)
throw "Cannot read";
//Clear screen and print increased counter
clear();
mvprintw(0, 0, "Tick: %d - Resize terminal and press CTRL+C to quit.\n", ++tick);
//Catch special key KEY_RESIZE and reinitialize ncurses to get new size (actually not necassary)
int key;
while ((key = getch()) != ERR) {
if (key == KEY_RESIZE) {
endwin();
refresh();
printw("Got KEY_RESIZE and handled it.\n");
}
}
//Update the screen
refresh();
}
}
catch (const char*& what) {
//We got an error - print it but don't quit in order to have time to read it
std::string error(what);
if (errno) {
error.append(": ");
error.append(strerror(errno));
}
error = "Catched exception: "+error+"\n";
printw(error.c_str());
refresh();
//Waiting for CTRL+C to quit
while (true)
nanosleep(&span, 0);
}
}
Благодарю вас!
С Уважением