简体   繁体   中英

How to direct NCurses output to a serial terminal on boot from Linux?

Overview

I have been writing code to display menus and screens using the ncurses++ library. The desired result is to have these menus and screens output across a serial terminal interface.

Current Attempts

I can successfully do this using the base C ncurses library using calls.

if( (FDTERM = fopen("/dev/ttyS0", "r+")) != NULL )
{

  if(FDTERM == NULL)
  {
    fprintf(stderr, "Error opening device: %s.\n", ncurses_device); 
  }

  /* Set Screen */
  MY_SCREEN = newterm(NULL, FDTERM, FDTERM);

  if(MY_SCREEN != NULL)
  {
    /* Set the terminal */
    set_term(MY_SCREEN);
  }
}

and for getting this to work in c++ I wrote some intercept.c code to override what the call to ::initscr() in cursesw.cc actually calls to

#define ncurses_device  "/dev/ttyS0"

NCURSES_EXPORT(WINDOW *) initscr(void)
{
  WINDOW *result;

pthread_mutex_lock(&CUSTOM_LOCK);

if (!CUSTOM_INITIALIZED)
{
  CUSTOM_INITIALIZED = true;

  if( (FDTERM = fopen(ncurses_device, "r+")) != NULL )
  {

  if(FDTERM == NULL)
  {
    fprintf(stderr, "Error opening device: %s.\n", ncurses_device); 
  }

  /* Set Screen */
  MY_SCREEN = newterm(NULL, FDTERM, FDTERM);

  if(MY_SCREEN != NULL)
  {
    /* Set the terminal */
    set_term(MY_CDU_SCREEN);
  }

  /* def_shell_mode - done in newterm/_nc_setupscreen */
  def_prog_mode();
}
else
{
  CUSTOM_INITIALIZED = true;
  NCURSES_CONST char *name;
  if ((name = getenv("TERM")) == 0 || *name == '\0')
  {
    static char unknown_name[] = "unknown";
    name = unknown_name;
  }

  if (newterm(name, stdout, stdin) == 0)
  {
    fprintf(stderr, "Error opening terminal: %s.\n", name);
    result = NULL;
  }
 }
}
#if NCURSES_SP_FUNCS
#ifdef CURRENT_SCREEN
 NCURSES_SP_NAME(def_prog_mode) (CURRENT_SCREEN);
#else
 NCURSES_SP_NAME(def_prog_mode) (SP);
#endif
#else
 def_prog_mode();
#endif
 result = stdscr;

 pthread_mutex_unlock(&CUSTOM_LOCK);

 return Win(result);
}

The intercept.c allows the device as defined to be used if available. It also allows initscr() to fall back to the default behavior of using the current terminal.

This works when used when for debugging, but I feel there has to be a better way to setup NCurses or the environment to direct NCurses output over the desired serial port.

The above solution now does not work when executing code on boot as there is not a terminal definition available.

This is being developed to support both RHEL 7 and 6. Some research seems to point to creating a new environment service using getty, agetty or by editing start-tty.conf as noted ( https://unix.stackexchange.com/a/318647 ).

But the other issue is pointing NCurses++ to output to the correct environment. From what I have seen in NCurses source code it appears that the ::initscr() is called by default from cursesw.cc, which makes it harder to just point NCurses to a new environment.

Questions

How do I setup NCurses++ to output to a specified tty?

How to properly setup an environment for NCurses to use on system boot?

Update 1:

Updated code to do the following:

// Save the current stdin/stdout file descriptors
int saved_stdin = dup(fileno(stdin));
int saved_stdout = dup(fileno(stdout));

// Set the stdin/stdout to the desired device
freopen(ncurses_device, "w+", stdout);
freopen(ncurses_device, "r+", stdin);

// Initialize the NCursesMenu
NCursesMenu *m_pMenu =  new NCursesMenu(m_pMenuItems);

// Restore the saved_stdin/stdout to the correct locations
dup2(saved_stdin, STDIN_FILENO);
dup2(saved_stdout, STDOUT_FILENO);

// Close the saved_stdin/stdout file descriptors
close(saved_stdin);
close(saved_stdout);

This allows NCurses to copy the current FILE* as defined here in newterm , but once the stdin/stdout FILE* is restored to the saved file descriptor the manipulation is lost. If the device is only pointed to the new device it works for NCurses, but all debug/test information is not visible because it is overwritten by NCurses in the current terminal.

initscr doesn't assume anything about the devices: it uses the current input/output stdin and stdout when initializing (and via newterm , setupterm , it copies the file-descriptors).

If your application (or environment/scripting) sets those streams to the device you'd like to connect to, that should be enough for the C++ interface. ncurses doesn't use the C+ cout , etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM