I'm using a console in my multithreaded application. Right now, it only accepts output (printf and the like) and so far I have no issues. However, I want to be able to support console input as well, and this is where my life gets complicated.
To forewarn, I'm very unfamiliar with the more complicated nuances of working with console input and output. My experience in the subject doesn't go much further than printf/cout, scanf/cin, and using SetConsoleTextAttribute() to change the color (on windows).
I would prefer to keep my program as cross-compatible as possible, but I'm not opposed to having to write platform-specific code, as long as I can find viable alternatives for other platforms.
Conceptually, I'd like the console to run on it's own thread, so that it can lock up while waiting with cin without freezing the whole program or one of the other threads. Any thread could send console output to this thread which would output it in a clean manner (probably using a thread-safe queue), and any input that the console reads would send the command off to the appropriate thread.
My first problem is that while I'm typing some input, any output will show up in the middle of what I'm typing. The solution I would like to handle this would be to reserve the bottom line of the console for input, and have output go to the second last line, pushing the input line down. How can I do this?
You really don't want to go down the road of trying to reserve part of the console for input while writing to the rest of the console. At least, not if you're just writing scrolling text. It's possible, but fraught with error and way more trouble than it's worth. See Async Console Output for a few hints of the problems.
Certainly, it's not possible to do this using just conio.h.
You could allocate two console screen buffers, with one being for input and one for program output. When your program is running normally, the output screen buffer is selected and you see the output scrolling on the screen. But when your program is waiting for user input, you swap screen buffers so that the output is still going, but in the other screen buffer.
You end up having to format the output yourself and call WriteConsoleOutput, passing it the handle of the screen buffer you want to write to. It gets complicated in a real hurry, and it's very difficult to get right. If it's even possible. I know I've spent way too much time on it in the past, and there were always odd problems.
I won't say that what you want to do isn't possible. I will say, however, that you're going to have a tough time with it.
Wellp, I solved it using pdcurses. In case someone else wants to do something similar, here's how I did it. First, I initialize the console thusly:
Console::Console(bool makeConsole)
{
if (makeConsole == false)
return;
if (self)
throw ("You only need one console - do not make another!\n");
self = this;
#ifdef WIN32
AllocConsole();
#endif
initscr();
inputLine = newwin(1, COLS, LINES - 1, 0);
outputLines = newwin(LINES - 1, COLS, 0, 0);
if (has_colors())
{
start_color();
for (int i = 1; i <= COLOR_WHITE; ++i)
{
init_pair(i, i, COLOR_BLACK);
}
}
else
wprintw(outputLines, "Terminal cannot print colors.\n");
scrollok(outputLines, TRUE);
scrollok(inputLine, TRUE);
leaveok(inputLine, TRUE);
nodelay(inputLine, TRUE);
cbreak();
noecho();
keypad(inputLine, TRUE);
initCommands();
hello("Starting %s.\n", APP_NAME);
hellomore("Version %i.%i.%i.\n\n", APP_MAJORVER, APP_MINORVER, APP_REVISION);
}
Next, This is the function responsible for handling output. It's actually very simple, I don't need to do anything special to keep it thread-safe. I might simply not have encountered any issues with it, but an easy fix would be to slap a mutex on it.
void Console::sendFormattedMsg(short prefixColor, const char* prefix, short color, const char* format, ...)
{
if (!self)
return;
va_list args;
va_start(args, format);
if (has_colors())
{
if (prefix)
{
wattron(outputLines, A_BOLD | COLOR_PAIR(prefixColor));
wprintw(outputLines, prefix);
}
if (color == COLOR_WHITE)
wattroff(outputLines, A_BOLD);
wattron(outputLines, COLOR_PAIR(color));
vwprintw(outputLines, format, args);
wattroff(outputLines, A_BOLD | COLOR_PAIR(color));
}
else
{
wprintw(outputLines, prefix);
vwprintw(outputLines, format, args);
}
wrefresh(outputLines);
va_end(args);
}
And finally, input. This one required quite a bit of fine-tuning.
void Console::inputLoop(void)
{
static string input;
wattron(inputLine, A_BOLD | COLOR_PAIR(COLOR_WHITE));
wprintw(inputLine, "\n> ");
wattroff(inputLine, A_BOLD | COLOR_PAIR(COLOR_WHITE));
wprintw(inputLine, input.c_str());
wrefresh(inputLine);
char c = wgetch(inputLine);
if (c == ERR)
return;
switch (c)
{
case '\n':
if (input.size() > 0)
{
sendFormattedMsg(COLOR_WHITE, "> ", COLOR_WHITE, input.c_str());
cprint("\n");
executeCommand(&input[0]);
input.clear();
}
break;
case 8:
case 127:
if (input.size() > 0) input.pop_back();
break;
default:
input += c;
break;
}
}
This is run every frame from the same thread that handles window messages. I disabled wgetch()'s blocking behavior using nodelay(), eliminating the need to have console input running in it's own thread. I also disable echoing and echo the input manually. Enabling scrolling on the input window allows me to clear it's contents using a simple "\n", replacing it with updated contents if the user has typed anything. It supports everything one would expect from a simple, multi-threaded terminal capable to typing input as well as receiving output from multiple threads.
To disable echoing characters check this out:
Reading a password from std::cin
Maybe combine that with this guy's blog post on non-blocking Win32 console io.
You might also find this stuff useful:
conio.h,
pdcurses
Related
I'm learning c++ again after having not touched it for the last few years, and I've run into a rather peculiar bug that I can't seem to figure out.
When I run the code below, it will accept 10 inputs as I expect it to, but immediately after the first for loop, the program exits. I have run it ingdbto try and figure out the issue, but it reported that the process 'exited normally'.
I compiled using g++ -std=c++11
#include <iostream>
#include <string>
using namespace std;
int main() {
//User inputs
string input[10];
//Get the inputs
for(int i = 0; i < 10; i++) {
//Get this input
printf("%i> ", i);
getline(cin, input[i]);
}
//The application does not make it to this point
//Collected
printf("Thank you for submitting your data.\n");
//Print inputs
for(int a = 0; a < 10; a++) {
//Show the input
printf("%i> %s\n", a, input[a].c_str());
}
}
Based on what you're describing, it sounds like stdout is not being flushed before the program ends. That's unusual; normally, stdout is automatically set up for line-buffered operation in which case it will be flushed as soon as a newline is encountered.
Your best bet is to follow #NathanOliver's advice and use cout << ... rather than printf. The printf command is a throwback to C, and you're using a C++ compiler and C++ features. In fact, you're not even including the header that's usually required for printf, so I'm a little surprised it even compiles.
FWIW, if you choose to continue using printf maybe try manually flushing stdout at the end like so:
fflush(stdout);
Your application does what is supposed to do:
Application will pause at getline() (10 times) (because getline is blocking execution), then it will do some for loops, print something and end (probably closing console window). If you add something at the end to block execution (such as cin.get(), which waits for enter key press) you will see results (because application won't terminate). If you run your code somewhere where output is not removed after program has ended you will see what was printed.
As of now, I'm making a program with different screens that use the escape key to exit, but what happens is if I press escape in, lets say the option menu. And then I go into the game, which the game allows escape to exit out, it will automatically leave the game. If that makes sense. :), but, it seems like the escape key floats around in the input buffer, and I tried many ways to clear it but I can't find a way. Here is a part of my code.
int Controls()
{
// Allows us to get a key when pressed
int Key;
Key = _getch();
switch(Key)
{
// Number 27
case KEY_ESCAPE:
do code...
break;
}
return Key;
}
Try doing a
fflush(stdin);
whenever you transition to any new page. It clears the input stream.
I'm new to C/C++ and I'm making a simple text user interface with NCurses.
Whenever I scroll up/down with the mouse wheel, or press arrow keys, the console echos characters like:
"[[A^[[C^[[B^[[D"
to show me that I've pressed the keys.
I would like to stop these from echoing and only echo basic keys (punctuation and letters).
Here is my main loop. I basically want it to be my own console with commands that I create.
string input;
char inputArr[80];
while (input != "q" && input != "quit" && input != "exit" && input != "leave") {
printw(" > ");
refresh();
getstr(inputArr);
input = inputArr;
if (input.substr(0, 3) != "someCommand") {
printw("\n ~ %s\n\n", inputArr);
refresh();
} else
execCmd();
}
quit();
For the most part I believe it is C++ but I do have a C function (that uses libCurl).
Also, is there no cleaner way to read in strings with NCurses? I dont really like using char arrays (I'm used to Java).
Call noecho() somewhere close to initscr().
This will avoid clobbering your screen with unwanted input. If you want your users to see what they type later on, you will need to call echo() before, however.
I have 2 threads: one of them is constantly cout'ing to the console some value, let's say increments an int value every second - so every second on the console is 1,2,3... and so on.
Another thread is waiting for user input - with the command cin.
Here is my problem: when I start typing something, when the time comes to cout the int value, my input gets erased from the input field, and put into the console with the int value. So when I want to type in "hello" it looks something like this:
1
2
3
he4
l5
lo6
7
8
Is there a way to prevent my input from getting put to the console, while other thread is writing to the console?
FYI this is needed for a chat app at client side - one thread is listening for messages and outputs this message as soon as it comes in, and the other thread is listening for user input to be sent to a server app.
Usually the terminal itself echos the keys typed. You can turn this off and get your program to echo it. This question will give you pointers on how to do it Hide password input on terminal
You can then just get the one thread to handle output.
If you are a slow typer, then the solution to your problem can be, as I said, making it a single thread, but that may make the app to receive only after it sends.
Another way is to increase your receiving thread's sleep time, which would provide you some more time to type (without interruption)
You could make a GUI (or use ncurses if you really want to work in the console). This way you avoid having std::cout shared by the threads.
I think you could solve this problem with a semaphore. When you have an incoming message you check to see if the user is writing something. If he does you wait until he finishes to print the message.
Is there a way to prevent my input from getting put to the console, while other thread is writing to the console?
It is the other way around. The other thread shouldn't interrupt the display of what you are typing.
Say you have typed "Hel" and then a new message comes in from the other thread. What do you do? How should it be displayed?
Totally disable echoing of what you type and only display it after you hit enter. In this way you can display messages from the different threads properly, in an atomic fashion. The big drawback is that you cannot see what you have typed already... :(
You immediately echo what you type. When the new message comes in, you undo the "Hel", print the new message and print again "Hel" on a new line and you can continue typing. Doable but a bit ugly.
You echo what you type in a separate place. That is, you split somehow the display. In one place you display the posted/received messages in order; and in another place you display what you are typing. You either need a GUI or at least some console library to do this. This would be the nicest solution but perhaps the most difficult to port to another OS due to the library dependencies.
In any case, you need a (preferably internally) synchronized stream that you can safely call from different threads and can write strings into it atomically. That is, you need to write your own synchronized stream class.
Hope this helps.
Well i recently solved this same issue with a basic workaround. This might not be the #1 solution but worked like a charm for me, as a newbie;
#include <iostream> // I/O
#include <Windows.h> // Sleep();
#include <conio.h> // _getch();
#include <string> // MessageBuffer
#include <thread> // Thread
using namespace std;
void ThreadedOutput();
string MessageBuffer; // or make it static
void main()
{
thread output(ThreadedOutput); // Attach the output thread
int count = 0;
char cur = 'a'; // Temporary at start
while (cur != '\r')
{
cur = _getch(); // Take 1 input
if (cur >= 32 && cur <= 126) // Check if input lies in alphanumeric and special keys
{
MessageBuffer += cur; // Store input in buffer
cout << cur; // Output the value user entered
count++;
}
else if (cur == 8) // If input key was backspace
{
cout << "\b \b"; // Move cursor 1 step back, overwrite previous character with space, move cursor 1 step back
MessageBuffer = MessageBuffer.substr(0, MessageBuffer.size() - 1); // Remove last character from buffer
count--;
}
else if (cur == 13) // If input was 'return' key
{
for (int i = 0; i < (signed)MessageBuffer.length(); i++) // Remove the written input
cout << "\b \b";
// "MessageBuffer" has your input, use it somewhere
MessageBuffer = ""; // Clear the buffer
}
}
output.join(); // Join the thread
}
void ThreadedOutput()
{
int i = 0;
while (true)
{
for (int i = 0; i < (signed)MessageBuffer.length(); i++) // Remove the written input
cout << "\b \b";
cout << ++i << endl; // Give parallel output with input
cout << MessageBuffer; // Rewrite the stored buffer
Sleep(1000); // Prevent this example spam
}
}
Does anyone know how to make an event loop in c++ without a library? It doesn't have to be cross-platform, I'm on a Mac. Basically, I want the program to run and do nothing until the user presses the up arrow key, then the program will output "You pressed up" or something. All i can think of is having an infinite while or for loop and get input with cin, but I don't think cin can detect arrow keys and I believe it pauses the program until it reaches a '\n';
I would want it to look like this:
void RUN()
{
while(true)
{
// poll events and do something if needed
}
}
int main()
{
RUN();
}
I'm kinda sure it's possible without threads, and I've heard that this can be accomplished with fd_set or something, but I'm not sure how.
Any help would be really appreciated.
EDIT:
The program has to run in the background when there aren't any events. For example, Microsoft Word doesn't stop until the user presses a button, it keeps running. I want something like that, but command-line not GUI.
Since you're talking keyboard input, and not looking for a Mac look and feel, what you want is the UNIX way of doing it. And that is,
1) set the terminal in either raw or cbrk mode (I forget which).
2) now use read() to read single characters at a time.
3) temporarily echo the character read (as an int) so you can find what the up arrow key gives you.
As for the more general event loop question, where the only input device is the keyboard, you sit in a loop, and whenever a key is typed (in raw mode?) you call a routine with the value of the key typed. If you had more input devices, you would need multiple threads each could listen to a different device, putting what they find on a queues (with appropriate locking). The main loop would then check the queue and call a routine appropriately everytime something appears in it.
You can use ncurses and enable cbreak to get the raw input stream.
I've used a while loop with signal handlers. Like this incomplete snippet.
void getSomething()
{
std::cout << "Enter new step size: "; std::cout.flush();
std::cin >> globalVariable;
std::getchar(); // consume enter key.
}
void printCommands()
{
std::cout << "1: do something\n"
<< "q: quit\n"
<< "h: help\n"
<< std::endl;
}
void getCommand()
{
// Output prompt
std::cout << "Enter command ('h' for help): "; std::cout.flush();
// Set terminal to raw mode
int ret = system("stty raw");
// Wait for single character
char input = std::getchar();
// Reset terminal to normal "cooked" mode
ret = system("stty cooked");
std::cout << std::endl;
if (input == 'h') printCommands();
else if (input == '1') getSomething();
else if (input == 'q') {
g_next = true;
g_quit = true;
}
}
void
signalHandler(int signo)
{
if (signo == SIGINT) {
g_next = true;
} else if (signo == SIGQUIT) {
getCommand();
}
}
int main(int argc, char* argv[])
{
signal(SIGINT, signalHandler);
signal(SIGUSR1, signalHandler);
signal(SIGQUIT, signalHandler);
do {
// Stuff
} while (!g_quit);
exit(0);
}
The question has been updated to say "The program has to run in the background ... but command-line not GUI."
All traditional; *NIX shells that can put a program into the background also disconnect the program's standard input from the terminal, so AFAIK, this has become impossible.
This does not need to be Mac specific. The Mac supports *NIX mechanisms for reading characters from a keyboard.
AFAICT all the program is doing is waiting for a character, so it might as well block.
Normally the terminal device, tty (teletype!), is interpreting characters typed on the keyboard before your program can read them from standard input. Specifically the tty device normally buffers an entire line of text, and intercepts the rubout character (and a few others like CTRL+w) to edit the line of text. This pre-processing of characters is called a 'line discipline'
You need to set the tty device driver to stop doing that! Then you can get all of the characters the user types.
You change the device using ioctl or termios on the file descriptor.
Search for e.g. "ioctl tty line discipline raw" to understand the details, and find program examples.
You can set the terminal to 'raw' using the command line program stty.
Please read the stty man page because setting it back can be slightly tricky (NB: if you make a mistake it is often easier to kill the terminal, than try to fix it, because there is not echoing of anything you type)
It is possible that the up-arrow is not a single char, so it will require some byte-at-a-time decoding to avoid blocking at the wrong point in the input stream, i.e. if some input sequences are one character, and others two, or three characters, the decoding needs to happen at each byte to decide if there is a pending byte, or one too many read's might get issued, which would cause the program to block.