私が望んでいたことを達成するには、問題を解決する必要がありました。すべての出力関数はデフォルトでSTDOUTを使用しますが、ほとんどの場合、この動作を変更する方法はありません(ライブラリに埋め込まれたprintfを考えてみてください。 、またはncurses自体)。
したがって、STDOUTを変更すると、すべてが影響を受けます。
しかし、私は自分のプログラムがSTDOUTの制御を維持することを望んでいます。したがって、どういうわけか、実際のSTDOUTを取得するものと偽のSTDOUTを取得するものを分離する必要があります。セパレートはフォークのように聞こえます...だからフォークです。
基本的に、プログラムの大部分を子プロセスでフォークし、STDOUTを変更して配管します。親プロセスはSTDOUTの制御を維持し、パイプを読み取り、パイプ内のデータを使用して必要な処理を実行できます。
#include <ncurses.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <iostream>
#include <sstream>
#define MAX_LEN 99
WINDOW *create_newwin(int height, int width, int starty, int startx, int vert, int horz);
char buffer[MAX_LEN+1] = {0};
int out_pipe[2];
int saved_stdout;
volatile sig_atomic_t s_interrupted = 0;
volatile sig_atomic_t alarm_expired = 0;
static void s_signal_handler (int signal_value)
{
if (signal_value == SIGTERM || signal_value == SIGSTOP || signal_value == SIGINT)
s_interrupted = 1;
if (signal_value == SIGALRM)
//signal(SIGALRM,alarm_wakeup);
alarm_expired = 1;
}
static void s_catch_signals (void)
{
struct sigaction action;
action.sa_handler = s_signal_handler;
action.sa_flags = 0;
sigemptyset (&action.sa_mask);
sigaction (SIGINT, &action, NULL);
sigaction (SIGTERM, &action, NULL);
sigaction (SIGALRM, &action, NULL);
}
int main (int arg, char **argv) {
const char mesg[]="Testing stdout redirection";
WINDOW *my_win, *my_win2;
int cycle = 0, pid, p[2], row, col;
if(pipe(p) == -1)
{
perror("pipe call error");
return(1);
}
switch (pid=fork())
{
case -1: perror("error: fork call");
return(2);
case 0: /* if child then write down pipe */
if(dup2(p[1], 1) == -1 ) /* stdout == write end of the pipe */
{
perror( "dup2 failed" );
return(1);
}
s_catch_signals ();
setvbuf(stdout, NULL, _IOLBF, 1000);
while(!s_interrupted)
{
printf("Test %d\n",cycle);
cycle++;
usleep(10000);
}
printf("\n\tChild quitting cleanly\n\n");
break;
default:
s_catch_signals ();
std::string mystr;
if(dup2(p[0], 0 ) == -1 ) /* stdin == read end of the pipe */
{
perror( "dup2 failed" );
return(1);
}
initscr(); /* start the curses mode */
getmaxyx(stdscr,row,col); /* get the number of rows and columns */
mvprintw(row/2,(col-strlen(mesg))/2,"%s",mesg);
/* print the message at the center of the screen */
refresh();
my_win2 = create_newwin(10,col,row-10, 0, 0, 0);
wrefresh(my_win2);
my_win = create_newwin(8,col-2,row-9, 1, ' ', ' ');
wrefresh(my_win);
wprintw(my_win,"This screen has %d rows and %d columns\n",row,col);
wprintw(my_win,"Try resizing your window(if possible) and then run this program again\n");
scrollok(my_win,TRUE);
int myy,myx;
std::stringstream ss;
while(!s_interrupted)
{
while( std::getline(std::cin, mystr) )
{
ss.clear();
ss << mystr << std::endl;
mystr = ss.str();
waddstr(my_win,mystr.c_str());
wrefresh(my_win);
}
usleep(100000);
}
endwin();
break;
}
endwin();
printf("\n\tProgram quitting cleanly\n\n");
return (EXIT_SUCCESS);
}
WINDOW *create_newwin(int height, int width, int starty, int startx, int vert, int horz)
{ WINDOW *local_win;
local_win = newwin(height, width, starty, startx);
box(local_win, vert , horz); /* 0, 0 gives default characters
* for the vertical and horizontal
* lines */
wrefresh(local_win); /* Show that box */
return local_win;
}
このプログラムは、まさにこの動作を示しています。メインは、自身のSTDOUTをパイプにリダイレクトする子を生成します。次に、このパイプは親によって読み取られ、親は制御されたウィンドウ内に出力します。画面の残りの部分は、親が自由に操作できますが、必要です。
switch (pid=fork())
{
case 0: /* if child then write down pipe */
if(dup2(p[1], 1) == -1 ) /* stdout == write end of the pipe */
{
perror( "dup2 failed" );
return(1);
}
フォークが呼び出されたときに、子(pid == 0)の場合は、前に作成したパイプを取得し、独自のSTDOUTをこのパイプの書き込み端に切り替えます。
setvbuf(stdout, NULL, _IOLBF, 1000);
親がより簡単に取得できるように、バッファリングモードをlineに変更します。
while(!s_interrupted)
{
printf("Test %d\n",cycle);
cycle++;
usleep(10000);
}
簡単なテキストを生成して、機能することを確認します。
switch (pid=fork())
{
default:
if(dup2(p[0], 0 ) == -1 ) /* stdin == read end of the pipe */
{
perror( "dup2 failed" );
return(1);
}
親の場合は、前に作成したパイプの読み取り端にSTDINを設定します。
initscr(); /* start the curses mode */
getmaxyx(stdscr,row,col); /* get the number of rows and columns */
mvprintw(row/2,(col-strlen(mesg))/2,"%s",mesg);
/* print the message at the center of the screen */
refresh();
my_win2 = create_newwin(10,col,row-10, 0, 0, 0);
wrefresh(my_win2);
my_win = create_newwin(8,col-2,row-9, 1, ' ', ' ');
wrefresh(my_win);
wprintw(my_win,"This screen has %d rows and %d columns\n",row,col);
wprintw(my_win,"Try resizing your window(if possible) and then run this program again\n");
scrollok(my_win,TRUE);
ncursesを初期化し、いくつかのウィンドウを作成します。my_win2を使用して境界線を描画し、境界線のないmy_winを使用して実際にデータを格納します。さらに、my_winを自動スクロールに設定して、スペースが不足することを心配せずに印刷を続行できるようにしました。
while( std::getline(std::cin, mystr) )
{
ss.clear();
ss << mystr << std::endl;
mystr = ss.str();
waddstr(my_win,mystr.c_str());
wrefresh(my_win);
}
usleep(100000);
ゆったりとしたペースのサイクルで、STDINをチェックし、新しい行がある場合は、スクロールウィンドウに追加するだけです。そのため、プロセスがアクティブ状態でスケジュールされている場合、繰り返しポーリングするのではなく、時間をかけて大量の出力を処理します。
これは、提案された問題に対する実用的な解決策であることがわかります。