Home > Software engineering >  How to pipe/redirect the final stdout of a NCurse command?
How to pipe/redirect the final stdout of a NCurse command?

Time:03-27

I have a command that displays Ncurses stuffs (initscr, printw, addch, ...). That's okay.

At the end (endwin), I want to "output" (std::cout << "some string") a string to be processed by other command (or maybe redirected to a stream). I want to do something like this :

my-ncurse-command | any-other-command
my-ncurse-command > some-stream

Problem is : my ncurses display is captured by the pipe or the redirect, not only the final string.

Is there a way to allow that ? Thanks.

CodePudding user response:

Instead of initscr(), use newterm(). If you are already using newterm it's just a matter of supplying a different output stream than stdout.

initscr() is equivalent to:

#include <cstdlib>

WINDOW* myinitscr() {
    newterm(getenv("TERM"), stdout, stdin);
    return stdscr;
}

so

#include <cstdio>
#include <cstdlib>

std::FILE* cursesout{};
WINDOW* myinitscr() {
    cursesout = std::fopen("/dev/tty", "w");   // open new stream to your terminal
    if(cursesout) newterm(std::getenv("TERM"), cursesout, stdin);
    return stdscr;
}

and after endwin():

std::fclose(cursesout);

Alternatively, use a smart pointer to not have to std::fclose the new output stream manually:

#include <cstdio>
#include <cstdlib>
#include <memory>

using FILE_ptr = std::unique_ptr<FILE, decltype(&std::fclose)>;
FILE_ptr cursesout{nullptr, nullptr};

WINDOW* myinitscr() {
    cursesout = FILE_ptr(std::fopen("/dev/tty", "w"), &std::fclose);
    if(cursesout) newterm(std::getenv("TERM"), cursesout.get(), stdin);
    return stdscr;
}

A version not taking the address of a standard library function (which is strictly prohibited) could look like this:

#include <cstdio>
#include <cstdlib>
#include <memory>

using FILE_ptr = std::unique_ptr<FILE, void (*)(FILE*)>;
FILE_ptr cursesout{nullptr, nullptr};

WINDOW* myinitscr() {
    cursesout = FILE_ptr(std::fopen("/dev/tty", "w"), [](FILE* fp) {
        std::fclose(fp);
    });
    if(cursesout) newterm(std::getenv("TERM"), cursesout.get(), stdin);
    return stdscr;
}
  • Related