3

The description of the problems:

  • I code a program that prints out some information frequently.
  • I want to input some commands during the program running.
  • std::out will flush out my input.

For examples:

>>> ./my_program
[sensorA] initial...ok
[sensorB] initial...ok
ge //!< I want to input 'get' here but the next output break it
[motorA] self-check...ok
t //!< break it into two spice

Expected:

>>> ./my_program
[sensorA] initial...ok
[sensorB] initial...ok
[motorA] self-check...ok
get //!< always fixed here whenever I input

Thanks a lot !

LIU
  • 41
  • 6
  • @SamVarshavchik Okay, I am sorry and I make it correct now. – LIU Sep 16 '18 at 04:12
  • 5
    For something like this, operating system-specific features will need to be used. This is not covered by the C++ standard. On Linux, for example, using the curses library for all input and output should be sufficient. – Sam Varshavchik Sep 16 '18 at 04:13

2 Answers2

1

Appreciation

Firstly, I show my Great appreciation to Sam Varshavchik


Primary Result I found

Sam gave me the hints to use Curses Library. I read the doc and now finish the basic function.

The Result of the program

My method is to create to sub-windows(output_win and input_win). User input show in input_win whereas program information print on output_win.
Let me share my code:

#include <iostream>
#include <string>
#include <curses.h>
#include <thread>
#include <atomic>
#include <chrono>
#include <unistd.h>

using namespace std;

WINDOW* win;
WINDOW* output_win;
WINDOW* input_win;
int row = 0, col = 0;
std::atomic<bool> flag(false);
string buf;

void ninit()
{
    win = initscr();
    getmaxyx(win, row, col);

    cbreak();
    noecho();

    nonl();
    intrflush(stdscr, FALSE);
    keypad(stdscr, TRUE);
    refresh();
}

void nprintf(string str)
{
    touchwin(win);
    str += '\n';
    wprintw(output_win, str.c_str());
    wrefresh(output_win);
}

void nprintf(const char* fmt, ...)
{
    touchwin(win);

    va_list ap;
    va_start(ap, fmt);
    vw_printw(output_win, fmt, ap);
    va_end(ap);

    wrefresh(output_win);
}

void nmonitor()
{
    while(1)
    {
        char x = getch();

        if(x != '\r')
        {
            touchwin(win);
            buf += x;
            waddch(input_win, x);
        }
        else
        {
            nprintf(buf);
            touchwin(input_win);
            flag = true;
            wclear(input_win);
        }
        wrefresh(input_win);
    }
}

string nget()
{
    while(!flag)
        usleep(100);
    string cmd = buf;
    flag = false;
    buf = "";
    return cmd;
}

////////////////////////////////

void print_thread()
{
    while(1)
    {
        static int i = 0;
        nprintf("no.%d\n", i++);
        usleep(100000);
    }
}

int main()
{
    ninit();
    fflush(stdin);

    output_win = subwin(win, row - 1, col, 0, 0);
    scrollok(output_win, true);
    input_win = subwin(win, 1, col, row - 1, 0);

    std::thread pthr(print_thread);
    std::thread nthr(nmonitor);

    string cmd;
    while(1)
    {
        cmd = nget();
        if(cmd == "quit")
            break;
        else
            nprintf("[info] You input: %s\n", cmd.c_str());
    }

    getch();

    endwin();
}

Environment Configure and Build

For Mac OSX:

brew install ncurses

For Ubuntu:

sudo apt-get install libcurses5-dev

To build:

g++ f04.cpp - f04 -lcurses  # I try for 4 times so name it f04

Some bugs

Actually it has some bugs, here I found:

  • when you input backspace, it will not delete a char but show a special char;
  • after inputting enter, output_win sometimes show some strange words.

I am a beginner and may need help. (Maybe I will solve them soon.)

May it can help others indeed.

LIU
  • 41
  • 6
0

You can try the following:

  1. Before you print something, read from stdin everything the user entered so far.
  2. If there was something in stdin, print '\r' (so that the next output will overwrite the text entered by the user).
  3. Print your output.
  4. Print the text the user entered so far (but without '\n').

Please also see: Rewinding std::cout to go back to the beginning of a line