LIU
LIU

Reputation: 51

C++: how to input text in console and keep it at the bottom

The description of the problems:


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 !

Upvotes: 4

Views: 2518

Answers (2)

LIU
LIU

Reputation: 51

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.

Upvotes: 1

Andreas Kaufmann
Andreas Kaufmann

Reputation: 599

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

Upvotes: 0

Related Questions