My C program run multiple thread in the terminal that print messages asynchronously. I'd like a thread to show curses output, but as it is asynchronous it must be in another terminal.
My idea is to write the curses output to a fifo and open a terminal with
cat fifo
.
But how could I recover the ouput the curses out and output it to a file?
Thank you.
curses uses a terminal for its input and output, so if you want to intercept that and make it go somewhere other than a terminal, the easiest method (though non-trivial) is to use a psuedoterminal. You do that by calling posix_openpt
which gives you a pseudoterminal master device. You then call grantpt
, unlockpt
, and ptsname
to get the name of a terminal device that you can then fopen
and pass to curses newterm
to initialize the terminal.
Once that is done, everything that curses writes to the terminal will be readble from the master, and everything written to the master will be input to curses. It is like a fifo, just with all the extra terminal functionality curses expects.
curses requires direct access to the terminal in order lookup escape codes, detect resolution etc. You should probably try to have one thread dedicated for curses output in the current (main) terminal, and redirect all debug messages from other threads to either a logfile/pipe/logging facility (and present them inside the curses if you want to)
ncurses needs a terminal because it initializes the I/O connection. A FIFO would not be suitable, since it is one-way and (see for example Perform action if input is redirected) probably cannot be initialized as a terminal. ncurses uses the TERM
environment variable to lookup the terminal description (the actual terminal is not consulted on the matter).
There is a simple example, ditto
in ncurses-examples which uses xterm
for multiple input/output screens. That uses the pty interface, e.g.,
#ifdef USE_XTERM_PTY
int amaster;
int aslave;
char slave_name[1024];
char s_option[sizeof(slave_name) + 80];
const char *xterm_prog = 0;
if ((xterm_prog = getenv("XTERM_PROG")) == 0)
xterm_prog = "xterm";
if (openpty(&amaster, &aslave, slave_name, 0, 0) != 0
|| strlen(slave_name) > sizeof(slave_name) - 1)
failed("openpty");
if (strrchr(slave_name, '/') == 0) {
errno = EISDIR;
failed(slave_name);
}
sprintf(s_option, "-S%s/%d", slave_name, aslave);
if (fork()) {
execlp(xterm_prog, xterm_prog, s_option, "-title", path, (char *) 0);
_exit(0);
}
fp = fdopen(amaster, "r+");
if (fp == 0)
failed(path);
#else
and newterm
to pass that file descriptor to ncurses:
static void
open_screen(DITTO * target, char **source, int length, int which1)
{
if (which1 != 0) {
target->input =
target->output = open_tty(source[which1]);
} else {
target->input = stdin;
target->output = stdout;
}
target->which1 = which1;
target->titles = source;
target->length = length;
target->fifo.head = -1;
target->screen = newterm((char *) 0, /* assume $TERM is the same */
target->output,
target->input);
if (target->screen == 0)
failed("newterm");
(void) USING_SCREEN(target->screen, init_screen, target);
}
If you read the first quoted section of ditto
, you will notice that it uses an option of xterm
which allows an application to pass a file descriptor to it:
-Sccn This option allows xterm to be used as an input and output
channel for an existing program and is sometimes used in spe-
cialized applications. The option value specifies the last few
letters of the name of a pseudo-terminal to use in slave mode,
plus the number of the inherited file descriptor. If the
option contains a "/" character, that delimits the characters
used for the pseudo-terminal name from the file descriptor.
Otherwise, exactly two characters are used from the option for
the pseudo-terminal name, the remainder is the file descriptor.
Examples (the first two are equivalent since the descriptor
follows the last "/"):
-S/dev/pts/123/45
-S123/45
-Sab34
Note that xterm does not close any file descriptor which it did
not open for its own use. It is possible (though probably not
portable) to have an application which passes an open file
descriptor down to xterm past the initialization or the -S
option to a process running in the xterm.
Here is a screenshot showing ditto
: